Mixed WIP

This commit is contained in:
Ali 2020-02-07 22:09:58 +00:00
parent e2071301c2
commit 9f73cc4018
31 changed files with 1424 additions and 334 deletions

View File

@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
return 109;
return 110;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {

View File

@ -107,6 +107,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
private let hideNetworkActivityStatus: Bool
public let groupId: PeerGroupId
public let filter: ChatListFilter?
public let previewing: Bool
let openMessageFromSearchDisposable: MetaDisposable = MetaDisposable()
@ -145,12 +146,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
}
public init(context: AccountContext, groupId: PeerGroupId, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool = false, previewing: Bool = false, enableDebugActions: Bool) {
public init(context: AccountContext, groupId: PeerGroupId, filter: ChatListFilter? = nil, controlsHistoryPreload: Bool, hideNetworkActivityStatus: Bool = false, previewing: Bool = false, enableDebugActions: Bool) {
self.context = context
self.controlsHistoryPreload = controlsHistoryPreload
self.hideNetworkActivityStatus = hideNetworkActivityStatus
self.groupId = groupId
self.filter = filter
self.previewing = previewing
self.presentationData = (context.sharedContext.currentPresentationData.with { $0 })
@ -163,7 +165,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
let title: String
if case .root = self.groupId {
if let filter = self.filter {
title = filter.title ?? ""
} else if self.groupId == .root {
title = self.presentationData.strings.DialogList_Title
self.navigationBar?.item = nil
} else {
@ -174,7 +178,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self.navigationItem.titleView = self.titleView
if !previewing {
if case .root = groupId {
if self.groupId == .root && self.filter == nil {
self.tabBarItem.title = self.presentationData.strings.DialogList_Title
let icon: UIImage?
@ -270,15 +274,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
).start(next: { [weak self] networkState, proxy, passcode, state, chatListFilter in
if let strongSelf = self {
let defaultTitle: String
if case .root = strongSelf.groupId {
if strongSelf.groupId == .root {
if let chatListFilter = chatListFilter {
let title: String
switch chatListFilter.name {
case .unread:
title = "Unread"
case let .custom(value):
title = value
}
let title: String = chatListFilter.title ?? ""
defaultTitle = title
} else {
defaultTitle = strongSelf.presentationData.strings.DialogList_Title
@ -287,7 +285,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
defaultTitle = strongSelf.presentationData.strings.ChatList_ArchivedChatsTitle
}
if state.editing {
if case .root = strongSelf.groupId {
if strongSelf.groupId == .root && strongSelf.filter == nil {
strongSelf.navigationItem.rightBarButtonItem = nil
}
@ -297,9 +295,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
var isRoot = false
if case .root = strongSelf.groupId {
isRoot = true
let rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationComposeIcon(strongSelf.presentationData.theme), style: .plain, target: strongSelf, action: #selector(strongSelf.composePressed))
rightBarButtonItem.accessibilityLabel = strongSelf.presentationData.strings.VoiceOver_Navigation_Compose
strongSelf.navigationItem.rightBarButtonItem = rightBarButtonItem
if strongSelf.filter == nil {
let rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationComposeIcon(strongSelf.presentationData.theme), style: .plain, target: strongSelf, action: #selector(strongSelf.composePressed))
rightBarButtonItem.accessibilityLabel = strongSelf.presentationData.strings.VoiceOver_Navigation_Compose
strongSelf.navigationItem.rightBarButtonItem = rightBarButtonItem
}
}
let (hasProxy, connectsViaProxy) = proxy
@ -322,7 +322,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
case .online:
strongSelf.titleView.title = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: isRoot && hasProxy, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked)
}
if case .root = groupId, checkProxy {
if groupId == .root && filter == nil && checkProxy {
if strongSelf.proxyUnavailableTooltipController == nil && !strongSelf.didShowProxyUnavailableTooltipController && strongSelf.isNodeLoaded && strongSelf.displayNode.view.window != nil && strongSelf.navigationController?.topViewController === self {
strongSelf.didShowProxyUnavailableTooltipController = true
let tooltipController = TooltipController(content: .text(strongSelf.presentationData.strings.Proxy_TooltipUnavailable), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, timeout: 60.0, dismissByTapOutside: true)
@ -444,7 +444,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
editItem.accessibilityLabel = self.presentationData.strings.Common_Edit
}
if case .root = self.groupId {
if self.groupId == .root && self.filter == nil {
self.navigationItem.leftBarButtonItem = editItem
let rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationComposeIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.composePressed))
rightBarButtonItem.accessibilityLabel = self.presentationData.strings.VoiceOver_Navigation_Compose
@ -464,7 +464,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
override public func loadDisplayNode() {
self.displayNode = ChatListControllerNode(context: self.context, groupId: self.groupId, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, controller: self)
self.displayNode = ChatListControllerNode(context: self.context, groupId: self.groupId, filter: self.filter, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, controller: self)
self.chatListDisplayNode.navigationBar = self.navigationBar
@ -1810,9 +1810,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
public func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) {
if self.isNodeLoaded {
let _ = (self.context.account.postbox.transaction { transaction -> [ChatListFilterPreset] in
let settings = transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatListFilterSettings) as? ChatListFilterSettings ?? ChatListFilterSettings.default
return settings.presets
let _ = (self.context.account.postbox.transaction { transaction -> [ChatListFilter] in
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default
return settings.filters
}
|> deliverOnMainQueue).start(next: { [weak self] presetList in
guard let strongSelf = self else {
@ -1826,7 +1826,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
guard let strongSelf = self else {
return
}
if let currentPreset = strongSelf.chatListDisplayNode.chatListNode.chatListFilter {
/*if let currentPreset = strongSelf.chatListDisplayNode.chatListNode.chatListFilter {
var found = false
if let index = presets.index(where: { $0.id == currentPreset.id }) {
found = true
@ -1837,13 +1837,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
if !found {
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = nil
}
}
}*/
}))
}, updatePreset: { value in
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = value
strongSelf.push(ChatListControllerImpl(context: strongSelf.context, groupId: .root, filter: value, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: false, enableDebugActions: false))
//strongSelf.chatListDisplayNode.chatListNode.chatListFilter = value
})
strongSelf.context.sharedContext.mainWindow?.present(controller, on: .root)
})

View File

@ -72,12 +72,12 @@ final class ChatListControllerNode: ASDisplayNode {
let debugListView = ListView()
init(context: AccountContext, groupId: PeerGroupId, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, controller: ChatListControllerImpl) {
init(context: AccountContext, groupId: PeerGroupId, filter: ChatListFilter?, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, controller: ChatListControllerImpl) {
self.context = context
self.groupId = groupId
self.presentationData = presentationData
self.chatListNode = ChatListNode(context: context, groupId: groupId, previewing: previewing, controlsHistoryPreload: controlsHistoryPreload, mode: .chatList, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations)
self.chatListNode = ChatListNode(context: context, groupId: groupId, chatListFilter: filter, previewing: previewing, controlsHistoryPreload: controlsHistoryPreload, mode: .chatList, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations)
self.controller = controller

View File

@ -35,7 +35,7 @@ private enum ChatListFilterPresetControllerSection: Int32 {
case additionalPeers
}
private func filterEntry(presentationData: ItemListPresentationData, arguments: ChatListFilterPresetControllerArguments, title: String, value: Bool, filter: ChatListIncludeCategoryFilter, section: Int32) -> ItemListCheckboxItem {
private func filterEntry(presentationData: ItemListPresentationData, arguments: ChatListFilterPresetControllerArguments, title: String, value: Bool, filter: ChatListFilterPeerCategories, section: Int32) -> ItemListCheckboxItem {
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: section, action: {
arguments.updateState { current in
var state = current
@ -188,11 +188,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { _ in
arguments.updateState { current in
var state = current
if state.includeCategories.contains(.muted) {
state.includeCategories.remove(.muted)
} else {
state.includeCategories.insert(.muted)
}
state.excludeMuted = !state.excludeMuted
return state
}
})
@ -200,11 +196,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { _ in
arguments.updateState { current in
var state = current
if state.includeCategories.contains(.read) {
state.includeCategories.remove(.read)
} else {
state.includeCategories.insert(.read)
}
state.excludeRead = !state.excludeRead
return state
}
})
@ -230,7 +222,9 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
private struct ChatListFilterPresetControllerState: Equatable {
var name: String
var includeCategories: ChatListIncludeCategoryFilter
var includeCategories: ChatListFilterPeerCategories
var excludeMuted: Bool
var excludeRead: Bool
var additionallyIncludePeers: [PeerId]
var revealedPeerId: PeerId?
@ -239,7 +233,7 @@ private struct ChatListFilterPresetControllerState: Equatable {
if self.name.isEmpty {
return false
}
if self.includeCategories.isEmpty && self.additionallyIncludePeers.isEmpty {
if self.includeCategories.isEmpty && self.additionallyIncludePeers.isEmpty && !self.excludeMuted && !self.excludeRead {
return false
}
return true
@ -259,8 +253,8 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio
entries.append(.filterPublicGroups(title: "Public Groups", value: state.includeCategories.contains(.publicGroups)))
entries.append(.filterChannels(title: "Channels", value: state.includeCategories.contains(.channels)))
entries.append(.filterMuted(title: "Exclude Muted", value: !state.includeCategories.contains(.muted)))
entries.append(.filterRead(title: "Exclude Read", value: !state.includeCategories.contains(.read)))
entries.append(.filterMuted(title: "Exclude Muted", value: state.excludeMuted))
entries.append(.filterRead(title: "Exclude Read", value: state.excludeRead))
entries.append(.additionalPeersHeader("ALWAYS INCLUDE"))
entries.append(.addAdditionalPeer(title: "Add"))
@ -274,19 +268,14 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio
return entries
}
func chatListFilterPresetController(context: AccountContext, currentPreset: ChatListFilterPreset?, updated: @escaping ([ChatListFilterPreset]) -> Void) -> ViewController {
func chatListFilterPresetController(context: AccountContext, currentPreset: ChatListFilter?, updated: @escaping ([ChatListFilter]) -> Void) -> ViewController {
let initialName: String
if let currentPreset = currentPreset {
switch currentPreset.name {
case .unread:
initialName = "Unread"
case let .custom(value):
initialName = value
}
initialName = currentPreset.title ?? ""
} else {
initialName = "New Preset"
}
let initialState = ChatListFilterPresetControllerState(name: initialName, includeCategories: currentPreset?.includeCategories ?? .all, additionallyIncludePeers: currentPreset?.additionallyIncludePeers ?? [])
let initialState = ChatListFilterPresetControllerState(name: initialName, includeCategories: currentPreset?.categories ?? .all, excludeMuted: currentPreset?.excludeMuted ?? false, excludeRead: currentPreset?.excludeRead ?? false, additionallyIncludePeers: currentPreset?.includePeers ?? [])
let stateValue = Atomic(value: initialState)
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let updateState: ((ChatListFilterPresetControllerState) -> ChatListFilterPresetControllerState) -> Void = { f in
@ -378,15 +367,19 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
})
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: {
let state = stateValue.with { $0 }
let preset = ChatListFilterPreset(id: currentPreset?.id ?? arc4random64(), name: .custom(state.name), includeCategories: state.includeCategories, additionallyIncludePeers: state.additionallyIncludePeers)
let preset = ChatListFilter(id: currentPreset?.id ?? -1, title: state.name, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, includePeers: state.additionallyIncludePeers)
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
var preset = preset
if currentPreset == nil {
preset.id = max(1, settings.filters.map({ $0.id }).max() ?? 1)
}
var settings = settings
settings.presets = settings.presets.filter { $0 != preset && $0 != currentPreset }
settings.presets.append(preset)
settings.filters = settings.filters.filter { $0 != preset && $0 != currentPreset }
settings.filters.append(preset)
return settings
})
|> deliverOnMainQueue).start(next: { settings in
updated(settings.presets)
updated(settings.filters)
dismissImpl?()
})
})

View File

@ -13,12 +13,12 @@ import AccountContext
private final class ChatListFilterPresetListControllerArguments {
let context: AccountContext
let openPreset: (ChatListFilterPreset) -> Void
let openPreset: (ChatListFilter) -> Void
let addNew: () -> Void
let setItemWithRevealedOptions: (ChatListFilterPreset?, ChatListFilterPreset?) -> Void
let removePreset: (ChatListFilterPreset) -> Void
let setItemWithRevealedOptions: (Int32?, Int32?) -> Void
let removePreset: (Int32) -> Void
init(context: AccountContext, openPreset: @escaping (ChatListFilterPreset) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (ChatListFilterPreset?, ChatListFilterPreset?) -> Void, removePreset: @escaping (ChatListFilterPreset) -> Void) {
init(context: AccountContext, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void) {
self.context = context
self.openPreset = openPreset
self.addNew = addNew
@ -45,14 +45,14 @@ private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings
private enum ChatListFilterPresetListEntryStableId: Hashable {
case listHeader
case preset(Int64)
case preset(Int32)
case addItem
case listFooter
}
private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
case listHeader(String)
case preset(index: Int, title: String, preset: ChatListFilterPreset, canBeReordered: Bool, canBeDeleted: Bool)
case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool)
case addItem(String)
case listFooter(String)
@ -99,12 +99,12 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
case let .listHeader(text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section)
case let .preset(index, title, preset, canBeReordered, canBeDeleted):
return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title, editing: ChatListFilterPresetListItemEditing(editable: true, editing: false, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, sectionId: self.section, action: {
return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title ?? "", editing: ChatListFilterPresetListItemEditing(editable: true, editing: false, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, sectionId: self.section, action: {
arguments.openPreset(preset)
}, setItemWithRevealedOptions: { lhs, rhs in
arguments.setItemWithRevealedOptions(lhs, rhs)
}, remove: {
arguments.removePreset(preset)
arguments.removePreset(preset.id)
})
case let .addItem(text):
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
@ -117,22 +117,15 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
}
private struct ChatListFilterPresetListControllerState: Equatable {
var revealedPreset: ChatListFilterPreset? = nil
var revealedPreset: Int32? = nil
}
private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, settings: ChatListFilterSettings) -> [ChatListFilterPresetListEntry] {
private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, settings: ChatListFiltersState) -> [ChatListFilterPresetListEntry] {
var entries: [ChatListFilterPresetListEntry] = []
entries.append(.listHeader("PRESETS"))
for preset in settings.presets {
let title: String
switch preset.name {
case .unread:
title = "Unread"
case let .custom(value):
title = value
}
entries.append(.preset(index: entries.count, title: title, preset: preset, canBeReordered: settings.presets.count > 1, canBeDeleted: true))
entries.append(.listHeader("FILTERS"))
for preset in settings.filters {
entries.append(.preset(index: entries.count, title: preset.title, preset: preset, canBeReordered: settings.filters.count > 1, canBeDeleted: true))
}
entries.append(.addItem("Add New"))
entries.append(.listFooter("Add custom presets"))
@ -140,7 +133,7 @@ private func chatListFilterPresetListControllerEntries(presentationData: Present
return entries
}
func chatListFilterPresetListController(context: AccountContext, updated: @escaping ([ChatListFilterPreset]) -> Void) -> ViewController {
func chatListFilterPresetListController(context: AccountContext, updated: @escaping ([ChatListFilter]) -> Void) -> ViewController {
let initialState = ChatListFilterPresetListControllerState()
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
@ -164,20 +157,20 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
}
return state
}
}, removePreset: { preset in
}, removePreset: { id in
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
var settings = settings
if let index = settings.presets.index(of: preset) {
settings.presets.remove(at: index)
if let index = settings.filters.index(where: { $0.id == id }) {
settings.filters.remove(at: index)
}
return settings
})
|> deliverOnMainQueue).start(next: { settings in
updated(settings.presets)
updated(settings.filters)
})
})
let preferences = context.account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.chatListFilterSettings])
let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
let signal = combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData,
@ -185,12 +178,13 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
preferences
)
|> map { presentationData, state, preferences -> (ItemListControllerState, (ItemListNodeState, Any)) in
let settings = preferences.values[ApplicationSpecificPreferencesKeys.chatListFilterSettings] as? ChatListFilterSettings ?? ChatListFilterSettings.default
let settings = preferences.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState ?? ChatListFiltersState.default
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Close), style: .regular, enabled: true, action: {
let _ = replaceRemoteChatListFilters(account: context.account).start()
dismissImpl?()
})
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Filter Presets"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Filter Presets"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetListControllerEntries(presentationData: presentationData, state: state, settings: settings), style: .blocks, animateChanges: true)
return (controllerState, (listState, arguments))

View File

@ -18,26 +18,26 @@ struct ChatListFilterPresetListItemEditing: Equatable {
final class ChatListFilterPresetListItem: ListViewItem, ItemListItem {
let presentationData: ItemListPresentationData
let preset: ChatListFilterPreset
let preset: ChatListFilter
let title: String
let editing: ChatListFilterPresetListItemEditing
let canBeReordered: Bool
let canBeDeleted: Bool
let sectionId: ItemListSectionId
let action: () -> Void
let setItemWithRevealedOptions: (ChatListFilterPreset?, ChatListFilterPreset?) -> Void
let setItemWithRevealedOptions: (Int32?, Int32?) -> Void
let remove: () -> Void
init(
presentationData: ItemListPresentationData,
preset: ChatListFilterPreset,
preset: ChatListFilter,
title: String,
editing: ChatListFilterPresetListItemEditing,
canBeReordered: Bool,
canBeDeleted: Bool,
sectionId: ItemListSectionId,
action: @escaping () -> Void,
setItemWithRevealedOptions: @escaping (ChatListFilterPreset?, ChatListFilterPreset?) -> Void,
setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void,
remove: @escaping () -> Void
) {
self.presentationData = presentationData
@ -409,13 +409,13 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN
override func revealOptionsInteractivelyOpened() {
if let item = self.item {
item.setItemWithRevealedOptions(item.preset, nil)
item.setItemWithRevealedOptions(item.preset.id, nil)
}
}
override func revealOptionsInteractivelyClosed() {
if let item = self.item {
item.setItemWithRevealedOptions(nil, item.preset)
item.setItemWithRevealedOptions(nil, item.preset.id)
}
}

View File

@ -366,21 +366,9 @@ public final class ChatListNode: ListView {
}
private var currentLocation: ChatListNodeLocation?
var chatListFilter: ChatListFilterPreset? {
didSet {
if self.chatListFilter != oldValue {
self.chatListFilterValue.set(self.chatListFilter)
if self.chatListFilter?.includeCategories != oldValue?.includeCategories || self.chatListFilter?.additionallyIncludePeers != oldValue?.additionallyIncludePeers {
if let currentLocation = self.currentLocation {
self.setChatListLocation(.initial(count: 50, filter: self.chatListFilter))
}
}
}
}
}
private let chatListFilterValue = ValuePromise<ChatListFilterPreset?>(nil)
var chatListFilterSignal: Signal<ChatListFilterPreset?, NoError> {
let chatListFilter: ChatListFilter?
private let chatListFilterValue = Promise<ChatListFilter?>()
var chatListFilterSignal: Signal<ChatListFilter?, NoError> {
return self.chatListFilterValue.get()
}
private let chatListLocation = ValuePromise<ChatListNodeLocation>()
@ -425,9 +413,11 @@ public final class ChatListNode: ListView {
private var hapticFeedback: HapticFeedback?
public init(context: AccountContext, groupId: PeerGroupId, previewing: Bool, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
public init(context: AccountContext, groupId: PeerGroupId, chatListFilter: ChatListFilter? = nil, previewing: Bool, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.context = context
self.groupId = groupId
self.chatListFilter = chatListFilter
self.chatListFilterValue.set(.single(chatListFilter))
self.controlsHistoryPreload = controlsHistoryPreload
self.mode = mode
@ -541,7 +531,7 @@ public final class ChatListNode: ListView {
let chatListViewUpdate = self.chatListLocation.get()
|> distinctUntilChanged
|> mapToSignal { location -> Signal<(ChatListNodeViewUpdate, ChatListFilterPreset?), NoError> in
|> mapToSignal { location -> Signal<(ChatListNodeViewUpdate, ChatListFilter?), NoError> in
return chatListViewForLocation(groupId: groupId, location: location, account: context.account)
|> map { update in
return (update, location.filter)

View File

@ -7,11 +7,11 @@ import Display
import TelegramUIPreferences
enum ChatListNodeLocation: Equatable {
case initial(count: Int, filter: ChatListFilterPreset?)
case navigation(index: ChatListIndex, filter: ChatListFilterPreset?)
case scroll(index: ChatListIndex, sourceIndex: ChatListIndex, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilterPreset?)
case initial(count: Int, filter: ChatListFilter?)
case navigation(index: ChatListIndex, filter: ChatListFilter?)
case scroll(index: ChatListIndex, sourceIndex: ChatListIndex, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilter?)
var filter: ChatListFilterPreset? {
var filter: ChatListFilter? {
switch self {
case let .initial(initial):
return initial.filter
@ -32,17 +32,17 @@ struct ChatListNodeViewUpdate {
func chatListViewForLocation(groupId: PeerGroupId, location: ChatListNodeLocation, account: Account) -> Signal<ChatListNodeViewUpdate, NoError> {
let filterPredicate: ((Peer, PeerNotificationSettings?, Bool) -> Bool)?
if let filter = location.filter {
let includePeers = Set(filter.additionallyIncludePeers)
let includePeers = Set(filter.includePeers)
filterPredicate = { peer, notificationSettings, isUnread in
if includePeers.contains(peer.id) {
return true
}
if !filter.includeCategories.contains(.read) {
if filter.excludeRead {
if !isUnread {
return false
}
}
if !filter.includeCategories.contains(.muted) {
if filter.excludeMuted {
if let notificationSettings = notificationSettings as? TelegramPeerNotificationSettings {
if case .muted = notificationSettings.muteState {
return false
@ -51,26 +51,26 @@ func chatListViewForLocation(groupId: PeerGroupId, location: ChatListNodeLocatio
return false
}
}
if !filter.includeCategories.contains(.privateChats) {
if !filter.categories.contains(.privateChats) {
if let user = peer as? TelegramUser {
if user.botInfo == nil {
return false
}
}
}
if !filter.includeCategories.contains(.secretChats) {
if !filter.categories.contains(.secretChats) {
if let _ = peer as? TelegramSecretChat {
return false
}
}
if !filter.includeCategories.contains(.bots) {
if !filter.categories.contains(.bots) {
if let user = peer as? TelegramUser {
if user.botInfo != nil {
return false
}
}
}
if !filter.includeCategories.contains(.privateGroups) {
if !filter.categories.contains(.privateGroups) {
if let _ = peer as? TelegramGroup {
return false
} else if let channel = peer as? TelegramChannel {
@ -81,7 +81,7 @@ func chatListViewForLocation(groupId: PeerGroupId, location: ChatListNodeLocatio
}
}
}
if !filter.includeCategories.contains(.publicGroups) {
if !filter.categories.contains(.publicGroups) {
if let channel = peer as? TelegramChannel {
if case .group = channel.info {
if channel.username != nil {
@ -90,7 +90,7 @@ func chatListViewForLocation(groupId: PeerGroupId, location: ChatListNodeLocatio
}
}
}
if !filter.includeCategories.contains(.channels) {
if !filter.categories.contains(.channels) {
if let channel = peer as? TelegramChannel {
if case .broadcast = channel.info {
return false

View File

@ -12,7 +12,7 @@ struct ChatListNodeView {
let originalView: ChatListView
let filteredEntries: [ChatListNodeEntry]
let isLoading: Bool
let filter: ChatListFilterPreset?
let filter: ChatListFilter?
}
enum ChatListNodeViewTransitionReason {

View File

@ -8,6 +8,7 @@ import AccountContext
import SyncCore
import Postbox
import TelegramUIPreferences
import TelegramCore
final class TabBarChatListFilterController: ViewController {
private var controllerNode: TabBarChatListFilterControllerNode {
@ -21,17 +22,17 @@ final class TabBarChatListFilterController: ViewController {
private let context: AccountContext
private let sourceNodes: [ASDisplayNode]
private let presetList: [ChatListFilterPreset]
private let currentPreset: ChatListFilterPreset?
private let presetList: [ChatListFilter]
private let currentPreset: ChatListFilter?
private let setup: () -> Void
private let updatePreset: (ChatListFilterPreset?) -> Void
private let updatePreset: (ChatListFilter?) -> Void
private var presentationData: PresentationData
private var didPlayPresentationAnimation = false
private let hapticFeedback = HapticFeedback()
public init(context: AccountContext, sourceNodes: [ASDisplayNode], presetList: [ChatListFilterPreset], currentPreset: ChatListFilterPreset?, setup: @escaping () -> Void, updatePreset: @escaping (ChatListFilterPreset?) -> Void) {
public init(context: AccountContext, sourceNodes: [ASDisplayNode], presetList: [ChatListFilter], currentPreset: ChatListFilter?, setup: @escaping () -> Void, updatePreset: @escaping (ChatListFilter?) -> Void) {
self.context = context
self.sourceNodes = sourceNodes
self.presetList = presetList
@ -184,7 +185,7 @@ private final class AddFilterItemNode: ASDisplayNode, AbstractTabBarChatListFilt
private final class FilterItemNode: ASDisplayNode, AbstractTabBarChatListFilterItemNode {
private let context: AccountContext
private let title: String
let preset: ChatListFilterPreset?
let preset: ChatListFilter?
private let isCurrent: Bool
private let presentationData: PresentationData
private let action: () -> Bool
@ -199,7 +200,7 @@ private final class FilterItemNode: ASDisplayNode, AbstractTabBarChatListFilterI
private let badgeTitleNode: ImmediateTextNode
private var badgeText: String = ""
init(context: AccountContext, title: String, preset: ChatListFilterPreset?, isCurrent: Bool, displaySeparator: Bool, presentationData: PresentationData, action: @escaping () -> Bool) {
init(context: AccountContext, title: String, preset: ChatListFilter?, isCurrent: Bool, displaySeparator: Bool, presentationData: PresentationData, action: @escaping () -> Bool) {
self.context = context
self.title = title
self.preset = preset
@ -324,7 +325,7 @@ private final class TabBarChatListFilterControllerNode: ViewControllerTracingNod
let isReady = Promise<Bool>()
private var didSetIsReady = false
init(context: AccountContext, presentationData: PresentationData, cancel: @escaping () -> Void, sourceNodes: [ASDisplayNode], presetList: [ChatListFilterPreset], currentPreset: ChatListFilterPreset?, setup: @escaping () -> Void, updatePreset: @escaping (ChatListFilterPreset?) -> Void) {
init(context: AccountContext, presentationData: PresentationData, cancel: @escaping () -> Void, sourceNodes: [ASDisplayNode], presetList: [ChatListFilter], currentPreset: ChatListFilter?, setup: @escaping () -> Void, updatePreset: @escaping (ChatListFilter?) -> Void) {
self.presentationData = presentationData
self.cancel = cancel
self.sourceNodes = sourceNodes
@ -358,21 +359,10 @@ private final class TabBarChatListFilterControllerNode: ViewControllerTracingNod
setup()
}))
contentNodes.append(FilterItemNode(context: context, title: "All", preset: nil, isCurrent: currentPreset == nil, displaySeparator: !presetList.isEmpty, presentationData: presentationData, action: {
updatePreset(nil)
return false
}))
for i in 0 ..< presetList.count {
let preset = presetList[i]
let title: String
switch preset.name {
case .unread:
title = "Unread"
case let .custom(value):
title = value
}
let title: String = preset.title ?? ""
contentNodes.append(FilterItemNode(context: context, title: title, preset: preset, isCurrent: currentPreset == preset, displaySeparator: i != presetList.count - 1, presentationData: presentationData, action: {
updatePreset(preset)
return false
@ -393,7 +383,7 @@ private final class TabBarChatListFilterControllerNode: ViewControllerTracingNod
unreadCountItems.append(.total(nil))
var additionalPeerIds = Set<PeerId>()
for preset in presetList {
additionalPeerIds.formUnion(preset.additionallyIncludePeers)
additionalPeerIds.formUnion(preset.includePeers)
}
if !additionalPeerIds.isEmpty {
for peerId in additionalPeerIds {
@ -447,36 +437,34 @@ private final class TabBarChatListFilterControllerNode: ViewControllerTracingNod
let badgeString: String
if let preset = contentNode.preset {
var tags: [PeerSummaryCounterTags] = []
if preset.includeCategories.contains(.privateChats) {
if preset.categories.contains(.privateChats) {
tags.append(.privateChat)
}
if preset.includeCategories.contains(.secretChats) {
if preset.categories.contains(.secretChats) {
tags.append(.secretChat)
}
if preset.includeCategories.contains(.privateGroups) {
if preset.categories.contains(.privateGroups) {
tags.append(.privateGroup)
}
if preset.includeCategories.contains(.bots) {
if preset.categories.contains(.bots) {
tags.append(.bot)
}
if preset.includeCategories.contains(.publicGroups) {
if preset.categories.contains(.publicGroups) {
tags.append(.publicGroup)
}
if preset.includeCategories.contains(.channels) {
if preset.categories.contains(.channels) {
tags.append(.channel)
}
var count = 0
if let totalState = totalState {
for tag in tags {
if preset.includeCategories.contains(.muted) {
}
if let value = totalState.filteredCounters[tag] {
count += Int(value.chatCount)
}
}
}
for peerId in preset.additionallyIncludePeers {
for peerId in preset.includePeers {
if let (tag, peerCount) = peerTagAndCount[peerId] {
if !tags.contains(tag) {
count += peerCount

View File

@ -60,6 +60,12 @@ public final class ListViewBackingView: UIView {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.isHidden, let target = self.target {
if target.bounds.contains(point) {
if target.decelerationAnimator != nil {
target.decelerationAnimator?.isPaused = true
target.decelerationAnimator = nil
}
}
if target.limitHitTestToNodes, !target.internalHitTest(point, with: event) {
return nil
}
@ -685,6 +691,48 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
}
fileprivate var decelerationAnimator: ConstantDisplayLinkAnimator?
private var accumulatedTransferVelocityOffset: CGFloat = 0.0
public func transferVelocity(_ velocity: CGFloat) {
self.decelerationAnimator?.isPaused = true
let startTime = CACurrentMediaTime()
let decelerationRate: CGFloat = 0.998
self.decelerationAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in
guard let strongSelf = self else {
return
}
let t = CACurrentMediaTime() - startTime
var currentVelocity = velocity * 15.0 * CGFloat(pow(Double(decelerationRate), 1000.0 * t))
strongSelf.accumulatedTransferVelocityOffset += currentVelocity
let signFactor: CGFloat = strongSelf.accumulatedTransferVelocityOffset >= 0.0 ? 1.0 : -1.0
let remainder = abs(strongSelf.accumulatedTransferVelocityOffset).remainder(dividingBy: UIScreenPixel)
//print("accumulated \(strongSelf.accumulatedTransferVelocityOffset), \(remainder), resulting accumulated \(strongSelf.accumulatedTransferVelocityOffset - remainder * signFactor) add delta \(strongSelf.accumulatedTransferVelocityOffset - remainder * signFactor)")
var currentOffset = strongSelf.scroller.contentOffset
let addedDela = strongSelf.accumulatedTransferVelocityOffset - remainder * signFactor
currentOffset.y += addedDela
strongSelf.accumulatedTransferVelocityOffset -= addedDela
let maxOffset = strongSelf.scroller.contentSize.height - strongSelf.scroller.bounds.height
if currentOffset.y >= maxOffset {
currentOffset.y = maxOffset
currentVelocity = 0.0
}
if currentOffset.y < 0.0 {
currentOffset.y = 0.0
currentVelocity = 0.0
}
if abs(currentVelocity) < 0.1 {
strongSelf.decelerationAnimator?.isPaused = true
strongSelf.decelerationAnimator = nil
}
var contentOffset = strongSelf.scroller.contentOffset
contentOffset.y = floorToScreenPixels(currentOffset.y)
strongSelf.scroller.setContentOffset(contentOffset, animated: false)
})
self.decelerationAnimator?.isPaused = false
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateScrollViewDidScroll(scrollView, synchronous: false)
}

View File

@ -196,6 +196,7 @@ private enum PreferencesKeyValues: Int32 {
case secretChatSettings = 17
case walletCollection = 18
case contentSettings = 19
case chatListFilters = 20
}
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -306,6 +307,12 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.contentSettings.rawValue)
return key
}()
public static let chatListFilters: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.chatListFilters.rawValue)
return key
}()
}
private enum SharedDataKeyValues: Int32 {

View File

@ -19,6 +19,7 @@ public enum MessageTextEntityType: Equatable {
case Strikethrough
case BlockQuote
case Underline
case BankCard
case Custom(type: CustomEntityType)
}
@ -65,6 +66,8 @@ public struct MessageTextEntity: PostboxCoding, Equatable {
self.type = .BlockQuote
case 15:
self.type = .Underline
case 16:
self.type = .BankCard
case Int32.max:
self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0))
default:
@ -110,6 +113,8 @@ public struct MessageTextEntity: PostboxCoding, Equatable {
encoder.encodeInt32(14, forKey: "_rawValue")
case .Underline:
encoder.encodeInt32(15, forKey: "_rawValue")
case .BankCard:
encoder.encodeInt32(16, forKey: "_rawValue")
case let .Custom(type):
encoder.encodeInt32(Int32.max, forKey: "_rawValue")
encoder.encodeInt32(type, forKey: "type")

View File

@ -10,7 +10,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-206066487] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[461151667] = { return Api.ChatFull.parse_chatFull($0) }
dict[763976820] = { return Api.ChatFull.parse_channelFull($0) }
dict[-253335766] = { return Api.ChatFull.parse_channelFull($0) }
dict[-932174686] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -247,6 +247,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) }
dict[1123585836] = { return Api.Update.parse_updateMessagePollVote($0) }
dict[654302845] = { return Api.Update.parse_updateDialogFilter($0) }
dict[-1512627963] = { return Api.Update.parse_updateDialogFilterOrder($0) }
dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) }
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@ -412,6 +415,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) }
dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) }
dict[-1269012015] = { return Api.messages.AffectedHistory.parse_affectedHistory($0) }
dict[1244130093] = { return Api.StatsGraph.parse_statsGraphAsync($0) }
dict[-1092839390] = { return Api.StatsGraph.parse_statsGraphError($0) }
dict[-1057809608] = { return Api.StatsGraph.parse_statsGraph($0) }
dict[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) }
dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) }
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
@ -480,6 +486,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-668391402] = { return Api.InputUser.parse_inputUser($0) }
dict[-1366746132] = { return Api.Page.parse_page($0) }
dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) }
dict[-875679776] = { return Api.StatsPercentValue.parse_statsPercentValue($0) }
dict[157948117] = { return Api.upload.File.parse_file($0) }
dict[-242427324] = { return Api.upload.File.parse_fileCdnRedirect($0) }
dict[-1078612597] = { return Api.ChannelLocation.parse_channelLocationEmpty($0) }
@ -506,6 +513,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
dict[-1564789301] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($0) }
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }
dict[-1567175714] = { return Api.MessageFwdAuthor.parse_messageFwdAuthor($0) }
dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) }
dict[-1963717851] = { return Api.WallPaper.parse_wallPaperNoFile($0) }
@ -520,6 +528,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1837345356] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) }
dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) }
dict[-368917890] = { return Api.PaymentCharge.parse_paymentCharge($0) }
dict[205195937] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) }
dict[-1857044719] = { return Api.Updates.parse_updateShortMessage($0) }
dict[377562760] = { return Api.Updates.parse_updateShortChatMessage($0) }
@ -527,6 +536,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) }
dict[1957577280] = { return Api.Updates.parse_updates($0) }
dict[301019932] = { return Api.Updates.parse_updateShortSentMessage($0) }
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
@ -589,6 +599,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1551583367] = { return Api.ReceivedNotifyMessage.parse_receivedNotifyMessage($0) }
dict[-57668565] = { return Api.ChatParticipants.parse_chatParticipantsForbidden($0) }
dict[1061556205] = { return Api.ChatParticipants.parse_chatParticipants($0) }
dict[351868460] = { return Api.DialogFilter.parse_dialogFilter($0) }
dict[-1056001329] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsSaved($0) }
dict[873977640] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentials($0) }
dict[178373535] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsApplePay($0) }
@ -761,6 +772,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
dict[-581804346] = { return Api.StatsRowAbsValueAndPrev.parse_statsRowAbsValueAndPrev($0) }
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) }
dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) }
@ -782,6 +794,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1672577397] = { return Api.MessageEntity.parse_messageEntityUnderline($0) }
dict[-1090087980] = { return Api.MessageEntity.parse_messageEntityStrike($0) }
dict[34469328] = { return Api.MessageEntity.parse_messageEntityBlockquote($0) }
dict[1981704948] = { return Api.MessageEntity.parse_messageEntityBankCard($0) }
dict[483901197] = { return Api.InputPhoto.parse_inputPhotoEmpty($0) }
dict[1001634122] = { return Api.InputPhoto.parse_inputPhoto($0) }
dict[-567906571] = { return Api.contacts.TopPeers.parse_topPeersNotModified($0) }
@ -804,6 +817,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) }
dict[-892779534] = { return Api.WebAuthorization.parse_webAuthorization($0) }
dict[-805141448] = { return Api.ImportedContact.parse_importedContact($0) }
dict[-1165694006] = { return Api.payments.BankCardData.parse_bankCardData($0) }
return dict
}()
@ -1081,6 +1095,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.AffectedHistory:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsGraph:
_1.serialize(buffer, boxed)
case let _1 as Api.account.PasswordInputSettings:
_1.serialize(buffer, boxed)
case let _1 as Api.PageTableCell:
@ -1151,6 +1167,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.SecureCredentialsEncrypted:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsPercentValue:
_1.serialize(buffer, boxed)
case let _1 as Api.upload.File:
_1.serialize(buffer, boxed)
case let _1 as Api.ChannelLocation:
@ -1185,6 +1203,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.PhoneCallProtocol:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsDateRangeDays:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageFwdAuthor:
_1.serialize(buffer, boxed)
case let _1 as Api.WallPaper:
@ -1201,8 +1221,12 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.PaymentCharge:
_1.serialize(buffer, boxed)
case let _1 as Api.stats.BroadcastStats:
_1.serialize(buffer, boxed)
case let _1 as Api.Updates:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsAbsValueAndPrev:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageMedia:
_1.serialize(buffer, boxed)
case let _1 as Api.PaymentSavedCredentials:
@ -1251,6 +1275,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.ChatParticipants:
_1.serialize(buffer, boxed)
case let _1 as Api.DialogFilter:
_1.serialize(buffer, boxed)
case let _1 as Api.InputPaymentCredentials:
_1.serialize(buffer, boxed)
case let _1 as Api.ShippingOption:
@ -1381,6 +1407,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.updates.ChannelDifference:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsRowAbsValueAndPrev:
_1.serialize(buffer, boxed)
case let _1 as Api.channels.AdminLogResults:
_1.serialize(buffer, boxed)
case let _1 as Api.ChatOnlines:
@ -1409,6 +1437,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.ImportedContact:
_1.serialize(buffer, boxed)
case let _1 as Api.payments.BankCardData:
_1.serialize(buffer, boxed)
default:
break
}

View File

@ -1805,7 +1805,7 @@ public extension Api {
}
public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, pts: Int32)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -1828,9 +1828,9 @@ public extension Api {
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let pts):
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts):
if boxed {
buffer.appendInt32(763976820)
buffer.appendInt32(-253335766)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -1861,6 +1861,7 @@ public extension Api {
if Int(flags) & Int(1 << 15) != 0 {location!.serialize(buffer, true)}
if Int(flags) & Int(1 << 17) != 0 {serializeInt32(slowmodeSeconds!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 18) != 0 {serializeInt32(slowmodeNextSendDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(statsDc!, buffer: buffer, boxed: false)}
serializeInt32(pts, buffer: buffer, boxed: false)
break
}
@ -1870,8 +1871,8 @@ public extension Api {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let pts):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("pts", pts)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts)])
}
}
@ -1987,7 +1988,9 @@ public extension Api {
var _25: Int32?
if Int(_1!) & Int(1 << 18) != 0 {_25 = reader.readInt32() }
var _26: Int32?
_26 = reader.readInt32()
if Int(_1!) & Int(1 << 12) != 0 {_26 = reader.readInt32() }
var _27: Int32?
_27 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -2013,9 +2016,10 @@ public extension Api {
let _c23 = (Int(_1!) & Int(1 << 15) == 0) || _23 != nil
let _c24 = (Int(_1!) & Int(1 << 17) == 0) || _24 != nil
let _c25 = (Int(_1!) & Int(1 << 18) == 0) || _25 != nil
let _c26 = _26 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, pts: _26!)
let _c26 = (Int(_1!) & Int(1 << 12) == 0) || _26 != nil
let _c27 = _27 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!)
}
else {
return nil
@ -5861,6 +5865,9 @@ public extension Api {
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
case updateLoginToken
case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer])
case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?)
case updateDialogFilterOrder(order: [Int32])
case updateDialogFilters
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -6509,6 +6516,30 @@ public extension Api {
for item in options {
serializeBytes(item, buffer: buffer, boxed: false)
}
break
case .updateDialogFilter(let flags, let id, let filter):
if boxed {
buffer.appendInt32(654302845)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)}
break
case .updateDialogFilterOrder(let order):
if boxed {
buffer.appendInt32(-1512627963)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(order.count))
for item in order {
serializeInt32(item, buffer: buffer, boxed: false)
}
break
case .updateDialogFilters:
if boxed {
buffer.appendInt32(889491791)
}
break
}
}
@ -6669,6 +6700,12 @@ public extension Api {
return ("updateLoginToken", [])
case .updateMessagePollVote(let pollId, let userId, let options):
return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", options)])
case .updateDialogFilter(let flags, let id, let filter):
return ("updateDialogFilter", [("flags", flags), ("id", id), ("filter", filter)])
case .updateDialogFilterOrder(let order):
return ("updateDialogFilterOrder", [("order", order)])
case .updateDialogFilters:
return ("updateDialogFilters", [])
}
}
@ -7965,6 +8002,41 @@ public extension Api {
return nil
}
}
public static func parse_updateDialogFilter(_ reader: BufferReader) -> Update? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Api.DialogFilter?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.DialogFilter
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3)
}
else {
return nil
}
}
public static func parse_updateDialogFilterOrder(_ reader: BufferReader) -> Update? {
var _1: [Int32]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.Update.updateDialogFilterOrder(order: _1!)
}
else {
return nil
}
}
public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? {
return Api.Update.updateDialogFilters
}
}
public enum PopularContact: TypeConstructorDescription {
@ -11956,6 +12028,82 @@ public extension Api {
}
}
}
public enum StatsGraph: TypeConstructorDescription {
case statsGraphAsync(token: String)
case statsGraphError(error: String)
case statsGraph(json: Api.DataJSON)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsGraphAsync(let token):
if boxed {
buffer.appendInt32(1244130093)
}
serializeString(token, buffer: buffer, boxed: false)
break
case .statsGraphError(let error):
if boxed {
buffer.appendInt32(-1092839390)
}
serializeString(error, buffer: buffer, boxed: false)
break
case .statsGraph(let json):
if boxed {
buffer.appendInt32(-1057809608)
}
json.serialize(buffer, true)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsGraphAsync(let token):
return ("statsGraphAsync", [("token", token)])
case .statsGraphError(let error):
return ("statsGraphError", [("error", error)])
case .statsGraph(let json):
return ("statsGraph", [("json", json)])
}
}
public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.StatsGraph.statsGraphAsync(token: _1!)
}
else {
return nil
}
}
public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.StatsGraph.statsGraphError(error: _1!)
}
else {
return nil
}
}
public static func parse_statsGraph(_ reader: BufferReader) -> StatsGraph? {
var _1: Api.DataJSON?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.DataJSON
}
let _c1 = _1 != nil
if _c1 {
return Api.StatsGraph.statsGraph(json: _1!)
}
else {
return nil
}
}
}
public enum PageTableCell: TypeConstructorDescription {
case pageTableCell(flags: Int32, text: Api.RichText?, colspan: Int32?, rowspan: Int32?)
@ -13662,6 +13810,44 @@ public extension Api {
}
}
}
public enum StatsPercentValue: TypeConstructorDescription {
case statsPercentValue(part: Double, total: Double)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsPercentValue(let part, let total):
if boxed {
buffer.appendInt32(-875679776)
}
serializeDouble(part, buffer: buffer, boxed: false)
serializeDouble(total, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsPercentValue(let part, let total):
return ("statsPercentValue", [("part", part), ("total", total)])
}
}
public static func parse_statsPercentValue(_ reader: BufferReader) -> StatsPercentValue? {
var _1: Double?
_1 = reader.readDouble()
var _2: Double?
_2 = reader.readDouble()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!)
}
else {
return nil
}
}
}
public enum ChannelLocation: TypeConstructorDescription {
case channelLocationEmpty
@ -14434,6 +14620,44 @@ public extension Api {
}
}
}
public enum StatsDateRangeDays: TypeConstructorDescription {
case statsDateRangeDays(minDate: Int32, maxDate: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsDateRangeDays(let minDate, let maxDate):
if boxed {
buffer.appendInt32(-1237848657)
}
serializeInt32(minDate, buffer: buffer, boxed: false)
serializeInt32(maxDate, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsDateRangeDays(let minDate, let maxDate):
return ("statsDateRangeDays", [("minDate", minDate), ("maxDate", maxDate)])
}
}
public static func parse_statsDateRangeDays(_ reader: BufferReader) -> StatsDateRangeDays? {
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.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!)
}
else {
return nil
}
}
}
public enum MessageFwdAuthor: TypeConstructorDescription {
case messageFwdAuthor(channelId: Int32)
@ -15098,6 +15322,44 @@ public extension Api {
}
}
}
public enum StatsAbsValueAndPrev: TypeConstructorDescription {
case statsAbsValueAndPrev(current: Double, previous: Double)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsAbsValueAndPrev(let current, let previous):
if boxed {
buffer.appendInt32(-884757282)
}
serializeDouble(current, buffer: buffer, boxed: false)
serializeDouble(previous, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsAbsValueAndPrev(let current, let previous):
return ("statsAbsValueAndPrev", [("current", current), ("previous", previous)])
}
}
public static func parse_statsAbsValueAndPrev(_ reader: BufferReader) -> StatsAbsValueAndPrev? {
var _1: Double?
_1 = reader.readDouble()
var _2: Double?
_2 = reader.readDouble()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.StatsAbsValueAndPrev.statsAbsValueAndPrev(current: _1!, previous: _2!)
}
else {
return nil
}
}
}
public enum MessageMedia: TypeConstructorDescription {
case messageMediaEmpty
@ -16814,6 +17076,58 @@ public extension Api {
}
}
}
public enum DialogFilter: TypeConstructorDescription {
case dialogFilter(flags: Int32, id: Int32, title: String, includePeers: [Api.InputPeer])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .dialogFilter(let flags, let id, let title, let includePeers):
if boxed {
buffer.appendInt32(351868460)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(includePeers.count))
for item in includePeers {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .dialogFilter(let flags, let id, let title, let includePeers):
return ("dialogFilter", [("flags", flags), ("id", id), ("title", title), ("includePeers", includePeers)])
}
}
public static func parse_dialogFilter(_ reader: BufferReader) -> DialogFilter? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: String?
_3 = parseString(reader)
var _4: [Api.InputPeer]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputPeer.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.DialogFilter.dialogFilter(flags: _1!, id: _2!, title: _3!, includePeers: _4!)
}
else {
return nil
}
}
}
public enum InputPaymentCredentials: TypeConstructorDescription {
case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer)
@ -20880,6 +21194,54 @@ public extension Api {
}
}
}
public enum StatsRowAbsValueAndPrev: TypeConstructorDescription {
case statsRowAbsValueAndPrev(id: String, title: String, shortTitle: String, values: Api.StatsAbsValueAndPrev)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsRowAbsValueAndPrev(let id, let title, let shortTitle, let values):
if boxed {
buffer.appendInt32(-581804346)
}
serializeString(id, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(shortTitle, buffer: buffer, boxed: false)
values.serialize(buffer, true)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsRowAbsValueAndPrev(let id, let title, let shortTitle, let values):
return ("statsRowAbsValueAndPrev", [("id", id), ("title", title), ("shortTitle", shortTitle), ("values", values)])
}
}
public static func parse_statsRowAbsValueAndPrev(_ reader: BufferReader) -> StatsRowAbsValueAndPrev? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.StatsAbsValueAndPrev?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.StatsRowAbsValueAndPrev.statsRowAbsValueAndPrev(id: _1!, title: _2!, shortTitle: _3!, values: _4!)
}
else {
return nil
}
}
}
public enum ChatOnlines: TypeConstructorDescription {
case chatOnlines(onlines: Int32)
@ -20982,6 +21344,7 @@ public extension Api {
case messageEntityUnderline(offset: Int32, length: Int32)
case messageEntityStrike(offset: Int32, length: Int32)
case messageEntityBlockquote(offset: Int32, length: Int32)
case messageEntityBankCard(offset: Int32, length: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -21115,6 +21478,13 @@ public extension Api {
serializeInt32(offset, buffer: buffer, boxed: false)
serializeInt32(length, buffer: buffer, boxed: false)
break
case .messageEntityBankCard(let offset, let length):
if boxed {
buffer.appendInt32(1981704948)
}
serializeInt32(offset, buffer: buffer, boxed: false)
serializeInt32(length, buffer: buffer, boxed: false)
break
}
}
@ -21156,6 +21526,8 @@ public extension Api {
return ("messageEntityStrike", [("offset", offset), ("length", length)])
case .messageEntityBlockquote(let offset, let length):
return ("messageEntityBlockquote", [("offset", offset), ("length", length)])
case .messageEntityBankCard(let offset, let length):
return ("messageEntityBankCard", [("offset", offset), ("length", length)])
}
}
@ -21425,6 +21797,20 @@ public extension Api {
return nil
}
}
public static func parse_messageEntityBankCard(_ reader: BufferReader) -> MessageEntity? {
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.MessageEntity.messageEntityBankCard(offset: _1!, length: _2!)
}
else {
return nil
}
}
}
public enum InputPhoto: TypeConstructorDescription {

View File

@ -490,6 +490,184 @@ public struct payments {
}
}
public enum BankCardData: TypeConstructorDescription {
case bankCardData(flags: Int32, brand: String?, country: String?, organization: String?, url: String?, urlName: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .bankCardData(let flags, let brand, let country, let organization, let url, let urlName):
if boxed {
buffer.appendInt32(-1165694006)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(brand!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(country!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(organization!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeString(urlName!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .bankCardData(let flags, let brand, let country, let organization, let url, let urlName):
return ("bankCardData", [("flags", flags), ("brand", brand), ("country", country), ("organization", organization), ("url", url), ("urlName", urlName)])
}
}
public static func parse_bankCardData(_ reader: BufferReader) -> BankCardData? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) }
var _3: String?
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
var _4: String?
if Int(_1!) & Int(1 << 2) != 0 {_4 = parseString(reader) }
var _5: String?
if Int(_1!) & Int(1 << 3) != 0 {_5 = parseString(reader) }
var _6: String?
if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.payments.BankCardData.bankCardData(flags: _1!, brand: _2, country: _3, organization: _4, url: _5, urlName: _6)
}
else {
return nil
}
}
}
}
}
public extension Api {
public struct stats {
public enum BroadcastStats: TypeConstructorDescription {
case broadcastStats(period: Api.StatsDateRangeDays, followers: Api.StatsAbsValueAndPrev, viewsPerPost: Api.StatsAbsValueAndPrev, sharesPerPost: Api.StatsAbsValueAndPrev, enabledNotifications: Api.StatsPercentValue, viewsBySource: [Api.StatsRowAbsValueAndPrev], newFollowersBySource: [Api.StatsRowAbsValueAndPrev], languages: [Api.StatsRowAbsValueAndPrev], growthGraph: Api.StatsGraph, followersGraph: Api.StatsGraph, muteGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, interactionsGraph: Api.StatsGraph)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let viewsBySource, let newFollowersBySource, let languages, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph):
if boxed {
buffer.appendInt32(205195937)
}
period.serialize(buffer, true)
followers.serialize(buffer, true)
viewsPerPost.serialize(buffer, true)
sharesPerPost.serialize(buffer, true)
enabledNotifications.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(viewsBySource.count))
for item in viewsBySource {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(newFollowersBySource.count))
for item in newFollowersBySource {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(languages.count))
for item in languages {
item.serialize(buffer, true)
}
growthGraph.serialize(buffer, true)
followersGraph.serialize(buffer, true)
muteGraph.serialize(buffer, true)
topHoursGraph.serialize(buffer, true)
interactionsGraph.serialize(buffer, true)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .broadcastStats(let period, let followers, let viewsPerPost, let sharesPerPost, let enabledNotifications, let viewsBySource, let newFollowersBySource, let languages, let growthGraph, let followersGraph, let muteGraph, let topHoursGraph, let interactionsGraph):
return ("broadcastStats", [("period", period), ("followers", followers), ("viewsPerPost", viewsPerPost), ("sharesPerPost", sharesPerPost), ("enabledNotifications", enabledNotifications), ("viewsBySource", viewsBySource), ("newFollowersBySource", newFollowersBySource), ("languages", languages), ("growthGraph", growthGraph), ("followersGraph", followersGraph), ("muteGraph", muteGraph), ("topHoursGraph", topHoursGraph), ("interactionsGraph", interactionsGraph)])
}
}
public static func parse_broadcastStats(_ reader: BufferReader) -> BroadcastStats? {
var _1: Api.StatsDateRangeDays?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays
}
var _2: Api.StatsAbsValueAndPrev?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev
}
var _3: Api.StatsAbsValueAndPrev?
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev
}
var _4: Api.StatsAbsValueAndPrev?
if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev
}
var _5: Api.StatsPercentValue?
if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue
}
var _6: [Api.StatsRowAbsValueAndPrev]?
if let _ = reader.readInt32() {
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self)
}
var _7: [Api.StatsRowAbsValueAndPrev]?
if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self)
}
var _8: [Api.StatsRowAbsValueAndPrev]?
if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsRowAbsValueAndPrev.self)
}
var _9: Api.StatsGraph?
if let signature = reader.readInt32() {
_9 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _10: Api.StatsGraph?
if let signature = reader.readInt32() {
_10 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _11: Api.StatsGraph?
if let signature = reader.readInt32() {
_11 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _12: Api.StatsGraph?
if let signature = reader.readInt32() {
_12 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
var _13: Api.StatsGraph?
if let signature = reader.readInt32() {
_13 = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
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
let _c8 = _8 != nil
let _c9 = _9 != nil
let _c10 = _10 != nil
let _c11 = _11 != nil
let _c12 = _12 != nil
let _c13 = _13 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 {
return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, enabledNotifications: _5!, viewsBySource: _6!, newFollowersBySource: _7!, languages: _8!, growthGraph: _9!, followersGraph: _10!, muteGraph: _11!, topHoursGraph: _12!, interactionsGraph: _13!)
}
else {
return nil
}
}
}
}
}
public extension Api {

View File

@ -3214,6 +3214,54 @@ public extension Api {
return result
})
}
public static func getDialogFilters() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.DialogFilter]>) {
let buffer = Buffer()
buffer.appendInt32(-241247891)
return (FunctionDescription(name: "messages.getDialogFilters", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.DialogFilter]? in
let reader = BufferReader(buffer)
var result: [Api.DialogFilter]?
if let _ = reader.readInt32() {
result = Api.parseVector(reader, elementSignature: 0, elementType: Api.DialogFilter.self)
}
return result
})
}
public static func updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(450142282)
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)}
return (FunctionDescription(name: "messages.updateDialogFilter", parameters: [("flags", flags), ("id", id), ("filter", filter)]), 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 static func updateDialogFiltersOrder(order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-983318044)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(order.count))
for item in order {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "messages.updateDialogFiltersOrder", parameters: [("order", order)]), 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 struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
@ -3885,6 +3933,50 @@ public extension Api {
return result
})
}
public static func getBankCardData(number: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.BankCardData>) {
let buffer = Buffer()
buffer.appendInt32(779736953)
serializeString(number, buffer: buffer, boxed: false)
return (FunctionDescription(name: "payments.getBankCardData", parameters: [("number", number)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.BankCardData? in
let reader = BufferReader(buffer)
var result: Api.payments.BankCardData?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.payments.BankCardData
}
return result
})
}
}
public struct stats {
public static func getBroadcastStats(flags: Int32, channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stats.BroadcastStats>) {
let buffer = Buffer()
buffer.appendInt32(-1421720550)
serializeInt32(flags, buffer: buffer, boxed: false)
channel.serialize(buffer, true)
return (FunctionDescription(name: "stats.getBroadcastStats", parameters: [("flags", flags), ("channel", channel)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.BroadcastStats? in
let reader = BufferReader(buffer)
var result: Api.stats.BroadcastStats?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.stats.BroadcastStats
}
return result
})
}
public static func loadAsyncGraph(token: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.StatsGraph>) {
let buffer = Buffer()
buffer.appendInt32(1749505346)
serializeString(token, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stats.loadAsyncGraph", parameters: [("token", token)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsGraph? in
let reader = BufferReader(buffer)
var result: Api.StatsGraph?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.StatsGraph
}
return result
})
}
}
public struct auth {
public static func checkPhone(phoneNumber: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.CheckedPhone>) {

View File

@ -1044,6 +1044,7 @@ public class Account {
self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedApplyPendingScheduledMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedChatListFilters(postbox: self.postbox, network: self.network).start())
let importantBackgroundOperations: [Signal<AccountRunningImportantTasks, NoError>] = [
managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network) |> map { $0 ? AccountRunningImportantTasks.other : [] },

View File

@ -152,6 +152,7 @@ private var declaredEncodables: Void = {
declareEncodable(EmbeddedMediaStickersMessageAttribute.self, f: { EmbeddedMediaStickersMessageAttribute(decoder: $0) })
declareEncodable(TelegramMediaWebpageAttribute.self, f: { TelegramMediaWebpageAttribute(decoder: $0) })
declareEncodable(CachedPollOptionResult.self, f: { CachedPollOptionResult(decoder: $0) })
declareEncodable(ChatListFiltersState.self, f: { ChatListFiltersState(decoder: $0) })
return
}()

View File

@ -0,0 +1,378 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import SyncCore
public struct ChatListFilterPeerCategories: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let privateChats = ChatListFilterPeerCategories(rawValue: 1 << 0)
public static let secretChats = ChatListFilterPeerCategories(rawValue: 1 << 1)
public static let privateGroups = ChatListFilterPeerCategories(rawValue: 1 << 2)
public static let bots = ChatListFilterPeerCategories(rawValue: 1 << 3)
public static let publicGroups = ChatListFilterPeerCategories(rawValue: 1 << 4)
public static let channels = ChatListFilterPeerCategories(rawValue: 1 << 5)
public static let all: ChatListFilterPeerCategories = [
.privateChats,
.secretChats,
.privateGroups,
.bots,
.publicGroups,
.channels
]
}
private struct ChatListFilterPeerApiCategories: OptionSet {
var rawValue: Int32
init(rawValue: Int32) {
self.rawValue = rawValue
}
static let privateChats = ChatListFilterPeerApiCategories(rawValue: 1 << 0)
static let secretChats = ChatListFilterPeerApiCategories(rawValue: 1 << 1)
static let privateGroups = ChatListFilterPeerApiCategories(rawValue: 1 << 2)
static let publicGroups = ChatListFilterPeerApiCategories(rawValue: 1 << 3)
static let channels = ChatListFilterPeerApiCategories(rawValue: 1 << 4)
static let bots = ChatListFilterPeerApiCategories(rawValue: 1 << 5)
}
extension ChatListFilterPeerCategories {
init(apiFlags: Int32) {
let flags = ChatListFilterPeerApiCategories(rawValue: apiFlags)
var result: ChatListFilterPeerCategories = []
if flags.contains(.privateChats) {
result.insert(.privateChats)
}
if flags.contains(.secretChats) {
result.insert(.secretChats)
}
if flags.contains(.privateGroups) {
result.insert(.privateGroups)
}
if flags.contains(.publicGroups) {
result.insert(.publicGroups)
}
if flags.contains(.channels) {
result.insert(.channels)
}
if flags.contains(.bots) {
result.insert(.bots)
}
self = result
}
var apiFlags: Int32 {
var result: ChatListFilterPeerApiCategories = []
if self.contains(.privateChats) {
result.insert(.privateChats)
}
if self.contains(.secretChats) {
result.insert(.secretChats)
}
if self.contains(.privateGroups) {
result.insert(.privateGroups)
}
if self.contains(.publicGroups) {
result.insert(.publicGroups)
}
if self.contains(.channels) {
result.insert(.channels)
}
if self.contains(.bots) {
result.insert(.bots)
}
return result.rawValue
}
}
public struct ChatListFilter: PostboxCoding, Equatable {
public var id: Int32
public var title: String?
public var categories: ChatListFilterPeerCategories
public var excludeMuted: Bool
public var excludeRead: Bool
public var includePeers: [PeerId]
public init(
id: Int32,
title: String?,
categories: ChatListFilterPeerCategories,
excludeMuted: Bool,
excludeRead: Bool,
includePeers: [PeerId]
) {
self.id = id
self.title = title
self.categories = categories
self.excludeMuted = excludeMuted
self.excludeRead = excludeRead
self.includePeers = includePeers
}
public init(decoder: PostboxDecoder) {
self.id = decoder.decodeInt32ForKey("id", orElse: 0)
self.title = decoder.decodeOptionalStringForKey("title")
self.categories = ChatListFilterPeerCategories(rawValue: decoder.decodeInt32ForKey("categories", orElse: 0))
self.excludeMuted = decoder.decodeInt32ForKey("excludeMuted", orElse: 0) != 0
self.excludeRead = decoder.decodeInt32ForKey("excludeRead", orElse: 0) != 0
self.includePeers = decoder.decodeInt64ArrayForKey("includePeers").map(PeerId.init)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.id, forKey: "id")
if let title = self.title {
encoder.encodeString(title, forKey: "title")
} else {
encoder.encodeNil(forKey: "title")
}
encoder.encodeInt32(self.categories.rawValue, forKey: "categories")
encoder.encodeInt32(self.excludeMuted ? 1 : 0, forKey: "excludeMuted")
encoder.encodeInt32(self.excludeRead ? 1 : 0, forKey: "excludeRead")
encoder.encodeInt64Array(self.includePeers.map { $0.toInt64() }, forKey: "includePeers")
}
}
extension ChatListFilter {
init(apiFilter: Api.DialogFilter) {
switch apiFilter {
case let .dialogFilter(flags, id, title, includePeers):
self.init(
id: id,
title: title.isEmpty ? nil : title,
categories: ChatListFilterPeerCategories(apiFlags: flags),
excludeMuted: (flags & (1 << 11)) != 0,
excludeRead: (flags & (1 << 12)) != 0,
includePeers: includePeers.compactMap { peer -> PeerId? in
switch peer {
case let .inputPeerUser(userId, _):
return PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .inputPeerChat(chatId):
return PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .inputPeerChannel(channelId, _):
return PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
default:
return nil
}
}
)
}
}
func apiFilter(transaction: Transaction) -> Api.DialogFilter {
var flags: Int32 = 0
if self.excludeMuted {
flags |= 1 << 11
}
if self.excludeRead {
flags |= 1 << 12
}
flags |= self.categories.apiFlags
return .dialogFilter(flags: flags, id: self.id, title: self.title ?? "", includePeers: self.includePeers.compactMap { peerId -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
})
}
}
public enum RequestUpdateChatListFilterError {
case generic
}
public func requestUpdateChatListFilter(account: Account, id: Int32, filter: ChatListFilter?) -> Signal<Never, RequestUpdateChatListFilterError> {
return account.postbox.transaction { transaction -> Api.DialogFilter? in
return filter?.apiFilter(transaction: transaction)
}
|> castError(RequestUpdateChatListFilterError.self)
|> mapToSignal { inputFilter -> Signal<Never, RequestUpdateChatListFilterError> in
var flags: Int32 = 0
if inputFilter != nil {
flags |= 1 << 0
}
return account.network.request(Api.functions.messages.updateDialogFilter(flags: flags, id: id, filter: inputFilter))
|> mapError { _ -> RequestUpdateChatListFilterError in
return .generic
}
|> mapToSignal { _ -> Signal<Never, RequestUpdateChatListFilterError> in
return .complete()
}
}
}
public enum RequestUpdateChatListFilterOrderError {
case generic
}
public func requestUpdateChatListFilterOrder(account: Account, ids: [Int32]) -> Signal<Never, RequestUpdateChatListFilterOrderError> {
return account.network.request(Api.functions.messages.updateDialogFiltersOrder(order: ids))
|> mapError { _ -> RequestUpdateChatListFilterOrderError in
return .generic
}
|> mapToSignal { _ -> Signal<Never, RequestUpdateChatListFilterOrderError> in
return .complete()
}
}
private enum RequestChatListFiltersError {
case generic
}
private func requestChatListFilters(postbox: Postbox, network: Network) -> Signal<[ChatListFilter], RequestChatListFiltersError> {
return network.request(Api.functions.messages.getDialogFilters())
|> mapError { _ -> RequestChatListFiltersError in
return .generic
}
|> mapToSignal { result -> Signal<[ChatListFilter], RequestChatListFiltersError> in
return postbox.transaction { transaction -> ([ChatListFilter], [Api.InputPeer]) in
var filters: [ChatListFilter] = []
var missingPeers: [Api.InputPeer] = []
var missingPeerIds = Set<PeerId>()
for apiFilter in result {
let filter = ChatListFilter(apiFilter: apiFilter)
filters.append(filter)
switch apiFilter {
case let .dialogFilter(_, _, _, includePeers):
for peer in includePeers {
var peerId: PeerId?
switch peer {
case let .inputPeerUser(userId, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .inputPeerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .inputPeerChannel(channelId, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
default:
break
}
if let peerId = peerId {
if transaction.getPeer(peerId) == nil && !missingPeerIds.contains(peerId) {
missingPeerIds.insert(peerId)
missingPeers.append(peer)
}
}
}
}
}
return (filters, missingPeers)
}
|> castError(RequestChatListFiltersError.self)
|> mapToSignal { filtersAndMissingPeers -> Signal<[ChatListFilter], RequestChatListFiltersError> in
let (filters, missingPeers) = filtersAndMissingPeers
return .single(filters)
}
}
}
func managedChatListFilters(postbox: Postbox, network: Network) -> Signal<Never, NoError> {
return requestChatListFilters(postbox: postbox, network: network)
|> `catch` { _ -> Signal<[ChatListFilter], NoError> in
return .complete()
}
|> mapToSignal { filters -> Signal<Never, NoError> in
return postbox.transaction { transaction in
transaction.updatePreferencesEntry(key: PreferencesKeys.chatListFilters, { entry in
var settings = entry as? ChatListFiltersState ?? ChatListFiltersState.default
settings.filters = filters
return settings
})
}
|> ignoreValues
}
}
public func replaceRemoteChatListFilters(account: Account) -> Signal<Never, NoError> {
return requestChatListFilters(postbox: account.postbox, network: account.network)
|> `catch` { _ -> Signal<[ChatListFilter], NoError> in
return .complete()
}
|> mapToSignal { remoteFilters -> Signal<Never, NoError> in
var deleteSignals: [Signal<Never, NoError>] = []
for filter in remoteFilters {
deleteSignals.append(requestUpdateChatListFilter(account: account, id: filter.id, filter: nil)
|> `catch` { _ -> Signal<Never, NoError> in
return .complete()
}
|> ignoreValues)
}
let addFilters = account.postbox.transaction { transaction -> [(Int32, ChatListFilter)] in
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default
return settings.filters.map { filter -> (Int32, ChatListFilter) in
return (filter.id, filter)
}
}
|> mapToSignal { filters -> Signal<Never, NoError> in
var signals: [Signal<Never, NoError>] = []
for (id, filter) in filters {
signals.append(requestUpdateChatListFilter(account: account, id: id, filter: filter)
|> `catch` { _ -> Signal<Never, NoError> in
return .complete()
}
|> ignoreValues)
}
return combineLatest(signals)
|> ignoreValues
}
return combineLatest(
deleteSignals
)
|> ignoreValues
|> then(
addFilters
)
}
}
public struct ChatListFiltersState: PreferencesEntry, Equatable {
public var filters: [ChatListFilter]
public var remoteFilters: [ChatListFilter]?
public static var `default` = ChatListFiltersState(filters: [], remoteFilters: nil)
public init(filters: [ChatListFilter], remoteFilters: [ChatListFilter]?) {
self.filters = filters
self.remoteFilters = remoteFilters
}
public init(decoder: PostboxDecoder) {
self.filters = decoder.decodeObjectArrayWithDecoderForKey("filters")
self.remoteFilters = decoder.decodeOptionalObjectArrayWithDecoderForKey("remoteFilters")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectArray(self.filters, forKey: "filters")
if let remoteFilters = self.remoteFilters {
encoder.encodeObjectArray(remoteFilters, forKey: "remoteFilters")
} else {
encoder.encodeNil(forKey: "remoteFilters")
}
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? ChatListFiltersState, self == to {
return true
} else {
return false
}
}
}
public func updateChatListFilterSettingsInteractively(postbox: Postbox, _ f: @escaping (ChatListFiltersState) -> ChatListFiltersState) -> Signal<ChatListFiltersState, NoError> {
return postbox.transaction { transaction -> ChatListFiltersState in
var result: ChatListFiltersState?
transaction.updatePreferencesEntry(key: PreferencesKeys.chatListFilters, { entry in
var settings = entry as? ChatListFiltersState ?? ChatListFiltersState.default
let updated = f(settings)
result = updated
return updated
})
return result ?? .default
}
}

View File

@ -676,6 +676,8 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi
break
case .Underline:
break
case .BankCard:
break
case .Custom:
break
}
@ -723,6 +725,8 @@ private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretAp
result.append(.messageEntityBlockquote(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .Underline:
result.append(.messageEntityUnderline(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case .BankCard:
break
case .Custom:
break
}

View File

@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 109
return 110
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -386,6 +386,8 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Strikethrough))
case let .messageEntityBlockquote(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BlockQuote))
case let .messageEntityBankCard(offset, length):
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .BankCard))
}
}
return result

View File

@ -45,6 +45,8 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa
apiEntities.append(.messageEntityBlockquote(offset: offset, length: length))
case .Underline:
apiEntities.append(.messageEntityUnderline(offset: offset, length: length))
case .BankCard:
apiEntities.append(.messageEntityBankCard(offset: offset, length: length))
case .Custom:
break
}

View File

@ -319,7 +319,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
switch fullChat {
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, pts):
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, folderId, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, _, pts):
var channelFlags = CachedChannelFlags()
if (flags & (1 << 3)) != 0 {
channelFlags.insert(.canDisplayParticipants)

View File

@ -54,7 +54,6 @@ private var telegramUIDeclaredEncodables: Void = {
declareEncodable(WebBrowserSettings.self, f: { WebBrowserSettings(decoder: $0) })
declareEncodable(IntentsSettings.self, f: { IntentsSettings(decoder: $0) })
declareEncodable(CachedGeocode.self, f: { CachedGeocode(decoder: $0) })
declareEncodable(ChatListFilterSettings.self, f: { ChatListFilterSettings(decoder: $0) })
return
}()

View File

@ -172,7 +172,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
}
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
func update(size: CGSize, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, isScrollingLockedAtTop, presentationData)
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
@ -185,6 +185,12 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
self.listNode.messageInCurrentHistoryView(id)
}
func transferVelocity(_ velocity: CGFloat) {
if velocity > 0.0 {
self.listNode.transferVelocity(velocity)
}
}
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
self.listNode.forEachItemNode { itemNode in

View File

@ -102,7 +102,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
}
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
func update(size: CGSize, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.currentParams == nil
self.currentParams = (size, isScrollingLockedAtTop, presentationData)
@ -156,6 +156,9 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
return nil
}
func transferVelocity(_ velocity: CGFloat) {
}
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
return nil
}

View File

@ -904,7 +904,7 @@ private final class PeerInfoHeaderNode: ASDisplayNode {
private(set) var isAvatarExpanded: Bool
private let avatarListNode: PeerInfoAvatarListNode
let avatarListNode: PeerInfoAvatarListNode
let regularContentNode: PeerInfoHeaderRegularContentNode
let editingContentNode: PeerInfoHeaderEditingContentNode
@ -1195,7 +1195,7 @@ private final class PeerInfoHeaderNode: ASDisplayNode {
}
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
let subtitleScale = (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)
let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)))
let titleOrigin = CGPoint(x: transitionFraction * transitionSourceTitleFrame.minX + (1.0 - transitionFraction) * titleFrame.minX, y: transitionFraction * transitionSourceTitleFrame.minY + (1.0 - transitionFraction) * titleFrame.minY)
let subtitleOrigin = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.minX + (1.0 - transitionFraction) * subtitleFrame.minX, y: transitionFraction * transitionSourceSubtitleFrame.minY + (1.0 - transitionFraction) * subtitleFrame.minY)
@ -1441,8 +1441,9 @@ private final class PeerInfoHeaderNode: ASDisplayNode {
protocol PeerInfoPaneNode: ASDisplayNode {
var isReady: Signal<Bool, NoError> { get }
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
func update(size: CGSize, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
func scrollToTop() -> Bool
func transferVelocity(_ velocity: CGFloat)
func findLoadedMessage(id: MessageId) -> Message?
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
func updateSelectedMessages(animated: Bool)
@ -1466,21 +1467,21 @@ final class PeerInfoPaneInteraction {
private final class PeerInfoPaneWrapper {
let key: PeerInfoPaneKey
let node: PeerInfoPaneNode
private var appliedParams: (CGSize, Bool, PresentationData)?
private var appliedParams: (CGSize, CGFloat, Bool, PresentationData)?
init(key: PeerInfoPaneKey, node: PeerInfoPaneNode) {
self.key = key
self.node = node
}
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
if let (currentSize, currentIsScrollingLockedAtTop, currentPresentationData) = self.appliedParams {
func update(size: CGSize, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
if let (currentSize, visibleHeight, currentIsScrollingLockedAtTop, currentPresentationData) = self.appliedParams {
if currentSize == size && currentIsScrollingLockedAtTop == isScrollingLockedAtTop && currentPresentationData === presentationData {
return
}
}
self.appliedParams = (size, isScrollingLockedAtTop, presentationData)
self.node.update(size: size, isScrollingLockedAtTop: isScrollingLockedAtTop, presentationData: presentationData, synchronous: synchronous, transition: transition)
self.appliedParams = (size, visibleHeight, isScrollingLockedAtTop, presentationData)
self.node.update(size: size, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, presentationData: presentationData, synchronous: synchronous, transition: transition)
}
}
@ -1582,6 +1583,8 @@ private final class PeerInfoPaneTabsContainerNode: ASDisplayNode {
func update(size: CGSize, presentationData: PresentationData, paneList: [PeerInfoPaneSpecifier], selectedPane: PeerInfoPaneKey?, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
let focusOnSelectedPane = self.currentParams?.1 != selectedPane
if self.currentParams?.2.theme !== presentationData.theme {
self.selectedLineNode.image = generateImage(CGSize(width: 7.0, height: 4.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
@ -1673,6 +1676,16 @@ private final class PeerInfoPaneTabsContainerNode: ASDisplayNode {
if let selectedFrame = selectedFrame {
self.selectedLineNode.isHidden = false
transition.updateFrame(node: self.selectedLineNode, frame: CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 4.0), size: CGSize(width: selectedFrame.width, height: 4.0)))
if focusOnSelectedPane {
if selectedPane == paneList.first?.key {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size))
} else if selectedPane == paneList.last?.key {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, y: 0.0), size: self.scrollNode.bounds.size))
} else {
let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0)))
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size))
}
}
} else {
self.selectedLineNode.isHidden = true
}
@ -1695,9 +1708,9 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
let isReady = Promise<Bool>()
var didSetIsReady = false
private var currentParams: (size: CGSize, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?)?
private var currentParams: (size: CGSize, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?)?
private(set) var currentPaneKey: PeerInfoPaneKey?
private var currentPane: PeerInfoPaneWrapper?
private(set) var currentPane: PeerInfoPaneWrapper?
private var currentCandidatePaneKey: PeerInfoPaneKey?
private var candidatePane: (PeerInfoPaneWrapper, Disposable, Bool)?
@ -1763,8 +1776,8 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
}
strongSelf.currentCandidatePaneKey = key
if let (size, expansionFraction, presentationData, data) = strongSelf.currentParams {
strongSelf.update(size: size, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .immediate)
if let (size, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams {
strongSelf.update(size: size, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .immediate)
}
}
}
@ -1793,7 +1806,7 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
}
}
func update(size: CGSize, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, transition: ContainedViewLayoutTransition) {
func update(size: CGSize, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, transition: ContainedViewLayoutTransition) {
let availablePanes = data?.availablePanes ?? []
let previousCurrentPaneKey = self.currentPaneKey
@ -1812,7 +1825,7 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
self.currentCandidatePaneKey = availablePanes.first
}
self.currentParams = (size, expansionFraction, presentationData, data)
self.currentParams = (size, visibleHeight, expansionFraction, presentationData, data)
transition.updateAlpha(node: self.coveringBackgroundNode, alpha: expansionFraction)
@ -1874,8 +1887,8 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
strongSelf.candidatePane = (candidatePane, disposable, true)
if shouldReLayout {
if let (size, expansionFraction, presentationData, data) = strongSelf.currentParams {
strongSelf.update(size: size, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: strongSelf.currentPane != nil ? .animated(duration: 0.35, curve: .spring) : .immediate)
if let (size, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams {
strongSelf.update(size: size, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: strongSelf.currentPane != nil ? .animated(duration: 0.35, curve: .spring) : .immediate)
}
}
}
@ -1897,7 +1910,7 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
self.addSubnode(candidatePane.node)
}
candidatePane.node.frame = paneFrame
candidatePane.update(size: paneFrame.size, isScrollingLockedAtTop: expansionFraction < 1.0 - CGFloat.ulpOfOne, presentationData: presentationData, synchronous: true, transition: .immediate)
candidatePane.update(size: paneFrame.size, visibleHeight: max(0.0, visibleHeight - paneFrame.minY), isScrollingLockedAtTop: expansionFraction < 1.0 - CGFloat.ulpOfOne, presentationData: presentationData, synchronous: true, transition: .immediate)
if let previousPane = previousPane {
let directionToRight: Bool
@ -1923,7 +1936,7 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
let paneTransition: ContainedViewLayoutTransition = paneWasAdded ? .immediate : transition
paneTransition.updateFrame(node: currentPane.node, frame: paneFrame)
currentPane.update(size: paneFrame.size, isScrollingLockedAtTop: expansionFraction < 1.0 - CGFloat.ulpOfOne, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition)
currentPane.update(size: paneFrame.size, visibleHeight: visibleHeight, isScrollingLockedAtTop: expansionFraction < 1.0 - CGFloat.ulpOfOne, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition)
}
transition.updateFrame(node: self.tabsContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: tabsHeight)))
@ -1949,13 +1962,14 @@ private final class PeerInfoPaneContainerNode: ASDisplayNode {
if let (candidatePane, _, _) = self.candidatePane {
let paneTransition: ContainedViewLayoutTransition = .immediate
paneTransition.updateFrame(node: candidatePane.node, frame: paneFrame)
candidatePane.update(size: paneFrame.size, isScrollingLockedAtTop: expansionFraction < 1.0 - CGFloat.ulpOfOne, presentationData: presentationData, synchronous: true, transition: paneTransition)
candidatePane.update(size: paneFrame.size, visibleHeight: visibleHeight, isScrollingLockedAtTop: expansionFraction < 1.0 - CGFloat.ulpOfOne, presentationData: presentationData, synchronous: true, transition: paneTransition)
}
if !self.didSetIsReady {
self.didSetIsReady = true
if !self.didSetIsReady && data != nil {
if let currentPane = self.currentPane {
self.didSetIsReady = true
self.isReady.set(currentPane.node.isReady)
} else {
} else if self.candidatePane == nil {
self.didSetIsReady = true
self.isReady.set(.single(true))
}
}
@ -3990,7 +4004,17 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
if !self.didSetReady && self.data != nil {
self.didSetReady = true
self._ready.set(self.paneContainerNode.isReady.get())
let avatarReady = self.headerNode.avatarListNode.isReady.get()
let combinedSignal = combineLatest(queue: .mainQueue(),
avatarReady,
self.paneContainerNode.isReady.get()
)
|> map { lhs, rhs in
return lhs && rhs
}
self._ready.set(combinedSignal
|> filter { $0 }
|> take(1))
}
}
@ -4025,7 +4049,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
transition.updateAlpha(node: self.headerNode.separatorNode, alpha: 1.0 - effectiveAreaExpansionFraction)
self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, expansionFraction: paneAreaExpansionFraction, presentationData: self.presentationData, data: self.data, transition: transition)
let visibleHeight = self.scrollNode.view.contentOffset.y + self.scrollNode.view.bounds.height - self.paneContainerNode.frame.minY
self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, visibleHeight: visibleHeight, expansionFraction: paneAreaExpansionFraction, presentationData: self.presentationData, data: self.data, transition: transition)
self.headerNode.navigationButtonContainer.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: 44.0))
self.headerNode.navigationButtonContainer.isWhite = self.headerNode.isAvatarExpanded
@ -4058,6 +4084,11 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.canUpdateAvatarExpansion = true
}
private var previousVelocityM1: CGFloat = 0.0
private var previousVelocity: CGFloat = 0.0
private let velocityKey: String = encodeText("`wfsujdbmWfmpdjuz", -1)
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.ignoreScrolling {
return
@ -4065,6 +4096,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.updateNavigation(transition: .immediate, additive: false)
if !self.state.isEditing {
self.previousVelocityM1 = self.previousVelocity
if let value = (scrollView.value(forKey: self.velocityKey) as? NSNumber)?.doubleValue {
//print("previousVelocity \(CGFloat(value))")
self.previousVelocity = CGFloat(value)
}
let offsetY = self.scrollNode.view.contentOffset.y
var shouldBeExpanded: Bool?
if offsetY <= -32.0 && scrollView.isDragging && scrollView.isTracking {
@ -4096,6 +4133,17 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
guard let (_, navigationHeight) = self.validLayout else {
return
}
let paneAreaExpansionFinalPoint: CGFloat = self.paneContainerNode.frame.minY - navigationHeight
if abs(scrollView.contentOffset.y - paneAreaExpansionFinalPoint) < .ulpOfOne {
self.paneContainerNode.currentPane?.node.transferVelocity(self.previousVelocityM1)
}
}
private func updateNavigationExpansionPresentation(isExpanded: Bool, animated: Bool) {
if let controller = self.controller {
controller.statusBar.updateStatusBarStyle(isExpanded ? .White : self.presentationData.theme.rootController.statusBarStyle.style, animated: animated)
@ -4413,7 +4461,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
}
let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNode.bounds.height) / previousTitleNode.bounds.height
let subtitleScale = (fraction * previousStatusNode.bounds.height + (1.0 - fraction) * self.headerNode.subtitleNode.bounds.height) / previousStatusNode.bounds.height
let subtitleScale = max(0.01, min(10.0, (fraction * previousStatusNode.bounds.height + (1.0 - fraction) * self.headerNode.subtitleNode.bounds.height) / previousStatusNode.bounds.height))
transition.updateFrame(node: previousTitleContainerNode, frame: CGRect(origin: self.headerNode.titleNodeRawContainer.frame.origin.offsetBy(dx: previousTitleFrame.size.width * 0.5 * (titleScale - 1.0), dy: previousTitleFrame.size.height * 0.5 * (titleScale - 1.0)), size: previousTitleFrame.size))
transition.updateFrame(node: previousTitleNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: previousTitleFrame.size))
@ -4443,3 +4491,11 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
self.screenNode.insertSubnode(self.headerNode, aboveSubnode: self.screenNode.scrollNode)
}
}
private func encodeText(_ string: String, _ key: Int) -> String {
var result = ""
for c in string.unicodeScalars {
result.append(Character(UnicodeScalar(UInt32(Int(c.value) + key))!))
}
return result
}

View File

@ -314,7 +314,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
return self._itemInteraction!
}
private var currentParams: (size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
private var currentParams: (size: CGSize, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
private let ready = Promise<Bool>()
private var didSetReady: Bool = false
@ -332,6 +332,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
private var isRequestingView: Bool = false
private var isFirstHistoryView: Bool = true
private var decelerationAnimator: ConstantDisplayLinkAnimator?
init(context: AccountContext, openMessage: @escaping (MessageId) -> Bool, peerId: PeerId, interaction: PeerInfoPaneInteraction) {
self.context = context
self.peerId = peerId
@ -414,8 +416,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
let wasFirstHistoryView = self.isFirstHistoryView
self.isFirstHistoryView = false
if let (size, isScrollingLockedAtTop, presentationData) = self.currentParams {
self.update(size: size, isScrollingLockedAtTop: isScrollingLockedAtTop, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate)
if let (size, visibleHeight, isScrollingLockedAtTop, presentationData) = self.currentParams {
self.update(size: size, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate)
if !self.didSetReady {
self.didSetReady = true
self.ready.set(.single(true))
@ -442,6 +444,43 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
return nil
}
func transferVelocity(_ velocity: CGFloat) {
if velocity > 0.0 {
//print("transferVelocity \(velocity)")
self.decelerationAnimator?.isPaused = true
let startTime = CACurrentMediaTime()
var currentOffset = self.scrollNode.view.contentOffset
let decelerationRate: CGFloat = 0.998
self.decelerationAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in
guard let strongSelf = self else {
return
}
let t = CACurrentMediaTime() - startTime
var currentVelocity = velocity * 15.0 * CGFloat(pow(Double(decelerationRate), 1000.0 * t))
//print("value at \(t) = \(currentVelocity)")
currentOffset.y += currentVelocity
let maxOffset = strongSelf.scrollNode.view.contentSize.height - strongSelf.scrollNode.bounds.height
if currentOffset.y >= maxOffset {
currentOffset.y = maxOffset
currentVelocity = 0.0
}
if currentOffset.y < 0.0 {
currentOffset.y = 0.0
currentVelocity = 0.0
}
if abs(currentVelocity) < 0.1 {
strongSelf.decelerationAnimator?.isPaused = true
strongSelf.decelerationAnimator = nil
}
var contentOffset = strongSelf.scrollNode.view.contentOffset
contentOffset.y = floorToScreenPixels(currentOffset.y)
strongSelf.scrollNode.view.setContentOffset(contentOffset, animated: false)
})
self.decelerationAnimator?.isPaused = false
}
}
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
for item in self.mediaItems {
if item.message.id == messageId {
@ -461,8 +500,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
}
}
func update(size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, isScrollingLockedAtTop, presentationData)
func update(size: CGSize, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) {
self.currentParams = (size, visibleHeight, isScrollingLockedAtTop, presentationData)
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
@ -474,7 +513,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
let contentHeight = CGFloat(rowCount + 1) * itemSpacing + CGFloat(rowCount) * itemSize
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
self.updateVisibleItems(size: size, theme: presentationData.theme, synchronousLoad: synchronous)
self.updateVisibleItems(size: size, visibleHeight: visibleHeight, theme: presentationData.theme, synchronousLoad: synchronous)
if isScrollingLockedAtTop {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size))
@ -482,9 +521,14 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.scrollNode.view.isScrollEnabled = !isScrollingLockedAtTop
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.decelerationAnimator?.isPaused = true
self.decelerationAnimator = nil
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let (size, _, presentationData) = self.currentParams {
self.updateVisibleItems(size: size, theme: presentationData.theme, synchronousLoad: false)
if let (size, visibleHeight, _, presentationData) = self.currentParams {
self.updateVisibleItems(size: size, visibleHeight: visibleHeight, theme: presentationData.theme, synchronousLoad: false)
if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil {
if !self.isRequestingView {
@ -495,9 +539,9 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
}
}
private func updateVisibleItems(size: CGSize, theme: PresentationTheme, synchronousLoad: Bool) {
private func updateVisibleItems(size: CGSize, visibleHeight: CGFloat, theme: PresentationTheme, synchronousLoad: Bool) {
let itemSpacing: CGFloat = 1.0
let itemsInRow: Int = max(3, min(6, Int(size.width / 100.0)))
let itemsInRow: Int = max(3, min(6, Int(size.width / 140.0)))
let itemSize: CGFloat = floor(size.width / CGFloat(itemsInRow))
let rowCount: Int = self.mediaItems.count / itemsInRow + (self.mediaItems.count % itemsInRow == 0 ? 0 : 1)
@ -529,7 +573,11 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.scrollNode.addSubnode(itemNode)
}
itemNode.frame = itemFrame
itemNode.update(size: itemFrame.size, item: self.mediaItems[i], theme: theme, synchronousLoad: synchronousLoad)
var itemSynchronousLoad = false
if itemFrame.maxY <= visibleHeight {
itemSynchronousLoad = synchronousLoad
}
itemNode.update(size: itemFrame.size, item: self.mediaItems[i], theme: theme, synchronousLoad: itemSynchronousLoad)
}
}
var removeKeys: [UInt32] = []
@ -544,4 +592,17 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
}
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let result = super.hitTest(point, with: event) else {
return nil
}
if self.decelerationAnimator != nil {
self.decelerationAnimator?.isPaused = true
self.decelerationAnimator = nil
return self.scrollNode.view
}
return result
}
}

View File

@ -1,135 +0,0 @@
import Foundation
import Postbox
import SwiftSignalKit
import SyncCore
public struct ChatListIncludeCategoryFilter: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let muted = ChatListIncludeCategoryFilter(rawValue: 1 << 1)
public static let privateChats = ChatListIncludeCategoryFilter(rawValue: 1 << 2)
public static let secretChats = ChatListIncludeCategoryFilter(rawValue: 1 << 3)
public static let privateGroups = ChatListIncludeCategoryFilter(rawValue: 1 << 4)
public static let bots = ChatListIncludeCategoryFilter(rawValue: 1 << 5)
public static let publicGroups = ChatListIncludeCategoryFilter(rawValue: 1 << 6)
public static let channels = ChatListIncludeCategoryFilter(rawValue: 1 << 7)
public static let read = ChatListIncludeCategoryFilter(rawValue: 1 << 8)
public static let all: ChatListIncludeCategoryFilter = [
.muted,
.privateChats,
.secretChats,
.privateGroups,
.bots,
.publicGroups,
.channels,
.read
]
}
public enum ChatListFilterPresetName: Equatable, Hashable, PostboxCoding {
case unread
case custom(String)
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("_t", orElse: 0) {
case 0:
self = .unread
case 1:
self = .custom(decoder.decodeStringForKey("title", orElse: "Preset"))
default:
assertionFailure()
self = .custom("Preset")
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case .unread:
encoder.encodeInt32(0, forKey: "_t")
case let .custom(title):
encoder.encodeInt32(1, forKey: "_t")
encoder.encodeString(title, forKey: "title")
}
}
}
public struct ChatListFilterPreset: Equatable, PostboxCoding {
public var id: Int64
public var name: ChatListFilterPresetName
public var includeCategories: ChatListIncludeCategoryFilter
public var additionallyIncludePeers: [PeerId]
public init(id: Int64, name: ChatListFilterPresetName, includeCategories: ChatListIncludeCategoryFilter, additionallyIncludePeers: [PeerId]) {
self.id = id
self.name = name
self.includeCategories = includeCategories
self.additionallyIncludePeers = additionallyIncludePeers
}
public init(decoder: PostboxDecoder) {
self.id = decoder.decodeInt64ForKey("id", orElse: 0)
self.name = decoder.decodeObjectForKey("name", decoder: { ChatListFilterPresetName(decoder: $0) }) as? ChatListFilterPresetName ?? ChatListFilterPresetName.custom("Preset")
self.includeCategories = ChatListIncludeCategoryFilter(rawValue: decoder.decodeInt32ForKey("includeCategories", orElse: 0))
self.additionallyIncludePeers = decoder.decodeInt64ArrayForKey("additionallyIncludePeers").map(PeerId.init)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64(self.id, forKey: "id")
encoder.encodeObject(self.name, forKey: "name")
encoder.encodeInt32(self.includeCategories.rawValue, forKey: "includeCategories")
encoder.encodeInt64Array(self.additionallyIncludePeers.map { $0.toInt64() }, forKey: "additionallyIncludePeers")
}
}
public struct ChatListFilterSettings: PreferencesEntry, Equatable {
public var presets: [ChatListFilterPreset]
public static var `default`: ChatListFilterSettings {
return ChatListFilterSettings(presets: [
ChatListFilterPreset(
id: Int64(arc4random()),
name: .unread,
includeCategories: ChatListIncludeCategoryFilter.all.subtracting(.read),
additionallyIncludePeers: []
)
])
}
public init(presets: [ChatListFilterPreset]) {
self.presets = presets
}
public init(decoder: PostboxDecoder) {
self.presets = decoder.decodeObjectArrayWithDecoderForKey("presets")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectArray(self.presets, forKey: "presets")
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? ChatListFilterSettings {
return self == to
} else {
return false
}
}
}
public func updateChatListFilterSettingsInteractively(postbox: Postbox, _ f: @escaping (ChatListFilterSettings) -> ChatListFilterSettings) -> Signal<ChatListFilterSettings, NoError> {
return postbox.transaction { transaction -> ChatListFilterSettings in
var result: ChatListFilterSettings?
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatListFilterSettings, { entry in
var settings = entry as? ChatListFilterSettings ?? ChatListFilterSettings.default
let updated = f(settings)
result = updated
return updated
})
return result ?? .default
}
}