Tab support

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

View File

@ -95,7 +95,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
}
}
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate/*, TabBarContainedController*/ {
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate, TabBarContainedController {
private var validLayout: ContainerViewLayout?
public let context: AccountContext
@ -133,9 +133,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
private var presentationDataDisposable: Disposable?
private let stateDisposable = MetaDisposable()
private var filterDisposable: Disposable?
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) {
if self.isNodeLoaded {
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.tabContainerNode = ChatListFilterTabContainerNode()
super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary)
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
@ -265,14 +273,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
context.account.networkState,
hasProxy,
passcode,
self.chatListDisplayNode.chatListNode.state,
self.chatListDisplayNode.chatListNode.chatListFilterSignal
).start(next: { [weak self] networkState, proxy, passcode, state, chatListFilter in
self.chatListDisplayNode.chatListNode.state
).start(next: { [weak self] networkState, proxy, passcode, state in
if let strongSelf = self {
let defaultTitle: String
if strongSelf.groupId == .root {
if let chatListFilter = chatListFilter {
let title: String = chatListFilter.title ?? ""
if let chatListFilter = strongSelf.filter {
let title: String = chatListFilter.title ?? strongSelf.presentationData.strings.DialogList_Title
defaultTitle = title
} else {
defaultTitle = strongSelf.presentationData.strings.DialogList_Title
@ -397,6 +404,107 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
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) {
@ -412,6 +520,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self.suggestLocalizationDisposable.dispose()
self.presentationDataDisposable?.dispose()
self.stateDisposable.dispose()
self.filterDisposable?.dispose()
}
private func updateThemeAndStrings() {
@ -461,6 +570,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
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.chatListFilterValue.set(self.chatListDisplayNode.chatListNode.chatListFilterSignal)
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 {
return
}
@ -735,13 +848,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
if let (options, peerIds) = peerIdsAndOptions {
let leftAction: ToolbarAction
switch options.read {
case let .all(enabled):
leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled)
case let .selective(enabled):
leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled)
case let .all(enabled):
leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled)
case let .selective(enabled):
leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled)
}
var archiveEnabled = options.delete
if archiveEnabled {
if archiveEnabled && strongSelf.filter == nil {
for peerId in peerIds {
if peerId == PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000) {
archiveEnabled = false
@ -919,12 +1032,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
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 {
searchContentNode.updateListVisibleContentOffset(.known(0.0))
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]) {
@ -934,7 +1056,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
@objc private func editPressed() {
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
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.navigationController as? NavigationController)?.updateMasterDetailsBlackout(.details, transition: .animated(duration: 0.5, curve: .spring))
} else {
@ -954,7 +1076,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
@objc private func donePressed() {
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
editItem.accessibilityLabel = self.presentationData.strings.Common_Edit
if case .root = self.groupId {
if case .root = self.groupId, self.filter == nil {
self.navigationItem.leftBarButtonItem = editItem
} else {
self.navigationItem.rightBarButtonItem = editItem
@ -1788,7 +1910,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
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)
} else {
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 {
let _ = (self.context.account.postbox.transaction { transaction -> [ChatListFilter] in
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) {
}*/
}
}

View File

@ -59,7 +59,7 @@ final class ChatListControllerNode: ASDisplayNode {
private(set) var searchDisplayController: SearchDisplayController?
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat)?
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat)?
var requestDeactivateSearch: (() -> 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)
strongSelf.chatListEmptyNode = chatListEmptyNode
strongSelf.insertSubnode(chatListEmptyNode, belowSubnode: strongSelf.chatListNode)
if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate)
if let (layout, navigationHeight, visualNavigationHeight, cleanNavigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
}
strongSelf.isEmptyUpdated?(true)
}
@ -114,9 +114,7 @@ final class ChatListControllerNode: ASDisplayNode {
default:
if let chatListEmptyNode = strongSelf.chatListEmptyNode {
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 {
@ -125,8 +123,8 @@ final class ChatListControllerNode: ASDisplayNode {
let chatListEmptyIndicator = ActivityIndicator(type: .custom(strongSelf.presentationData.theme.list.itemSecondaryTextColor, 22.0, 1.0, false))
strongSelf.chatListEmptyIndicator = chatListEmptyIndicator
strongSelf.insertSubnode(chatListEmptyIndicator, belowSubnode: strongSelf.chatListNode)
if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate)
if let (layout, navigationHeight, visualNavigationHeight, cleanNavigationBarHeight) = strongSelf.containerLayout {
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, transition: .immediate)
}
}
default:
@ -160,8 +158,8 @@ final class ChatListControllerNode: ASDisplayNode {
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight)
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight)
var insets = layout.insets(options: [.input])
insets.top += navigationBarHeight
@ -233,12 +231,12 @@ final class ChatListControllerNode: ASDisplayNode {
}
if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition)
}
}
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
}
@ -262,7 +260,7 @@ final class ChatListControllerNode: ASDisplayNode {
})
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
if let strongSelf = self, let strongPlaceholderNode = placeholderNode {
if isSearchBar {

View File

@ -1,4 +1,4 @@
/*import Foundation
import Foundation
import UIKit
import Display
import SwiftSignalKit
@ -58,6 +58,7 @@ private enum ChatListFilterPresetEntryStableId: Hashable {
private enum ChatListFilterPresetEntry: ItemListNodeEntry {
case nameHeader(String)
case name(placeholder: String, value: String)
case typesHeader(text: String)
case filterPrivateChats(title: String, value: Bool)
case filterSecretChats(title: String, value: Bool)
case filterPrivateGroups(title: String, value: Bool)
@ -75,7 +76,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
switch self {
case .nameHeader, .name:
return ChatListFilterPresetControllerSection.name.rawValue
case .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels:
case .typesHeader, .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels:
return ChatListFilterPresetControllerSection.categories.rawValue
case .filterMuted, .filterRead:
return ChatListFilterPresetControllerSection.excludeCategories.rawValue
@ -90,26 +91,28 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
return .index(0)
case .name:
return .index(1)
case .filterPrivateChats:
case .typesHeader:
return .index(2)
case .filterSecretChats:
case .filterPrivateChats:
return .index(3)
case .filterPrivateGroups:
case .filterSecretChats:
return .index(4)
case .filterBots:
case .filterPrivateGroups:
return .index(5)
case .filterPublicGroups:
return .index(6)
case .filterChannels:
return .index(7)
case .filterMuted:
case .filterBots:
return .index(8)
case .filterRead:
case .filterMuted:
return .index(9)
case .additionalPeersHeader:
case .filterRead:
return .index(10)
case .addAdditionalPeer:
case .additionalPeersHeader:
return .index(11)
case .addAdditionalPeer:
return .index(12)
case let .additionalPeer(additionalPeer):
return .peer(additionalPeer.peer.peerId)
case .additionalPeerInfo:
@ -172,6 +175,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
return state
}
}, action: {})
case let .typesHeader(text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .filterPrivateChats(title, value):
return filterEntry(presentationData: presentationData, arguments: arguments, title: title, value: value, filter: .privateChats, section: self.section)
case let .filterSecretChats(title, value):
@ -243,21 +248,22 @@ private struct ChatListFilterPresetControllerState: Equatable {
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetControllerState, peers: [RenderedPeer]) -> [ChatListFilterPresetEntry] {
var entries: [ChatListFilterPresetEntry] = []
entries.append(.nameHeader("NAME"))
entries.append(.name(placeholder: "Preset Name", value: state.name))
entries.append(.nameHeader("FILTER 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(.filterSecretChats(title: "Secret Chats", value: state.includeCategories.contains(.secretChats)))
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(.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(.filterRead(title: "Exclude Read", value: state.excludeRead))
entries.append(.additionalPeersHeader("ALWAYS INCLUDE"))
entries.append(.addAdditionalPeer(title: "Add"))
entries.append(.addAdditionalPeer(title: "Add Chats"))
for peer in peers {
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 {
initialName = currentPreset.title ?? ""
} 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 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: {
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 preset = ChatListFilter(id: currentPreset?.id ?? -1, title: state.name, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, includePeers: state.additionallyIncludePeers)
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
var preset = preset
if currentPreset == nil {
preset.id = max(2, settings.filters.map({ $0.id }).max() ?? 2)
preset.id = max(2, settings.filters.map({ $0.id + 1 }).max() ?? 2)
}
var settings = settings
settings.filters = settings.filters.filter { $0 != preset && $0 != currentPreset }
settings.filters.append(preset)
if let currentPreset = currentPreset {
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
})
|> 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)
return (controllerState, (listState, arguments))
@ -405,4 +423,3 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
return controller
}
*/

View File

@ -1,4 +1,4 @@
/*import Foundation
import Foundation
import UIKit
import Display
import SwiftSignalKit
@ -9,17 +9,20 @@ import TelegramPresentationData
import TelegramUIPreferences
import ItemListUI
import AccountContext
import ItemListPeerActionItem
private final class ChatListFilterPresetListControllerArguments {
let context: AccountContext
let toggleEnableTabs: (Bool) -> Void
let openPreset: (ChatListFilter) -> Void
let addNew: () -> Void
let setItemWithRevealedOptions: (Int32?, 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.toggleEnableTabs = toggleEnableTabs
self.openPreset = openPreset
self.addNew = addNew
self.setItemWithRevealedOptions = setItemWithRevealedOptions
@ -28,6 +31,7 @@ private final class ChatListFilterPresetListControllerArguments {
}
private enum ChatListFilterPresetListSection: Int32 {
case tabs
case list
}
@ -44,6 +48,8 @@ private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings
}
private enum ChatListFilterPresetListEntryStableId: Hashable {
case displayTabs
case displayTabsFooter
case listHeader
case preset(Int32)
case addItem
@ -51,13 +57,17 @@ private enum ChatListFilterPresetListEntryStableId: Hashable {
}
private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
case displayTabs(String, Bool)
case displayTabsFooter(String)
case listHeader(String)
case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool)
case addItem(String)
case preset(index: Int, title: String?, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool)
case addItem(text: String, isEditing: Bool)
case listFooter(String)
var section: ItemListSectionId {
switch self {
case .displayTabs, .displayTabsFooter:
return ChatListFilterPresetListSection.tabs.rawValue
case .listHeader, .preset, .addItem, .listFooter:
return ChatListFilterPresetListSection.list.rawValue
}
@ -65,10 +75,14 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
var sortId: Int {
switch self {
case .listHeader:
case .displayTabs:
return 0
case .displayTabsFooter:
return 1
case .listHeader:
return 2
case let .preset(preset):
return 1 + preset.index
return 3 + preset.index
case .addItem:
return 1000
case .listFooter:
@ -78,6 +92,10 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
var stableId: ChatListFilterPresetListEntryStableId {
switch self {
case .displayTabs:
return .displayTabs
case .displayTabsFooter:
return .displayTabsFooter
case .listHeader:
return .listHeader
case let .preset(preset):
@ -96,18 +114,24 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! ChatListFilterPresetListControllerArguments
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):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section)
case let .preset(index, title, preset, canBeReordered, canBeDeleted):
return ChatListFilterPresetListItem(presentationData: presentationData, preset: preset, title: title ?? "", editing: ChatListFilterPresetListItemEditing(editable: true, editing: false, revealed: false), canBeReordered: canBeReordered, canBeDeleted: canBeDeleted, sectionId: self.section, action: {
case let .preset(index, title, preset, canBeReordered, canBeDeleted, isEditing):
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)
}, setItemWithRevealedOptions: { lhs, rhs in
arguments.setItemWithRevealedOptions(lhs, rhs)
}, remove: {
arguments.removePreset(preset.id)
})
case let .addItem(text):
return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
case let .addItem(text, isEditing):
return ItemListPeerActionItem(presentationData: presentationData, icon: nil, title: text, sectionId: self.section, height: .generic, editing: isEditing, action: {
arguments.addNew()
})
case let .listFooter(text):
@ -117,18 +141,22 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
}
private struct ChatListFilterPresetListControllerState: Equatable {
var isEditing: Bool = false
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] = []
entries.append(.displayTabs("Show Tabs", settings.displayTabs))
entries.append(.displayTabsFooter("Display filter tabs on the main screen for quick switching."))
entries.append(.listHeader("FILTERS"))
for preset in settings.filters {
entries.append(.preset(index: entries.count, title: preset.title, preset: preset, canBeReordered: settings.filters.count > 1, canBeDeleted: true))
for preset in filtersState.filters {
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(.listFooter("Add custom presets"))
entries.append(.addItem(text: "Create New Filter", isEditing: state.isEditing))
entries.append(.listFooter("Tap \"Edit\" to change the order or delete filters."))
return entries
}
@ -145,7 +173,15 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
var pushControllerImpl: ((ViewController) -> 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))
}, addNew: {
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(),
context.sharedContext.presentationData,
@ -178,14 +214,33 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
preferences
)
|> 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 _ = replaceRemoteChatListFilters(account: context.account).start()
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 listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: chatListFilterPresetListControllerEntries(presentationData: presentationData, state: state, settings: settings), style: .blocks, animateChanges: true)
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, filtersState: filtersState, settings: filterSettings), style: .blocks, animateChanges: true)
return (controllerState, (listState, arguments))
}
@ -203,7 +258,62 @@ func chatListFilterPresetListController(context: AccountContext, updated: @escap
dismissImpl = { [weak controller] in
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
}
*/

View File

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

View File

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

View File

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

View File

@ -44,6 +44,7 @@ struct ChatListNodeViewTransition {
let options: ListViewDeleteAndInsertOptions
let scrollToItem: ListViewScrollToItem?
let stationaryItemRange: (Int, Int)?
let adjustScrollToFirstItem: Bool
}
enum ChatListNodeViewScrollPosition {
@ -176,11 +177,12 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
fromEmptyView = true
}
var adjustScrollToFirstItem = false
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()
return EmptyDisposable

View File

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

View File

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

View File

@ -105,7 +105,7 @@ enum NavigationPreviousAction: Equatable {
open class NavigationBar: ASDisplayNode {
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
var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in }
@ -124,6 +124,7 @@ open class NavigationBar: ASDisplayNode {
private let clippingNode: ASDisplayNode
public private(set) var contentNode: NavigationBarContentNode?
public private(set) var secondaryContentNode: ASDisplayNode?
private var itemTitleListenerKey: Int?
private var itemTitleViewListenerKey: Int?
@ -801,16 +802,22 @@ open class NavigationBar: ASDisplayNode {
if let validLayout = self.validLayout, self.requestedLayout {
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 {
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 backButtonInset: CGFloat = leftInset + 27.0
@ -818,14 +825,19 @@ open class NavigationBar: ASDisplayNode {
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size))
var expansionHeight: CGFloat = 0.0
if let contentNode = self.contentNode {
let contentNodeFrame: CGRect
var contentNodeFrame: CGRect
switch contentNode.mode {
case .replacement:
expansionHeight = contentNode.height - defaultHeight
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
case .expansion:
expansionHeight = contentNode.height
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - expansionHeight), size: CGSize(width: size.width, height: expansionHeight))
case .replacement:
expansionHeight = contentNode.height - defaultHeight
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
case .expansion:
expansionHeight = contentNode.height
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)
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))
let nominalHeight: CGFloat = defaultHeight
let contentVerticalOrigin = size.height - nominalHeight - expansionHeight
let nominalHeight: CGFloat = defaultHeight - additionalHeight
let contentVerticalOrigin = size.height - nominalHeight - expansionHeight - additionalHeight - apparentAdditionalHeight
var leftTitleInset: CGFloat = leftInset + 1.0
var rightTitleInset: CGFloat = rightInset + 1.0
@ -1034,7 +1046,7 @@ open class NavigationBar: ASDisplayNode {
let node = NavigationButtonNode()
node.updateManualText(self.backButtonNode.manualText)
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.frame = self.backButtonNode.frame
}
@ -1057,7 +1069,7 @@ open class NavigationBar: ASDisplayNode {
}
node.updateItems(items)
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.frame = self.backButtonNode.frame
}
@ -1103,16 +1115,23 @@ open class NavigationBar: ASDisplayNode {
}
public func contentHeight(defaultHeight: CGFloat) -> CGFloat {
var result: CGFloat = 0.0
if let contentNode = self.contentNode {
switch contentNode.mode {
case .expansion:
return defaultHeight + contentNode.height
case .replacement:
return contentNode.height
case .expansion:
result += defaultHeight + contentNode.height
case .replacement:
result += contentNode.height
}
} else {
return defaultHeight
result += defaultHeight
}
if let _ = self.secondaryContentNode {
result += 46.0
}
return result
}
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 {
if self.backButtonNode.isInHierarchy {
self.backButtonNode.pressed(0)

View File

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

View File

@ -213,6 +213,21 @@ public enum ViewControllerNavigationPresentation {
}
}
open var cleanNavigationHeight: CGFloat {
if let navigationBar = self.navigationBar {
var height = navigationBar.frame.maxY
if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode {
height += contentNode.nominalHeight - contentNode.height
}
if navigationBar.secondaryContentNode != nil {
//height -= 46.0
}
return height
} else {
return 0.0
}
}
open var visualNavigationInsetHeight: CGFloat {
if let navigationBar = self.navigationBar {
let height = navigationBar.frame.maxY
@ -225,6 +240,8 @@ public enum ViewControllerNavigationPresentation {
}
}
public var additionalNavigationBarHeight: CGFloat = 0.0
private let _ready = Promise<Bool>(true)
open var ready: Promise<Bool> {
return self._ready
@ -338,7 +355,7 @@ public enum ViewControllerNavigationPresentation {
private func updateNavigationBarLayout(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
let defaultNavigationBarHeight: CGFloat
var defaultNavigationBarHeight: CGFloat
if self._presentedInModal {
defaultNavigationBarHeight = 56.0
} else {
@ -353,9 +370,6 @@ public enum ViewControllerNavigationPresentation {
navigationBarOffset = 0.0
}
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 {
navigationBarFrame.origin.y = -navigationBarFrame.size.height
@ -368,7 +382,7 @@ public enum ViewControllerNavigationPresentation {
if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar {
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 {
navigationBar.layer.cancelAnimationsRecursive(key: "bounds")
navigationBar.layer.cancelAnimationsRecursive(key: "position")

View File

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

View File

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

View File

@ -351,7 +351,7 @@ final class FFMpegMediaFrameSourceContext: NSObject {
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
return
}

View File

@ -74,7 +74,7 @@ public final class SoftwareVideoSource {
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
avFormatContext.setIO(self.avIoContext!)

View File

@ -123,7 +123,7 @@ private final class UniversalSoftwareVideoSourceImpl {
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
}
self.avIoContext = avIoContext

View File

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

View File

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

View File

@ -1805,7 +1805,7 @@ public extension Api {
}
public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int32, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, pts: Int32)
case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int32?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -1828,9 +1828,9 @@ public extension Api {
if Int(flags) & Int(1 << 6) != 0 {serializeInt32(pinnedMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 11) != 0 {serializeInt32(folderId!, buffer: buffer, boxed: false)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let pts):
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts):
if boxed {
buffer.appendInt32(763976820)
buffer.appendInt32(-253335766)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
@ -1861,6 +1861,7 @@ public extension Api {
if Int(flags) & Int(1 << 15) != 0 {location!.serialize(buffer, true)}
if Int(flags) & Int(1 << 17) != 0 {serializeInt32(slowmodeSeconds!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 18) != 0 {serializeInt32(slowmodeNextSendDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 12) != 0 {serializeInt32(statsDc!, buffer: buffer, boxed: false)}
serializeInt32(pts, buffer: buffer, boxed: false)
break
}
@ -1870,8 +1871,8 @@ public extension Api {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let pts):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("pts", pts)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts)])
}
}
@ -1987,7 +1988,9 @@ public extension Api {
var _25: Int32?
if Int(_1!) & Int(1 << 18) != 0 {_25 = reader.readInt32() }
var _26: Int32?
_26 = reader.readInt32()
if Int(_1!) & Int(1 << 12) != 0 {_26 = reader.readInt32() }
var _27: Int32?
_27 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -2013,9 +2016,10 @@ public extension Api {
let _c23 = (Int(_1!) & Int(1 << 15) == 0) || _23 != nil
let _c24 = (Int(_1!) & Int(1 << 17) == 0) || _24 != nil
let _c25 = (Int(_1!) & Int(1 << 18) == 0) || _25 != nil
let _c26 = _26 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, pts: _26!)
let _c26 = (Int(_1!) & Int(1 << 12) == 0) || _26 != nil
let _c27 = _27 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14!, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!)
}
else {
return nil
@ -5861,6 +5865,9 @@ public extension Api {
case updateGeoLiveViewed(peer: Api.Peer, msgId: Int32)
case updateLoginToken
case updateMessagePollVote(pollId: Int64, userId: Int32, options: [Buffer])
case updateDialogFilter(flags: Int32, id: Int32, filter: Api.DialogFilter?)
case updateDialogFilterOrder(order: [Int32])
case updateDialogFilters
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -6509,6 +6516,30 @@ public extension Api {
for item in options {
serializeBytes(item, buffer: buffer, boxed: false)
}
break
case .updateDialogFilter(let flags, let id, let filter):
if boxed {
buffer.appendInt32(654302845)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {filter!.serialize(buffer, true)}
break
case .updateDialogFilterOrder(let order):
if boxed {
buffer.appendInt32(-1512627963)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(order.count))
for item in order {
serializeInt32(item, buffer: buffer, boxed: false)
}
break
case .updateDialogFilters:
if boxed {
buffer.appendInt32(889491791)
}
break
}
}
@ -6669,6 +6700,12 @@ public extension Api {
return ("updateLoginToken", [])
case .updateMessagePollVote(let pollId, let userId, let options):
return ("updateMessagePollVote", [("pollId", pollId), ("userId", userId), ("options", options)])
case .updateDialogFilter(let flags, let id, let filter):
return ("updateDialogFilter", [("flags", flags), ("id", id), ("filter", filter)])
case .updateDialogFilterOrder(let order):
return ("updateDialogFilterOrder", [("order", order)])
case .updateDialogFilters:
return ("updateDialogFilters", [])
}
}
@ -7965,6 +8002,41 @@ public extension Api {
return nil
}
}
public static func parse_updateDialogFilter(_ reader: BufferReader) -> Update? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Api.DialogFilter?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.DialogFilter
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3)
}
else {
return nil
}
}
public static func parse_updateDialogFilterOrder(_ reader: BufferReader) -> Update? {
var _1: [Int32]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.Update.updateDialogFilterOrder(order: _1!)
}
else {
return nil
}
}
public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? {
return Api.Update.updateDialogFilters
}
}
public enum PopularContact: TypeConstructorDescription {
@ -10365,6 +10437,7 @@ public extension Api {
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, period: Int32?)
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?)
case inputMediaDice
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -10511,6 +10584,12 @@ public extension Api {
for item in correctAnswers! {
serializeBytes(item, buffer: buffer, boxed: false)
}}
break
case .inputMediaDice:
if boxed {
buffer.appendInt32(-1358977017)
}
break
}
}
@ -10547,6 +10626,8 @@ public extension Api {
return ("inputMediaGeoLive", [("flags", flags), ("geoPoint", geoPoint), ("period", period)])
case .inputMediaPoll(let flags, let poll, let correctAnswers):
return ("inputMediaPoll", [("flags", flags), ("poll", poll), ("correctAnswers", correctAnswers)])
case .inputMediaDice:
return ("inputMediaDice", [])
}
}
@ -10855,6 +10936,9 @@ public extension Api {
return nil
}
}
public static func parse_inputMediaDice(_ reader: BufferReader) -> InputMedia? {
return Api.InputMedia.inputMediaDice
}
}
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 {
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 {
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 {
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 {
case messageMediaEmpty
@ -15112,6 +15386,7 @@ public extension Api {
case messageMediaDocument(flags: Int32, document: Api.Document?, ttlSeconds: Int32?)
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int32)
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
case messageMediaDice(value: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -15209,6 +15484,12 @@ public extension Api {
poll.serialize(buffer, true)
results.serialize(buffer, true)
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)])
case .messageMediaPoll(let poll, let results):
return ("messageMediaPoll", [("poll", poll), ("results", results)])
case .messageMediaDice(let value):
return ("messageMediaDice", [("value", value)])
}
}
@ -15443,6 +15726,17 @@ public extension Api {
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 {
@ -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 {
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 {
case chatOnlines(onlines: Int32)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -277,71 +277,73 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? {
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?) {
if let media = media {
switch media {
case let .messageMediaPhoto(_, photo, ttlSeconds):
if let photo = photo {
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
return (mediaImage, ttlSeconds)
}
} else {
return (TelegramMediaExpiredContent(data: .image), nil)
case let .messageMediaPhoto(_, photo, ttlSeconds):
if let photo = photo {
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
return (mediaImage, ttlSeconds)
}
case let .messageMediaContact(phoneNumber, firstName, lastName, vcard, userId):
let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard)
return (mediaContact, nil)
case let .messageMediaGeo(geo):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil)
return (mediaMap, nil)
case let .messageMediaVenue(geo, title, address, provider, venueId, venueType):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil)
return (mediaMap, nil)
case let .messageMediaGeoLive(geo, period):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period)
return (mediaMap, nil)
case let .messageMediaDocument(_, document, ttlSeconds):
if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document) {
return (mediaFile, ttlSeconds)
}
} 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)
} else {
return (TelegramMediaExpiredContent(data: .image), nil)
}
case let .messageMediaContact(phoneNumber, firstName, lastName, vcard, userId):
let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId, vCardData: vcard.isEmpty ? nil : vcard)
return (mediaContact, nil)
case let .messageMediaGeo(geo):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil)
return (mediaMap, nil)
case let .messageMediaVenue(geo, title, address, provider, venueId, venueType):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil)
return (mediaMap, nil)
case let .messageMediaGeoLive(geo, period):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period)
return (mediaMap, nil)
case let .messageMediaDocument(_, document, ttlSeconds):
if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document) {
return (mediaFile, ttlSeconds)
}
} 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 {
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)
case let .messageMediaPoll(poll, results):
switch poll {
case let .poll(id, flags, question, answers):
let publicity: TelegramMediaPollPublicity
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)
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)
}
case .messageMediaDice:
break
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface FFMpegAVIOContext : NSObject
- (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;

View File

@ -10,11 +10,11 @@
@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];
if (self != nil) {
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;
if (_impl == nil) {
av_free(avIoBuffer);

View File

@ -1,11 +1,64 @@
#import "FFMpegRemuxer.h"
#import "FFMpegAVIOContext.h"
#include "libavutil/timestamp.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#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
+ (bool)remux:(NSString * _Nonnull)path to:(NSString * _Nonnull)outPath {
@ -21,6 +74,9 @@
in_filename = [path 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) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
@ -31,6 +87,11 @@
}
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) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
@ -103,7 +164,7 @@
// 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);
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
@ -144,8 +205,9 @@
end:
avformat_close_input(&input_format_context);
/* 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);
}
avformat_free_context(output_format_context);
av_freep(&streams_list);
if (ret < 0 && ret != AVERROR_EOF) {