Tab support

This commit is contained in:
Ali 2020-02-19 00:10:28 +04:00
parent 2505082eee
commit 507e17f40f
34 changed files with 1759 additions and 201 deletions

View File

@ -95,7 +95,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
} }
} }
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate/*, TabBarContainedController*/ { public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate, TabBarContainedController {
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
public let context: AccountContext public let context: AccountContext
@ -133,9 +133,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let stateDisposable = MetaDisposable() private let stateDisposable = MetaDisposable()
private var filterDisposable: Disposable?
private var searchContentNode: NavigationBarSearchContentNode? private var searchContentNode: NavigationBarSearchContentNode?
private let tabContainerNode: ChatListFilterTabContainerNode
private var tabContainerData: ([ChatListFilterTabEntry], ChatListFilterTabEntryId)?
private let chatListFilterValue = Promise<ChatListFilter?>()
public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
if self.isNodeLoaded { if self.isNodeLoaded {
self.chatListDisplayNode.chatListNode.updateSelectedChatLocation(data as? ChatLocation, progress: progress, transition: transition) self.chatListDisplayNode.chatListNode.updateSelectedChatLocation(data as? ChatLocation, progress: progress, transition: transition)
@ -156,6 +162,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self.titleView = ChatListTitleView(theme: self.presentationData.theme, strings: self.presentationData.strings) self.titleView = ChatListTitleView(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.tabContainerNode = ChatListFilterTabContainerNode()
super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary) super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary)
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
@ -265,14 +273,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
context.account.networkState, context.account.networkState,
hasProxy, hasProxy,
passcode, passcode,
self.chatListDisplayNode.chatListNode.state, self.chatListDisplayNode.chatListNode.state
self.chatListDisplayNode.chatListNode.chatListFilterSignal ).start(next: { [weak self] networkState, proxy, passcode, state in
).start(next: { [weak self] networkState, proxy, passcode, state, chatListFilter in
if let strongSelf = self { if let strongSelf = self {
let defaultTitle: String let defaultTitle: String
if strongSelf.groupId == .root { if strongSelf.groupId == .root {
if let chatListFilter = chatListFilter { if let chatListFilter = strongSelf.filter {
let title: String = chatListFilter.title ?? "" let title: String = chatListFilter.title ?? strongSelf.presentationData.strings.DialogList_Title
defaultTitle = title defaultTitle = title
} else { } else {
defaultTitle = strongSelf.presentationData.strings.DialogList_Title defaultTitle = strongSelf.presentationData.strings.DialogList_Title
@ -397,6 +404,107 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
preconditionFailure("debug tap") preconditionFailure("debug tap")
} }
} }
if self.filter == nil {
let preferencesKey: PostboxViewKey = .preferences(keys: Set([
ApplicationSpecificPreferencesKeys.chatListFilterSettings
]))
let filterItems = chatListFilterItems(context: context)
|> map { items -> [ChatListFilterTabEntry] in
var result: [ChatListFilterTabEntry] = []
result.append(.all)
for (filter, unreadCount) in items {
result.append(.filter(id: filter.id, text: filter.title ?? "", unreadCount: unreadCount))
}
return result
}
|> distinctUntilChanged
self.filterDisposable = (combineLatest(queue: .mainQueue(),
context.account.postbox.combinedView(keys: [
preferencesKey
]),
filterItems,
self.chatListFilterValue.get() |> map { $0?.id } |> distinctUntilChanged
)
|> deliverOnMainQueue).start(next: { [weak self] combinedView, filterItems, selectedFilter in
guard let strongSelf = self else {
return
}
var filterSettings: ChatListFilterSettings = .default
if let preferencesView = combinedView.views[preferencesKey] as? PreferencesView {
if let value = preferencesView.values[ApplicationSpecificPreferencesKeys.chatListFilterSettings] as? ChatListFilterSettings {
filterSettings = value
}
}
var resolvedItems = filterItems
if !filterSettings.displayTabs {
resolvedItems = []
}
var wasEmpty = false
if let tabContainerData = strongSelf.tabContainerData {
wasEmpty = tabContainerData.0.count <= 1
} else {
wasEmpty = true
}
let selectedEntryId: ChatListFilterTabEntryId = selectedFilter.flatMap { .filter($0) } ?? .all
strongSelf.tabContainerData = (resolvedItems, selectedEntryId)
let isEmpty = resolvedItems.count <= 1
if wasEmpty != isEmpty {
strongSelf.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode)
if let parentController = strongSelf.parent as? TabBarController {
parentController.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode)
}
}
if let layout = strongSelf.validLayout {
if wasEmpty != isEmpty {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
(strongSelf.parent as? TabBarController)?.updateLayout()
} else {
strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring))
}
}
})
}
self.tabContainerNode.tabSelected = { [weak self] id in
guard let strongSelf = self else {
return
}
let _ = (strongSelf.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] filters in
guard let strongSelf = self else {
return
}
switch id {
case .all:
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = nil
case let .filter(id):
var found = false
for filter in filters {
if filter.id == id {
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = filter
found = true
break
}
}
if !found {
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = nil
}
}
})
}
self.tabContainerNode.addFilter = { [weak self] in
self?.openFilterSettings()
}
} }
required public init(coder aDecoder: NSCoder) { required public init(coder aDecoder: NSCoder) {
@ -412,6 +520,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self.suggestLocalizationDisposable.dispose() self.suggestLocalizationDisposable.dispose()
self.presentationDataDisposable?.dispose() self.presentationDataDisposable?.dispose()
self.stateDisposable.dispose() self.stateDisposable.dispose()
self.filterDisposable?.dispose()
} }
private func updateThemeAndStrings() { private func updateThemeAndStrings() {
@ -461,6 +570,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = ChatListControllerNode(context: self.context, groupId: self.groupId, filter: self.filter, 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.chatListFilterValue.set(self.chatListDisplayNode.chatListNode.chatListFilterSignal)
self.chatListDisplayNode.navigationBar = self.navigationBar self.chatListDisplayNode.navigationBar = self.navigationBar
@ -726,7 +836,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
} }
} }
self.stateDisposable.set(combineLatest(queue: .mainQueue(), self.presentationDataValue.get(), peerIdsAndOptions).start(next: { [weak self] presentationData, peerIdsAndOptions in self.stateDisposable.set(combineLatest(queue: .mainQueue(),
self.presentationDataValue.get(),
peerIdsAndOptions
).start(next: { [weak self] presentationData, peerIdsAndOptions in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
@ -735,13 +848,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
if let (options, peerIds) = peerIdsAndOptions { if let (options, peerIds) = peerIdsAndOptions {
let leftAction: ToolbarAction let leftAction: ToolbarAction
switch options.read { switch options.read {
case let .all(enabled): case let .all(enabled):
leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled) leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled)
case let .selective(enabled): case let .selective(enabled):
leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled) leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled)
} }
var archiveEnabled = options.delete var archiveEnabled = options.delete
if archiveEnabled { if archiveEnabled && strongSelf.filter == nil {
for peerId in peerIds { for peerId in peerIds {
if peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000) { if peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000) {
archiveEnabled = false archiveEnabled = false
@ -919,12 +1032,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self.validLayout = layout self.validLayout = layout
var tabContainerOffset: CGFloat = 0.0
if !self.displayNavigationBar {
tabContainerOffset += layout.statusBarHeight ?? 0.0
tabContainerOffset += 44.0 + 44.0 + 44.0
}
transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: self.visualNavigationInsetHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0)))
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.tabContainerData?.1, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
if let searchContentNode = self.searchContentNode, layout.inVoiceOver != wasInVoiceOver { if let searchContentNode = self.searchContentNode, layout.inVoiceOver != wasInVoiceOver {
searchContentNode.updateListVisibleContentOffset(.known(0.0)) searchContentNode.updateListVisibleContentOffset(.known(0.0))
self.chatListDisplayNode.chatListNode.scrollToPosition(.top) self.chatListDisplayNode.chatListNode.scrollToPosition(.top)
} }
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, visualNavigationHeight: self.visualNavigationInsetHeight, transition: transition) self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, visualNavigationHeight: self.visualNavigationInsetHeight, cleanNavigationBarHeight: self.cleanNavigationHeight, transition: transition)
} }
override public func navigationStackConfigurationUpdated(next: [ViewController]) { override public func navigationStackConfigurationUpdated(next: [ViewController]) {
@ -934,7 +1056,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
@objc private func editPressed() { @objc private func editPressed() {
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
editItem.accessibilityLabel = self.presentationData.strings.Common_Done editItem.accessibilityLabel = self.presentationData.strings.Common_Done
if case .root = self.groupId { if case .root = self.groupId, self.filter == nil {
self.navigationItem.leftBarButtonItem = editItem self.navigationItem.leftBarButtonItem = editItem
(self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.details, transition: .animated(duration: 0.5, curve: .spring)) (self.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.details, transition: .animated(duration: 0.5, curve: .spring))
} else { } else {
@ -954,7 +1076,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
@objc private func donePressed() { @objc private func donePressed() {
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
editItem.accessibilityLabel = self.presentationData.strings.Common_Edit editItem.accessibilityLabel = self.presentationData.strings.Common_Edit
if case .root = self.groupId { if case .root = self.groupId, self.filter == nil {
self.navigationItem.leftBarButtonItem = editItem self.navigationItem.leftBarButtonItem = editItem
} else { } else {
self.navigationItem.rightBarButtonItem = editItem self.navigationItem.rightBarButtonItem = editItem
@ -1788,7 +1910,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
} }
override public func setToolbar(_ toolbar: Toolbar?, transition: ContainedViewLayoutTransition) { override public func setToolbar(_ toolbar: Toolbar?, transition: ContainedViewLayoutTransition) {
if case .root = self.groupId { if case .root = self.groupId, self.filter == nil {
super.setToolbar(toolbar, transition: transition) super.setToolbar(toolbar, transition: transition)
} else { } else {
self.chatListDisplayNode.toolbar = toolbar self.chatListDisplayNode.toolbar = toolbar
@ -1804,7 +1926,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
} }
} }
/*public func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) { private func openFilterSettings() {
self.push(chatListFilterPresetListController(context: self.context, updated: { _ in
}))
}
public func presentTabBarPreviewingController(sourceNodes: [ASDisplayNode]) {
if self.isNodeLoaded { if self.isNodeLoaded {
let _ = (self.context.account.postbox.transaction { transaction -> [ChatListFilter] in let _ = (self.context.account.postbox.transaction { transaction -> [ChatListFilter] in
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters) as? ChatListFiltersState ?? ChatListFiltersState.default
@ -1849,5 +1976,5 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
public func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate) { public func updateTabBarPreviewingControllerPresentation(_ update: TabBarContainedControllerPresentationUpdate) {
}*/ }
} }

View File

@ -59,7 +59,7 @@ final class ChatListControllerNode: ASDisplayNode {
private(set) var searchDisplayController: SearchDisplayController? private(set) var searchDisplayController: SearchDisplayController?
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat)? private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat)?
var requestDeactivateSearch: (() -> Void)? var requestDeactivateSearch: (() -> Void)?
var requestOpenPeerFromSearch: ((Peer, Bool) -> Void)? var requestOpenPeerFromSearch: ((Peer, Bool) -> Void)?
@ -102,8 +102,8 @@ final class ChatListControllerNode: ASDisplayNode {
let chatListEmptyNode = ChatListEmptyNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings) let chatListEmptyNode = ChatListEmptyNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings)
strongSelf.chatListEmptyNode = chatListEmptyNode strongSelf.chatListEmptyNode = chatListEmptyNode
strongSelf.insertSubnode(chatListEmptyNode, belowSubnode: strongSelf.chatListNode) strongSelf.insertSubnode(chatListEmptyNode, belowSubnode: strongSelf.chatListNode)
if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout { if let (layout, navigationHeight, visualNavigationHeight, cleanNavigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate) strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
} }
strongSelf.isEmptyUpdated?(true) strongSelf.isEmptyUpdated?(true)
} }
@ -114,9 +114,7 @@ final class ChatListControllerNode: ASDisplayNode {
default: default:
if let chatListEmptyNode = strongSelf.chatListEmptyNode { if let chatListEmptyNode = strongSelf.chatListEmptyNode {
strongSelf.chatListEmptyNode = nil strongSelf.chatListEmptyNode = nil
chatListEmptyNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak chatListEmptyNode] _ in chatListEmptyNode.removeFromSupernode()
chatListEmptyNode?.removeFromSupernode()
})
} }
} }
switch isEmptyState { switch isEmptyState {
@ -125,8 +123,8 @@ final class ChatListControllerNode: ASDisplayNode {
let chatListEmptyIndicator = ActivityIndicator(type: .custom(strongSelf.presentationData.theme.list.itemSecondaryTextColor, 22.0, 1.0, false)) let chatListEmptyIndicator = ActivityIndicator(type: .custom(strongSelf.presentationData.theme.list.itemSecondaryTextColor, 22.0, 1.0, false))
strongSelf.chatListEmptyIndicator = chatListEmptyIndicator strongSelf.chatListEmptyIndicator = chatListEmptyIndicator
strongSelf.insertSubnode(chatListEmptyIndicator, belowSubnode: strongSelf.chatListNode) strongSelf.insertSubnode(chatListEmptyIndicator, belowSubnode: strongSelf.chatListNode)
if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout { if let (layout, navigationHeight, visualNavigationHeight, cleanNavigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate) strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
} }
} }
default: default:
@ -160,8 +158,8 @@ final class ChatListControllerNode: ASDisplayNode {
} }
} }
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight) self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight)
var insets = layout.insets(options: [.input]) var insets = layout.insets(options: [.input])
insets.top += navigationBarHeight insets.top += navigationBarHeight
@ -233,12 +231,12 @@ final class ChatListControllerNode: ASDisplayNode {
} }
if let searchDisplayController = self.searchDisplayController { if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition)
} }
} }
func activateSearch(placeholderNode: SearchBarPlaceholderNode) { func activateSearch(placeholderNode: SearchBarPlaceholderNode) {
guard let (containerLayout, navigationBarHeight, _) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { guard let (containerLayout, navigationBarHeight, _, cleanNavigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
return return
} }
@ -262,7 +260,7 @@ final class ChatListControllerNode: ASDisplayNode {
}) })
self.chatListNode.accessibilityElementsHidden = true self.chatListNode.accessibilityElementsHidden = true
self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in
if let strongSelf = self, let strongPlaceholderNode = placeholderNode { if let strongSelf = self, let strongPlaceholderNode = placeholderNode {
if isSearchBar { if isSearchBar {

View File

@ -1,4 +1,4 @@
/*import Foundation import Foundation
import UIKit import UIKit
import Display import Display
import SwiftSignalKit import SwiftSignalKit
@ -58,6 +58,7 @@ private enum ChatListFilterPresetEntryStableId: Hashable {
private enum ChatListFilterPresetEntry: ItemListNodeEntry { private enum ChatListFilterPresetEntry: ItemListNodeEntry {
case nameHeader(String) case nameHeader(String)
case name(placeholder: String, value: String) case name(placeholder: String, value: String)
case typesHeader(text: String)
case filterPrivateChats(title: String, value: Bool) case filterPrivateChats(title: String, value: Bool)
case filterSecretChats(title: String, value: Bool) case filterSecretChats(title: String, value: Bool)
case filterPrivateGroups(title: String, value: Bool) case filterPrivateGroups(title: String, value: Bool)
@ -75,7 +76,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
switch self { switch self {
case .nameHeader, .name: case .nameHeader, .name:
return ChatListFilterPresetControllerSection.name.rawValue return ChatListFilterPresetControllerSection.name.rawValue
case .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels: case .typesHeader, .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels:
return ChatListFilterPresetControllerSection.categories.rawValue return ChatListFilterPresetControllerSection.categories.rawValue
case .filterMuted, .filterRead: case .filterMuted, .filterRead:
return ChatListFilterPresetControllerSection.excludeCategories.rawValue return ChatListFilterPresetControllerSection.excludeCategories.rawValue
@ -90,26 +91,28 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
return .index(0) return .index(0)
case .name: case .name:
return .index(1) return .index(1)
case .filterPrivateChats: case .typesHeader:
return .index(2) return .index(2)
case .filterSecretChats: case .filterPrivateChats:
return .index(3) return .index(3)
case .filterPrivateGroups: case .filterSecretChats:
return .index(4) return .index(4)
case .filterBots: case .filterPrivateGroups:
return .index(5) return .index(5)
case .filterPublicGroups: case .filterPublicGroups:
return .index(6) return .index(6)
case .filterChannels: case .filterChannels:
return .index(7) return .index(7)
case .filterMuted: case .filterBots:
return .index(8) return .index(8)
case .filterRead: case .filterMuted:
return .index(9) return .index(9)
case .additionalPeersHeader: case .filterRead:
return .index(10) return .index(10)
case .addAdditionalPeer: case .additionalPeersHeader:
return .index(11) return .index(11)
case .addAdditionalPeer:
return .index(12)
case let .additionalPeer(additionalPeer): case let .additionalPeer(additionalPeer):
return .peer(additionalPeer.peer.peerId) return .peer(additionalPeer.peer.peerId)
case .additionalPeerInfo: case .additionalPeerInfo:
@ -172,6 +175,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
return state return state
} }
}, action: {}) }, action: {})
case let .typesHeader(text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .filterPrivateChats(title, value): case let .filterPrivateChats(title, value):
return filterEntry(presentationData: presentationData, arguments: arguments, title: title, value: value, filter: .privateChats, section: self.section) return filterEntry(presentationData: presentationData, arguments: arguments, title: title, value: value, filter: .privateChats, section: self.section)
case let .filterSecretChats(title, value): case let .filterSecretChats(title, value):
@ -243,21 +248,22 @@ private struct ChatListFilterPresetControllerState: Equatable {
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetControllerState, peers: [RenderedPeer]) -> [ChatListFilterPresetEntry] { private func chatListFilterPresetControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetControllerState, peers: [RenderedPeer]) -> [ChatListFilterPresetEntry] {
var entries: [ChatListFilterPresetEntry] = [] var entries: [ChatListFilterPresetEntry] = []
entries.append(.nameHeader("NAME")) entries.append(.nameHeader("FILTER NAME"))
entries.append(.name(placeholder: "Preset Name", value: state.name)) entries.append(.name(placeholder: "Filter Name", value: state.name))
entries.append(.typesHeader(text: "INCLUDE CHAT TYPES"))
entries.append(.filterPrivateChats(title: "Private Chats", value: state.includeCategories.contains(.privateChats))) entries.append(.filterPrivateChats(title: "Private Chats", value: state.includeCategories.contains(.privateChats)))
entries.append(.filterSecretChats(title: "Secret Chats", value: state.includeCategories.contains(.secretChats))) entries.append(.filterSecretChats(title: "Secret Chats", value: state.includeCategories.contains(.secretChats)))
entries.append(.filterPrivateGroups(title: "Private Groups", value: state.includeCategories.contains(.privateGroups))) entries.append(.filterPrivateGroups(title: "Private Groups", value: state.includeCategories.contains(.privateGroups)))
entries.append(.filterBots(title: "Bots", value: state.includeCategories.contains(.bots)))
entries.append(.filterPublicGroups(title: "Public Groups", value: state.includeCategories.contains(.publicGroups))) entries.append(.filterPublicGroups(title: "Public Groups", value: state.includeCategories.contains(.publicGroups)))
entries.append(.filterChannels(title: "Channels", value: state.includeCategories.contains(.channels))) entries.append(.filterChannels(title: "Channels", value: state.includeCategories.contains(.channels)))
entries.append(.filterBots(title: "Bots", value: state.includeCategories.contains(.bots)))
entries.append(.filterMuted(title: "Exclude Muted", value: state.excludeMuted)) entries.append(.filterMuted(title: "Exclude Muted", value: state.excludeMuted))
entries.append(.filterRead(title: "Exclude Read", value: state.excludeRead)) entries.append(.filterRead(title: "Exclude Read", value: state.excludeRead))
entries.append(.additionalPeersHeader("ALWAYS INCLUDE")) entries.append(.additionalPeersHeader("ALWAYS INCLUDE"))
entries.append(.addAdditionalPeer(title: "Add")) entries.append(.addAdditionalPeer(title: "Add Chats"))
for peer in peers { for peer in peers {
entries.append(.additionalPeer(index: entries.count, peer: peer, isRevealed: state.revealedPeerId == peer.peerId)) entries.append(.additionalPeer(index: entries.count, peer: peer, isRevealed: state.revealedPeerId == peer.peerId))
@ -273,7 +279,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
if let currentPreset = currentPreset { if let currentPreset = currentPreset {
initialName = currentPreset.title ?? "" initialName = currentPreset.title ?? ""
} else { } else {
initialName = "New Preset" initialName = "New Filter"
} }
let initialState = ChatListFilterPresetControllerState(name: initialName, includeCategories: currentPreset?.categories ?? .all, excludeMuted: currentPreset?.excludeMuted ?? false, excludeRead: currentPreset?.excludeRead ?? false, additionallyIncludePeers: currentPreset?.includePeers ?? []) 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 stateValue = Atomic(value: initialState)
@ -365,17 +371,29 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
dismissImpl?() dismissImpl?()
}) })
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: { let rightNavigationButton = ItemListNavigationButton(content: .text("Create"), style: .bold, enabled: state.isComplete, action: {
let state = stateValue.with { $0 } let state = stateValue.with { $0 }
let preset = ChatListFilter(id: currentPreset?.id ?? -1, title: state.name, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, includePeers: 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 let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
var preset = preset var preset = preset
if currentPreset == nil { if currentPreset == nil {
preset.id = max(2, settings.filters.map({ $0.id }).max() ?? 2) preset.id = max(2, settings.filters.map({ $0.id + 1 }).max() ?? 2)
} }
var settings = settings var settings = settings
settings.filters = settings.filters.filter { $0 != preset && $0 != currentPreset } if let currentPreset = currentPreset {
settings.filters.append(preset) var found = false
for i in 0 ..< settings.filters.count {
if settings.filters[i].id == preset.id {
settings.filters[i] = preset
found = true
}
}
if !found {
settings.filters.append(preset)
}
} else {
settings.filters.append(preset)
}
return settings return settings
}) })
|> deliverOnMainQueue).start(next: { settings in |> deliverOnMainQueue).start(next: { settings in
@ -384,7 +402,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
}) })
}) })
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.SocksProxySetup_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(currentPreset != nil ? "Filter" : "Create Filter"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, state: state, peers: statePeers), style: .blocks, emptyStateItem: nil, animateChanges: true) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetControllerEntries(presentationData: presentationData, state: state, peers: statePeers), style: .blocks, emptyStateItem: nil, animateChanges: true)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
@ -405,4 +423,3 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
return controller return controller
} }
*/

View File

@ -1,4 +1,4 @@
/*import Foundation import Foundation
import UIKit import UIKit
import Display import Display
import SwiftSignalKit import SwiftSignalKit
@ -9,17 +9,20 @@ import TelegramPresentationData
import TelegramUIPreferences import TelegramUIPreferences
import ItemListUI import ItemListUI
import AccountContext import AccountContext
import ItemListPeerActionItem
private final class ChatListFilterPresetListControllerArguments { private final class ChatListFilterPresetListControllerArguments {
let context: AccountContext let context: AccountContext
let toggleEnableTabs: (Bool) -> Void
let openPreset: (ChatListFilter) -> Void let openPreset: (ChatListFilter) -> Void
let addNew: () -> Void let addNew: () -> Void
let setItemWithRevealedOptions: (Int32?, Int32?) -> Void let setItemWithRevealedOptions: (Int32?, Int32?) -> Void
let removePreset: (Int32) -> Void let removePreset: (Int32) -> Void
init(context: AccountContext, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void) { init(context: AccountContext, toggleEnableTabs: @escaping (Bool) -> Void, openPreset: @escaping (ChatListFilter) -> Void, addNew: @escaping () -> Void, setItemWithRevealedOptions: @escaping (Int32?, Int32?) -> Void, removePreset: @escaping (Int32) -> Void) {
self.context = context self.context = context
self.toggleEnableTabs = toggleEnableTabs
self.openPreset = openPreset self.openPreset = openPreset
self.addNew = addNew self.addNew = addNew
self.setItemWithRevealedOptions = setItemWithRevealedOptions self.setItemWithRevealedOptions = setItemWithRevealedOptions
@ -28,6 +31,7 @@ private final class ChatListFilterPresetListControllerArguments {
} }
private enum ChatListFilterPresetListSection: Int32 { private enum ChatListFilterPresetListSection: Int32 {
case tabs
case list case list
} }
@ -44,6 +48,8 @@ private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings
} }
private enum ChatListFilterPresetListEntryStableId: Hashable { private enum ChatListFilterPresetListEntryStableId: Hashable {
case displayTabs
case displayTabsFooter
case listHeader case listHeader
case preset(Int32) case preset(Int32)
case addItem case addItem
@ -51,13 +57,17 @@ private enum ChatListFilterPresetListEntryStableId: Hashable {
} }
private enum ChatListFilterPresetListEntry: ItemListNodeEntry { private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
case displayTabs(String, Bool)
case displayTabsFooter(String)
case listHeader(String) case listHeader(String)
case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool) case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool)
case addItem(String) case addItem(text: String, isEditing: Bool)
case listFooter(String) case listFooter(String)
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .displayTabs, .displayTabsFooter:
return ChatListFilterPresetListSection.tabs.rawValue
case .listHeader, .preset, .addItem, .listFooter: case .listHeader, .preset, .addItem, .listFooter:
return ChatListFilterPresetListSection.list.rawValue return ChatListFilterPresetListSection.list.rawValue
} }
@ -65,10 +75,14 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
var sortId: Int { var sortId: Int {
switch self { switch self {
case .listHeader: case .displayTabs:
return 0 return 0
case .displayTabsFooter:
return 1
case .listHeader:
return 2
case let .preset(preset): case let .preset(preset):
return 1 + preset.index return 3 + preset.index
case .addItem: case .addItem:
return 1000 return 1000
case .listFooter: case .listFooter:
@ -78,6 +92,10 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
var stableId: ChatListFilterPresetListEntryStableId { var stableId: ChatListFilterPresetListEntryStableId {
switch self { switch self {
case .displayTabs:
return .displayTabs
case .displayTabsFooter:
return .displayTabsFooter
case .listHeader: case .listHeader:
return .listHeader return .listHeader
case let .preset(preset): case let .preset(preset):
@ -96,18 +114,24 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! ChatListFilterPresetListControllerArguments let arguments = arguments as! ChatListFilterPresetListControllerArguments
switch self { switch self {
case let .displayTabs(title, value):
return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleEnableTabs(value)
})
case let .displayTabsFooter(text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .listHeader(text): case let .listHeader(text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section)
case let .preset(index, title, preset, canBeReordered, canBeDeleted): case let .preset(index, title, preset, canBeReordered, canBeDeleted, isEditing):
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: isEditing, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, sectionId: self.section, action: {
arguments.openPreset(preset) arguments.openPreset(preset)
}, setItemWithRevealedOptions: { lhs, rhs in }, setItemWithRevealedOptions: { lhs, rhs in
arguments.setItemWithRevealedOptions(lhs, rhs) arguments.setItemWithRevealedOptions(lhs, rhs)
}, remove: { }, remove: {
arguments.removePreset(preset.id) arguments.removePreset(preset.id)
}) })
case let .addItem(text): case let .addItem(text, isEditing):
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { return ItemListPeerActionItem(presentationData: presentationData, icon: nil, title: text, sectionId: self.section, height: .generic, editing: isEditing, action: {
arguments.addNew() arguments.addNew()
}) })
case let .listFooter(text): case let .listFooter(text):
@ -117,18 +141,22 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
} }
private struct ChatListFilterPresetListControllerState: Equatable { private struct ChatListFilterPresetListControllerState: Equatable {
var isEditing: Bool = false
var revealedPreset: Int32? = nil var revealedPreset: Int32? = nil
} }
private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, settings: ChatListFiltersState) -> [ChatListFilterPresetListEntry] { private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, filtersState: ChatListFiltersState, settings: ChatListFilterSettings) -> [ChatListFilterPresetListEntry] {
var entries: [ChatListFilterPresetListEntry] = [] var entries: [ChatListFilterPresetListEntry] = []
entries.append(.displayTabs("Show Tabs", settings.displayTabs))
entries.append(.displayTabsFooter("Display filter tabs on the main screen for quick switching."))
entries.append(.listHeader("FILTERS")) entries.append(.listHeader("FILTERS"))
for preset in settings.filters { for preset in filtersState.filters {
entries.append(.preset(index: entries.count, title: preset.title, preset: preset, canBeReordered: settings.filters.count > 1, canBeDeleted: true)) entries.append(.preset(index: entries.count, title: preset.title, preset: preset, canBeReordered: filtersState.filters.count > 1, canBeDeleted: true, isEditing: state.isEditing))
} }
entries.append(.addItem("Add New")) entries.append(.addItem(text: "Create New Filter", isEditing: state.isEditing))
entries.append(.listFooter("Add custom presets")) entries.append(.listFooter("Tap \"Edit\" to change the order or delete filters."))
return entries return entries
} }
@ -145,7 +173,15 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
var pushControllerImpl: ((ViewController) -> Void)? var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)? var presentControllerImpl: ((ViewController, Any?) -> Void)?
let arguments = ChatListFilterPresetListControllerArguments(context: context, openPreset: { preset in let arguments = ChatListFilterPresetListControllerArguments(context: context, toggleEnableTabs: { value in
let _ = context.account.postbox.transaction({ transaction -> Void in
let _ = updateChatListFilterSettings(transaction: transaction, { settings in
var settings = settings
settings.displayTabs = value
return settings
})
}).start()
}, openPreset: { preset in
pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: preset, updated: updated)) pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: preset, updated: updated))
}, addNew: { }, addNew: {
pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: nil, updated: updated)) pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: nil, updated: updated))
@ -170,7 +206,7 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
}) })
}) })
let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFilters]) let preferences = context.account.postbox.preferencesView(keys: [PreferencesKeys.chatListFilters, ApplicationSpecificPreferencesKeys.chatListFilterSettings])
let signal = combineLatest(queue: .mainQueue(), let signal = combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData, context.sharedContext.presentationData,
@ -178,14 +214,33 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
preferences preferences
) )
|> map { presentationData, state, preferences -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, state, preferences -> (ItemListControllerState, (ItemListNodeState, Any)) in
let settings = preferences.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState ?? ChatListFiltersState.default let filtersState = preferences.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState ?? ChatListFiltersState.default
let filterSettings = preferences.values[ApplicationSpecificPreferencesKeys.chatListFilterSettings] as? ChatListFilterSettings ?? ChatListFilterSettings.default
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Close), style: .regular, enabled: true, action: { let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Close), style: .regular, enabled: true, action: {
let _ = replaceRemoteChatListFilters(account: context.account).start() let _ = replaceRemoteChatListFilters(account: context.account).start()
dismissImpl?() dismissImpl?()
}) })
let rightNavigationButton: ItemListNavigationButton
if state.isEditing {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
updateState { state in
var state = state
state.isEditing = false
return state
}
})
} else {
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
updateState { state in
var state = state
state.isEditing = true
return state
}
})
}
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Filter Presets"), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Filters"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, 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) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetListControllerEntries(presentationData: presentationData, state: state, filtersState: filtersState, settings: filterSettings), style: .blocks, animateChanges: true)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }
@ -203,7 +258,62 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
dismissImpl = { [weak controller] in dismissImpl = { [weak controller] in
controller?.dismiss() controller?.dismiss()
} }
controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [ChatListFilterPresetListEntry]) -> Signal<Bool, NoError> in
let fromEntry = entries[fromIndex]
guard case let .preset(fromFilter) = fromEntry else {
return .single(false)
}
var referenceFilter: ChatListFilter?
var beforeAll = false
var afterAll = false
if toIndex < entries.count {
switch entries[toIndex] {
case let .preset(toFilter):
referenceFilter = toFilter.preset
default:
if entries[toIndex] < fromEntry {
beforeAll = true
} else {
afterAll = true
}
}
} else {
afterAll = true
}
return updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { filtersState in
var filtersState = filtersState
if let index = filtersState.filters.firstIndex(where: { $0.id == fromFilter.preset.id }) {
filtersState.filters.remove(at: index)
}
if let referenceFilter = referenceFilter {
var inserted = false
for i in 0 ..< filtersState.filters.count {
if filtersState.filters[i].id == referenceFilter.id {
if fromIndex < toIndex {
filtersState.filters.insert(fromFilter.preset, at: i + 1)
} else {
filtersState.filters.insert(fromFilter.preset, at: i)
}
inserted = true
break
}
}
if !inserted {
filtersState.filters.append(fromFilter.preset)
}
} else if beforeAll {
filtersState.filters.insert(fromFilter.preset, at: 0)
} else if afterAll {
filtersState.filters.append(fromFilter.preset)
}
return filtersState
})
|> map { _ -> Bool in
return false
}
})
return controller return controller
} }
*/

View File

@ -184,13 +184,13 @@ private final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemN
var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)?
var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)?
let editingOffset: CGFloat = 0.0 var editingOffset: CGFloat = 0.0
var reorderInset: CGFloat = 0.0 var reorderInset: CGFloat = 0.0
if item.editing.editing && item.canBeReordered { if item.editing.editing && item.canBeReordered {
/*let sizeAndApply = editableControlLayout(item.presentationData.theme, false) let sizeAndApply = editableControlLayout(item.presentationData.theme, false)
editableControlSizeAndApply = sizeAndApply editableControlSizeAndApply = sizeAndApply
editingOffset = sizeAndApply.0*/ editingOffset = sizeAndApply.0
let reorderSizeAndApply = reorderControlLayout(item.presentationData.theme) let reorderSizeAndApply = reorderControlLayout(item.presentationData.theme)
reorderControlSizeAndApply = reorderSizeAndApply reorderControlSizeAndApply = reorderSizeAndApply

View File

@ -0,0 +1,336 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SyncCore
import Postbox
import TelegramCore
import TelegramPresentationData
private final class ItemNode: ASDisplayNode {
private let pressed: () -> Void
private let titleNode: ImmediateTextNode
private let badgeTextNode: ImmediateTextNode
private let badgeBackgroundNode: ASImageNode
private let buttonNode: HighlightTrackingButtonNode
private var isSelected: Bool = false
private var unreadCount: Int = 0
private var theme: PresentationTheme?
init(pressed: @escaping () -> Void) {
self.pressed = pressed
self.titleNode = ImmediateTextNode()
self.titleNode.displaysAsynchronously = false
self.badgeTextNode = ImmediateTextNode()
self.badgeTextNode.displaysAsynchronously = false
self.badgeBackgroundNode = ASImageNode()
self.badgeBackgroundNode.displaysAsynchronously = false
self.badgeBackgroundNode.displayWithoutProcessing = true
self.buttonNode = HighlightTrackingButtonNode()
super.init()
self.addSubnode(self.titleNode)
self.addSubnode(self.badgeBackgroundNode)
self.addSubnode(self.badgeTextNode)
self.addSubnode(self.buttonNode)
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
}
@objc private func buttonPressed() {
self.pressed()
}
func updateText(title: String, unreadCount: Int, isSelected: Bool, presentationData: PresentationData) {
if self.theme !== presentationData.theme {
self.theme = presentationData.theme
self.badgeBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.rootController.navigationBar.badgeBackgroundColor)
}
self.isSelected = isSelected
self.unreadCount = unreadCount
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(14.0), textColor: isSelected ? presentationData.theme.list.itemAccentColor : presentationData.theme.list.itemSecondaryTextColor)
if unreadCount != 0 {
self.badgeTextNode.attributedText = NSAttributedString(string: "\(unreadCount)", font: Font.regular(14.0), textColor: presentationData.theme.rootController.navigationBar.badgeTextColor)
}
}
func updateLayout(height: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
let titleSize = self.titleNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
self.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
let badgeSize = self.badgeTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
let badgeInset: CGFloat = 5.0
let badgeBackgroundFrame = CGRect(origin: CGPoint(x: titleSize.width + 5.0, y: floor((height - 18.0) / 2.0)), size: CGSize(width: max(18.0, badgeSize.width + badgeInset * 2.0), height: 18.0))
self.badgeBackgroundNode.frame = badgeBackgroundFrame
self.badgeTextNode.frame = CGRect(origin: CGPoint(x: badgeBackgroundFrame.minX + floor((badgeBackgroundFrame.width - badgeSize.width) / 2.0), y: badgeBackgroundFrame.minY + floor((badgeBackgroundFrame.height - badgeSize.height) / 2.0)), size: badgeSize)
if self.unreadCount == 0 {
self.badgeBackgroundNode.alpha = 0.0
self.badgeTextNode.alpha = 0.0
return titleSize.width
} else {
self.badgeBackgroundNode.alpha = 1.0
self.badgeTextNode.alpha = 1.0
return badgeBackgroundFrame.maxX
}
}
func updateArea(size: CGSize, sideInset: CGFloat) {
self.buttonNode.frame = CGRect(origin: CGPoint(x: -sideInset, y: 0.0), size: CGSize(width: size.width + sideInset * 2.0, height: size.height))
}
}
enum ChatListFilterTabEntryId: Hashable {
case all
case filter(Int32)
}
enum ChatListFilterTabEntry: Equatable {
case all
case filter(id: Int32, text: String, unreadCount: Int)
var id: ChatListFilterTabEntryId {
switch self {
case .all:
return .all
case let .filter(filter):
return .filter(filter.id)
}
}
func title(strings: PresentationStrings) -> String {
switch self {
case .all:
return "All Chats"
case let .filter(filter):
return filter.text
}
}
}
private final class AddItemNode: HighlightableButtonNode {
private let iconNode: ASImageNode
var pressed: (() -> Void)?
private var theme: PresentationTheme?
override init() {
self.iconNode = ASImageNode()
self.iconNode.displaysAsynchronously = false
self.iconNode.displayWithoutProcessing = true
super.init()
self.addSubnode(self.iconNode)
self.addTarget(self, action: #selector(self.onPressed), forControlEvents: .touchUpInside)
}
@objc private func onPressed() {
self.pressed?()
}
func update(size: CGSize, theme: PresentationTheme) {
if self.theme !== theme {
self.theme = theme
self.iconNode.image = PresentationResourcesItemList.plusIconImage(theme)
}
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
}
}
}
final class ChatListFilterTabContainerNode: ASDisplayNode {
private let scrollNode: ASScrollNode
private let selectedLineNode: ASImageNode
private var itemNodes: [ChatListFilterTabEntryId: ItemNode] = [:]
private let addNode: AddItemNode
var tabSelected: ((ChatListFilterTabEntryId) -> Void)?
var addFilter: (() -> Void)?
private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, presentationData: PresentationData)?
override init() {
self.scrollNode = ASScrollNode()
self.selectedLineNode = ASImageNode()
self.selectedLineNode.displaysAsynchronously = false
self.selectedLineNode.displayWithoutProcessing = true
self.addNode = AddItemNode()
super.init()
self.scrollNode.view.showsHorizontalScrollIndicator = false
self.scrollNode.view.scrollsToTop = false
self.scrollNode.view.delaysContentTouches = false
self.scrollNode.view.canCancelContentTouches = true
if #available(iOS 11.0, *) {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
self.addSubnode(self.scrollNode)
self.scrollNode.addSubnode(self.selectedLineNode)
self.scrollNode.addSubnode(self.addNode)
self.addNode.pressed = { [weak self] in
self?.addFilter?()
}
}
func update(size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
let focusOnSelectedFilter = self.currentParams?.selectedFilter != selectedFilter
if self.currentParams?.presentationData.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))
context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width)))
})?.stretchableImage(withLeftCapWidth: 4, topCapHeight: 1)
}
self.currentParams = (size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, presentationData: presentationData)
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size))
for filter in filters {
let itemNode: ItemNode
var wasAdded = false
if let current = self.itemNodes[filter.id] {
itemNode = current
} else {
wasAdded = true
itemNode = ItemNode(pressed: { [weak self] in
self?.tabSelected?(filter.id)
})
self.itemNodes[filter.id] = itemNode
}
let unreadCount: Int
switch filter {
case .all:
unreadCount = 0
case let .filter(filter):
unreadCount = filter.unreadCount
}
itemNode.updateText(title: filter.title(strings: presentationData.strings), unreadCount: unreadCount, isSelected: selectedFilter == filter.id, presentationData: presentationData)
}
var removeKeys: [ChatListFilterTabEntryId] = []
for (id, _) in self.itemNodes {
if !filters.contains(where: { $0.id == id }) {
removeKeys.append(id)
}
}
for id in removeKeys {
if let itemNode = self.itemNodes.removeValue(forKey: id) {
itemNode.removeFromSupernode()
}
}
var tabSizes: [(CGSize, ItemNode, Bool)] = []
var totalRawTabSize: CGFloat = 0.0
var selectionFrames: [CGRect] = []
for filter in filters {
guard let itemNode = self.itemNodes[filter.id] else {
continue
}
let wasAdded = itemNode.supernode == nil
if wasAdded {
self.scrollNode.addSubnode(itemNode)
}
let paneNodeWidth = itemNode.updateLayout(height: size.height, transition: transition)
let paneNodeSize = CGSize(width: paneNodeWidth, height: size.height)
tabSizes.append((paneNodeSize, itemNode, wasAdded))
totalRawTabSize += paneNodeSize.width
}
let minSpacing: CGFloat = 30.0
let sideInset: CGFloat = 16.0
var leftOffset: CGFloat = sideInset
for i in 0 ..< tabSizes.count {
let (paneNodeSize, paneNode, wasAdded) = tabSizes[i]
let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize)
if wasAdded {
paneNode.frame = paneFrame
paneNode.alpha = 0.0
transition.updateAlpha(node: paneNode, alpha: 1.0)
} else {
transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame)
}
paneNode.updateArea(size: paneFrame.size, sideInset: minSpacing / 2.0)
paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -minSpacing / 2.0, bottom: 0.0, right: -minSpacing / 2.0)
selectionFrames.append(paneFrame)
leftOffset += paneNodeSize.width + minSpacing
}
let addSize = CGSize(width: 32.0, height: size.height)
transition.updateFrame(node: self.addNode, frame: CGRect(origin: CGPoint(x: max(leftOffset, size.width - sideInset - addSize.width + 6.0), y: 0.0), size: addSize))
self.addNode.update(size: addSize, theme: presentationData.theme)
leftOffset += addSize.width + minSpacing
self.scrollNode.view.contentSize = CGSize(width: leftOffset - minSpacing + sideInset, height: size.height)
let transitionFraction: CGFloat = 0.0
var selectedFrame: CGRect?
if let selectedFilter = selectedFilter, let currentIndex = filters.index(where: { $0.id == selectedFilter }) {
func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect {
return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t)))
}
if currentIndex != 0 && transitionFraction > 0.0 {
let currentFrame = selectionFrames[currentIndex]
let previousFrame = selectionFrames[currentIndex - 1]
selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction))
} else if currentIndex != filters.count - 1 && transitionFraction < 0.0 {
let currentFrame = selectionFrames[currentIndex]
let previousFrame = selectionFrames[currentIndex + 1]
selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction))
} else {
selectedFrame = selectionFrames[currentIndex]
}
}
if let selectedFrame = selectedFrame {
let wasAdded = self.selectedLineNode.isHidden
self.selectedLineNode.isHidden = false
let lineFrame = CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 4.0), size: CGSize(width: selectedFrame.width, height: 4.0))
if wasAdded {
self.selectedLineNode.frame = lineFrame
self.selectedLineNode.alpha = 0.0
transition.updateAlpha(node: self.selectedLineNode, alpha: 1.0)
} else {
transition.updateFrame(node: self.selectedLineNode, frame: lineFrame)
}
if focusOnSelectedFilter {
if selectedFilter == filters.first?.id {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size))
} else if selectedFilter == filters.last?.id {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: max(0.0, 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
}
}
}

View File

@ -13,6 +13,7 @@ import TelegramNotices
import ContactsPeerItem import ContactsPeerItem
import ContextUI import ContextUI
import ItemListUI import ItemListUI
import SearchUI
public enum ChatListNodeMode { public enum ChatListNodeMode {
case chatList case chatList
@ -27,6 +28,7 @@ struct ChatListNodeListViewTransition {
let options: ListViewDeleteAndInsertOptions let options: ListViewDeleteAndInsertOptions
let scrollToItem: ListViewScrollToItem? let scrollToItem: ListViewScrollToItem?
let stationaryItemRange: (Int, Int)? let stationaryItemRange: (Int, Int)?
let adjustScrollToFirstItem: Bool
} }
final class ChatListHighlightedLocation { final class ChatListHighlightedLocation {
@ -281,7 +283,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
} }
private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, mode: ChatListNodeMode, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition { private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, peerGroupId: PeerGroupId, mode: ChatListNodeMode, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition {
return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange) return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, peerGroupId: peerGroupId, mode: mode, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem)
} }
private final class ChatListOpaqueTransactionState { private final class ChatListOpaqueTransactionState {
@ -366,7 +368,17 @@ public final class ChatListNode: ListView {
} }
private var currentLocation: ChatListNodeLocation? private var currentLocation: ChatListNodeLocation?
let chatListFilter: ChatListFilter? var chatListFilter: ChatListFilter? {
didSet {
self.chatListFilterValue.set(.single(self.chatListFilter))
if self.chatListFilter != oldValue {
if let currentLocation = self.currentLocation {
self.setChatListLocation(.initial(count: 50, filter: self.chatListFilter))
}
}
}
}
private let chatListFilterValue = Promise<ChatListFilter?>() private let chatListFilterValue = Promise<ChatListFilter?>()
var chatListFilterSignal: Signal<ChatListFilter?, NoError> { var chatListFilterSignal: Signal<ChatListFilter?, NoError> {
return self.chatListFilterValue.get() return self.chatListFilterValue.get()
@ -431,6 +443,8 @@ public final class ChatListNode: ListView {
self.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor self.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
self.verticalScrollIndicatorFollowsOverscroll = true self.verticalScrollIndicatorFollowsOverscroll = true
self.keepMinimalScrollHeightWithTopInset = navigationBarSearchContentHeight
let nodeInteraction = ChatListNodeInteraction(activateSearch: { [weak self] in let nodeInteraction = ChatListNodeInteraction(activateSearch: { [weak self] in
if let strongSelf = self, let activateSearch = strongSelf.activateSearch { if let strongSelf = self, let activateSearch = strongSelf.activateSearch {
activateSearch() activateSearch()
@ -489,6 +503,10 @@ public final class ChatListNode: ListView {
} }
}) })
}, setPeerMuted: { [weak self] peerId, _ in }, setPeerMuted: { [weak self] peerId, _ in
guard let strongSelf = self else {
return
}
strongSelf.setCurrentRemovingPeerId(peerId)
let _ = (togglePeerMuted(account: context.account, peerId: peerId) let _ = (togglePeerMuted(account: context.account, peerId: peerId)
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
self?.updateState { state in self?.updateState { state in
@ -496,6 +514,7 @@ public final class ChatListNode: ListView {
state.peerIdWithRevealedOptions = nil state.peerIdWithRevealedOptions = nil
return state return state
} }
self?.setCurrentRemovingPeerId(nil)
}) })
}, deletePeer: { [weak self] peerId in }, deletePeer: { [weak self] peerId in
self?.deletePeerChat?(peerId) self?.deletePeerChat?(peerId)
@ -505,7 +524,7 @@ public final class ChatListNode: ListView {
guard let context = context else { guard let context = context else {
return return
} }
self?.setCurrentRemovingPeerId(peerId)
let _ = (togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId) let _ = (togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId)
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
self?.updateState { state in self?.updateState { state in
@ -513,6 +532,7 @@ public final class ChatListNode: ListView {
state.peerIdWithRevealedOptions = nil state.peerIdWithRevealedOptions = nil
return state return state
} }
self?.setCurrentRemovingPeerId(nil)
}) })
}, toggleArchivedFolderHiddenByDefault: { [weak self] in }, toggleArchivedFolderHiddenByDefault: { [weak self] in
self?.toggleArchivedFolderHiddenByDefault?() self?.toggleArchivedFolderHiddenByDefault?()
@ -1279,7 +1299,19 @@ public final class ChatListNode: ListView {
options.insert(.PreferSynchronousDrawing) options.insert(.PreferSynchronousDrawing)
} }
self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatListOpaqueTransactionState(chatListView: transition.chatListView), completion: completion) var scrollToItem = transition.scrollToItem
if transition.adjustScrollToFirstItem {
var offset: CGFloat = 0.0
switch self.visibleContentOffset() {
case let .known(value) where abs(value) < .ulpOfOne:
offset = 0.0
default:
offset = -navigationBarSearchContentHeight
}
scrollToItem = ListViewScrollToItem(index: 0, position: .top(offset), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
}
self.transaction(deleteIndices: transition.deleteItems, insertIndicesAndItems: transition.insertItems, updateIndicesAndItems: transition.updateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: transition.stationaryItemRange, updateOpaqueState: ChatListOpaqueTransactionState(chatListView: transition.chatListView), completion: completion)
} }
} }

View File

@ -44,6 +44,7 @@ struct ChatListNodeViewTransition {
let options: ListViewDeleteAndInsertOptions let options: ListViewDeleteAndInsertOptions
let scrollToItem: ListViewScrollToItem? let scrollToItem: ListViewScrollToItem?
let stationaryItemRange: (Int, Int)? let stationaryItemRange: (Int, Int)?
let adjustScrollToFirstItem: Bool
} }
enum ChatListNodeViewScrollPosition { enum ChatListNodeViewScrollPosition {
@ -176,11 +177,12 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
fromEmptyView = true fromEmptyView = true
} }
var adjustScrollToFirstItem = false
if !previewing && !searchMode && fromEmptyView && scrollToItem == nil && toView.filteredEntries.count >= 1 { if !previewing && !searchMode && fromEmptyView && scrollToItem == nil && toView.filteredEntries.count >= 1 {
scrollToItem = ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up) adjustScrollToFirstItem = true
} }
subscriber.putNext(ChatListNodeViewTransition(chatListView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange)) subscriber.putNext(ChatListNodeViewTransition(chatListView: toView, deleteItems: adjustedDeleteIndices, insertEntries: adjustedIndicesAndItems, updateEntries: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, stationaryItemRange: stationaryItemRange, adjustScrollToFirstItem: adjustScrollToFirstItem))
subscriber.putCompletion() subscriber.putCompletion()
return EmptyDisposable return EmptyDisposable

View File

@ -304,6 +304,123 @@ private final class FilterItemNode: ASDisplayNode, AbstractTabBarChatListFilterI
} }
} }
func chatListFilterItems(context: AccountContext) -> Signal<[(ChatListFilter, Int)], NoError> {
let preferencesKey: PostboxViewKey = .preferences(keys: [PreferencesKeys.chatListFilters])
return context.account.postbox.combinedView(keys: [preferencesKey])
|> map { combinedView -> [ChatListFilter] in
if let filtersState = (combinedView.views[preferencesKey] as? PreferencesView)?.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState {
return filtersState.filters
} else {
return []
}
}
|> distinctUntilChanged
|> mapToSignal { filters -> Signal<[(ChatListFilter, Int)], NoError> in
var unreadCountItems: [UnreadMessageCountsItem] = []
unreadCountItems.append(.total(nil))
var additionalPeerIds = Set<PeerId>()
for filter in filters {
additionalPeerIds.formUnion(filter.includePeers)
}
if !additionalPeerIds.isEmpty {
for peerId in additionalPeerIds {
unreadCountItems.append(.peer(peerId))
}
}
let unreadKey: PostboxViewKey = .unreadCounts(items: unreadCountItems)
var keys: [PostboxViewKey] = []
keys.append(unreadKey)
for peerId in additionalPeerIds {
keys.append(.basicPeer(peerId))
}
return context.account.postbox.combinedView(keys: keys)
|> map { view -> [(ChatListFilter, Int)] in
guard let unreadCounts = view.views[unreadKey] as? UnreadMessageCountsView else {
return []
}
var result: [(ChatListFilter, Int)] = []
var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:]
var totalState: ChatListTotalUnreadState?
for entry in unreadCounts.entries {
switch entry {
case let .total(_, totalStateValue):
totalState = totalStateValue
case let .peer(peerId, state):
if let state = state, state.isUnread {
if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer {
let tag = context.account.postbox.seedConfiguration.peerSummaryCounterTags(peer)
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, case .muted = notificationSettings.muteState {
peerTagAndCount[peerId] = (tag, 0)
} else {
var peerCount = Int(state.count)
if state.isUnread {
peerCount = max(1, peerCount)
}
peerTagAndCount[peerId] = (tag, peerCount)
}
}
}
}
}
var totalUnreadChatCount = 0
if let totalState = totalState {
for (_, counters) in totalState.filteredCounters {
totalUnreadChatCount += Int(counters.chatCount)
}
}
var shouldUpdateLayout = false
for filter in filters {
var tags: [PeerSummaryCounterTags] = []
if filter.categories.contains(.privateChats) {
tags.append(.privateChat)
}
if filter.categories.contains(.secretChats) {
tags.append(.secretChat)
}
if filter.categories.contains(.privateGroups) {
tags.append(.privateGroup)
}
if filter.categories.contains(.bots) {
tags.append(.bot)
}
if filter.categories.contains(.publicGroups) {
tags.append(.publicGroup)
}
if filter.categories.contains(.channels) {
tags.append(.channel)
}
var count = 0
if let totalState = totalState {
for tag in tags {
if let value = totalState.filteredCounters[tag] {
count += Int(value.chatCount)
}
}
}
for peerId in filter.includePeers {
if let (tag, peerCount) = peerTagAndCount[peerId] {
if !tags.contains(tag) {
if peerCount != 0 {
count += 1
}
}
}
}
result.append((filter, count))
}
return result
}
}
}
private final class TabBarChatListFilterControllerNode: ViewControllerTracingNode { private final class TabBarChatListFilterControllerNode: ViewControllerTracingNode {
private let presentationData: PresentationData private let presentationData: PresentationData
private let cancel: () -> Void private let cancel: () -> Void

View File

@ -168,6 +168,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} }
} }
public final var keepMinimalScrollHeightWithTopInset: CGFloat?
public final var stackFromBottom: Bool = false public final var stackFromBottom: Bool = false
public final var stackFromBottomInsetItemFactor: CGFloat = 0.0 public final var stackFromBottomInsetItemFactor: CGFloat = 0.0
public final var limitHitTestToNodes: Bool = false public final var limitHitTestToNodes: Bool = false
@ -986,6 +988,11 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} }
} }
if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset {
completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset)
bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight)
}
var transition: ContainedViewLayoutTransition = .immediate var transition: ContainedViewLayoutTransition = .immediate
if let updateSizeAndInsets = updateSizeAndInsets { if let updateSizeAndInsets = updateSizeAndInsets {
if !updateSizeAndInsets.duration.isZero && !isExperimentalSnapToScrollToItem { if !updateSizeAndInsets.duration.isZero && !isExperimentalSnapToScrollToItem {
@ -1383,6 +1390,11 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
completeHeight += itemNode.apparentBounds.height completeHeight += itemNode.apparentBounds.height
} }
if let keepMinimalScrollHeightWithTopInset = self.keepMinimalScrollHeightWithTopInset {
completeHeight = max(completeHeight, self.visibleSize.height + keepMinimalScrollHeightWithTopInset)
bottomItemEdge = max(bottomItemEdge, topItemEdge + completeHeight)
}
if self.stackFromBottom { if self.stackFromBottom {
let updatedCompleteHeight = max(completeHeight, self.visibleSize.height) let updatedCompleteHeight = max(completeHeight, self.visibleSize.height)
let deltaCompleteHeight = updatedCompleteHeight - completeHeight let deltaCompleteHeight = updatedCompleteHeight - completeHeight

View File

@ -105,7 +105,7 @@ enum NavigationPreviousAction: Equatable {
open class NavigationBar: ASDisplayNode { open class NavigationBar: ASDisplayNode {
private var presentationData: NavigationBarPresentationData private var presentationData: NavigationBarPresentationData
private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)? private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, Bool)?
private var requestedLayout: Bool = false private var requestedLayout: Bool = false
var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in } var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
@ -124,6 +124,7 @@ open class NavigationBar: ASDisplayNode {
private let clippingNode: ASDisplayNode private let clippingNode: ASDisplayNode
public private(set) var contentNode: NavigationBarContentNode? public private(set) var contentNode: NavigationBarContentNode?
public private(set) var secondaryContentNode: ASDisplayNode?
private var itemTitleListenerKey: Int? private var itemTitleListenerKey: Int?
private var itemTitleViewListenerKey: Int? private var itemTitleViewListenerKey: Int?
@ -801,16 +802,22 @@ open class NavigationBar: ASDisplayNode {
if let validLayout = self.validLayout, self.requestedLayout { if let validLayout = self.validLayout, self.requestedLayout {
self.requestedLayout = false self.requestedLayout = false
self.updateLayout(size: validLayout.0, defaultHeight: validLayout.1, leftInset: validLayout.2, rightInset: validLayout.3, transition: .immediate) self.updateLayout(size: validLayout.0, defaultHeight: validLayout.1, additionalHeight: validLayout.2, leftInset: validLayout.3, rightInset: validLayout.4, appearsHidden: validLayout.5, transition: .immediate)
} }
} }
func updateLayout(size: CGSize, defaultHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalHeight: CGFloat, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, transition: ContainedViewLayoutTransition) {
if self.layoutSuspended { if self.layoutSuspended {
return return
} }
self.validLayout = (size, defaultHeight, leftInset, rightInset) self.validLayout = (size, defaultHeight, additionalHeight, leftInset, rightInset, appearsHidden)
if let secondaryContentNode = self.secondaryContentNode {
transition.updateAlpha(node: secondaryContentNode, alpha: appearsHidden ? 0.0 : 1.0)
}
let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? 46.0 : 0.0
let leftButtonInset: CGFloat = leftInset + 16.0 let leftButtonInset: CGFloat = leftInset + 16.0
let backButtonInset: CGFloat = leftInset + 27.0 let backButtonInset: CGFloat = leftInset + 27.0
@ -818,14 +825,19 @@ open class NavigationBar: ASDisplayNode {
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size))
var expansionHeight: CGFloat = 0.0 var expansionHeight: CGFloat = 0.0
if let contentNode = self.contentNode { if let contentNode = self.contentNode {
let contentNodeFrame: CGRect var contentNodeFrame: CGRect
switch contentNode.mode { switch contentNode.mode {
case .replacement: case .replacement:
expansionHeight = contentNode.height - defaultHeight expansionHeight = contentNode.height - defaultHeight
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
case .expansion: case .expansion:
expansionHeight = contentNode.height expansionHeight = contentNode.height
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - expansionHeight), size: CGSize(width: size.width, height: expansionHeight)) contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - expansionHeight - apparentAdditionalHeight), size: CGSize(width: size.width, height: expansionHeight))
if appearsHidden {
if self.secondaryContentNode != nil {
contentNodeFrame.origin.y += 46.0
}
}
} }
transition.updateFrame(node: contentNode, frame: contentNodeFrame) transition.updateFrame(node: contentNode, frame: contentNodeFrame)
contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
@ -833,8 +845,8 @@ open class NavigationBar: ASDisplayNode {
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel)) transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel))
let nominalHeight: CGFloat = defaultHeight let nominalHeight: CGFloat = defaultHeight - additionalHeight
let contentVerticalOrigin = size.height - nominalHeight - expansionHeight let contentVerticalOrigin = size.height - nominalHeight - expansionHeight - additionalHeight - apparentAdditionalHeight
var leftTitleInset: CGFloat = leftInset + 1.0 var leftTitleInset: CGFloat = leftInset + 1.0
var rightTitleInset: CGFloat = rightInset + 1.0 var rightTitleInset: CGFloat = rightInset + 1.0
@ -1034,7 +1046,7 @@ open class NavigationBar: ASDisplayNode {
let node = NavigationButtonNode() let node = NavigationButtonNode()
node.updateManualText(self.backButtonNode.manualText) node.updateManualText(self.backButtonNode.manualText)
node.color = accentColor node.color = accentColor
if let (size, defaultHeight, _, _) = self.validLayout { if let (size, defaultHeight, _, _, _, _) = self.validLayout {
node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight)) node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight))
node.frame = self.backButtonNode.frame node.frame = self.backButtonNode.frame
} }
@ -1057,7 +1069,7 @@ open class NavigationBar: ASDisplayNode {
} }
node.updateItems(items) node.updateItems(items)
node.color = accentColor node.color = accentColor
if let (size, defaultHeight, _, _) = self.validLayout { if let (size, defaultHeight, _, _, _, _) = self.validLayout {
node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight)) node.updateLayout(constrainedSize: CGSize(width: size.width, height: defaultHeight))
node.frame = self.backButtonNode.frame node.frame = self.backButtonNode.frame
} }
@ -1103,16 +1115,23 @@ open class NavigationBar: ASDisplayNode {
} }
public func contentHeight(defaultHeight: CGFloat) -> CGFloat { public func contentHeight(defaultHeight: CGFloat) -> CGFloat {
var result: CGFloat = 0.0
if let contentNode = self.contentNode { if let contentNode = self.contentNode {
switch contentNode.mode { switch contentNode.mode {
case .expansion: case .expansion:
return defaultHeight + contentNode.height result += defaultHeight + contentNode.height
case .replacement: case .replacement:
return contentNode.height result += contentNode.height
} }
} else { } else {
return defaultHeight result += defaultHeight
} }
if let _ = self.secondaryContentNode {
result += 46.0
}
return result
} }
public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) {
@ -1164,6 +1183,18 @@ open class NavigationBar: ASDisplayNode {
} }
} }
public func setSecondaryContentNode(_ secondatryContentNode: ASDisplayNode?) {
if self.secondaryContentNode !== secondatryContentNode {
if let previous = self.secondaryContentNode {
previous.removeFromSupernode()
}
self.secondaryContentNode = secondatryContentNode
if let secondaryContentNode = secondatryContentNode {
self.clippingNode.addSubnode(secondaryContentNode)
}
}
}
public func executeBack() -> Bool { public func executeBack() -> Bool {
if self.backButtonNode.isInHierarchy { if self.backButtonNode.isInHierarchy {
self.backButtonNode.pressed(0) self.backButtonNode.pressed(0)

View File

@ -269,6 +269,7 @@ open class TabBarController: ViewController {
currentController.navigationItem.setTarget(self.navigationItem) currentController.navigationItem.setTarget(self.navigationItem)
displayNavigationBar = currentController.displayNavigationBar displayNavigationBar = currentController.displayNavigationBar
self.navigationBar?.setContentNode(currentController.navigationBar?.contentNode, animated: false) self.navigationBar?.setContentNode(currentController.navigationBar?.contentNode, animated: false)
self.navigationBar?.setSecondaryContentNode(currentController.navigationBar?.secondaryContentNode)
currentController.displayNode.recursivelyEnsureDisplaySynchronously(true) currentController.displayNode.recursivelyEnsureDisplaySynchronously(true)
self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle
} else { } else {
@ -278,14 +279,21 @@ open class TabBarController: ViewController {
self.navigationItem.titleView = nil self.navigationItem.titleView = nil
self.navigationItem.backBarButtonItem = nil self.navigationItem.backBarButtonItem = nil
self.navigationBar?.setContentNode(nil, animated: false) self.navigationBar?.setContentNode(nil, animated: false)
self.navigationBar?.setSecondaryContentNode(nil)
displayNavigationBar = false displayNavigationBar = false
} }
if self.displayNavigationBar != displayNavigationBar { if self.displayNavigationBar != displayNavigationBar {
self.setDisplayNavigationBar(displayNavigationBar) self.setDisplayNavigationBar(displayNavigationBar)
} }
if let validLayout = self.validLayout { if let layout = self.validLayout {
self.containerLayoutUpdated(validLayout, transition: .immediate) self.containerLayoutUpdated(layout, transition: .immediate)
}
}
public func updateLayout() {
if let layout = self.validLayout {
self.containerLayoutUpdated(layout, transition: .immediate)
} }
} }

View File

@ -213,6 +213,21 @@ public enum ViewControllerNavigationPresentation {
} }
} }
open var cleanNavigationHeight: CGFloat {
if let navigationBar = self.navigationBar {
var height = navigationBar.frame.maxY
if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode {
height += contentNode.nominalHeight - contentNode.height
}
if navigationBar.secondaryContentNode != nil {
//height -= 46.0
}
return height
} else {
return 0.0
}
}
open var visualNavigationInsetHeight: CGFloat { open var visualNavigationInsetHeight: CGFloat {
if let navigationBar = self.navigationBar { if let navigationBar = self.navigationBar {
let height = navigationBar.frame.maxY let height = navigationBar.frame.maxY
@ -225,6 +240,8 @@ public enum ViewControllerNavigationPresentation {
} }
} }
public var additionalNavigationBarHeight: CGFloat = 0.0
private let _ready = Promise<Bool>(true) private let _ready = Promise<Bool>(true)
open var ready: Promise<Bool> { open var ready: Promise<Bool> {
return self._ready return self._ready
@ -338,7 +355,7 @@ public enum ViewControllerNavigationPresentation {
private func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { private func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0 let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
let defaultNavigationBarHeight: CGFloat var defaultNavigationBarHeight: CGFloat
if self._presentedInModal { if self._presentedInModal {
defaultNavigationBarHeight = 56.0 defaultNavigationBarHeight = 56.0
} else { } else {
@ -353,9 +370,6 @@ public enum ViewControllerNavigationPresentation {
navigationBarOffset = 0.0 navigationBarOffset = 0.0
} }
var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarOffset), size: CGSize(width: layout.size.width, height: navigationBarHeight)) var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarOffset), size: CGSize(width: layout.size.width, height: navigationBarHeight))
if layout.statusBarHeight == nil {
//navigationBarFrame.size.height = (self.navigationBar?.contentHeight ?? 44.0) + 20.0
}
if !self.displayNavigationBar { if !self.displayNavigationBar {
navigationBarFrame.origin.y = -navigationBarFrame.size.height navigationBarFrame.origin.y = -navigationBarFrame.size.height
@ -368,7 +382,7 @@ public enum ViewControllerNavigationPresentation {
if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar { if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar {
navigationBarFrame.origin.y += contentNode.height + statusBarHeight navigationBarFrame.origin.y += contentNode.height + statusBarHeight
} }
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: defaultNavigationBarHeight, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: defaultNavigationBarHeight, additionalHeight: 0.0, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, transition: transition)
if !transition.isAnimated { if !transition.isAnimated {
navigationBar.layer.cancelAnimationsRecursive(key: "bounds") navigationBar.layer.cancelAnimationsRecursive(key: "bounds")
navigationBar.layer.cancelAnimationsRecursive(key: "position") navigationBar.layer.cancelAnimationsRecursive(key: "position")

View File

@ -165,7 +165,7 @@ class ItemListPeerActionItemNode: ListViewItemNode {
case .generic: case .generic:
verticalInset = 11.0 verticalInset = 11.0
verticalOffset = 0.0 verticalOffset = 0.0
leftInset = 59.0 + params.leftInset leftInset = (item.icon == nil ? 16.0 : 59.0) + params.leftInset
case .peerList: case .peerList:
verticalInset = 14.0 verticalInset = 14.0
verticalOffset = 0.0 verticalOffset = 0.0

View File

@ -442,6 +442,9 @@
SSubscriber *subscriber = context.subscriber; SSubscriber *subscriber = context.subscriber;
[context.videoProcessor startWithTimeRange:context.timeRange progressBlock:^(CGFloat progress) [context.videoProcessor startWithTimeRange:context.timeRange progressBlock:^(CGFloat progress)
{ {
#if DEBUG
printf("Video progress: %f\n", progress);
#endif
[subscriber putNext:@(progress)]; [subscriber putNext:@(progress)];
} completionBlock:^ } completionBlock:^
{ {
@ -1032,7 +1035,11 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer,
NSDictionary *codecSettings = @ NSDictionary *codecSettings = @
{ {
#if DEBUG
AVVideoAverageBitRateKey: @([self _videoBitrateKbpsForPreset:preset] * 500),
#else
AVVideoAverageBitRateKey: @([self _videoBitrateKbpsForPreset:preset] * 1000), AVVideoAverageBitRateKey: @([self _videoBitrateKbpsForPreset:preset] * 1000),
#endif
AVVideoCleanApertureKey: videoCleanApertureSettings, AVVideoCleanApertureKey: videoCleanApertureSettings,
AVVideoPixelAspectRatioKey: videoAspectRatioSettings AVVideoPixelAspectRatioKey: videoAspectRatioSettings
}; };

View File

@ -351,7 +351,7 @@ final class FFMpegMediaFrameSourceContext: NSObject {
let avFormatContext = FFMpegAVFormatContext() let avFormatContext = FFMpegAVFormatContext()
guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(self.ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else { guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(self.ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) else {
self.readingError = true self.readingError = true
return return
} }

View File

@ -74,7 +74,7 @@ public final class SoftwareVideoSource {
let ioBufferSize = 64 * 1024 let ioBufferSize = 64 * 1024
let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback)
self.avIoContext = avIoContext self.avIoContext = avIoContext
avFormatContext.setIO(self.avIoContext!) avFormatContext.setIO(self.avIoContext!)

View File

@ -123,7 +123,7 @@ private final class UniversalSoftwareVideoSourceImpl {
let ioBufferSize = 1 * 1024 let ioBufferSize = 1 * 1024
guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else { guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, writePacket: nil, seek: seekCallback) else {
return nil return nil
} }
self.avIoContext = avIoContext self.avIoContext = avIoContext

View File

@ -3,10 +3,12 @@ import Foundation
final class MutableBasicPeerView: MutablePostboxView { final class MutableBasicPeerView: MutablePostboxView {
private let peerId: PeerId private let peerId: PeerId
fileprivate var peer: Peer? fileprivate var peer: Peer?
fileprivate var notificationSettings: PeerNotificationSettings?
init(postbox: Postbox, peerId: PeerId) { init(postbox: Postbox, peerId: PeerId) {
self.peerId = peerId self.peerId = peerId
self.peer = postbox.peerTable.get(peerId) self.peer = postbox.peerTable.get(peerId)
self.notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId)
} }
func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
@ -15,6 +17,10 @@ final class MutableBasicPeerView: MutablePostboxView {
self.peer = peer self.peer = peer
updated = true updated = true
} }
if transaction.currentUpdatedPeerNotificationSettings[self.peerId] != nil {
self.notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId)
updated = true
}
return updated return updated
} }
@ -26,8 +32,10 @@ final class MutableBasicPeerView: MutablePostboxView {
public final class BasicPeerView: PostboxView { public final class BasicPeerView: PostboxView {
public let peer: Peer? public let peer: Peer?
public let notificationSettings: PeerNotificationSettings?
init(_ view: MutableBasicPeerView) { init(_ view: MutableBasicPeerView) {
self.peer = view.peer self.peer = view.peer
self.notificationSettings = view.notificationSettings
} }
} }

View File

@ -10,7 +10,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-206066487] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) } dict[-206066487] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) } dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[461151667] = { return Api.ChatFull.parse_chatFull($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[-932174686] = { return Api.PollResults.parse_pollResults($0) }
dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) } dict[-925415106] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-636267638] = { return Api.ChatParticipant.parse_chatParticipantCreator($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[-2027964103] = { return Api.Update.parse_updateGeoLiveViewed($0) }
dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) } dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) }
dict[1123585836] = { return Api.Update.parse_updateMessagePollVote($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[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@ -357,6 +360,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) } dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
dict[-833715459] = { return Api.InputMedia.parse_inputMediaGeoLive($0) } dict[-833715459] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
dict[-1410741723] = { return Api.InputMedia.parse_inputMediaPoll($0) } dict[-1410741723] = { return Api.InputMedia.parse_inputMediaPoll($0) }
dict[-1358977017] = { return Api.InputMedia.parse_inputMediaDice($0) }
dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) } dict[2134579434] = { return Api.InputPeer.parse_inputPeerEmpty($0) }
dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) } dict[2107670217] = { return Api.InputPeer.parse_inputPeerSelf($0) }
dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) } dict[396093539] = { return Api.InputPeer.parse_inputPeerChat($0) }
@ -412,6 +416,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) } dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) }
dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) } dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) }
dict[-1269012015] = { return Api.messages.AffectedHistory.parse_affectedHistory($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[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) }
dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) } dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) }
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) } dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
@ -480,6 +487,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-668391402] = { return Api.InputUser.parse_inputUser($0) } dict[-668391402] = { return Api.InputUser.parse_inputUser($0) }
dict[-1366746132] = { return Api.Page.parse_page($0) } dict[-1366746132] = { return Api.Page.parse_page($0) }
dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($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[157948117] = { return Api.upload.File.parse_file($0) }
dict[-242427324] = { return Api.upload.File.parse_fileCdnRedirect($0) } dict[-242427324] = { return Api.upload.File.parse_fileCdnRedirect($0) }
dict[-1078612597] = { return Api.ChannelLocation.parse_channelLocationEmpty($0) } dict[-1078612597] = { return Api.ChannelLocation.parse_channelLocationEmpty($0) }
@ -506,6 +514,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) } dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) } dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
dict[-1564789301] = { return Api.PhoneCallProtocol.parse_phoneCallProtocol($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[-1567175714] = { return Api.MessageFwdAuthor.parse_messageFwdAuthor($0) }
dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) } dict[-1539849235] = { return Api.WallPaper.parse_wallPaper($0) }
dict[-1963717851] = { return Api.WallPaper.parse_wallPaperNoFile($0) } dict[-1963717851] = { return Api.WallPaper.parse_wallPaperNoFile($0) }
@ -520,6 +529,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1837345356] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) } dict[-1837345356] = { return Api.InputChatPhoto.parse_inputChatUploadedPhoto($0) }
dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) } dict[-1991004873] = { return Api.InputChatPhoto.parse_inputChatPhoto($0) }
dict[-368917890] = { return Api.PaymentCharge.parse_paymentCharge($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[-484987010] = { return Api.Updates.parse_updatesTooLong($0) }
dict[-1857044719] = { return Api.Updates.parse_updateShortMessage($0) } dict[-1857044719] = { return Api.Updates.parse_updateShortMessage($0) }
dict[377562760] = { return Api.Updates.parse_updateShortChatMessage($0) } dict[377562760] = { return Api.Updates.parse_updateShortChatMessage($0) }
@ -527,6 +537,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) } dict[1918567619] = { return Api.Updates.parse_updatesCombined($0) }
dict[1957577280] = { return Api.Updates.parse_updates($0) } dict[1957577280] = { return Api.Updates.parse_updates($0) }
dict[301019932] = { return Api.Updates.parse_updateShortSentMessage($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[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) } dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) } dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
@ -539,6 +550,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) } dict[-1666158377] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) } dict[-873313984] = { return Api.MessageMedia.parse_messageMediaContact($0) }
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) } dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
dict[1670374507] = { return Api.MessageMedia.parse_messageMediaDice($0) }
dict[-842892769] = { return Api.PaymentSavedCredentials.parse_paymentSavedCredentialsCard($0) } dict[-842892769] = { return Api.PaymentSavedCredentials.parse_paymentSavedCredentialsCard($0) }
dict[1450380236] = { return Api.Null.parse_null($0) } dict[1450380236] = { return Api.Null.parse_null($0) }
dict[1923290508] = { return Api.auth.CodeType.parse_codeTypeSms($0) } dict[1923290508] = { return Api.auth.CodeType.parse_codeTypeSms($0) }
@ -590,6 +602,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1551583367] = { return Api.ReceivedNotifyMessage.parse_receivedNotifyMessage($0) } dict[-1551583367] = { return Api.ReceivedNotifyMessage.parse_receivedNotifyMessage($0) }
dict[-57668565] = { return Api.ChatParticipants.parse_chatParticipantsForbidden($0) } dict[-57668565] = { return Api.ChatParticipants.parse_chatParticipantsForbidden($0) }
dict[1061556205] = { return Api.ChatParticipants.parse_chatParticipants($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[-1056001329] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsSaved($0) }
dict[873977640] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentials($0) } dict[873977640] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentials($0) }
dict[178373535] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsApplePay($0) } dict[178373535] = { return Api.InputPaymentCredentials.parse_inputPaymentCredentialsApplePay($0) }
@ -762,6 +775,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) } dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($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[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) } dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) }
dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) } dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) }
@ -1085,6 +1099,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.messages.AffectedHistory: case let _1 as Api.messages.AffectedHistory:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StatsGraph:
_1.serialize(buffer, boxed)
case let _1 as Api.account.PasswordInputSettings: case let _1 as Api.account.PasswordInputSettings:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PageTableCell: case let _1 as Api.PageTableCell:
@ -1155,6 +1171,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.SecureCredentialsEncrypted: case let _1 as Api.SecureCredentialsEncrypted:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StatsPercentValue:
_1.serialize(buffer, boxed)
case let _1 as Api.upload.File: case let _1 as Api.upload.File:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.ChannelLocation: case let _1 as Api.ChannelLocation:
@ -1189,6 +1207,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PhoneCallProtocol: case let _1 as Api.PhoneCallProtocol:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StatsDateRangeDays:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageFwdAuthor: case let _1 as Api.MessageFwdAuthor:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.WallPaper: case let _1 as Api.WallPaper:
@ -1205,8 +1225,12 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PaymentCharge: case let _1 as Api.PaymentCharge:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.stats.BroadcastStats:
_1.serialize(buffer, boxed)
case let _1 as Api.Updates: case let _1 as Api.Updates:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StatsAbsValueAndPrev:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageMedia: case let _1 as Api.MessageMedia:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PaymentSavedCredentials: case let _1 as Api.PaymentSavedCredentials:
@ -1257,6 +1281,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.ChatParticipants: case let _1 as Api.ChatParticipants:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.DialogFilter:
_1.serialize(buffer, boxed)
case let _1 as Api.InputPaymentCredentials: case let _1 as Api.InputPaymentCredentials:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.ShippingOption: case let _1 as Api.ShippingOption:
@ -1387,6 +1413,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.updates.ChannelDifference: case let _1 as Api.updates.ChannelDifference:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.StatsRowAbsValueAndPrev:
_1.serialize(buffer, boxed)
case let _1 as Api.channels.AdminLogResults: case let _1 as Api.channels.AdminLogResults:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.ChatOnlines: case let _1 as Api.ChatOnlines:

View File

@ -1805,7 +1805,7 @@ public extension Api {
} }
public enum ChatFull: TypeConstructorDescription { 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 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) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { 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 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
break 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 { if boxed {
buffer.appendInt32(763976820) buffer.appendInt32(-253335766)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, 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 << 15) != 0 {location!.serialize(buffer, true)}
if Int(flags) & Int(1 << 17) != 0 {serializeInt32(slowmodeSeconds!, buffer: buffer, boxed: false)} 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 << 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) serializeInt32(pts, buffer: buffer, boxed: false)
break break
} }
@ -1870,8 +1871,8 @@ public extension Api {
switch self { switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId): 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)]) 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): 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), ("pts", 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? var _25: Int32?
if Int(_1!) & Int(1 << 18) != 0 {_25 = reader.readInt32() } if Int(_1!) & Int(1 << 18) != 0 {_25 = reader.readInt32() }
var _26: Int32? 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 _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil
@ -2013,9 +2016,10 @@ public extension Api {
let _c23 = (Int(_1!) & Int(1 << 15) == 0) || _23 != nil let _c23 = (Int(_1!) & Int(1 << 15) == 0) || _23 != nil
let _c24 = (Int(_1!) & Int(1 << 17) == 0) || _24 != nil let _c24 = (Int(_1!) & Int(1 << 17) == 0) || _24 != nil
let _c25 = (Int(_1!) & Int(1 << 18) == 0) || _25 != nil let _c25 = (Int(_1!) & Int(1 << 18) == 0) || _25 != nil
let _c26 = _26 != nil let _c26 = (Int(_1!) & Int(1 << 12) == 0) || _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 { let _c27 = _27 != nil
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!) 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 { else {
return nil return nil
@ -5861,6 +5865,9 @@ public extension Api {
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32) case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
case updateLoginToken case updateLoginToken
case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer]) 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) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -6509,6 +6516,30 @@ public extension Api {
for item in options { for item in options {
serializeBytes(item, buffer: buffer, boxed: false) 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 break
} }
} }
@ -6669,6 +6700,12 @@ public extension Api {
return ("updateLoginToken", []) return ("updateLoginToken", [])
case .updateMessagePollVote(let pollId, let userId, let options): case .updateMessagePollVote(let pollId, let userId, let options):
return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", 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 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 { public enum PopularContact: TypeConstructorDescription {
@ -10365,6 +10437,7 @@ public extension Api {
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String) case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, period: Int32?) case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, period: Int32?)
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?) case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?)
case inputMediaDice
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -10511,6 +10584,12 @@ public extension Api {
for item in correctAnswers! { for item in correctAnswers! {
serializeBytes(item, buffer: buffer, boxed: false) serializeBytes(item, buffer: buffer, boxed: false)
}} }}
break
case .inputMediaDice:
if boxed {
buffer.appendInt32(-1358977017)
}
break break
} }
} }
@ -10547,6 +10626,8 @@ public extension Api {
return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("period", period)]) return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("period", period)])
case .inputMediaPoll(let flags, let poll, let correctAnswers): case .inputMediaPoll(let flags, let poll, let correctAnswers):
return ("inputMediaPoll", [("flags", flags), ("poll", poll), ("correctAnswers", correctAnswers)]) return ("inputMediaPoll", [("flags", flags), ("poll", poll), ("correctAnswers", correctAnswers)])
case .inputMediaDice:
return ("inputMediaDice", [])
} }
} }
@ -10855,6 +10936,9 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_inputMediaDice(_ reader: BufferReader) -> InputMedia? {
return Api.InputMedia.inputMediaDice
}
} }
public enum InputPeer: TypeConstructorDescription { public enum InputPeer: TypeConstructorDescription {
@ -11956,6 +12040,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 { public enum PageTableCell: TypeConstructorDescription {
case pageTableCell(flags: Int32, text: Api.RichText?, colspan: Int32?, rowspan: Int32?) case pageTableCell(flags: Int32, text: Api.RichText?, colspan: Int32?, rowspan: Int32?)
@ -13662,6 +13822,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 { public enum ChannelLocation: TypeConstructorDescription {
case channelLocationEmpty case channelLocationEmpty
@ -14434,6 +14632,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 { public enum MessageFwdAuthor: TypeConstructorDescription {
case messageFwdAuthor(channelId: Int32) case messageFwdAuthor(channelId: Int32)
@ -15098,6 +15334,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 { public enum MessageMedia: TypeConstructorDescription {
case messageMediaEmpty case messageMediaEmpty
@ -15112,6 +15386,7 @@ public extension Api {
case messageMediaDocument(flags: Int32, document: Api.Document?, ttlSeconds: Int32?) case messageMediaDocument(flags: Int32, document: Api.Document?, ttlSeconds: Int32?)
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int32) case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int32)
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults) case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
case messageMediaDice(value: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -15209,6 +15484,12 @@ public extension Api {
poll.serialize(buffer, true) poll.serialize(buffer, true)
results.serialize(buffer, true) results.serialize(buffer, true)
break break
case .messageMediaDice(let value):
if boxed {
buffer.appendInt32(1670374507)
}
serializeInt32(value, buffer: buffer, boxed: false)
break
} }
} }
@ -15238,6 +15519,8 @@ public extension Api {
return ("messageMediaContact", [("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("userId", userId)]) return ("messageMediaContact", [("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("userId", userId)])
case .messageMediaPoll(let poll, let results): case .messageMediaPoll(let poll, let results):
return ("messageMediaPoll", [("poll", poll), ("results", results)]) return ("messageMediaPoll", [("poll", poll), ("results", results)])
case .messageMediaDice(let value):
return ("messageMediaDice", [("value", value)])
} }
} }
@ -15443,6 +15726,17 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_messageMediaDice(_ reader: BufferReader) -> MessageMedia? {
var _1: Int32?
_1 = reader.readInt32()
let _c1 = _1 != nil
if _c1 {
return Api.MessageMedia.messageMediaDice(value: _1!)
}
else {
return nil
}
}
} }
public enum PaymentSavedCredentials: TypeConstructorDescription { public enum PaymentSavedCredentials: TypeConstructorDescription {
@ -16852,6 +17146,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 { public enum InputPaymentCredentials: TypeConstructorDescription {
case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer) case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer)
@ -20918,6 +21264,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 { public enum ChatOnlines: TypeConstructorDescription {
case chatOnlines(onlines: Int32) case chatOnlines(onlines: Int32)

View File

@ -537,6 +537,130 @@ public struct payments {
} }
} }
public extension Api { 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 {
public struct auth { public struct auth {
public enum LoginToken: TypeConstructorDescription { public enum LoginToken: TypeConstructorDescription {
case loginToken(expires: Int32, token: Buffer) case loginToken(expires: Int32, token: Buffer)

View File

@ -3214,6 +3214,54 @@ public extension Api {
return result 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 struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
@ -3900,6 +3948,36 @@ public extension Api {
}) })
} }
} }
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 struct auth {
public static func checkPhone(phoneNumber: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.CheckedPhone>) { public static func checkPhone(phoneNumber: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.CheckedPhone>) {
let buffer = Buffer() let buffer = Buffer()

View File

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

View File

@ -140,7 +140,7 @@ public struct ChatListFilter: PostboxCoding, Equatable {
} }
} }
/*extension ChatListFilter { extension ChatListFilter {
init(apiFilter: Api.DialogFilter) { init(apiFilter: Api.DialogFilter) {
switch apiFilter { switch apiFilter {
case let .dialogFilter(flags, id, title, includePeers): case let .dialogFilter(flags, id, title, includePeers):
@ -179,13 +179,13 @@ public struct ChatListFilter: PostboxCoding, Equatable {
return transaction.getPeer(peerId).flatMap(apiInputPeer) return transaction.getPeer(peerId).flatMap(apiInputPeer)
}) })
} }
}*/ }
public enum RequestUpdateChatListFilterError { public enum RequestUpdateChatListFilterError {
case generic case generic
} }
/*public func requestUpdateChatListFilter(account: Account, id: Int32, filter: ChatListFilter?) -> Signal<Never, RequestUpdateChatListFilterError> { public func requestUpdateChatListFilter(account: Account, id: Int32, filter: ChatListFilter?) -> Signal<Never, RequestUpdateChatListFilterError> {
return account.postbox.transaction { transaction -> Api.DialogFilter? in return account.postbox.transaction { transaction -> Api.DialogFilter? in
return filter?.apiFilter(transaction: transaction) return filter?.apiFilter(transaction: transaction)
} }
@ -270,6 +270,8 @@ private func requestChatListFilters(postbox: Postbox, network: Network) -> Signa
} }
func managedChatListFilters(postbox: Postbox, network: Network) -> Signal<Never, NoError> { func managedChatListFilters(postbox: Postbox, network: Network) -> Signal<Never, NoError> {
return .complete()
return requestChatListFilters(postbox: postbox, network: network) return requestChatListFilters(postbox: postbox, network: network)
|> `catch` { _ -> Signal<[ChatListFilter], NoError> in |> `catch` { _ -> Signal<[ChatListFilter], NoError> in
return .complete() return .complete()
@ -376,4 +378,4 @@ public func updateChatListFilterSettingsInteractively(postbox: Postbox, _ f: @es
return result ?? .default return result ?? .default
} }
} }
*/

View File

@ -1,6 +1,7 @@
import Foundation import Foundation
import Postbox import Postbox
import SwiftSignalKit import SwiftSignalKit
import SyncCore
private final class ManagedChatListHolesState { private final class ManagedChatListHolesState {
private var holeDisposables: [ChatListHolesEntry: Disposable] = [:] private var holeDisposables: [ChatListHolesEntry: Disposable] = [:]
@ -53,15 +54,17 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
return Signal { _ in return Signal { _ in
let state = Atomic(value: ManagedChatListHolesState()) let state = Atomic(value: ManagedChatListHolesState())
let topRootHoleKey = PostboxViewKey.allChatListHoles(.root) let topRootHoleKey: PostboxViewKey = .allChatListHoles(.root)
let topRootHole = postbox.combinedView(keys: [topRootHoleKey]) let filtersKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.chatListFilters]))
let combinedView = postbox.combinedView(keys: [topRootHoleKey, filtersKey])
let disposable = combineLatest(postbox.chatListHolesView(), topRootHole).start(next: { view, topRootHoleView in let disposable = combineLatest(postbox.chatListHolesView(), combinedView).start(next: { view, combinedView in
var additionalLatestHole: ChatListHole? var additionalLatestHole: ChatListHole?
if let topRootHole = topRootHoleView.views[topRootHoleKey] as? AllChatListHolesView {
#if os(macOS) if let preferencesView = combinedView.views[filtersKey] as? PreferencesView, let filtersState = preferencesView.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState, !filtersState.filters.isEmpty {
additionalLatestHole = topRootHole.latestHole if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView {
#endif additionalLatestHole = topRootHole.latestHole
}
} }
let (removed, added, addedAdditionalLatestHole) = state.with { state in let (removed, added, addedAdditionalLatestHole) = state.with { state in

View File

@ -277,71 +277,73 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? {
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?) { func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?) {
if let media = media { if let media = media {
switch media { switch media {
case let .messageMediaPhoto(_, photo, ttlSeconds): case let .messageMediaPhoto(_, photo, ttlSeconds):
if let photo = photo { if let photo = photo {
if let mediaImage = telegramMediaImageFromApiPhoto(photo) { if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
return (mediaImage, ttlSeconds) return (mediaImage, ttlSeconds)
}
} else {
return (TelegramMediaExpiredContent(data: .image), nil)
} }
case let .messageMediaContact(phoneNumber, firstName, lastName, vcard, userId): } else {
let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) return (TelegramMediaExpiredContent(data: .image), nil)
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard) }
return (mediaContact, nil) case let .messageMediaContact(phoneNumber, firstName, lastName, vcard, userId):
case let .messageMediaGeo(geo): let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil) let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard)
return (mediaMap, nil) return (mediaContact, nil)
case let .messageMediaVenue(geo, title, address, provider, venueId, venueType): case let .messageMediaGeo(geo):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil) let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil)
return (mediaMap, nil) return (mediaMap, nil)
case let .messageMediaGeoLive(geo, period): case let .messageMediaVenue(geo, title, address, provider, venueId, venueType):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period) let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil)
return (mediaMap, nil) return (mediaMap, nil)
case let .messageMediaDocument(_, document, ttlSeconds): case let .messageMediaGeoLive(geo, period):
if let document = document { let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period)
if let mediaFile = telegramMediaFileFromApiDocument(document) { return (mediaMap, nil)
return (mediaFile, ttlSeconds) case let .messageMediaDocument(_, document, ttlSeconds):
} if let document = document {
} else { if let mediaFile = telegramMediaFileFromApiDocument(document) {
return (TelegramMediaExpiredContent(data: .file), nil) return (mediaFile, ttlSeconds)
}
case let .messageMediaWebPage(webpage):
if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage, url: nil) {
return (mediaWebpage, nil)
}
case .messageMediaUnsupported:
return (TelegramMediaUnsupported(), nil)
case .messageMediaEmpty:
break
case let .messageMediaGame(game):
return (TelegramMediaGame(apiGame: game), nil)
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
var parsedFlags = TelegramMediaInvoiceFlags()
if (flags & (1 << 3)) != 0 {
parsedFlags.insert(.isTest)
} }
} else {
return (TelegramMediaExpiredContent(data: .file), nil)
}
case let .messageMediaWebPage(webpage):
if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage, url: nil) {
return (mediaWebpage, nil)
}
case .messageMediaUnsupported:
return (TelegramMediaUnsupported(), nil)
case .messageMediaEmpty:
break
case let .messageMediaGame(game):
return (TelegramMediaGame(apiGame: game), nil)
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
var parsedFlags = TelegramMediaInvoiceFlags()
if (flags & (1 << 3)) != 0 {
parsedFlags.insert(.isTest)
}
if (flags & (1 << 1)) != 0 {
parsedFlags.insert(.shippingAddressRequested)
}
return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil)
case let .messageMediaPoll(poll, results):
switch poll {
case let .poll(id, flags, question, answers):
let publicity: TelegramMediaPollPublicity
if (flags & (1 << 1)) != 0 { if (flags & (1 << 1)) != 0 {
parsedFlags.insert(.shippingAddressRequested) publicity = .public
} else {
publicity = .anonymous
} }
return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil) let kind: TelegramMediaPollKind
case let .messageMediaPoll(poll, results): if (flags & (1 << 3)) != 0 {
switch poll { kind = .quiz
case let .poll(id, flags, question, answers): } else {
let publicity: TelegramMediaPollPublicity kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
if (flags & (1 << 1)) != 0 {
publicity = .public
} else {
publicity = .anonymous
}
let kind: TelegramMediaPollKind
if (flags & (1 << 3)) != 0 {
kind = .quiz
} else {
kind = .poll(multipleAnswers: (flags & (1 << 2)) != 0)
}
return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0), nil)
} }
return (TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.CloudPoll, id: id), publicity: publicity, kind: kind, text: question, options: answers.map(TelegramMediaPollOption.init(apiOption:)), correctAnswers: nil, results: TelegramMediaPollResults(apiResults: results), isClosed: (flags & (1 << 0)) != 0), nil)
}
case .messageMediaDice:
break
} }
} }

View File

@ -319,7 +319,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
} }
switch fullChat { 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() var channelFlags = CachedChannelFlags()
if (flags & (1 << 3)) != 0 { if (flags & (1 << 3)) != 0 {
channelFlags.insert(.canDisplayParticipants) channelFlags.insert(.canDisplayParticipants)

View File

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

View File

@ -65,6 +65,8 @@ struct VideoConversionConfiguration {
} }
static func with(appConfiguration: AppConfiguration) -> VideoConversionConfiguration { static func with(appConfiguration: AppConfiguration) -> VideoConversionConfiguration {
return VideoConversionConfiguration(remuxToFMp4: true)
if let data = appConfiguration.data, let conversion = data["video_conversion"] as? [String: Any] { if let data = appConfiguration.data, let conversion = data["video_conversion"] as? [String: Any] {
let remuxToFMp4 = conversion["remux_fmp4"] as? Bool ?? VideoConversionConfiguration.defaultValue.remuxToFMp4 let remuxToFMp4 = conversion["remux_fmp4"] as? Bool ?? VideoConversionConfiguration.defaultValue.remuxToFMp4
return VideoConversionConfiguration(remuxToFMp4: remuxToFMp4) return VideoConversionConfiguration(remuxToFMp4: remuxToFMp4)

View File

@ -0,0 +1,43 @@
import Foundation
import Postbox
import SwiftSignalKit
public struct ChatListFilterSettings: Equatable, PreferencesEntry {
public var displayTabs: Bool
public static var `default`: ChatListFilterSettings {
return ChatListFilterSettings(displayTabs: true)
}
public init(displayTabs: Bool) {
self.displayTabs = displayTabs
}
public init(decoder: PostboxDecoder) {
self.displayTabs = decoder.decodeInt32ForKey("displayTabs", orElse: 1) != 0
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.displayTabs ? 1 : 0, forKey: "displayTabs")
}
public func isEqual(to: PreferencesEntry) -> Bool {
if let to = to as? ChatListFilterSettings {
return self == to
} else {
return false
}
}
}
public func updateChatListFilterSettings(transaction: Transaction, _ f: @escaping (ChatListFilterSettings) -> ChatListFilterSettings) {
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatListFilterSettings, { entry in
let currentSettings: ChatListFilterSettings
if let entry = entry as? ChatListFilterSettings {
currentSettings = entry
} else {
currentSettings = .default
}
return f(currentSettings)
})
}

View File

@ -6,7 +6,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface FFMpegAVIOContext : NSObject @interface FFMpegAVIOContext : NSObject
- (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (*)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek; - (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket writePacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))writePacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek;
- (void *)impl; - (void *)impl;

View File

@ -10,11 +10,11 @@
@implementation FFMpegAVIOContext @implementation FFMpegAVIOContext
- (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (*)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek { - (instancetype _Nullable)initWithBufferSize:(int32_t)bufferSize opaqueContext:(void * const)opaqueContext readPacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))readPacket writePacket:(int (* _Nullable)(void * _Nullable opaque, uint8_t * _Nullable buf, int buf_size))writePacket seek:(int64_t (*)(void * _Nullable opaque, int64_t offset, int whence))seek {
self = [super init]; self = [super init];
if (self != nil) { if (self != nil) {
void *avIoBuffer = av_malloc(bufferSize); void *avIoBuffer = av_malloc(bufferSize);
_impl = avio_alloc_context(avIoBuffer, bufferSize, 0, opaqueContext, readPacket, nil, seek); _impl = avio_alloc_context(avIoBuffer, bufferSize, 0, opaqueContext, readPacket, writePacket, seek);
_impl->direct = 1; _impl->direct = 1;
if (_impl == nil) { if (_impl == nil) {
av_free(avIoBuffer); av_free(avIoBuffer);

View File

@ -1,11 +1,64 @@
#import "FFMpegRemuxer.h" #import "FFMpegRemuxer.h"
#import "FFMpegAVIOContext.h"
#include "libavutil/timestamp.h" #include "libavutil/timestamp.h"
#include "libavformat/avformat.h" #include "libavformat/avformat.h"
#include "libavcodec/avcodec.h" #include "libavcodec/avcodec.h"
#define MOV_TIMESCALE 1000 #define MOV_TIMESCALE 1000
@interface FFMpegRemuxerContext : NSObject {
@public
int _fd;
int64_t _offset;
}
@end
@implementation FFMpegRemuxerContext
- (instancetype)initWithFileName:(NSString *)fileName {
self = [super init];
if (self != nil) {
_fd = open(fileName.UTF8String, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
}
return self;
}
- (void)dealloc {
if (_fd > 0) {
close(_fd);
}
}
@end
static int readPacketImpl(void * _Nullable opaque, uint8_t * _Nullable buffer, int length) {
FFMpegRemuxerContext *context = (__bridge FFMpegRemuxerContext *)opaque;
context->_offset += length;
printf("read %lld bytes (offset is now %lld)\n", length, context->_offset);
return read(context->_fd, buffer, length);
}
static int writePacketImpl(void * _Nullable opaque, uint8_t * _Nullable buffer, int length) {
FFMpegRemuxerContext *context = (__bridge FFMpegRemuxerContext *)opaque;
context->_offset += length;
printf("write %lld bytes (offset is now %lld)\n", length, context->_offset);
return write(context->_fd, buffer, length);
}
static int64_t seekImpl(void * _Nullable opaque, int64_t offset, int whence) {
FFMpegRemuxerContext *context = (__bridge FFMpegRemuxerContext *)opaque;
printf("seek to %lld\n", offset);
if (whence == FFMPEG_AVSEEK_SIZE) {
return 0;
} else {
context->_offset = offset;
return lseek(context->_fd, offset, SEEK_SET);
}
}
@implementation FFMpegRemuxer @implementation FFMpegRemuxer
+ (bool)remux:(NSString * _Nonnull)path to:(NSString * _Nonnull)outPath { + (bool)remux:(NSString * _Nonnull)path to:(NSString * _Nonnull)outPath {
@ -21,6 +74,9 @@
in_filename = [path UTF8String]; in_filename = [path UTF8String];
out_filename = [outPath UTF8String]; out_filename = [outPath UTF8String];
//FFMpegRemuxerContext *outputContext = [[FFMpegRemuxerContext alloc] initWithFileName:outPath];
//FFMpegAVIOContext *outputIoContext = [[FFMpegAVIOContext alloc] initWithBufferSize:1024 opaqueContext:(__bridge void *)outputContext readPacket:&readPacketImpl writePacket:&writePacketImpl seek:&seekImpl];
if ((ret = avformat_open_input(&input_format_context, in_filename, av_find_input_format("mov"), NULL)) < 0) { if ((ret = avformat_open_input(&input_format_context, in_filename, av_find_input_format("mov"), NULL)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename); fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end; goto end;
@ -31,6 +87,11 @@
} }
avformat_alloc_output_context2(&output_format_context, NULL, NULL, out_filename); avformat_alloc_output_context2(&output_format_context, NULL, NULL, out_filename);
//output_format_context = avformat_alloc_context();
//output_format_context->pb = outputIoContext.impl;
//output_format_context->flags |= AVFMT_FLAG_CUSTOM_IO;
//output_format_context->oformat = av_guess_format("mp4", NULL, NULL);
if (!output_format_context) { if (!output_format_context) {
fprintf(stderr, "Could not create output context\n"); fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN; ret = AVERROR_UNKNOWN;
@ -103,7 +164,7 @@
// https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE // https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE
av_dict_set(&opts, "movflags", "dash+faststart+global_sidx+skip_trailer", 0); av_dict_set(&opts, "movflags", "dash+faststart+global_sidx+skip_trailer", 0);
if (maxTrackLength > 0) { if (maxTrackLength > 0) {
av_dict_set_int(&opts, "custom_maxTrackLength", maxTrackLength, 0); //av_dict_set_int(&opts, "custom_maxTrackLength", maxTrackLength, 0);
} }
} }
// https://ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga18b7b10bb5b94c4842de18166bc677cb // https://ffmpeg.org/doxygen/trunk/group__lavf__encoding.html#ga18b7b10bb5b94c4842de18166bc677cb
@ -144,8 +205,9 @@
end: end:
avformat_close_input(&input_format_context); avformat_close_input(&input_format_context);
/* close output */ /* close output */
if (output_format_context && !(output_format_context->oformat->flags & AVFMT_NOFILE)) if (output_format_context && !(output_format_context->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&output_format_context->pb); avio_closep(&output_format_context->pb);
}
avformat_free_context(output_format_context); avformat_free_context(output_format_context);
av_freep(&streams_list); av_freep(&streams_list);
if (ret < 0 && ret != AVERROR_EOF) { if (ret < 0 && ret != AVERROR_EOF) {