mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Tab support
This commit is contained in:
parent
2505082eee
commit
507e17f40f
@ -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) {
|
||||||
|
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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!)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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
|
||||||
}()
|
}()
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user