This commit is contained in:
Ali 2023-03-17 23:35:52 +04:00
parent 60db6559ea
commit a78924ccd9
23 changed files with 3226 additions and 536 deletions

View File

@ -88,6 +88,7 @@ swift_library(
"//submodules/TelegramUI/Components/ChatListTitleView",
"//submodules/AvatarNode:AvatarNode",
"//submodules/AvatarVideoNode:AvatarVideoNode",
"//submodules/InviteLinksUI",
],
visibility = [
"//visibility:public",

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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
}
}
}
}

View File

@ -26,6 +26,7 @@ swift_library(
"//submodules/AccountContext:AccountContext",
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent",
"//submodules/CheckNode",
],
visibility = [
"//visibility:public",

View File

@ -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))

View File

@ -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:

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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()

View File

@ -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
}

View File

@ -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
}
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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! {

View File

@ -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,

View File

@ -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
}
}
}
}

View File

@ -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)
}
}
}