mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Folders
This commit is contained in:
parent
60db6559ea
commit
a78924ccd9
@ -88,6 +88,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ChatListTitleView",
|
||||
"//submodules/AvatarNode:AvatarNode",
|
||||
"//submodules/AvatarVideoNode:AvatarVideoNode",
|
||||
"//submodules/InviteLinksUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -14,6 +14,7 @@ import ItemListPeerActionItem
|
||||
import AvatarNode
|
||||
import ChatListFilterSettingsHeaderItem
|
||||
import PremiumUI
|
||||
import InviteLinksUI
|
||||
|
||||
private enum FilterSection: Int32, Hashable {
|
||||
case include
|
||||
@ -32,6 +33,8 @@ private final class ChatListFilterPresetControllerArguments {
|
||||
let deleteExcludeCategory: (ChatListFilterExcludeCategory) -> Void
|
||||
let focusOnName: () -> Void
|
||||
let expandSection: (FilterSection) -> Void
|
||||
let createLink: () -> Void
|
||||
let openLink: (ExportedChatFolderLink) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
@ -44,7 +47,9 @@ private final class ChatListFilterPresetControllerArguments {
|
||||
deleteIncludeCategory: @escaping (ChatListFilterIncludeCategory) -> Void,
|
||||
deleteExcludeCategory: @escaping (ChatListFilterExcludeCategory) -> Void,
|
||||
focusOnName: @escaping () -> Void,
|
||||
expandSection: @escaping (FilterSection) -> Void
|
||||
expandSection: @escaping (FilterSection) -> Void,
|
||||
createLink: @escaping () -> Void,
|
||||
openLink: @escaping (ExportedChatFolderLink) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.updateState = updateState
|
||||
@ -57,6 +62,8 @@ private final class ChatListFilterPresetControllerArguments {
|
||||
self.deleteExcludeCategory = deleteExcludeCategory
|
||||
self.focusOnName = focusOnName
|
||||
self.expandSection = expandSection
|
||||
self.createLink = createLink
|
||||
self.openLink = openLink
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +72,7 @@ private enum ChatListFilterPresetControllerSection: Int32 {
|
||||
case name
|
||||
case includePeers
|
||||
case excludePeers
|
||||
case inviteLinks
|
||||
}
|
||||
|
||||
private enum ChatListFilterPresetEntryStableId: Hashable {
|
||||
@ -76,6 +84,7 @@ private enum ChatListFilterPresetEntryStableId: Hashable {
|
||||
case excludeCategory(ChatListFilterExcludeCategory)
|
||||
case includeExpand
|
||||
case excludeExpand
|
||||
case inviteLink(String)
|
||||
}
|
||||
|
||||
private enum ChatListFilterPresetEntrySortId: Comparable {
|
||||
@ -83,6 +92,9 @@ private enum ChatListFilterPresetEntrySortId: Comparable {
|
||||
case topIndex(Int)
|
||||
case includeIndex(Int)
|
||||
case excludeIndex(Int)
|
||||
case bottomIndex(Int)
|
||||
case inviteLink(Int)
|
||||
case inviteLinkFooter
|
||||
|
||||
static func <(lhs: ChatListFilterPresetEntrySortId, rhs: ChatListFilterPresetEntrySortId) -> Bool {
|
||||
switch lhs {
|
||||
@ -103,6 +115,12 @@ private enum ChatListFilterPresetEntrySortId: Comparable {
|
||||
return true
|
||||
case .excludeIndex:
|
||||
return true
|
||||
case .bottomIndex:
|
||||
return true
|
||||
case .inviteLink:
|
||||
return true
|
||||
case .inviteLinkFooter:
|
||||
return true
|
||||
}
|
||||
case let .includeIndex(lhsIndex):
|
||||
switch rhs {
|
||||
@ -114,6 +132,12 @@ private enum ChatListFilterPresetEntrySortId: Comparable {
|
||||
return lhsIndex < rhsIndex
|
||||
case .excludeIndex:
|
||||
return true
|
||||
case .bottomIndex:
|
||||
return true
|
||||
case .inviteLink:
|
||||
return true
|
||||
case .inviteLinkFooter:
|
||||
return true
|
||||
}
|
||||
case let .excludeIndex(lhsIndex):
|
||||
switch rhs {
|
||||
@ -125,6 +149,63 @@ private enum ChatListFilterPresetEntrySortId: Comparable {
|
||||
return false
|
||||
case let .excludeIndex(rhsIndex):
|
||||
return lhsIndex < rhsIndex
|
||||
case .bottomIndex:
|
||||
return true
|
||||
case .inviteLink:
|
||||
return true
|
||||
case .inviteLinkFooter:
|
||||
return true
|
||||
}
|
||||
case let .bottomIndex(lhsIndex):
|
||||
switch rhs {
|
||||
case .screenHeader:
|
||||
return false
|
||||
case .topIndex:
|
||||
return false
|
||||
case .includeIndex:
|
||||
return false
|
||||
case .excludeIndex:
|
||||
return false
|
||||
case let .bottomIndex(rhsIndex):
|
||||
return lhsIndex < rhsIndex
|
||||
case .inviteLink:
|
||||
return true
|
||||
case .inviteLinkFooter:
|
||||
return true
|
||||
}
|
||||
case let .inviteLink(lhsIndex):
|
||||
switch rhs {
|
||||
case .screenHeader:
|
||||
return false
|
||||
case .topIndex:
|
||||
return false
|
||||
case .includeIndex:
|
||||
return false
|
||||
case .excludeIndex:
|
||||
return false
|
||||
case .bottomIndex:
|
||||
return false
|
||||
case let .inviteLink(rhsIndex):
|
||||
return lhsIndex < rhsIndex
|
||||
case .inviteLinkFooter:
|
||||
return false
|
||||
}
|
||||
case .inviteLinkFooter:
|
||||
switch rhs {
|
||||
case .screenHeader:
|
||||
return false
|
||||
case .topIndex:
|
||||
return false
|
||||
case .includeIndex:
|
||||
return false
|
||||
case .excludeIndex:
|
||||
return false
|
||||
case .bottomIndex:
|
||||
return false
|
||||
case .inviteLink:
|
||||
return false
|
||||
case .inviteLinkFooter:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -235,6 +316,10 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
case excludePeerInfo(String)
|
||||
case includeExpand(String)
|
||||
case excludeExpand(String)
|
||||
case inviteLinkHeader
|
||||
case inviteLinkCreate
|
||||
case inviteLink(Int, ExportedChatFolderLink)
|
||||
case inviteLinkInfo
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -246,6 +331,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
return ChatListFilterPresetControllerSection.includePeers.rawValue
|
||||
case .excludePeersHeader, .addExcludePeer, .excludeCategory, .excludePeer, .excludePeerInfo, .excludeExpand:
|
||||
return ChatListFilterPresetControllerSection.excludePeers.rawValue
|
||||
case .inviteLinkHeader, .inviteLinkCreate, .inviteLink, .inviteLinkInfo:
|
||||
return ChatListFilterPresetControllerSection.inviteLinks.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,6 +368,14 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
return .peer(peer.peerId)
|
||||
case let .excludePeer(_, peer, _):
|
||||
return .peer(peer.peerId)
|
||||
case .inviteLinkHeader:
|
||||
return .index(11)
|
||||
case .inviteLinkCreate:
|
||||
return .index(12)
|
||||
case let .inviteLink(_, link):
|
||||
return .inviteLink(link.link)
|
||||
case .inviteLinkInfo:
|
||||
return .index(13)
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,6 +411,14 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
return .excludeIndex(999)
|
||||
case .excludePeerInfo:
|
||||
return .excludeIndex(1000)
|
||||
case .inviteLinkHeader:
|
||||
return .bottomIndex(0)
|
||||
case .inviteLinkCreate:
|
||||
return .bottomIndex(1)
|
||||
case let .inviteLink(index, _):
|
||||
return .inviteLink(index)
|
||||
case .inviteLinkInfo:
|
||||
return .inviteLinkFooter
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,6 +516,23 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(presentationData.theme), title: text, sectionId: self.section, editing: false, action: {
|
||||
arguments.expandSection(.exclude)
|
||||
})
|
||||
case .inviteLinkHeader:
|
||||
//TODO:localize
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: "INVITE LINK", sectionId: self.section)
|
||||
case .inviteLinkCreate:
|
||||
//TODO:localize
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "Share Folder with Others", sectionId: self.section, editing: false, action: {
|
||||
arguments.createLink()
|
||||
})
|
||||
case let .inviteLink(_, link):
|
||||
return ItemListFolderInviteLinkListItem(presentationData: presentationData, invite: link, share: false, sectionId: self.section, style: .blocks) { invite in
|
||||
arguments.openLink(invite)
|
||||
} contextAction: { invite, node, gesture in
|
||||
//arguments.linkContextAction(invite, canEdit, node, gesture)
|
||||
}
|
||||
case .inviteLinkInfo:
|
||||
//TODO:localize
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown("Give vour friends and colleagues access to the entire folder including all of its groups and channels where you have the necessary rights."), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -455,7 +575,7 @@ private struct ChatListFilterPresetControllerState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, state: ChatListFilterPresetControllerState, includePeers: [EngineRenderedPeer], excludePeers: [EngineRenderedPeer], isPremium: Bool, limit: Int32) -> [ChatListFilterPresetEntry] {
|
||||
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, isNewFilter: Bool, state: ChatListFilterPresetControllerState, includePeers: [EngineRenderedPeer], excludePeers: [EngineRenderedPeer], isPremium: Bool, limit: Int32, inviteLinks: [ExportedChatFolderLink]?) -> [ChatListFilterPresetEntry] {
|
||||
var entries: [ChatListFilterPresetEntry] = []
|
||||
|
||||
if isNewFilter {
|
||||
@ -531,6 +651,19 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio
|
||||
|
||||
entries.append(.excludePeerInfo(presentationData.strings.ChatListFolder_ExcludeSectionInfo))
|
||||
|
||||
if !isNewFilter, let inviteLinks {
|
||||
entries.append(.inviteLinkHeader)
|
||||
entries.append(.inviteLinkCreate)
|
||||
|
||||
var index = 0
|
||||
for link in inviteLinks {
|
||||
entries.append(.inviteLink(index, link))
|
||||
index += 1
|
||||
}
|
||||
|
||||
entries.append(.inviteLinkInfo)
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
@ -887,7 +1020,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var includePeers = ChatListFilterIncludePeers()
|
||||
includePeers.setPeers(state.additionallyIncludePeers)
|
||||
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
if let data = filter.data {
|
||||
switch chatListFilterType(data) {
|
||||
case .generic:
|
||||
@ -924,6 +1057,12 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
var dismissImpl: (() -> Void)?
|
||||
var focusOnNameImpl: (() -> Void)?
|
||||
|
||||
let sharedLinks = Promise<[ExportedChatFolderLink]?>(nil)
|
||||
if let currentPreset {
|
||||
sharedLinks.set(context.engine.peers.getExportedChatLinks(id: currentPreset.id)
|
||||
|> map(Optional.init))
|
||||
}
|
||||
|
||||
let currentPeers = Atomic<[PeerId: EngineRenderedPeer]>(value: [:])
|
||||
let stateWithPeers = statePromise.get()
|
||||
|> mapToSignal { state -> Signal<(ChatListFilterPresetControllerState, [EngineRenderedPeer], [EngineRenderedPeer]), NoError> in
|
||||
@ -1025,7 +1164,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
let state = stateValue.with { $0 }
|
||||
var includePeers = ChatListFilterIncludePeers()
|
||||
includePeers.setPeers(state.additionallyIncludePeers)
|
||||
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
|
||||
let _ = (context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { filters in
|
||||
@ -1047,7 +1186,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
let state = stateValue.with { $0 }
|
||||
var includePeers = ChatListFilterIncludePeers()
|
||||
includePeers.setPeers(state.additionallyIncludePeers)
|
||||
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
|
||||
let _ = (context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { filters in
|
||||
@ -1124,6 +1263,24 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
state.expandedSections.insert(section)
|
||||
return state
|
||||
}
|
||||
},
|
||||
createLink: {
|
||||
if let currentPreset, let data = currentPreset.data, !data.includePeers.peers.isEmpty {
|
||||
pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, allPeerIds: data.includePeers.peers, currentInvitation: nil))
|
||||
|
||||
/*if data.isShared {
|
||||
|
||||
} else {
|
||||
let _ = (context.engine.peers.exportChatFolder(filterId: currentPreset.id, title: "Link", peerIds: data.includePeers.peers)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
dismissImpl?()
|
||||
})
|
||||
}*/
|
||||
}
|
||||
}, openLink: { link in
|
||||
if let currentPreset, let data = currentPreset.data {
|
||||
pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, allPeerIds: data.includePeers.peers, currentInvitation: link))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@ -1138,7 +1295,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
if currentPreset == nil {
|
||||
filterId = context.engine.peers.generateNewChatListFilterId(filters: filters)
|
||||
}
|
||||
var updatedFilter: ChatListFilter = .filter(id: filterId, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
var updatedFilter: ChatListFilter = .filter(id: filterId, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
|
||||
var filters = filters
|
||||
if let _ = currentPreset {
|
||||
@ -1182,10 +1339,11 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
context.account.postbox.peerView(id: context.account.peerId),
|
||||
context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
|
||||
)
|
||||
),
|
||||
sharedLinks.get()
|
||||
)
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, stateWithPeers, peerView, premiumLimits -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|> map { presentationData, stateWithPeers, peerView, premiumLimits, sharedLinks -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let (state, includePeers, excludePeers) = stateWithPeers
|
||||
|
||||
let isPremium = peerView.peers[peerView.peerId]?.isPremium ?? false
|
||||
@ -1206,7 +1364,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(currentPreset != nil ? presentationData.strings.ChatListFolder_TitleEdit : presentationData.strings.ChatListFolder_TitleCreate), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, isNewFilter: currentPreset == nil, state: state, includePeers: includePeers, excludePeers: excludePeers, isPremium: isPremium, limit: premiumLimits.maxFolderChatsCount), style: .blocks, emptyStateItem: nil, animateChanges: !skipStateAnimation)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, isNewFilter: currentPreset == nil, state: state, includePeers: includePeers, excludePeers: excludePeers, isPremium: isPremium, limit: premiumLimits.maxFolderChatsCount, inviteLinks: sharedLinks), style: .blocks, emptyStateItem: nil, animateChanges: !skipStateAnimation)
|
||||
skipStateAnimation = false
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
@ -1261,7 +1419,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
|
||||
var includePeers = ChatListFilterIncludePeers()
|
||||
includePeers.setPeers(state.additionallyIncludePeers)
|
||||
let filter: ChatListFilter = .filter(id: currentPreset.id, title: state.name, emoticon: currentPreset.emoticon, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
let filter: ChatListFilter = .filter(id: currentPreset.id, title: state.name, emoticon: currentPreset.emoticon, data: ChatListFilterData(isShared: currentPreset.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
|
||||
if currentPresetWithoutPinnedPeers != filter {
|
||||
displaySaveAlert()
|
||||
return false
|
||||
|
@ -56,8 +56,8 @@ private enum ChatListFilterPresetListEntryStableId: Hashable {
|
||||
case suggestedPreset(ChatListFilterData)
|
||||
case suggestedAddCustom
|
||||
case listHeader
|
||||
case preset(Int32)
|
||||
case addItem
|
||||
case preset(Int32)
|
||||
case listFooter
|
||||
}
|
||||
|
||||
@ -75,7 +75,6 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
case suggestedPreset(index: PresetIndex, title: String, label: String, preset: ChatListFilterData)
|
||||
case suggestedAddCustom(String)
|
||||
case listHeader(String)
|
||||
case addFolder
|
||||
case preset(index: PresetIndex, title: String, label: String, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool, isAllChats: Bool, isDisabled: Bool)
|
||||
case addItem(text: String, isEditing: Bool)
|
||||
case listFooter(String)
|
||||
@ -86,7 +85,7 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
return ChatListFilterPresetListSection.screenHeader.rawValue
|
||||
case .suggestedListHeader, .suggestedPreset, .suggestedAddCustom:
|
||||
return ChatListFilterPresetListSection.suggested.rawValue
|
||||
case .listHeader, .addFolder, .preset, .addItem, .listFooter:
|
||||
case .listHeader, .preset, .addItem, .listFooter:
|
||||
return ChatListFilterPresetListSection.list.rawValue
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,471 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import OverlayStatusController
|
||||
import AccountContext
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import AppBundle
|
||||
import ContextUI
|
||||
import TelegramStringFormatting
|
||||
import ItemListPeerActionItem
|
||||
import ItemListPeerItem
|
||||
import ShareController
|
||||
import UndoUI
|
||||
import QrCodeUI
|
||||
|
||||
private final class FolderInviteLinkListControllerArguments {
|
||||
let context: AccountContext
|
||||
let shareMainLink: (String) -> Void
|
||||
let openMainLink: (String) -> Void
|
||||
let copyLink: (String) -> Void
|
||||
let mainLinkContextAction: (String?, ASDisplayNode, ContextGesture?) -> Void
|
||||
let peerAction: (EnginePeer) -> Void
|
||||
let generateLink: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
shareMainLink: @escaping (String) -> Void,
|
||||
openMainLink: @escaping (String) -> Void,
|
||||
copyLink: @escaping (String) -> Void,
|
||||
mainLinkContextAction: @escaping (String?, ASDisplayNode, ContextGesture?) -> Void,
|
||||
peerAction: @escaping (EnginePeer) -> Void,
|
||||
generateLink: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.shareMainLink = shareMainLink
|
||||
self.openMainLink = openMainLink
|
||||
self.copyLink = copyLink
|
||||
self.mainLinkContextAction = mainLinkContextAction
|
||||
self.peerAction = peerAction
|
||||
self.generateLink = generateLink
|
||||
}
|
||||
}
|
||||
|
||||
private enum InviteLinksListSection: Int32 {
|
||||
case header
|
||||
case mainLink
|
||||
case peers
|
||||
}
|
||||
|
||||
private enum InviteLinksListEntry: ItemListNodeEntry {
|
||||
enum StableId: Hashable {
|
||||
case index(Int)
|
||||
case peer(EnginePeer.Id)
|
||||
}
|
||||
|
||||
case header(String)
|
||||
|
||||
case mainLinkHeader(String)
|
||||
case mainLink(link: ExportedChatFolderLink?, isGenerating: Bool)
|
||||
|
||||
case peersHeader(String)
|
||||
case peer(index: Int, peer: EnginePeer, isSelected: Bool)
|
||||
case peersInfo(String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .header:
|
||||
return InviteLinksListSection.header.rawValue
|
||||
case .mainLinkHeader, .mainLink:
|
||||
return InviteLinksListSection.mainLink.rawValue
|
||||
case .peersHeader, .peer, .peersInfo:
|
||||
return InviteLinksListSection.peers.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: StableId {
|
||||
switch self {
|
||||
case .header:
|
||||
return .index(0)
|
||||
case .mainLinkHeader:
|
||||
return .index(1)
|
||||
case .mainLink:
|
||||
return .index(2)
|
||||
case .peersHeader:
|
||||
return .index(4)
|
||||
case .peersInfo:
|
||||
return .index(5)
|
||||
case let .peer(_, peer, _):
|
||||
return .peer(peer.id)
|
||||
}
|
||||
}
|
||||
|
||||
var sortIndex: Int {
|
||||
switch self {
|
||||
case .header:
|
||||
return 0
|
||||
case .mainLinkHeader:
|
||||
return 1
|
||||
case .mainLink:
|
||||
return 2
|
||||
case .peersHeader:
|
||||
return 4
|
||||
case let .peer(index, _, _):
|
||||
return 10 + index
|
||||
case .peersInfo:
|
||||
return 1000
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: InviteLinksListEntry, rhs: InviteLinksListEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .header(text):
|
||||
if case .header(text) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .mainLinkHeader(text):
|
||||
if case .mainLinkHeader(text) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .mainLink(lhsLink, lhsIsGenerating):
|
||||
if case let .mainLink(rhsLink, rhsIsGenerating) = rhs, lhsLink == rhsLink, lhsIsGenerating == rhsIsGenerating {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peersHeader(text):
|
||||
if case .peersHeader(text) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peersInfo(text):
|
||||
if case .peersInfo(text) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peer(index, peer, isSelected):
|
||||
if case .peer(index, peer, isSelected) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: InviteLinksListEntry, rhs: InviteLinksListEntry) -> Bool {
|
||||
return lhs.sortIndex < rhs.sortIndex
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! FolderInviteLinkListControllerArguments
|
||||
switch self {
|
||||
case let .header(text):
|
||||
return InviteLinkHeaderItem(context: arguments.context, theme: presentationData.theme, text: text, animationName: "ChatListNewFolder", sectionId: self.section)
|
||||
case let .mainLinkHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .mainLink(link, isGenerating):
|
||||
return ItemListFolderInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: link, count: 0, peers: [], displayButton: true, enableButton: !isGenerating, buttonTitle: link != nil ? "Share Invite Link" : "Generate Invite Link", displayImporters: false, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||
if let link {
|
||||
arguments.copyLink(link.link)
|
||||
}
|
||||
}, shareAction: {
|
||||
if let link {
|
||||
arguments.shareMainLink(link.link)
|
||||
} else {
|
||||
arguments.generateLink()
|
||||
}
|
||||
}, contextAction: { node, gesture in
|
||||
arguments.mainLinkContextAction(link?.link, node, gesture)
|
||||
}, viewAction: {
|
||||
if let link {
|
||||
arguments.openMainLink(link.link)
|
||||
}
|
||||
})
|
||||
case let .peersHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .peersInfo(text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
||||
case let .peer(_, peer, isSelected):
|
||||
return ItemListPeerItem(
|
||||
presentationData: presentationData,
|
||||
dateTimeFormat: PresentationDateTimeFormat(),
|
||||
nameDisplayOrder: presentationData.nameDisplayOrder,
|
||||
context: arguments.context,
|
||||
peer: peer,
|
||||
presence: nil,
|
||||
text: .none,
|
||||
label: .none,
|
||||
editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false),
|
||||
switchValue: ItemListPeerItemSwitch(value: isSelected, style: .leftCheck),
|
||||
enabled: true,
|
||||
selectable: true,
|
||||
sectionId: self.section,
|
||||
action: {
|
||||
arguments.peerAction(peer)
|
||||
},
|
||||
setPeerIdWithRevealedOptions: { _, _ in
|
||||
},
|
||||
removePeer: { _ in
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func folderInviteLinkListControllerEntries(
|
||||
presentationData: PresentationData,
|
||||
state: FolderInviteLinkListControllerState,
|
||||
allPeers: [EnginePeer]
|
||||
) -> [InviteLinksListEntry] {
|
||||
var entries: [InviteLinksListEntry] = []
|
||||
|
||||
entries.append(.header("Anyone with this link can add Gaming Club folder and the 2 chats selected below."))
|
||||
|
||||
//TODO:localize
|
||||
|
||||
entries.append(.mainLinkHeader("INVITE LINK"))
|
||||
entries.append(.mainLink(link: state.currentLink, isGenerating: state.generatingLink))
|
||||
|
||||
entries.append(.peersHeader("\(allPeers.count) CHATS SELECTED"))
|
||||
for peer in allPeers {
|
||||
entries.append(.peer(index: entries.count, peer: peer, isSelected: state.selectedPeerIds.contains(peer.id)))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
private struct FolderInviteLinkListControllerState: Equatable {
|
||||
var currentLink: ExportedChatFolderLink?
|
||||
var selectedPeerIds = Set<EnginePeer.Id>()
|
||||
var generatingLink: Bool = false
|
||||
}
|
||||
|
||||
public func folderInviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filterId: Int32, allPeerIds: [PeerId], currentInvitation: ExportedChatFolderLink?) -> ViewController {
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
let _ = pushControllerImpl
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var presentInGlobalOverlayImpl: ((ViewController) -> Void)?
|
||||
|
||||
var dismissTooltipsImpl: (() -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
var initialState = FolderInviteLinkListControllerState()
|
||||
initialState.currentLink = currentInvitation
|
||||
for peerId in allPeerIds {
|
||||
initialState.selectedPeerIds.insert(peerId)
|
||||
}
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((FolderInviteLinkListControllerState) -> FolderInviteLinkListControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
let _ = updateState
|
||||
|
||||
let revokeLinkDisposable = MetaDisposable()
|
||||
actionsDisposable.add(revokeLinkDisposable)
|
||||
|
||||
let deleteAllRevokedLinksDisposable = MetaDisposable()
|
||||
actionsDisposable.add(deleteAllRevokedLinksDisposable)
|
||||
|
||||
var getControllerImpl: (() -> ViewController?)?
|
||||
|
||||
let arguments = FolderInviteLinkListControllerArguments(context: context, shareMainLink: { inviteLink in
|
||||
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
|
||||
shareController.completed = { peerIds in
|
||||
let _ = (context.engine.data.get(
|
||||
EngineDataList(
|
||||
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
|
||||
)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { peerList in
|
||||
let peers = peerList.compactMap { $0 }
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let text: String
|
||||
var savedMessages = false
|
||||
if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId {
|
||||
text = presentationData.strings.InviteLink_InviteLinkForwardTooltip_SavedMessages_One
|
||||
savedMessages = true
|
||||
} else {
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let peerName = peer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
text = presentationData.strings.InviteLink_InviteLinkForwardTooltip_Chat_One(peerName).string
|
||||
} else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last {
|
||||
let firstPeerName = firstPeer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
let secondPeerName = secondPeer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
text = presentationData.strings.InviteLink_InviteLinkForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string
|
||||
} else if let peer = peers.first {
|
||||
let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
text = presentationData.strings.InviteLink_InviteLinkForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string
|
||||
} else {
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), nil)
|
||||
})
|
||||
}
|
||||
shareController.actionCompleted = {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
}
|
||||
presentControllerImpl?(shareController, nil)
|
||||
}, openMainLink: { _ in
|
||||
}, copyLink: { link in
|
||||
UIPasteboard.general.string = link
|
||||
|
||||
dismissTooltipsImpl?()
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
}, mainLinkContextAction: { invite, node, gesture in
|
||||
guard let node = node as? ContextReferenceContentNode, let controller = getControllerImpl?(), let invite = invite else {
|
||||
return
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
dismissTooltipsImpl?()
|
||||
|
||||
UIPasteboard.general.string = invite
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||
})))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
|
||||
//presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: updatedPresentationData, subject: .invite(invite: invite, isGroup: isGroup)), nil)
|
||||
})))
|
||||
|
||||
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, peerAction: { peer in
|
||||
updateState { state in
|
||||
var state = state
|
||||
|
||||
if state.selectedPeerIds.contains(peer.id) {
|
||||
state.selectedPeerIds.remove(peer.id)
|
||||
} else {
|
||||
state.selectedPeerIds.insert(peer.id)
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
}, generateLink: {
|
||||
let currentState = stateValue.with({ $0 })
|
||||
if !currentState.generatingLink {
|
||||
updateState { state in
|
||||
var state = state
|
||||
|
||||
state.generatingLink = true
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
actionsDisposable.add((context.engine.peers.exportChatFolder(filterId: filterId, title: "Link", peerIds: Array(currentState.selectedPeerIds))
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
updateState { state in
|
||||
var state = state
|
||||
|
||||
state.generatingLink = false
|
||||
state.currentLink = result
|
||||
|
||||
return state
|
||||
}
|
||||
}, error: { _ in
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
let allPeers = context.engine.data.subscribe(
|
||||
EngineDataList(allPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
|
||||
)
|
||||
|
||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||
let signal = combineLatest(queue: .mainQueue(),
|
||||
presentationData,
|
||||
statePromise.get(),
|
||||
allPeers
|
||||
)
|
||||
|> map { presentationData, state, allPeers -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let crossfade = false
|
||||
let animateChanges = false
|
||||
|
||||
//TODO:localize
|
||||
let title: ItemListControllerTitle
|
||||
title = .text("Share Folder")
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: folderInviteLinkListControllerEntries(
|
||||
presentationData: presentationData,
|
||||
state: state,
|
||||
allPeers: allPeers.compactMap { $0 }
|
||||
), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: animateChanges)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|> afterDisposed {
|
||||
actionsDisposable.dispose()
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
controller.navigationPresentation = .modal
|
||||
controller.willDisappear = { _ in
|
||||
dismissTooltipsImpl?()
|
||||
}
|
||||
controller.didDisappear = { [weak controller] _ in
|
||||
controller?.clearItemNodesHighlight(animated: true)
|
||||
}
|
||||
controller.visibleBottomContentOffsetChanged = { offset in
|
||||
if case let .known(value) = offset, value < 40.0 {
|
||||
|
||||
}
|
||||
}
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
if let controller = controller {
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(c, animated: true)
|
||||
}
|
||||
}
|
||||
presentControllerImpl = { [weak controller] c, p in
|
||||
if let controller = controller {
|
||||
controller.present(c, in: .window(.root), with: p)
|
||||
}
|
||||
}
|
||||
presentInGlobalOverlayImpl = { [weak controller] c in
|
||||
if let controller = controller {
|
||||
controller.presentInGlobalOverlay(c)
|
||||
}
|
||||
}
|
||||
getControllerImpl = { [weak controller] in
|
||||
return controller
|
||||
}
|
||||
dismissTooltipsImpl = { [weak controller] in
|
||||
controller?.window?.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
})
|
||||
controller?.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismissWithCommitAction()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return controller
|
||||
}
|
@ -0,0 +1,586 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import SolidRoundedButtonNode
|
||||
import AnimatedAvatarSetNode
|
||||
import ShimmerEffect
|
||||
import TelegramCore
|
||||
|
||||
private func actionButtonImage(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 24.0, height: 24.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(color.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 4.0, y: 10.0), size: CGSize(width: 4.0, height: 4.0)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: CGSize(width: 4.0, height: 4.0)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 16.0, y: 10.0), size: CGSize(width: 4.0, height: 4.0)))
|
||||
})
|
||||
}
|
||||
|
||||
public class ItemListFolderInviteLinkItem: ListViewItem, ItemListItem {
|
||||
let context: AccountContext
|
||||
let presentationData: ItemListPresentationData
|
||||
let invite: ExportedChatFolderLink?
|
||||
let count: Int32
|
||||
let peers: [EnginePeer]
|
||||
let displayButton: Bool
|
||||
let enableButton: Bool
|
||||
let buttonTitle: String
|
||||
let displayImporters: Bool
|
||||
let buttonColor: UIColor?
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
let copyAction: (() -> Void)?
|
||||
let shareAction: (() -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let viewAction: (() -> Void)?
|
||||
public let tag: ItemListItemTag?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
presentationData: ItemListPresentationData,
|
||||
invite: ExportedChatFolderLink?,
|
||||
count: Int32,
|
||||
peers: [EnginePeer],
|
||||
displayButton: Bool,
|
||||
enableButton: Bool,
|
||||
buttonTitle: String,
|
||||
displayImporters: Bool,
|
||||
buttonColor: UIColor?,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle,
|
||||
copyAction: (() -> Void)?,
|
||||
shareAction: (() -> Void)?,
|
||||
contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?,
|
||||
viewAction: (() -> Void)?,
|
||||
tag: ItemListItemTag? = nil
|
||||
) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.invite = invite
|
||||
self.count = count
|
||||
self.peers = peers
|
||||
self.displayButton = displayButton
|
||||
self.enableButton = enableButton
|
||||
self.buttonTitle = buttonTitle
|
||||
self.displayImporters = displayImporters
|
||||
self.buttonColor = buttonColor
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
self.copyAction = copyAction
|
||||
self.shareAction = shareAction
|
||||
self.contextAction = contextAction
|
||||
self.viewAction = viewAction
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
let node = ItemListFolderInviteLinkItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
|
||||
node.contentSize = layout.contentSize
|
||||
node.insets = layout.insets
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply() })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? ItemListFolderInviteLinkItemNode {
|
||||
let makeLayout = nodeValue.asyncLayout()
|
||||
|
||||
async {
|
||||
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectable: Bool = false
|
||||
}
|
||||
|
||||
public class ItemListFolderInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private let fieldNode: ASImageNode
|
||||
private let addressNode: TextNode
|
||||
private let fieldButtonNode: HighlightTrackingButtonNode
|
||||
private let referenceContainerNode: ContextReferenceContentNode
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
private let addressButtonNode: HighlightTrackingButtonNode
|
||||
private let addressButtonIconNode: ASImageNode
|
||||
private var addressShimmerNode: ShimmerEffectNode?
|
||||
private var shareButtonNode: SolidRoundedButtonNode?
|
||||
|
||||
private let avatarsButtonNode: HighlightTrackingButtonNode
|
||||
private let avatarsContext: AnimatedAvatarSetContext
|
||||
private var avatarsContent: AnimatedAvatarSetContext.Content?
|
||||
private let avatarsNode: AnimatedAvatarSetNode
|
||||
private let invitedPeersNode: TextNode
|
||||
private var shimmerNode: ShimmerEffectNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var item: ItemListFolderInviteLinkItem?
|
||||
|
||||
override public var canBeSelected: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
public var tag: ItemListItemTag? {
|
||||
return self.item?.tag
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = .white
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
|
||||
self.fieldNode = ASImageNode()
|
||||
self.fieldNode.displaysAsynchronously = false
|
||||
self.fieldNode.displayWithoutProcessing = true
|
||||
|
||||
self.addressNode = TextNode()
|
||||
self.addressNode.isUserInteractionEnabled = false
|
||||
|
||||
self.fieldButtonNode = HighlightTrackingButtonNode()
|
||||
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
self.containerNode.animateScale = false
|
||||
self.referenceContainerNode = ContextReferenceContentNode()
|
||||
|
||||
self.addressButtonNode = HighlightTrackingButtonNode()
|
||||
self.addressButtonIconNode = ASImageNode()
|
||||
self.addressButtonIconNode.contentMode = .center
|
||||
self.addressButtonIconNode.displaysAsynchronously = false
|
||||
self.addressButtonIconNode.displayWithoutProcessing = true
|
||||
|
||||
self.avatarsButtonNode = HighlightTrackingButtonNode()
|
||||
self.avatarsContext = AnimatedAvatarSetContext()
|
||||
self.avatarsNode = AnimatedAvatarSetNode()
|
||||
self.invitedPeersNode = TextNode()
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.fieldNode)
|
||||
self.addSubnode(self.addressNode)
|
||||
self.addSubnode(self.fieldButtonNode)
|
||||
self.addSubnode(self.avatarsNode)
|
||||
self.addSubnode(self.invitedPeersNode)
|
||||
self.addSubnode(self.avatarsButtonNode)
|
||||
|
||||
self.containerNode.addSubnode(self.referenceContainerNode)
|
||||
self.referenceContainerNode.addSubnode(self.addressButtonIconNode)
|
||||
self.referenceContainerNode.addSubnode(self.addressButtonNode)
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
self.addSubnode(self.activateArea)
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
if let strongSelf = self, let item = strongSelf.item {
|
||||
item.contextAction?(strongSelf.referenceContainerNode, gesture)
|
||||
}
|
||||
}
|
||||
|
||||
self.fieldButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.addressNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.addressNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.addressNode.alpha = 1.0
|
||||
strongSelf.addressNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fieldButtonNode.addTarget(self, action: #selector(self.fieldButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.addressButtonNode.addTarget(self, action: #selector(self.addressButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.addressButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.addressButtonIconNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.addressButtonIconNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.addressButtonIconNode.alpha = 1.0
|
||||
strongSelf.addressButtonIconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.shareButtonNode?.pressed = { [weak self] in
|
||||
if let strongSelf = self, let item = strongSelf.item {
|
||||
item.shareAction?()
|
||||
}
|
||||
}
|
||||
self.avatarsButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.avatarsNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.invitedPeersNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.avatarsNode.alpha = 0.4
|
||||
strongSelf.invitedPeersNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.avatarsNode.alpha = 1.0
|
||||
strongSelf.invitedPeersNode.alpha = 1.0
|
||||
strongSelf.avatarsNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
strongSelf.invitedPeersNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.avatarsButtonNode.addTarget(self, action: #selector(self.avatarsButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func fieldButtonPressed() {
|
||||
if let item = self.item {
|
||||
item.copyAction?()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func addressButtonPressed() {
|
||||
if let item = self.item {
|
||||
item.contextAction?(self.referenceContainerNode, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func avatarsButtonPressed() {
|
||||
if let item = self.item {
|
||||
item.viewAction?()
|
||||
}
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ item: ItemListFolderInviteLinkItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeAddressLayout = TextNode.asyncLayout(self.addressNode)
|
||||
let makeInvitedPeersLayout = TextNode.asyncLayout(self.invitedPeersNode)
|
||||
|
||||
let currentItem = self.item
|
||||
let avatarsContext = self.avatarsContext
|
||||
|
||||
return { item, params, neighbors in
|
||||
var updatedTheme: PresentationTheme?
|
||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
||||
updatedTheme = item.presentationData.theme
|
||||
}
|
||||
|
||||
let contentSize: CGSize
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
let itemBackgroundColor: UIColor
|
||||
let itemSeparatorColor: UIColor
|
||||
|
||||
let leftInset = 16.0 + params.leftInset
|
||||
let rightInset = 16.0 + params.rightInset
|
||||
|
||||
let titleColor: UIColor
|
||||
titleColor = item.presentationData.theme.list.itemInputField.primaryColor
|
||||
|
||||
let alignCentrally = !"".isEmpty//!(item.invite?.link?.contains("joinchat") ?? true)
|
||||
|
||||
let addressFont = Font.regular(!alignCentrally && params.width == 320 ? floor(item.presentationData.fontSize.itemListBaseFontSize * 15.0 / 17.0) : item.presentationData.fontSize.itemListBaseFontSize)
|
||||
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
|
||||
|
||||
let constrainedWidth = alignCentrally ? params.width - leftInset - rightInset - 90.0 : params.width - leftInset - rightInset - 60.0
|
||||
|
||||
let (addressLayout, addressApply) = makeAddressLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.invite.flatMap({ $0.link.replacingOccurrences(of: "https://", with: "") }) ?? "", font: addressFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: constrainedWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let subtitle: String
|
||||
let subtitleColor: UIColor
|
||||
if item.count > 0 {
|
||||
subtitle = item.presentationData.strings.InviteLink_PeopleJoined(item.count)
|
||||
subtitleColor = item.presentationData.theme.list.itemAccentColor
|
||||
} else {
|
||||
subtitle = item.presentationData.strings.InviteLink_PeopleJoinedNone
|
||||
subtitleColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||
}
|
||||
|
||||
let (invitedPeersLayout, invitedPeersApply) = makeInvitedPeersLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: subtitle, font: titleFont, textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let avatarsContent = avatarsContext.update(peers: item.peers, animated: false)
|
||||
|
||||
let verticalInset: CGFloat = 16.0
|
||||
let fieldHeight: CGFloat = 52.0
|
||||
let fieldSpacing: CGFloat = 16.0
|
||||
let buttonHeight: CGFloat = 50.0
|
||||
|
||||
var height = verticalInset * 2.0 + fieldHeight + fieldSpacing + buttonHeight + 54.0
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
itemSeparatorColor = .clear
|
||||
insets = UIEdgeInsets()
|
||||
case .blocks:
|
||||
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
}
|
||||
|
||||
if !item.displayImporters {
|
||||
height -= 57.0
|
||||
}
|
||||
if !item.displayButton {
|
||||
height -= 63.0
|
||||
}
|
||||
|
||||
contentSize = CGSize(width: params.width, height: height)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.avatarsContent = avatarsContent
|
||||
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
// strongSelf.activateArea.accessibilityLabel = item.title
|
||||
// strongSelf.activateArea.accessibilityValue = item.label
|
||||
strongSelf.activateArea.accessibilityTraits = []
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.fieldNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: item.presentationData.theme.list.itemInputField.backgroundColor)
|
||||
strongSelf.addressButtonIconNode.image = actionButtonImage(color: item.presentationData.theme.list.itemInputField.controlColor)
|
||||
}
|
||||
|
||||
let _ = addressApply()
|
||||
let _ = invitedPeersApply()
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
strongSelf.backgroundNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode != nil {
|
||||
strongSelf.topStripeNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||
}
|
||||
if strongSelf.maskNode.supernode != nil {
|
||||
strongSelf.maskNode.removeFromSupernode()
|
||||
}
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
|
||||
case .blocks:
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||
}
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = leftInset
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
let fieldFrame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: CGSize(width: params.width - leftInset - rightInset, height: fieldHeight))
|
||||
strongSelf.fieldNode.frame = fieldFrame
|
||||
strongSelf.fieldButtonNode.frame = fieldFrame
|
||||
|
||||
strongSelf.addressNode.frame = CGRect(origin: CGPoint(x: fieldFrame.minX + (alignCentrally ? floorToScreenPixels((fieldFrame.width - addressLayout.size.width) / 2.0) : 14.0), y: fieldFrame.minY + floorToScreenPixels((fieldFrame.height - addressLayout.size.height) / 2.0) + 1.0), size: addressLayout.size)
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(x: params.width - rightInset - 38.0 - 14.0, y: verticalInset), size: CGSize(width: 52.0, height: 52.0))
|
||||
strongSelf.addressButtonNode.frame = strongSelf.containerNode.bounds
|
||||
strongSelf.referenceContainerNode.frame = strongSelf.containerNode.bounds
|
||||
strongSelf.addressButtonIconNode.frame = strongSelf.containerNode.bounds
|
||||
|
||||
let shareButtonNode: SolidRoundedButtonNode
|
||||
if let currentShareButtonNode = strongSelf.shareButtonNode {
|
||||
shareButtonNode = currentShareButtonNode
|
||||
} else {
|
||||
let buttonTheme: SolidRoundedButtonTheme
|
||||
if let buttonColor = item.buttonColor {
|
||||
buttonTheme = SolidRoundedButtonTheme(backgroundColor: buttonColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
} else {
|
||||
buttonTheme = SolidRoundedButtonTheme(theme: item.presentationData.theme)
|
||||
}
|
||||
shareButtonNode = SolidRoundedButtonNode(theme: buttonTheme, height: 50.0, cornerRadius: 11.0)
|
||||
shareButtonNode.pressed = { [weak self] in
|
||||
self?.item?.shareAction?()
|
||||
}
|
||||
strongSelf.addSubnode(shareButtonNode)
|
||||
strongSelf.shareButtonNode = shareButtonNode
|
||||
}
|
||||
|
||||
shareButtonNode.title = item.buttonTitle
|
||||
|
||||
let buttonWidth = contentSize.width - leftInset - rightInset
|
||||
let _ = shareButtonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
||||
shareButtonNode.frame = CGRect(x: leftInset, y: verticalInset + fieldHeight + fieldSpacing, width: buttonWidth, height: buttonHeight)
|
||||
|
||||
var totalWidth = invitedPeersLayout.size.width
|
||||
var leftOrigin: CGFloat = floorToScreenPixels((params.width - invitedPeersLayout.size.width) / 2.0)
|
||||
let avatarSpacing: CGFloat = 21.0
|
||||
if let avatarsContent = strongSelf.avatarsContent {
|
||||
let avatarsSize = strongSelf.avatarsNode.update(context: item.context, content: avatarsContent, itemSize: CGSize(width: 32.0, height: 32.0), animated: true, synchronousLoad: true)
|
||||
|
||||
if !avatarsSize.width.isZero {
|
||||
totalWidth += avatarsSize.width + avatarSpacing
|
||||
}
|
||||
|
||||
let avatarsNodeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - totalWidth) / 2.0), y: fieldFrame.maxY + 87.0), size: avatarsSize)
|
||||
strongSelf.avatarsNode.frame = avatarsNodeFrame
|
||||
if !avatarsSize.width.isZero {
|
||||
leftOrigin = avatarsNodeFrame.maxX + avatarSpacing
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.invitedPeersNode.frame = CGRect(origin: CGPoint(x: leftOrigin, y: fieldFrame.maxY + 92.0), size: invitedPeersLayout.size)
|
||||
|
||||
strongSelf.avatarsButtonNode.frame = CGRect(x: floorToScreenPixels((params.width - totalWidth) / 2.0), y: fieldFrame.maxY + 87.0, width: totalWidth, height: 32.0)
|
||||
strongSelf.avatarsButtonNode.isUserInteractionEnabled = !item.peers.isEmpty && item.invite != nil
|
||||
|
||||
strongSelf.addressButtonNode.isUserInteractionEnabled = item.invite != nil
|
||||
strongSelf.fieldButtonNode.isUserInteractionEnabled = item.invite != nil
|
||||
strongSelf.addressButtonIconNode.alpha = item.invite != nil ? 1.0 : 0.0
|
||||
|
||||
strongSelf.shareButtonNode?.isUserInteractionEnabled = item.enableButton
|
||||
strongSelf.shareButtonNode?.alpha = item.enableButton ? 1.0 : 0.4
|
||||
strongSelf.shareButtonNode?.isHidden = !item.displayButton
|
||||
strongSelf.avatarsButtonNode.isHidden = !item.displayImporters
|
||||
strongSelf.avatarsNode.isHidden = !item.displayImporters || item.invite == nil
|
||||
strongSelf.invitedPeersNode.isHidden = !item.displayImporters || item.invite == nil
|
||||
|
||||
if item.invite == nil {
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.shimmerNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.shimmerNode = shimmerNode
|
||||
strongSelf.insertSubnode(shimmerNode, belowSubnode: strongSelf.fieldNode)
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
let lineWidth: CGFloat = 180.0
|
||||
let lineDiameter: CGFloat = 12.0
|
||||
let titleFrame = strongSelf.invitedPeersNode.frame
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: floor(titleFrame.center.x - lineWidth / 2.0), y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: lineWidth, diameter: lineDiameter))
|
||||
shimmerNode.update(backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: item.presentationData.theme.list.mediaPlaceholderColor, shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: layout.contentSize)
|
||||
|
||||
let addressShimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.addressShimmerNode {
|
||||
addressShimmerNode = current
|
||||
} else {
|
||||
addressShimmerNode = ShimmerEffectNode()
|
||||
strongSelf.addressShimmerNode = addressShimmerNode
|
||||
strongSelf.insertSubnode(addressShimmerNode, aboveSubnode: strongSelf.fieldNode)
|
||||
}
|
||||
addressShimmerNode.frame = strongSelf.fieldNode.frame.insetBy(dx: 18.0, dy: 0.0)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
addressShimmerNode.updateAbsoluteRect(CGRect(x: rect.minX + strongSelf.fieldNode.frame.minX + 18.0, y: rect.minY + strongSelf.fieldNode.frame.minY, width: strongSelf.fieldNode.frame.width - 18.0 * 2.0, height: strongSelf.fieldNode.frame.height), within: size)
|
||||
}
|
||||
|
||||
let addressLineWidth: CGFloat = strongSelf.fieldNode.frame.width - 100.0
|
||||
var addressShapes: [ShimmerEffectNode.Shape] = []
|
||||
addressShapes.append(.roundedRectLine(startPoint: CGPoint(x: floor(addressShimmerNode.frame.width / 2.0 - addressLineWidth / 2.0), y: 16.0 + floor((22.0 - lineDiameter) / 2.0)), width: addressLineWidth, diameter: lineDiameter))
|
||||
addressShimmerNode.update(backgroundColor: item.presentationData.theme.list.itemInputField.backgroundColor, foregroundColor: item.presentationData.theme.list.itemInputField.controlColor.mixedWith(item.presentationData.theme.list.itemInputField.backgroundColor, alpha: 0.7), shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: addressShapes, size: addressShimmerNode.frame.size)
|
||||
|
||||
} else {
|
||||
if let shimmerNode = strongSelf.shimmerNode {
|
||||
strongSelf.shimmerNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
if let shimmerNode = strongSelf.addressShimmerNode {
|
||||
strongSelf.shimmerNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
var rect = rect
|
||||
rect.origin.y += self.insets.top
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
if let shimmerNode = self.addressShimmerNode {
|
||||
shimmerNode.updateAbsoluteRect(CGRect(x: rect.minX + self.fieldNode.frame.minX + 18.0, y: rect.minY + self.fieldNode.frame.minY, width: self.fieldNode.frame.width - 18.0 * 2.0, height: self.fieldNode.frame.height), within: containerSize)
|
||||
}
|
||||
if let shimmerNode = self.shimmerNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,757 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import ShimmerEffect
|
||||
import TelegramCore
|
||||
|
||||
private enum ItemBackgroundColor: Equatable {
|
||||
case blue
|
||||
case green
|
||||
case yellow
|
||||
case red
|
||||
case gray
|
||||
|
||||
var colors: (top: UIColor, bottom: UIColor, text: UIColor) {
|
||||
switch self {
|
||||
case .blue:
|
||||
return (UIColor(rgb: 0x00b5f7), UIColor(rgb: 0x00b2f6), UIColor(rgb: 0xa7f4ff))
|
||||
case .green:
|
||||
return (UIColor(rgb: 0x4aca62), UIColor(rgb: 0x43c85c), UIColor(rgb: 0xc5ffe6))
|
||||
case .yellow:
|
||||
return (UIColor(rgb: 0xf8a953), UIColor(rgb: 0xf7a64e), UIColor(rgb: 0xfeffd7))
|
||||
case .red:
|
||||
return (UIColor(rgb: 0xf2656a), UIColor(rgb: 0xf25f65), UIColor(rgb: 0xffd3de))
|
||||
case .gray:
|
||||
return (UIColor(rgb: 0xa8b2bb), UIColor(rgb: 0xa2abb4), UIColor(rgb: 0xe3e6e8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemListFolderInviteLinkListItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let invite: ExportedChatFolderLink?
|
||||
let share: Bool
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
let tapAction: ((ExportedChatFolderLink) -> Void)?
|
||||
let contextAction: ((ExportedChatFolderLink, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
public let tag: ItemListItemTag?
|
||||
|
||||
public init(
|
||||
presentationData: ItemListPresentationData,
|
||||
invite: ExportedChatFolderLink?,
|
||||
share: Bool,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle,
|
||||
tapAction: ((ExportedChatFolderLink) -> Void)?,
|
||||
contextAction: ((ExportedChatFolderLink, ASDisplayNode, ContextGesture?) -> Void)?,
|
||||
tag: ItemListItemTag? = nil
|
||||
) {
|
||||
self.presentationData = presentationData
|
||||
self.invite = invite
|
||||
self.share = share
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
self.tapAction = tapAction
|
||||
self.contextAction = contextAction
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
async {
|
||||
var firstWithHeader = false
|
||||
var last = false
|
||||
if self.style == .plain {
|
||||
if previousItem == nil {
|
||||
firstWithHeader = true
|
||||
}
|
||||
if nextItem == nil {
|
||||
last = true
|
||||
}
|
||||
}
|
||||
let node = ItemListFolderInviteLinkListItemNode()
|
||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem), firstWithHeader, last)
|
||||
|
||||
node.contentSize = layout.contentSize
|
||||
node.insets = layout.insets
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply() })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? ItemListFolderInviteLinkListItemNode {
|
||||
let makeLayout = nodeValue.asyncLayout()
|
||||
|
||||
async {
|
||||
var firstWithHeader = false
|
||||
var last = false
|
||||
if self.style == .plain {
|
||||
if previousItem == nil {
|
||||
firstWithHeader = true
|
||||
}
|
||||
if nextItem == nil {
|
||||
last = true
|
||||
}
|
||||
}
|
||||
|
||||
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem), firstWithHeader, last)
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var selectable: Bool = true
|
||||
|
||||
public func selected(listView: ListView) {
|
||||
listView.clearHighlightAnimated(true)
|
||||
if let invite = self.invite {
|
||||
self.tapAction?(invite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemListFolderInviteLinkListItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
||||
private let extractedBackgroundImageNode: ASImageNode
|
||||
|
||||
private let containerNode: ContextControllerSourceNode
|
||||
private let contextSourceNode: ContextExtractedContentContainingNode
|
||||
|
||||
private var extractedRect: CGRect?
|
||||
private var nonExtractedRect: CGRect?
|
||||
|
||||
private let offsetContainerNode: ASDisplayNode
|
||||
|
||||
private let iconBackgroundNode: ASDisplayNode
|
||||
private let iconNode: ASImageNode
|
||||
private var timerNode: TimerNode?
|
||||
|
||||
private let titleNode: TextNode
|
||||
private let subtitleNode: TextNode
|
||||
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
|
||||
private var currentColor: ItemBackgroundColor?
|
||||
private var layoutParams: (ItemListFolderInviteLinkListItem, ListViewItemLayoutParams, ItemListNeighbors, Bool, Bool)?
|
||||
|
||||
public var tag: ItemListItemTag?
|
||||
|
||||
public init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
self.bottomStripeNode = ASDisplayNode()
|
||||
self.bottomStripeNode.isLayerBacked = true
|
||||
self.maskNode = ASImageNode()
|
||||
|
||||
self.extractedBackgroundImageNode = ASImageNode()
|
||||
self.extractedBackgroundImageNode.displaysAsynchronously = false
|
||||
self.extractedBackgroundImageNode.alpha = 0.0
|
||||
|
||||
self.contextSourceNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.offsetContainerNode = ASDisplayNode()
|
||||
|
||||
self.iconBackgroundNode = ASDisplayNode()
|
||||
self.iconBackgroundNode.setLayerBlock { () -> CALayer in
|
||||
return CAShapeLayer()
|
||||
}
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
self.iconNode.displayWithoutProcessing = true
|
||||
self.iconNode.contentMode = .center
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
self.titleNode.contentMode = .left
|
||||
self.titleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.subtitleNode = TextNode()
|
||||
self.subtitleNode.isUserInteractionEnabled = false
|
||||
self.subtitleNode.contentMode = .left
|
||||
self.subtitleNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
|
||||
self.containerNode.addSubnode(self.contextSourceNode)
|
||||
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
self.contextSourceNode.contentNode.addSubnode(self.extractedBackgroundImageNode)
|
||||
self.contextSourceNode.contentNode.addSubnode(self.offsetContainerNode)
|
||||
|
||||
self.offsetContainerNode.addSubnode(self.iconBackgroundNode)
|
||||
self.offsetContainerNode.addSubnode(self.iconNode)
|
||||
self.offsetContainerNode.addSubnode(self.titleNode)
|
||||
self.offsetContainerNode.addSubnode(self.subtitleNode)
|
||||
|
||||
self.containerNode.activated = { [weak self] gesture, _ in
|
||||
guard let strongSelf = self, let item = strongSelf.layoutParams?.0, let invite = item.invite, let contextAction = item.contextAction else {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(invite, strongSelf.contextSourceNode, gesture)
|
||||
}
|
||||
|
||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
guard let strongSelf = self, let item = strongSelf.layoutParams?.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
if isExtracted {
|
||||
strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: item.presentationData.theme.list.plainBackgroundColor)
|
||||
}
|
||||
|
||||
if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect {
|
||||
let rect = isExtracted ? extractedRect : nonExtractedRect
|
||||
transition.updateFrame(node: strongSelf.extractedBackgroundImageNode, frame: rect)
|
||||
}
|
||||
|
||||
transition.updateSublayerTransformOffset(layer: strongSelf.offsetContainerNode.layer, offset: CGPoint(x: isExtracted ? 12.0 : 0.0, y: 0.0))
|
||||
transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in
|
||||
if !isExtracted {
|
||||
self?.extractedBackgroundImageNode.image = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
if let shapeLayer = self.iconBackgroundNode.layer as? CAShapeLayer {
|
||||
shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0)).cgPath
|
||||
}
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ item: ItemListFolderInviteLinkListItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors, _ firstWithHeader: Bool, _ last: Bool) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode)
|
||||
|
||||
let currentItem = self.layoutParams?.0
|
||||
|
||||
return { item, params, neighbors, firstWithHeader, last in
|
||||
var updatedTheme: PresentationTheme?
|
||||
|
||||
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
|
||||
let subtitleFont = Font.regular(floor(item.presentationData.fontSize.itemListBaseFontSize * 14.0 / 17.0))
|
||||
|
||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
||||
updatedTheme = item.presentationData.theme
|
||||
}
|
||||
|
||||
let color: ItemBackgroundColor
|
||||
let nextColor: ItemBackgroundColor
|
||||
let transitionFraction: CGFloat
|
||||
|
||||
color = .green
|
||||
nextColor = .yellow
|
||||
transitionFraction = 1.0
|
||||
|
||||
let topColor = color.colors.top
|
||||
let nextTopColor = nextColor.colors.top
|
||||
let iconColor: UIColor
|
||||
if let _ = item.invite {
|
||||
if case .blue = color {
|
||||
iconColor = item.presentationData.theme.list.itemAccentColor
|
||||
} else {
|
||||
iconColor = nextTopColor.mixedWith(topColor, alpha: transitionFraction)
|
||||
}
|
||||
} else {
|
||||
iconColor = item.presentationData.theme.list.mediaPlaceholderColor
|
||||
}
|
||||
|
||||
let inviteLink = item.invite?.link.replacingOccurrences(of: "https://", with: "") ?? ""
|
||||
var titleText = inviteLink
|
||||
var subtitleText: String = ""
|
||||
|
||||
if let invite = item.invite {
|
||||
if !invite.title.isEmpty {
|
||||
titleText = invite.title
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
if invite.peerIds.count == 1 {
|
||||
subtitleText = "includes 1 chat"
|
||||
} else {
|
||||
subtitleText = "includes \(invite.peerIds.count) chats"
|
||||
}
|
||||
} else {
|
||||
titleText = " "
|
||||
subtitleText = " "
|
||||
}
|
||||
|
||||
let titleAttributedString = NSAttributedString(string: titleText, font: titleFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
let subtitleAttributedString = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||
|
||||
let leftInset: CGFloat = 65.0 + params.leftInset
|
||||
let rightInset: CGFloat = 16.0 + params.rightInset
|
||||
let verticalInset: CGFloat = subtitleAttributedString.string.isEmpty ? 14.0 : 8.0
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let titleSpacing: CGFloat = 1.0
|
||||
|
||||
let minHeight: CGFloat = titleLayout.size.height + verticalInset * 2.0
|
||||
let rawHeight: CGFloat = verticalInset * 2.0 + titleLayout.size.height + titleSpacing + subtitleLayout.size.height
|
||||
|
||||
var insets: UIEdgeInsets
|
||||
let itemBackgroundColor: UIColor
|
||||
let itemSeparatorColor: UIColor
|
||||
switch item.style {
|
||||
case .plain:
|
||||
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
insets = itemListNeighborsPlainInsets(neighbors)
|
||||
insets.top = firstWithHeader ? 29.0 : 0.0
|
||||
insets.bottom = 0.0
|
||||
case .blocks:
|
||||
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
||||
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
}
|
||||
|
||||
let contentSize = CGSize(width: params.width, height: max(minHeight, rawHeight))
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.layoutParams = (item, params, neighbors, firstWithHeader, last)
|
||||
|
||||
strongSelf.accessibilityLabel = titleAttributedString.string
|
||||
strongSelf.accessibilityValue = subtitleAttributedString.string
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
strongSelf.offsetContainerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
strongSelf.containerNode.isGestureEnabled = item.contextAction != nil
|
||||
|
||||
let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width - 16.0, height: layout.contentSize.height))
|
||||
let extractedRect = CGRect(origin: CGPoint(), size: layout.contentSize).insetBy(dx: 16.0 + params.leftInset, dy: 0.0)
|
||||
strongSelf.extractedRect = extractedRect
|
||||
strongSelf.nonExtractedRect = nonExtractedRect
|
||||
|
||||
if strongSelf.contextSourceNode.isExtractedToContextPreview {
|
||||
strongSelf.extractedBackgroundImageNode.frame = extractedRect
|
||||
} else {
|
||||
strongSelf.extractedBackgroundImageNode.frame = nonExtractedRect
|
||||
}
|
||||
strongSelf.contextSourceNode.contentRect = extractedRect
|
||||
|
||||
if let layer = strongSelf.iconBackgroundNode.layer as? CAShapeLayer {
|
||||
layer.fillColor = iconColor.cgColor
|
||||
}
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||
|
||||
strongSelf.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: item.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||
}
|
||||
|
||||
let transition = ContainedViewLayoutTransition.immediate
|
||||
|
||||
let _ = titleApply()
|
||||
let _ = subtitleApply()
|
||||
|
||||
switch item.style {
|
||||
case .plain:
|
||||
if strongSelf.backgroundNode.supernode != nil {
|
||||
strongSelf.backgroundNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode != nil {
|
||||
strongSelf.topStripeNode.removeFromSupernode()
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
||||
}
|
||||
if strongSelf.maskNode.supernode != nil {
|
||||
strongSelf.maskNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let stripeInset: CGFloat
|
||||
if case .none = neighbors.bottom {
|
||||
stripeInset = 0.0
|
||||
} else {
|
||||
stripeInset = leftInset
|
||||
}
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: stripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - stripeInset, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.isHidden = last
|
||||
case .blocks:
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
}
|
||||
if strongSelf.topStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||
}
|
||||
if strongSelf.bottomStripeNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||
}
|
||||
if strongSelf.maskNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||
}
|
||||
|
||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||
var hasTopCorners = false
|
||||
var hasBottomCorners = false
|
||||
switch neighbors.top {
|
||||
case .sameSection(false):
|
||||
strongSelf.topStripeNode.isHidden = true
|
||||
default:
|
||||
hasTopCorners = true
|
||||
strongSelf.topStripeNode.isHidden = hasCorners
|
||||
}
|
||||
let bottomStripeInset: CGFloat
|
||||
switch neighbors.bottom {
|
||||
case .sameSection(false):
|
||||
bottomStripeInset = leftInset
|
||||
strongSelf.bottomStripeNode.isHidden = false
|
||||
default:
|
||||
bottomStripeInset = 0.0
|
||||
hasBottomCorners = true
|
||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||
}
|
||||
|
||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
||||
}
|
||||
|
||||
let iconSize: CGSize = CGSize(width: 40.0, height: 40.0)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: params.leftInset + 12.0, y: floorToScreenPixels((layout.contentSize.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
strongSelf.iconBackgroundNode.bounds = CGRect(origin: CGPoint(), size: iconSize)
|
||||
strongSelf.iconBackgroundNode.position = iconFrame.center
|
||||
strongSelf.iconNode.frame = iconFrame
|
||||
|
||||
transition.updateTransformScale(node: strongSelf.iconBackgroundNode, scale: 1.0)
|
||||
|
||||
strongSelf.timerNode?.frame = iconFrame.insetBy(dx: -5.0, dy: -5.0)
|
||||
|
||||
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.size))
|
||||
transition.updateFrame(node: strongSelf.subtitleNode, frame: CGRect(origin: CGPoint(x: leftInset, y: verticalInset + titleLayout.size.height + titleSpacing), size: subtitleLayout.size))
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentSize.height + UIScreenPixel + UIScreenPixel))
|
||||
|
||||
if item.invite == nil {
|
||||
let shimmerNode: ShimmerEffectNode
|
||||
if let current = strongSelf.placeholderNode {
|
||||
shimmerNode = current
|
||||
} else {
|
||||
shimmerNode = ShimmerEffectNode()
|
||||
strongSelf.placeholderNode = shimmerNode
|
||||
strongSelf.addSubnode(shimmerNode)
|
||||
}
|
||||
shimmerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
if let (rect, size) = strongSelf.absoluteLocation {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
||||
var shapes: [ShimmerEffectNode.Shape] = []
|
||||
|
||||
let titleLineWidth: CGFloat = 180.0
|
||||
let subtitleLineWidth: CGFloat = 60.0
|
||||
let lineDiameter: CGFloat = 10.0
|
||||
|
||||
let iconFrame = strongSelf.iconBackgroundNode.frame
|
||||
shapes.append(.circle(iconFrame))
|
||||
|
||||
let titleFrame = strongSelf.titleNode.frame
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter))
|
||||
|
||||
let subtitleFrame = strongSelf.subtitleNode.frame
|
||||
shapes.append(.roundedRectLine(startPoint: CGPoint(x: subtitleFrame.minX, y: subtitleFrame.minY + floor((subtitleFrame.height - lineDiameter) / 2.0)), width: subtitleLineWidth, diameter: lineDiameter))
|
||||
|
||||
shimmerNode.update(backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor, foregroundColor: item.presentationData.theme.list.mediaPlaceholderColor, shimmeringColor: item.presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, size: layout.contentSize)
|
||||
} else if let shimmerNode = strongSelf.placeholderNode {
|
||||
strongSelf.placeholderNode = nil
|
||||
shimmerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override public func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
|
||||
super.setHighlighted(highlighted, at: point, animated: animated)
|
||||
|
||||
if highlighted {
|
||||
self.highlightedBackgroundNode.alpha = 1.0
|
||||
if self.highlightedBackgroundNode.supernode == nil {
|
||||
var anchorNode: ASDisplayNode?
|
||||
if self.bottomStripeNode.supernode != nil {
|
||||
anchorNode = self.bottomStripeNode
|
||||
} else if self.topStripeNode.supernode != nil {
|
||||
anchorNode = self.topStripeNode
|
||||
} else if self.backgroundNode.supernode != nil {
|
||||
anchorNode = self.backgroundNode
|
||||
}
|
||||
if let anchorNode = anchorNode {
|
||||
self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode)
|
||||
} else {
|
||||
self.addSubnode(self.highlightedBackgroundNode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.highlightedBackgroundNode.supernode != nil {
|
||||
if animated {
|
||||
self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in
|
||||
if let strongSelf = self {
|
||||
if completed {
|
||||
strongSelf.highlightedBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
})
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
} else {
|
||||
self.highlightedBackgroundNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
|
||||
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
var rect = rect
|
||||
rect.origin.y += self.insets.top
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
if let shimmerNode = self.placeholderNode {
|
||||
shimmerNode.updateAbsoluteRect(rect, within: containerSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ContentParticle {
|
||||
var position: CGPoint
|
||||
var direction: CGPoint
|
||||
var velocity: CGFloat
|
||||
var alpha: CGFloat
|
||||
var lifetime: Double
|
||||
var beginTime: Double
|
||||
|
||||
init(position: CGPoint, direction: CGPoint, velocity: CGFloat, alpha: CGFloat, lifetime: Double, beginTime: Double) {
|
||||
self.position = position
|
||||
self.direction = direction
|
||||
self.velocity = velocity
|
||||
self.alpha = alpha
|
||||
self.lifetime = lifetime
|
||||
self.beginTime = beginTime
|
||||
}
|
||||
}
|
||||
|
||||
private final class TimerNode: ASDisplayNode {
|
||||
enum Value: Equatable {
|
||||
case timestamp(creation: Int32, deadline: Int32)
|
||||
case fraction(CGFloat)
|
||||
}
|
||||
private struct Params: Equatable {
|
||||
var color: UIColor
|
||||
var value: Value
|
||||
}
|
||||
|
||||
private let hierarchyTrackingNode: HierarchyTrackingNode
|
||||
private var inHierarchyValue: Bool = false
|
||||
|
||||
private var animator: ConstantDisplayLinkAnimator?
|
||||
private let contentNode: ASDisplayNode
|
||||
private var particles: [ContentParticle] = []
|
||||
|
||||
private var currentParams: Params?
|
||||
|
||||
var reachedTimeout: (() -> Void)?
|
||||
|
||||
override init() {
|
||||
var updateInHierarchy: ((Bool) -> Void)?
|
||||
self.hierarchyTrackingNode = HierarchyTrackingNode({ value in
|
||||
updateInHierarchy?(value)
|
||||
})
|
||||
|
||||
self.contentNode = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.contentNode)
|
||||
|
||||
updateInHierarchy = { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.inHierarchyValue = value
|
||||
strongSelf.animator?.isPaused = value
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.animator?.invalidate()
|
||||
}
|
||||
|
||||
func update(color: UIColor, value: Value) {
|
||||
let params = Params(
|
||||
color: color,
|
||||
value: value
|
||||
)
|
||||
self.currentParams = params
|
||||
|
||||
self.updateValues()
|
||||
}
|
||||
|
||||
private func updateValues() {
|
||||
guard let params = self.currentParams else {
|
||||
return
|
||||
}
|
||||
|
||||
let color = params.color
|
||||
|
||||
let currentTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
var fraction: CGFloat
|
||||
switch params.value {
|
||||
case let .fraction(value):
|
||||
fraction = value
|
||||
case let .timestamp(creation, deadline):
|
||||
fraction = CGFloat(deadline - currentTimestamp) / CGFloat(deadline - creation)
|
||||
}
|
||||
fraction = max(0.0001, 1.0 - max(0.0, min(1.0, fraction)))
|
||||
|
||||
let image: UIImage?
|
||||
|
||||
let diameter: CGFloat = 42.0
|
||||
let inset: CGFloat = 8.0
|
||||
let lineWidth: CGFloat = 2.0
|
||||
|
||||
let timestamp = CACurrentMediaTime()
|
||||
|
||||
let center = CGPoint(x: (diameter + inset) / 2.0, y: (diameter + inset) / 2.0)
|
||||
let radius: CGFloat = (diameter - lineWidth / 2.0) / 2.0
|
||||
|
||||
let startAngle: CGFloat = -CGFloat.pi / 2.0
|
||||
let endAngle: CGFloat = -CGFloat.pi / 2.0 + 2.0 * CGFloat.pi * fraction
|
||||
|
||||
let sparks = fraction > 0.05 && fraction != 1.0
|
||||
if sparks {
|
||||
let v = CGPoint(x: sin(endAngle), y: -cos(endAngle))
|
||||
let c = CGPoint(x: -v.y * radius + center.x, y: v.x * radius + center.y)
|
||||
|
||||
let dt: CGFloat = 1.0 / 60.0
|
||||
var removeIndices: [Int] = []
|
||||
for i in 0 ..< self.particles.count {
|
||||
let currentTime = timestamp - self.particles[i].beginTime
|
||||
if currentTime > self.particles[i].lifetime {
|
||||
removeIndices.append(i)
|
||||
} else {
|
||||
let input: CGFloat = CGFloat(currentTime / self.particles[i].lifetime)
|
||||
let decelerated: CGFloat = (1.0 - (1.0 - input) * (1.0 - input))
|
||||
self.particles[i].alpha = 1.0 - decelerated
|
||||
|
||||
var p = self.particles[i].position
|
||||
let d = self.particles[i].direction
|
||||
let v = self.particles[i].velocity
|
||||
p = CGPoint(x: p.x + d.x * v * dt, y: p.y + d.y * v * dt)
|
||||
self.particles[i].position = p
|
||||
}
|
||||
}
|
||||
|
||||
for i in removeIndices.reversed() {
|
||||
self.particles.remove(at: i)
|
||||
}
|
||||
|
||||
let newParticleCount = 1
|
||||
for _ in 0 ..< newParticleCount {
|
||||
let degrees: CGFloat = CGFloat(arc4random_uniform(140)) - 40.0
|
||||
let angle: CGFloat = degrees * CGFloat.pi / 180.0
|
||||
|
||||
let direction = CGPoint(x: v.x * cos(angle) - v.y * sin(angle), y: v.x * sin(angle) + v.y * cos(angle))
|
||||
let velocity = (20.0 + (CGFloat(arc4random()) / CGFloat(UINT32_MAX)) * 4.0) * 0.3
|
||||
|
||||
let lifetime = Double(0.4 + CGFloat(arc4random_uniform(100)) * 0.01)
|
||||
|
||||
let particle = ContentParticle(position: c, direction: direction, velocity: velocity, alpha: 1.0, lifetime: lifetime, beginTime: timestamp)
|
||||
self.particles.append(particle)
|
||||
}
|
||||
}
|
||||
|
||||
image = generateImage(CGSize(width: diameter + inset, height: diameter + inset), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.setFillColor(color.cgColor)
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setLineCap(.round)
|
||||
|
||||
let path = CGMutablePath()
|
||||
path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
|
||||
if sparks {
|
||||
for particle in self.particles {
|
||||
let size: CGFloat = 2.0
|
||||
context.setAlpha(particle.alpha)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: particle.position.x - size / 2.0, y: particle.position.y - size / 2.0), size: CGSize(width: size, height: size)))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.contentNode.contents = image?.cgImage
|
||||
if let image = image {
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(), size: image.size)
|
||||
}
|
||||
|
||||
if fraction <= .ulpOfOne {
|
||||
self.animator?.invalidate()
|
||||
self.animator = nil
|
||||
} else {
|
||||
if self.animator == nil {
|
||||
let animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||
self?.updateValues()
|
||||
})
|
||||
self.animator = animator
|
||||
animator.isPaused = self.inHierarchyValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ swift_library(
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/ComponentFlow:ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent",
|
||||
"//submodules/CheckNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -16,6 +16,7 @@ import ContextUI
|
||||
import AccountContext
|
||||
import ComponentFlow
|
||||
import EmojiStatusComponent
|
||||
import CheckNode
|
||||
|
||||
private final class ShimmerEffectNode: ASDisplayNode {
|
||||
private var currentBackgroundColor: UIColor?
|
||||
@ -262,6 +263,7 @@ public struct ItemListPeerItemSwitch {
|
||||
public enum ItemListPeerItemSwitchStyle {
|
||||
case standard
|
||||
case check
|
||||
case leftCheck
|
||||
}
|
||||
|
||||
public enum ItemListPeerItemAliasHandling {
|
||||
@ -474,6 +476,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
private var credibilityIconView: ComponentHostView<Empty>?
|
||||
private var switchNode: SwitchNode?
|
||||
private var checkNode: ASImageNode?
|
||||
private var leftCheckNode: CheckNode?
|
||||
|
||||
private var shimmerNode: LoadingShimmerNode?
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
@ -736,6 +739,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
peerRevealOptions = []
|
||||
}
|
||||
|
||||
var additionalLeftInset: CGFloat = 0.0
|
||||
var leftInset: CGFloat = params.leftInset
|
||||
var rightInset: CGFloat = params.rightInset
|
||||
let switchSize = CGSize(width: 51.0, height: 31.0)
|
||||
var checkImage: UIImage?
|
||||
@ -755,6 +760,11 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
}
|
||||
rightInset += 24.0
|
||||
currentSwitchNode = nil
|
||||
case .leftCheck:
|
||||
additionalLeftInset += 40.0
|
||||
leftInset += additionalLeftInset
|
||||
currentSwitchNode = nil
|
||||
currentCheckNode = nil
|
||||
}
|
||||
} else {
|
||||
currentSwitchNode = nil
|
||||
@ -842,7 +852,6 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
break
|
||||
}
|
||||
|
||||
let leftInset: CGFloat
|
||||
let verticalInset: CGFloat
|
||||
let verticalOffset: CGFloat
|
||||
let avatarSize: CGFloat
|
||||
@ -856,7 +865,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
}
|
||||
verticalOffset = 0.0
|
||||
avatarSize = 31.0
|
||||
leftInset = 59.0 + params.leftInset
|
||||
leftInset += 59.0
|
||||
avatarFontSize = floor(31.0 * 16.0 / 37.0)
|
||||
case .peerList:
|
||||
if case .none = item.text {
|
||||
@ -866,7 +875,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
}
|
||||
verticalOffset = 0.0
|
||||
avatarSize = 40.0
|
||||
leftInset = 65.0 + params.leftInset
|
||||
leftInset += 65.0
|
||||
avatarFontSize = floor(40.0 * 16.0 / 37.0)
|
||||
}
|
||||
|
||||
@ -1234,9 +1243,27 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
|
||||
strongSelf.labelBadgeNode.frame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth, y: labelFrame.minY - 1.0), size: CGSize(width: badgeWidth, height: badgeDiameter))
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset + editingOffset + 15.0, y: floorToScreenPixels((layout.contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + additionalLeftInset + revealOffset + editingOffset + 15.0, y: floorToScreenPixels((layout.contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize))
|
||||
transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame)
|
||||
|
||||
if let switchValue = item.switchValue, case .leftCheck = switchValue.style {
|
||||
let leftCheckNode: CheckNode
|
||||
if let current = strongSelf.leftCheckNode {
|
||||
leftCheckNode = current
|
||||
} else {
|
||||
leftCheckNode = CheckNode(theme: CheckNodeTheme(theme: item.presentationData.theme, style: .plain))
|
||||
strongSelf.leftCheckNode = leftCheckNode
|
||||
strongSelf.avatarNode.supernode?.addSubnode(leftCheckNode)
|
||||
}
|
||||
leftCheckNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 16.0, y: floor((layout.contentSize.height - 22.0) / 2.0)), size: CGSize(width: 22.0, height: 22.0))
|
||||
leftCheckNode.setSelected(switchValue.value, animated: animated)
|
||||
} else {
|
||||
if let leftCheckNode = strongSelf.leftCheckNode {
|
||||
strongSelf.leftCheckNode = nil
|
||||
leftCheckNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
|
||||
if let threadInfo = item.threadInfo {
|
||||
let threadIconSize = floor(avatarSize * 0.9)
|
||||
let threadIconFrame = CGRect(origin: CGPoint(x: avatarFrame.minX + floor((avatarFrame.width - threadIconSize) / 2.0), y: avatarFrame.minY + floor((avatarFrame.height - threadIconSize) / 2.0)), size: CGSize(width: threadIconSize, height: threadIconSize))
|
||||
|
@ -3,6 +3,8 @@ public enum Api {
|
||||
public enum account {}
|
||||
public enum auth {}
|
||||
public enum channels {}
|
||||
public enum communities {}
|
||||
public enum community {}
|
||||
public enum contacts {}
|
||||
public enum help {}
|
||||
public enum messages {}
|
||||
@ -20,6 +22,7 @@ public enum Api {
|
||||
public enum auth {}
|
||||
public enum bots {}
|
||||
public enum channels {}
|
||||
public enum communities {}
|
||||
public enum contacts {}
|
||||
public enum folders {}
|
||||
public enum help {}
|
||||
@ -190,6 +193,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-712374074] = { return Api.Dialog.parse_dialog($0) }
|
||||
dict[1908216652] = { return Api.Dialog.parse_dialogFolder($0) }
|
||||
dict[1949890536] = { return Api.DialogFilter.parse_dialogFilter($0) }
|
||||
dict[-665432009] = { return Api.DialogFilter.parse_dialogFilterCommunity($0) }
|
||||
dict[909284270] = { return Api.DialogFilter.parse_dialogFilterDefault($0) }
|
||||
dict[2004110666] = { return Api.DialogFilterSuggested.parse_dialogFilterSuggested($0) }
|
||||
dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) }
|
||||
@ -234,6 +238,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[594758406] = { return Api.EncryptedMessage.parse_encryptedMessageService($0) }
|
||||
dict[179611673] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) }
|
||||
dict[-317687113] = { return Api.ExportedChatInvite.parse_chatInvitePublicJoinRequests($0) }
|
||||
dict[-1350894801] = { return Api.ExportedCommunityInvite.parse_exportedCommunityInvite($0) }
|
||||
dict[1103040667] = { return Api.ExportedContactToken.parse_exportedContactToken($0) }
|
||||
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
|
||||
dict[-207944868] = { return Api.FileHash.parse_fileHash($0) }
|
||||
@ -285,6 +290,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1736378792] = { return Api.InputCheckPasswordSRP.parse_inputCheckPasswordEmpty($0) }
|
||||
dict[-763367294] = { return Api.InputCheckPasswordSRP.parse_inputCheckPasswordSRP($0) }
|
||||
dict[1968737087] = { return Api.InputClientProxy.parse_inputClientProxy($0) }
|
||||
dict[450955169] = { return Api.InputCommunity.parse_inputCommunityDialogFilter($0) }
|
||||
dict[-208488460] = { return Api.InputContact.parse_inputPhoneContact($0) }
|
||||
dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) }
|
||||
dict[1684014375] = { return Api.InputDialogPeer.parse_inputDialogPeerFolder($0) }
|
||||
@ -990,6 +996,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1699676497] = { return Api.channels.ChannelParticipants.parse_channelParticipants($0) }
|
||||
dict[-266911767] = { return Api.channels.ChannelParticipants.parse_channelParticipantsNotModified($0) }
|
||||
dict[-191450938] = { return Api.channels.SendAsPeers.parse_sendAsPeers($0) }
|
||||
dict[1805101290] = { return Api.communities.ExportedCommunityInvite.parse_exportedCommunityInvite($0) }
|
||||
dict[-2662489] = { return Api.communities.ExportedInvites.parse_exportedInvites($0) }
|
||||
dict[408604768] = { return Api.community.CommunityInvite.parse_communityInvite($0) }
|
||||
dict[74184410] = { return Api.community.CommunityInvite.parse_communityInviteAlready($0) }
|
||||
dict[182326673] = { return Api.contacts.Blocked.parse_blocked($0) }
|
||||
dict[-513392236] = { return Api.contacts.Blocked.parse_blockedSlice($0) }
|
||||
dict[-353862078] = { return Api.contacts.Contacts.parse_contacts($0) }
|
||||
@ -1341,6 +1351,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ExportedChatInvite:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ExportedCommunityInvite:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ExportedContactToken:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ExportedMessageLink:
|
||||
@ -1397,6 +1409,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputClientProxy:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputCommunity:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputContact:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputDialogPeer:
|
||||
@ -1783,6 +1797,12 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.channels.SendAsPeers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.communities.ExportedCommunityInvite:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.communities.ExportedInvites:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.community.CommunityInvite:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.contacts.Blocked:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.contacts.Contacts:
|
||||
|
@ -850,6 +850,224 @@ public extension Api.channels {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.communities {
|
||||
enum ExportedCommunityInvite: TypeConstructorDescription {
|
||||
case exportedCommunityInvite(filter: Api.DialogFilter, invite: Api.ExportedCommunityInvite)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .exportedCommunityInvite(let filter, let invite):
|
||||
if boxed {
|
||||
buffer.appendInt32(1805101290)
|
||||
}
|
||||
filter.serialize(buffer, true)
|
||||
invite.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .exportedCommunityInvite(let filter, let invite):
|
||||
return ("exportedCommunityInvite", [("filter", filter as Any), ("invite", invite as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_exportedCommunityInvite(_ reader: BufferReader) -> ExportedCommunityInvite? {
|
||||
var _1: Api.DialogFilter?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.DialogFilter
|
||||
}
|
||||
var _2: Api.ExportedCommunityInvite?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.ExportedCommunityInvite
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.communities.ExportedCommunityInvite.exportedCommunityInvite(filter: _1!, invite: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.communities {
|
||||
enum ExportedInvites: TypeConstructorDescription {
|
||||
case exportedInvites(invites: [Api.ExportedCommunityInvite], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .exportedInvites(let invites, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2662489)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(invites.count))
|
||||
for item in invites {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .exportedInvites(let invites, let chats, let users):
|
||||
return ("exportedInvites", [("invites", invites as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_exportedInvites(_ reader: BufferReader) -> ExportedInvites? {
|
||||
var _1: [Api.ExportedCommunityInvite]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ExportedCommunityInvite.self)
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.communities.ExportedInvites.exportedInvites(invites: _1!, chats: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.community {
|
||||
enum CommunityInvite: TypeConstructorDescription {
|
||||
case communityInvite(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
case communityInviteAlready(filterId: Int32, missingPeers: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .communityInvite(let peers, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(408604768)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
for item in peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .communityInviteAlready(let filterId, let missingPeers, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(74184410)
|
||||
}
|
||||
serializeInt32(filterId, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(missingPeers.count))
|
||||
for item in missingPeers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .communityInvite(let peers, let chats, let users):
|
||||
return ("communityInvite", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
case .communityInviteAlready(let filterId, let missingPeers, let chats, let users):
|
||||
return ("communityInviteAlready", [("filterId", filterId as Any), ("missingPeers", missingPeers as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_communityInvite(_ reader: BufferReader) -> CommunityInvite? {
|
||||
var _1: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.community.CommunityInvite.communityInvite(peers: _1!, chats: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_communityInviteAlready(_ reader: BufferReader) -> CommunityInvite? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _3: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _4: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.community.CommunityInvite.communityInviteAlready(filterId: _1!, missingPeers: _2!, chats: _3!, users: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.contacts {
|
||||
enum Blocked: TypeConstructorDescription {
|
||||
case blocked(blocked: [Api.PeerBlocked], chats: [Api.Chat], users: [Api.User])
|
||||
@ -962,143 +1180,3 @@ public extension Api.contacts {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.contacts {
|
||||
enum Contacts: TypeConstructorDescription {
|
||||
case contacts(contacts: [Api.Contact], savedCount: Int32, users: [Api.User])
|
||||
case contactsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .contacts(let contacts, let savedCount, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-353862078)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(contacts.count))
|
||||
for item in contacts {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(savedCount, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .contactsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1219778094)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .contacts(let contacts, let savedCount, let users):
|
||||
return ("contacts", [("contacts", contacts as Any), ("savedCount", savedCount as Any), ("users", users as Any)])
|
||||
case .contactsNotModified:
|
||||
return ("contactsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_contacts(_ reader: BufferReader) -> Contacts? {
|
||||
var _1: [Api.Contact]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Contact.self)
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_contactsNotModified(_ reader: BufferReader) -> Contacts? {
|
||||
return Api.contacts.Contacts.contactsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.contacts {
|
||||
enum Found: TypeConstructorDescription {
|
||||
case found(myResults: [Api.Peer], results: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .found(let myResults, let results, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1290580579)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(myResults.count))
|
||||
for item in myResults {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(results.count))
|
||||
for item in results {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .found(let myResults, let results, let chats, let users):
|
||||
return ("found", [("myResults", myResults as Any), ("results", results as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_found(_ reader: BufferReader) -> Found? {
|
||||
var _1: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _2: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _3: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _4: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.contacts.Found.found(myResults: _1!, results: _2!, chats: _3!, users: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,143 @@
|
||||
public extension Api.contacts {
|
||||
enum Contacts: TypeConstructorDescription {
|
||||
case contacts(contacts: [Api.Contact], savedCount: Int32, users: [Api.User])
|
||||
case contactsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .contacts(let contacts, let savedCount, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-353862078)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(contacts.count))
|
||||
for item in contacts {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(savedCount, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .contactsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1219778094)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .contacts(let contacts, let savedCount, let users):
|
||||
return ("contacts", [("contacts", contacts as Any), ("savedCount", savedCount as Any), ("users", users as Any)])
|
||||
case .contactsNotModified:
|
||||
return ("contactsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_contacts(_ reader: BufferReader) -> Contacts? {
|
||||
var _1: [Api.Contact]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Contact.self)
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_contactsNotModified(_ reader: BufferReader) -> Contacts? {
|
||||
return Api.contacts.Contacts.contactsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.contacts {
|
||||
enum Found: TypeConstructorDescription {
|
||||
case found(myResults: [Api.Peer], results: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .found(let myResults, let results, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1290580579)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(myResults.count))
|
||||
for item in myResults {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(results.count))
|
||||
for item in results {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .found(let myResults, let results, let chats, let users):
|
||||
return ("found", [("myResults", myResults as Any), ("results", results as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_found(_ reader: BufferReader) -> Found? {
|
||||
var _1: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _2: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
var _3: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _4: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.contacts.Found.found(myResults: _1!, results: _2!, chats: _3!, users: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.contacts {
|
||||
enum ImportedContacts: TypeConstructorDescription {
|
||||
case importedContacts(imported: [Api.ImportedContact], popularInvites: [Api.PopularContact], retryContacts: [Int64], users: [Api.User])
|
||||
@ -1190,141 +1330,3 @@ public extension Api.help {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum AffectedFoundMessages: TypeConstructorDescription {
|
||||
case affectedFoundMessages(pts: Int32, ptsCount: Int32, offset: Int32, messages: [Int32])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages):
|
||||
if boxed {
|
||||
buffer.appendInt32(-275956116)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages):
|
||||
return ("affectedFoundMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any), ("messages", messages as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_affectedFoundMessages(_ reader: BufferReader) -> AffectedFoundMessages? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.messages.AffectedFoundMessages.affectedFoundMessages(pts: _1!, ptsCount: _2!, offset: _3!, messages: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum AffectedHistory: TypeConstructorDescription {
|
||||
case affectedHistory(pts: Int32, ptsCount: Int32, offset: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .affectedHistory(let pts, let ptsCount, let offset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1269012015)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .affectedHistory(let pts, let ptsCount, let offset):
|
||||
return ("affectedHistory", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_affectedHistory(_ reader: BufferReader) -> AffectedHistory? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.AffectedHistory.affectedHistory(pts: _1!, ptsCount: _2!, offset: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum AffectedMessages: TypeConstructorDescription {
|
||||
case affectedMessages(pts: Int32, ptsCount: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .affectedMessages(let pts, let ptsCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2066640507)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .affectedMessages(let pts, let ptsCount):
|
||||
return ("affectedMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_affectedMessages(_ reader: BufferReader) -> AffectedMessages? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.AffectedMessages.affectedMessages(pts: _1!, ptsCount: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,141 @@
|
||||
public extension Api.messages {
|
||||
enum AffectedFoundMessages: TypeConstructorDescription {
|
||||
case affectedFoundMessages(pts: Int32, ptsCount: Int32, offset: Int32, messages: [Int32])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages):
|
||||
if boxed {
|
||||
buffer.appendInt32(-275956116)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .affectedFoundMessages(let pts, let ptsCount, let offset, let messages):
|
||||
return ("affectedFoundMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any), ("messages", messages as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_affectedFoundMessages(_ reader: BufferReader) -> AffectedFoundMessages? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
var _4: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.messages.AffectedFoundMessages.affectedFoundMessages(pts: _1!, ptsCount: _2!, offset: _3!, messages: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum AffectedHistory: TypeConstructorDescription {
|
||||
case affectedHistory(pts: Int32, ptsCount: Int32, offset: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .affectedHistory(let pts, let ptsCount, let offset):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1269012015)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .affectedHistory(let pts, let ptsCount, let offset):
|
||||
return ("affectedHistory", [("pts", pts as Any), ("ptsCount", ptsCount as Any), ("offset", offset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_affectedHistory(_ reader: BufferReader) -> AffectedHistory? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.AffectedHistory.affectedHistory(pts: _1!, ptsCount: _2!, offset: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum AffectedMessages: TypeConstructorDescription {
|
||||
case affectedMessages(pts: Int32, ptsCount: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .affectedMessages(let pts, let ptsCount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2066640507)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .affectedMessages(let pts, let ptsCount):
|
||||
return ("affectedMessages", [("pts", pts as Any), ("ptsCount", ptsCount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_affectedMessages(_ reader: BufferReader) -> AffectedMessages? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.AffectedMessages.affectedMessages(pts: _1!, ptsCount: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum AllStickers: TypeConstructorDescription {
|
||||
case allStickers(hash: Int64, sets: [Api.StickerSet])
|
||||
@ -1258,145 +1396,3 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum ForumTopics: TypeConstructorDescription {
|
||||
case forumTopics(flags: Int32, count: Int32, topics: [Api.ForumTopic], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], pts: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts):
|
||||
if boxed {
|
||||
buffer.appendInt32(913709011)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(topics.count))
|
||||
for item in topics {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts):
|
||||
return ("forumTopics", [("flags", flags as Any), ("count", count as Any), ("topics", topics as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any), ("pts", pts as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_forumTopics(_ reader: BufferReader) -> ForumTopics? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.ForumTopic]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self)
|
||||
}
|
||||
var _4: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _5: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.messages.ForumTopics.forumTopics(flags: _1!, count: _2!, topics: _3!, messages: _4!, chats: _5!, users: _6!, pts: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum FoundStickerSets: TypeConstructorDescription {
|
||||
case foundStickerSets(hash: Int64, sets: [Api.StickerSetCovered])
|
||||
case foundStickerSetsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .foundStickerSets(let hash, let sets):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1963942446)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(sets.count))
|
||||
for item in sets {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .foundStickerSetsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(223655517)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .foundStickerSets(let hash, let sets):
|
||||
return ("foundStickerSets", [("hash", hash as Any), ("sets", sets as Any)])
|
||||
case .foundStickerSetsNotModified:
|
||||
return ("foundStickerSetsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_foundStickerSets(_ reader: BufferReader) -> FoundStickerSets? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.StickerSetCovered]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? {
|
||||
return Api.messages.FoundStickerSets.foundStickerSetsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,145 @@
|
||||
public extension Api.messages {
|
||||
enum ForumTopics: TypeConstructorDescription {
|
||||
case forumTopics(flags: Int32, count: Int32, topics: [Api.ForumTopic], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], pts: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts):
|
||||
if boxed {
|
||||
buffer.appendInt32(913709011)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(topics.count))
|
||||
for item in topics {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(messages.count))
|
||||
for item in messages {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(chats.count))
|
||||
for item in chats {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .forumTopics(let flags, let count, let topics, let messages, let chats, let users, let pts):
|
||||
return ("forumTopics", [("flags", flags as Any), ("count", count as Any), ("topics", topics as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any), ("pts", pts as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_forumTopics(_ reader: BufferReader) -> ForumTopics? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.ForumTopic]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ForumTopic.self)
|
||||
}
|
||||
var _4: [Api.Message]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
|
||||
}
|
||||
var _5: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _6: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.messages.ForumTopics.forumTopics(flags: _1!, count: _2!, topics: _3!, messages: _4!, chats: _5!, users: _6!, pts: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum FoundStickerSets: TypeConstructorDescription {
|
||||
case foundStickerSets(hash: Int64, sets: [Api.StickerSetCovered])
|
||||
case foundStickerSetsNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .foundStickerSets(let hash, let sets):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1963942446)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(sets.count))
|
||||
for item in sets {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .foundStickerSetsNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(223655517)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .foundStickerSets(let hash, let sets):
|
||||
return ("foundStickerSets", [("hash", hash as Any), ("sets", sets as Any)])
|
||||
case .foundStickerSetsNotModified:
|
||||
return ("foundStickerSetsNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_foundStickerSets(_ reader: BufferReader) -> FoundStickerSets? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.StickerSetCovered]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? {
|
||||
return Api.messages.FoundStickerSets.foundStickerSetsNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum HighScores: TypeConstructorDescription {
|
||||
case highScores(scores: [Api.HighScore], users: [Api.User])
|
||||
@ -1368,61 +1510,3 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum Stickers: TypeConstructorDescription {
|
||||
case stickers(hash: Int64, stickers: [Api.Document])
|
||||
case stickersNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickers(let hash, let stickers):
|
||||
if boxed {
|
||||
buffer.appendInt32(816245886)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stickers.count))
|
||||
for item in stickers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .stickersNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-244016606)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickers(let hash, let stickers):
|
||||
return ("stickers", [("hash", hash as Any), ("stickers", stickers as Any)])
|
||||
case .stickersNotModified:
|
||||
return ("stickersNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickers(_ reader: BufferReader) -> Stickers? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.Stickers.stickers(hash: _1!, stickers: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickersNotModified(_ reader: BufferReader) -> Stickers? {
|
||||
return Api.messages.Stickers.stickersNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,61 @@
|
||||
public extension Api.messages {
|
||||
enum Stickers: TypeConstructorDescription {
|
||||
case stickers(hash: Int64, stickers: [Api.Document])
|
||||
case stickersNotModified
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickers(let hash, let stickers):
|
||||
if boxed {
|
||||
buffer.appendInt32(816245886)
|
||||
}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stickers.count))
|
||||
for item in stickers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .stickersNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-244016606)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickers(let hash, let stickers):
|
||||
return ("stickers", [("hash", hash as Any), ("stickers", stickers as Any)])
|
||||
case .stickersNotModified:
|
||||
return ("stickersNotModified", [])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickers(_ reader: BufferReader) -> Stickers? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.Stickers.stickers(hash: _1!, stickers: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickersNotModified(_ reader: BufferReader) -> Stickers? {
|
||||
return Api.messages.Stickers.stickersNotModified
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum TranscribedAudio: TypeConstructorDescription {
|
||||
case transcribedAudio(flags: Int32, transcriptionId: Int64, text: String)
|
||||
|
@ -2911,6 +2911,111 @@ public extension Api.functions.channels {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.communities {
|
||||
static func checkCommunityInvite(slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.community.CommunityInvite>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1753956947)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "communities.checkCommunityInvite", parameters: [("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.community.CommunityInvite? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.community.CommunityInvite?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.community.CommunityInvite
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.communities {
|
||||
static func deleteExportedInvite(community: Api.InputCommunity, slug: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-110213610)
|
||||
community.serialize(buffer, true)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "communities.deleteExportedInvite", parameters: [("community", String(describing: community)), ("slug", String(describing: slug))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.communities {
|
||||
static func editExportedInvite(flags: Int32, community: Api.InputCommunity, slug: String, title: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ExportedCommunityInvite>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(873155725)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
community.serialize(buffer, true)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "communities.editExportedInvite", parameters: [("flags", String(describing: flags)), ("community", String(describing: community)), ("slug", String(describing: slug)), ("title", String(describing: title))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedCommunityInvite? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.ExportedCommunityInvite?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.ExportedCommunityInvite
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.communities {
|
||||
static func exportCommunityInvite(community: Api.InputCommunity, title: String, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.communities.ExportedCommunityInvite>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1107192281)
|
||||
community.serialize(buffer, true)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
for item in peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
return (FunctionDescription(name: "communities.exportCommunityInvite", parameters: [("community", String(describing: community)), ("title", String(describing: title)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.communities.ExportedCommunityInvite? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.communities.ExportedCommunityInvite?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.communities.ExportedCommunityInvite
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.communities {
|
||||
static func getExportedInvites(community: Api.InputCommunity) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.communities.ExportedInvites>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1183359901)
|
||||
community.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "communities.getExportedInvites", parameters: [("community", String(describing: community))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.communities.ExportedInvites? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.communities.ExportedInvites?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.communities.ExportedInvites
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.communities {
|
||||
static func joinCommunityInvite(slug: String, peers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(82835751)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
for item in peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
return (FunctionDescription(name: "communities.joinCommunityInvite", parameters: [("slug", String(describing: slug)), ("peers", String(describing: peers))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.contacts {
|
||||
static func acceptContact(id: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -1015,6 +1015,7 @@ public extension Api {
|
||||
public extension Api {
|
||||
enum DialogFilter: TypeConstructorDescription {
|
||||
case dialogFilter(flags: Int32, id: Int32, title: String, emoticon: String?, pinnedPeers: [Api.InputPeer], includePeers: [Api.InputPeer], excludePeers: [Api.InputPeer])
|
||||
case dialogFilterCommunity(flags: Int32, id: Int32, title: String, emoticon: String?, pinnedPeers: [Api.InputPeer], includePeers: [Api.InputPeer])
|
||||
case dialogFilterDefault
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
@ -1043,6 +1044,25 @@ public extension Api {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .dialogFilterCommunity(let flags, let id, let title, let emoticon, let pinnedPeers, let includePeers):
|
||||
if boxed {
|
||||
buffer.appendInt32(-665432009)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 25) != 0 {serializeString(emoticon!, buffer: buffer, boxed: false)}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(pinnedPeers.count))
|
||||
for item in pinnedPeers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(includePeers.count))
|
||||
for item in includePeers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .dialogFilterDefault:
|
||||
if boxed {
|
||||
buffer.appendInt32(909284270)
|
||||
@ -1056,6 +1076,8 @@ public extension Api {
|
||||
switch self {
|
||||
case .dialogFilter(let flags, let id, let title, let emoticon, let pinnedPeers, let includePeers, let excludePeers):
|
||||
return ("dialogFilter", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("pinnedPeers", pinnedPeers as Any), ("includePeers", includePeers as Any), ("excludePeers", excludePeers as Any)])
|
||||
case .dialogFilterCommunity(let flags, let id, let title, let emoticon, let pinnedPeers, let includePeers):
|
||||
return ("dialogFilterCommunity", [("flags", flags as Any), ("id", id as Any), ("title", title as Any), ("emoticon", emoticon as Any), ("pinnedPeers", pinnedPeers as Any), ("includePeers", includePeers as Any)])
|
||||
case .dialogFilterDefault:
|
||||
return ("dialogFilterDefault", [])
|
||||
}
|
||||
@ -1096,6 +1118,36 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_dialogFilterCommunity(_ reader: BufferReader) -> DialogFilter? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: String?
|
||||
if Int(_1!) & Int(1 << 25) != 0 {_4 = parseString(reader) }
|
||||
var _5: [Api.InputPeer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputPeer.self)
|
||||
}
|
||||
var _6: [Api.InputPeer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputPeer.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 25) == 0) || _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.DialogFilter.dialogFilterCommunity(flags: _1!, id: _2!, title: _3!, emoticon: _4, pinnedPeers: _5!, includePeers: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_dialogFilterDefault(_ reader: BufferReader) -> DialogFilter? {
|
||||
return Api.DialogFilter.dialogFilterDefault
|
||||
}
|
||||
|
@ -1024,6 +1024,56 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ExportedCommunityInvite: TypeConstructorDescription {
|
||||
case exportedCommunityInvite(title: String, url: String, peers: [Api.Peer])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .exportedCommunityInvite(let title, let url, let peers):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1350894801)
|
||||
}
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(url, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
for item in peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .exportedCommunityInvite(let title, let url, let peers):
|
||||
return ("exportedCommunityInvite", [("title", title as Any), ("url", url as Any), ("peers", peers as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_exportedCommunityInvite(_ reader: BufferReader) -> ExportedCommunityInvite? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: [Api.Peer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.ExportedCommunityInvite.exportedCommunityInvite(title: _1!, url: _2!, peers: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ExportedContactToken: TypeConstructorDescription {
|
||||
case exportedContactToken(url: String, expires: Int32)
|
||||
@ -1064,43 +1114,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum ExportedMessageLink: TypeConstructorDescription {
|
||||
case exportedMessageLink(link: String, html: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .exportedMessageLink(let link, let html):
|
||||
if boxed {
|
||||
buffer.appendInt32(1571494644)
|
||||
}
|
||||
serializeString(link, buffer: buffer, boxed: false)
|
||||
serializeString(html, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .exportedMessageLink(let link, let html):
|
||||
return ("exportedMessageLink", [("link", link as Any), ("html", html as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_exportedMessageLink(_ reader: BufferReader) -> ExportedMessageLink? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ExportedMessageLink.exportedMessageLink(link: _1!, html: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,43 @@
|
||||
public extension Api {
|
||||
enum ExportedMessageLink: TypeConstructorDescription {
|
||||
case exportedMessageLink(link: String, html: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .exportedMessageLink(let link, let html):
|
||||
if boxed {
|
||||
buffer.appendInt32(1571494644)
|
||||
}
|
||||
serializeString(link, buffer: buffer, boxed: false)
|
||||
serializeString(html, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .exportedMessageLink(let link, let html):
|
||||
return ("exportedMessageLink", [("link", link as Any), ("html", html as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_exportedMessageLink(_ reader: BufferReader) -> ExportedMessageLink? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.ExportedMessageLink.exportedMessageLink(link: _1!, html: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum FileHash: TypeConstructorDescription {
|
||||
case fileHash(offset: Int64, limit: Int32, hash: Buffer)
|
||||
|
@ -532,6 +532,42 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputCommunity: TypeConstructorDescription {
|
||||
case inputCommunityDialogFilter(filterId: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputCommunityDialogFilter(let filterId):
|
||||
if boxed {
|
||||
buffer.appendInt32(450955169)
|
||||
}
|
||||
serializeInt32(filterId, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputCommunityDialogFilter(let filterId):
|
||||
return ("inputCommunityDialogFilter", [("filterId", filterId as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputCommunityDialogFilter(_ reader: BufferReader) -> InputCommunity? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputCommunity.inputCommunityDialogFilter(filterId: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputContact: TypeConstructorDescription {
|
||||
case inputPhoneContact(clientId: Int64, phone: String, firstName: String, lastName: String)
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 156
|
||||
return 158
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -174,6 +174,7 @@ extension ChatListFilterIncludePeers {
|
||||
}
|
||||
|
||||
public struct ChatListFilterData: Equatable, Hashable {
|
||||
public var isShared: Bool
|
||||
public var categories: ChatListFilterPeerCategories
|
||||
public var excludeMuted: Bool
|
||||
public var excludeRead: Bool
|
||||
@ -182,6 +183,7 @@ public struct ChatListFilterData: Equatable, Hashable {
|
||||
public var excludePeers: [PeerId]
|
||||
|
||||
public init(
|
||||
isShared: Bool,
|
||||
categories: ChatListFilterPeerCategories,
|
||||
excludeMuted: Bool,
|
||||
excludeRead: Bool,
|
||||
@ -189,6 +191,7 @@ public struct ChatListFilterData: Equatable, Hashable {
|
||||
includePeers: ChatListFilterIncludePeers,
|
||||
excludePeers: [PeerId]
|
||||
) {
|
||||
self.isShared = isShared
|
||||
self.categories = categories
|
||||
self.excludeMuted = excludeMuted
|
||||
self.excludeRead = excludeRead
|
||||
@ -246,6 +249,7 @@ public enum ChatListFilter: Codable, Equatable {
|
||||
let emoticon = try container.decodeIfPresent(String.self, forKey: "emoticon")
|
||||
|
||||
let data = ChatListFilterData(
|
||||
isShared: try container.decodeIfPresent(Bool.self, forKey: "isShared") ?? false,
|
||||
categories: ChatListFilterPeerCategories(rawValue: try container.decode(Int32.self, forKey: "categories")),
|
||||
excludeMuted: (try container.decode(Int32.self, forKey: "excludeMuted")) != 0,
|
||||
excludeRead: (try container.decode(Int32.self, forKey: "excludeRead")) != 0,
|
||||
@ -275,6 +279,7 @@ public enum ChatListFilter: Codable, Equatable {
|
||||
try container.encode(title, forKey: "title")
|
||||
try container.encodeIfPresent(emoticon, forKey: "emoticon")
|
||||
|
||||
try container.encode(data.isShared, forKey: "isShared")
|
||||
try container.encode(data.categories.rawValue, forKey: "categories")
|
||||
try container.encode((data.excludeMuted ? 1 : 0) as Int32, forKey: "excludeMuted")
|
||||
try container.encode((data.excludeRead ? 1 : 0) as Int32, forKey: "excludeRead")
|
||||
@ -297,6 +302,7 @@ extension ChatListFilter {
|
||||
title: title,
|
||||
emoticon: emoticon,
|
||||
data: ChatListFilterData(
|
||||
isShared: false,
|
||||
categories: ChatListFilterPeerCategories(apiFlags: flags),
|
||||
excludeMuted: (flags & (1 << 11)) != 0,
|
||||
excludeRead: (flags & (1 << 12)) != 0,
|
||||
@ -338,6 +344,43 @@ extension ChatListFilter {
|
||||
}
|
||||
)
|
||||
)
|
||||
case let .dialogFilterCommunity(_, id, title, emoticon, pinnedPeers, includePeers):
|
||||
self = .filter(
|
||||
id: id,
|
||||
title: title,
|
||||
emoticon: emoticon,
|
||||
data: ChatListFilterData(
|
||||
isShared: true,
|
||||
categories: [],
|
||||
excludeMuted: false,
|
||||
excludeRead: false,
|
||||
excludeArchived: false,
|
||||
includePeers: ChatListFilterIncludePeers(rawPeers: includePeers.compactMap { peer -> PeerId? in
|
||||
switch peer {
|
||||
case let .inputPeerUser(userId, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
case let .inputPeerChat(chatId):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(chatId))
|
||||
case let .inputPeerChannel(channelId, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}, rawPinnedPeers: pinnedPeers.compactMap { peer -> PeerId? in
|
||||
switch peer {
|
||||
case let .inputPeerUser(userId, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
case let .inputPeerChat(chatId):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(chatId))
|
||||
case let .inputPeerChannel(channelId, _):
|
||||
return PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}),
|
||||
excludePeers: []
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,6 +498,46 @@ private func requestChatListFilters(accountPeerId: PeerId, postbox: Postbox, net
|
||||
}
|
||||
}
|
||||
|
||||
for peer in pinnedPeers {
|
||||
var peerId: PeerId?
|
||||
switch peer {
|
||||
case let .inputPeerUser(userId, _):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
case let .inputPeerChat(chatId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(chatId))
|
||||
case let .inputPeerChannel(channelId, _):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||
default:
|
||||
break
|
||||
}
|
||||
if let peerId = peerId, !missingChatIds.contains(peerId) {
|
||||
if transaction.getPeerChatListIndex(peerId) == nil {
|
||||
missingChatIds.insert(peerId)
|
||||
missingChats.append(peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .dialogFilterCommunity(_, _, _, _, pinnedPeers, includePeers):
|
||||
for peer in pinnedPeers + includePeers {
|
||||
var peerId: PeerId?
|
||||
switch peer {
|
||||
case let .inputPeerUser(userId, _):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
|
||||
case let .inputPeerChat(chatId):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(chatId))
|
||||
case let .inputPeerChannel(channelId, _):
|
||||
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId))
|
||||
default:
|
||||
break
|
||||
}
|
||||
if let peerId = peerId {
|
||||
if transaction.getPeer(peerId) == nil && !missingPeerIds.contains(peerId) {
|
||||
missingPeerIds.insert(peerId)
|
||||
missingPeers.append(peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for peer in pinnedPeers {
|
||||
var peerId: PeerId?
|
||||
switch peer {
|
||||
@ -931,6 +1014,7 @@ public struct ChatListFeaturedFilter: Codable, Equatable {
|
||||
self.title = try container.decode(String.self, forKey: "title")
|
||||
self.description = try container.decode(String.self, forKey: "description")
|
||||
self.data = ChatListFilterData(
|
||||
isShared: try container.decodeIfPresent(Bool.self, forKey: "isShared") ?? false,
|
||||
categories: ChatListFilterPeerCategories(rawValue: try container.decode(Int32.self, forKey: "categories")),
|
||||
excludeMuted: (try container.decode(Int32.self, forKey: "excludeMuted")) != 0,
|
||||
excludeRead: (try container.decode(Int32.self, forKey: "excludeRead")) != 0,
|
||||
|
@ -0,0 +1,117 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramApi
|
||||
|
||||
//communities.exportCommunityInvite#41fe69d9 community:InputCommunity title:string peers:Vector<InputPeer> = communities.ExportedCommunityInvite;
|
||||
//communities.exportedCommunityInvite#6b97a8ea filter:DialogFilter invite:ExportedCommunityInvite = communities.ExportedCommunityInvite;
|
||||
//exportedCommunityInvite#af7afb2f title:string url:string peers:Vector<Peer> = ExportedCommunityInvite;
|
||||
|
||||
public enum ExportChatFolderError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public struct ExportedChatFolderLink: Equatable {
|
||||
public var title: String
|
||||
public var link: String
|
||||
public var peerIds: [EnginePeer.Id]
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
link: String,
|
||||
peerIds: [EnginePeer.Id]
|
||||
) {
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.peerIds = peerIds
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_exportChatFolder(account: Account, filterId: Int32, title: String, peerIds: [PeerId]) -> Signal<ExportedChatFolderLink, ExportChatFolderError> {
|
||||
return account.postbox.transaction { transaction -> [Api.InputPeer] in
|
||||
return peerIds.compactMap(transaction.getPeer).compactMap(apiInputPeer)
|
||||
}
|
||||
|> castError(ExportChatFolderError.self)
|
||||
|> mapToSignal { inputPeers -> Signal<ExportedChatFolderLink, ExportChatFolderError> in
|
||||
return account.network.request(Api.functions.communities.exportCommunityInvite(community: .inputCommunityDialogFilter(filterId: filterId), title: title, peers: inputPeers))
|
||||
|> mapError { _ -> ExportChatFolderError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<ExportedChatFolderLink, ExportChatFolderError> in
|
||||
return account.postbox.transaction { transaction -> Signal<ExportedChatFolderLink, ExportChatFolderError> in
|
||||
switch result {
|
||||
case let .exportedCommunityInvite(filter, invite):
|
||||
let parsedFilter = ChatListFilter(apiFilter: filter)
|
||||
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
if let index = state.filters.firstIndex(where: { $0.id == filterId }) {
|
||||
state.filters[index] = parsedFilter
|
||||
} else {
|
||||
state.filters.append(parsedFilter)
|
||||
}
|
||||
state.remoteFilters = state.filters
|
||||
return state
|
||||
})
|
||||
|
||||
switch invite {
|
||||
case let .exportedCommunityInvite(title, url, peers):
|
||||
return .single(ExportedChatFolderLink(
|
||||
title: title,
|
||||
link: url,
|
||||
peerIds: peers.map(\.peerId)
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|> castError(ExportChatFolderError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_getExportedChatLinks(account: Account, id: Int32) -> Signal<[ExportedChatFolderLink], NoError> {
|
||||
return account.network.request(Api.functions.communities.getExportedInvites(community: .inputCommunityDialogFilter(filterId: id)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.communities.ExportedInvites?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<[ExportedChatFolderLink], NoError> in
|
||||
guard let result = result else {
|
||||
return .single([])
|
||||
}
|
||||
return account.postbox.transaction { transaction -> [ExportedChatFolderLink] in
|
||||
switch result {
|
||||
case let .exportedInvites(invites, chats, users):
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peerPresences[telegramUser.id] = user
|
||||
}
|
||||
for chat in chats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
var result: [ExportedChatFolderLink] = []
|
||||
for invite in invites {
|
||||
switch invite {
|
||||
case let .exportedCommunityInvite(title, url, peers):
|
||||
result.append(ExportedChatFolderLink(title: title, link: url, peerIds: peers.map(\.peerId)))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1025,6 +1025,14 @@ public extension TelegramEngine {
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
|
||||
public func exportChatFolder(filterId: Int32, title: String, peerIds: [PeerId]) -> Signal<ExportedChatFolderLink, ExportChatFolderError> {
|
||||
return _internal_exportChatFolder(account: self.account, filterId: filterId, title: title, peerIds: peerIds)
|
||||
}
|
||||
|
||||
public func getExportedChatLinks(id: Int32) -> Signal<[ExportedChatFolderLink], NoError> {
|
||||
return _internal_getExportedChatLinks(account: self.account, id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user