mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Mixed WIP
This commit is contained in:
parent
e2071301c2
commit
9f73cc4018
@ -3,7 +3,7 @@
|
||||
@implementation Serialization
|
||||
|
||||
- (NSUInteger)currentLayer {
|
||||
return 109;
|
||||
return 110;
|
||||
}
|
||||
|
||||
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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
|
||||
|
||||
|
@ -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?()
|
||||
})
|
||||
})
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -12,7 +12,7 @@ struct ChatListNodeView {
|
||||
let originalView: ChatListView
|
||||
let filteredEntries: [ChatListNodeEntry]
|
||||
let isLoading: Bool
|
||||
let filter: ChatListFilterPreset?
|
||||
let filter: ChatListFilter?
|
||||
}
|
||||
|
||||
enum ChatListNodeViewTransitionReason {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>) {
|
||||
|
@ -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 : [] },
|
||||
|
@ -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
|
||||
}()
|
||||
|
378
submodules/TelegramCore/Sources/ChatListFiltering.swift
Normal file
378
submodules/TelegramCore/Sources/ChatListFiltering.swift
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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! {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user