From 4881f49bf1046e8fea67340ab5a1bbff22750d96 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 11 Dec 2018 12:18:38 +0400 Subject: [PATCH 1/3] Group member search actions --- TelegramUI/ChannelAdminsController.swift | 4 +- TelegramUI/ChannelBlacklistController.swift | 4 +- .../ChannelMemberCategoryListContext.swift | 26 +- TelegramUI/ChannelMembersController.swift | 4 +- .../ChannelMembersSearchContainerNode.swift | 580 +++++++++++------- .../ChannelMembersSearchController.swift | 3 + .../ChannelMembersSearchControllerNode.swift | 3 + TelegramUI/ContactsPeerItem.swift | 56 +- TelegramUI/GroupInfoController.swift | 2 + TelegramUI/GroupInfoSearchItem.swift | 14 +- 10 files changed, 465 insertions(+), 231 deletions(-) diff --git a/TelegramUI/ChannelAdminsController.swift b/TelegramUI/ChannelAdminsController.swift index e53c2fa1dd..140cea39dc 100644 --- a/TelegramUI/ChannelAdminsController.swift +++ b/TelegramUI/ChannelAdminsController.swift @@ -473,7 +473,7 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon } var pushControllerImpl: ((ViewController) -> Void)? - var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)? + var presentControllerImpl: ((ViewController, Any?) -> Void)? let actionsDisposable = DisposableSet() @@ -669,6 +669,8 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } + }, present: { c, a in + presentControllerImpl?(c, a) }) } diff --git a/TelegramUI/ChannelBlacklistController.swift b/TelegramUI/ChannelBlacklistController.swift index afa60eaa34..e69655b667 100644 --- a/TelegramUI/ChannelBlacklistController.swift +++ b/TelegramUI/ChannelBlacklistController.swift @@ -311,7 +311,7 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View statePromise.set(stateValue.modify { f($0) }) } - var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)? + var presentControllerImpl: ((ViewController, Any?) -> Void)? var pushControllerImpl: ((ViewController) -> Void)? let actionsDisposable = DisposableSet() @@ -477,6 +477,8 @@ public func channelBlacklistController(account: Account, peerId: PeerId) -> View arguments.openPeerInfo(rendered.peer) } } + }, present: { c, a in + presentControllerImpl?(c, a) }) } diff --git a/TelegramUI/ChannelMemberCategoryListContext.swift b/TelegramUI/ChannelMemberCategoryListContext.swift index 7e6df18ec8..ec0de98429 100644 --- a/TelegramUI/ChannelMemberCategoryListContext.swift +++ b/TelegramUI/ChannelMemberCategoryListContext.swift @@ -379,9 +379,29 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor } } case let .recentSearch(query): - break - default: - break + if let updated = updated, isParticipantMember(updated.participant), updated.peer.indexName.matchesByTokens(query) { + var found = false + loop: for i in 0 ..< list.count { + if list[i].peer.id == updated.peer.id { + list[i] = updated + found = true + updatedList = true + break loop + } + } + if !found { + list.insert(updated, at: 0) + updatedList = true + } + } else if let previous = previous, isParticipantMember(previous) { + loop: for i in 0 ..< list.count { + if list[i].peer.id == previous.peerId { + list.remove(at: i) + updatedList = true + break loop + } + } + } } } if updatedList { diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index 10316c296d..5a732f4432 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -310,7 +310,7 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo statePromise.set(stateValue.modify { f($0) }) } - var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)? + var presentControllerImpl: ((ViewController, Any?) -> Void)? var pushControllerImpl: ((ViewController) -> Void)? let actionsDisposable = DisposableSet() @@ -444,6 +444,8 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo pushControllerImpl?(infoController) // arguments.pushController(infoController) } + }, present: { c, a in + presentControllerImpl?(c, a) }) } diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index a8341d1d23..6150a55a96 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -35,7 +35,7 @@ private enum ChannelMembersSearchSection { private enum ChannelMembersSearchContent: Equatable { case peer(Peer) - case participant(participant: RenderedChannelParticipant, label: String?, revealActions: [ParticipantRevealAction], enabled: Bool) + case participant(participant: RenderedChannelParticipant, label: String?, revealActions: [ParticipantRevealAction], revealed: Bool, enabled: Bool) static func ==(lhs: ChannelMembersSearchContent, rhs: ChannelMembersSearchContent) -> Bool { switch lhs { @@ -45,8 +45,8 @@ private enum ChannelMembersSearchContent: Equatable { } else { return false } - case let .participant(participant, label, revealActions, enabled): - if case .participant(participant, label, revealActions, enabled) = rhs { + case let .participant(participant, label, revealActions, revealed, enabled): + if case .participant(participant, label, revealActions, revealed, enabled) = rhs { return true } else { return false @@ -58,12 +58,28 @@ private enum ChannelMembersSearchContent: Equatable { switch self { case let .peer(peer): return peer.id - case let .participant(participant, _, _, _): + case let .participant(participant, _, _, _, _): return participant.peer.id } } } +private final class ChannelMembersSearchContainerInteraction { + let peerSelected: (Peer, RenderedChannelParticipant?) -> Void + let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void + let promotePeer: (RenderedChannelParticipant) -> Void + let restrictPeer: (RenderedChannelParticipant) -> Void + let removePeer: (PeerId) -> Void + + init(peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, promotePeer: @escaping (RenderedChannelParticipant) -> Void, restrictPeer: @escaping (RenderedChannelParticipant) -> Void, removePeer: @escaping (PeerId) -> Void) { + self.peerSelected = peerSelected + self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions + self.promotePeer = promotePeer + self.restrictPeer = restrictPeer + self.removePeer = removePeer + } +} + private final class ChannelMembersSearchEntry: Comparable, Identifiable { let index: Int let content: ChannelMembersSearchContent @@ -87,31 +103,40 @@ private final class ChannelMembersSearchEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ListViewItem { + func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchContainerInteraction) -> ListViewItem { switch self.content { case let .peer(peer): return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: .none, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in - peerSelected(peer, nil) + interaction.peerSelected(peer, nil) }) - case let .participant(participant, label, revealActions, enabled): + case let .participant(participant, label, revealActions, revealed, enabled): + let status: ContactsPeerItemStatus + if let label = label { + status = .custom(label) + } else { + status = .none + } + var options: [ItemListPeerItemRevealOption] = [] for action in revealActions { options.append(ItemListPeerItemRevealOption(type: action.type, title: action.title, action: { switch action.action { case .promote: - //arguments.promotePeer(participant) + interaction.promotePeer(participant) break case .restrict: - //arguments.restrictPeer(participant) + interaction.restrictPeer(participant) break case .remove: - //arguments.removePeer(peer.id) + interaction.removePeer(participant.peer.id) break } })) } - return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: participant.peer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in - peerSelected(participant.peer, participant) + return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: participant.peer), status: status, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: revealed), options: options, index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in + interaction.peerSelected(participant.peer, participant) + }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in + interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId) }) } } @@ -123,16 +148,21 @@ struct ChannelMembersSearchContainerTransition { let isSearching: Bool } -private func channelMembersSearchContainerPreparedRecentTransition(from fromEntries: [ChannelMembersSearchEntry], to toEntries: [ChannelMembersSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ChannelMembersSearchContainerTransition { +private func channelMembersSearchContainerPreparedRecentTransition(from fromEntries: [ChannelMembersSearchEntry], to toEntries: [ChannelMembersSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchContainerInteraction) -> ChannelMembersSearchContainerTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, peerSelected: peerSelected), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, peerSelected: peerSelected), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction), directionHint: nil) } return ChannelMembersSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching) } +private struct ChannelMembersSearchContainerState: Equatable { + var revealedPeerId: PeerId? + var removingParticipantIds = Set() +} + final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNode { private let account: Account private let openPeer: (Peer, RenderedChannelParticipant?) -> Void @@ -150,9 +180,11 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod private var presentationData: PresentationData private var presentationDataDisposable: Disposable? + private let removeMemberDisposable = MetaDisposable() + private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)> - init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping(Bool)->Void) { + init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { self.account = account self.openPeer = openPeer self.mode = mode @@ -173,194 +205,323 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod self.addSubnode(self.dimNode) self.addSubnode(self.listNode) - let themeAndStringsPromise = self.themeAndStringsPromise - let foundItems = searchQuery.get() - |> mapToSignal { query -> Signal<[ChannelMembersSearchEntry]?, NoError> in - updateActivity(true) - if let query = query, !query.isEmpty { - let foundGroupMembers: Signal<[RenderedChannelParticipant], NoError> - let foundMembers: Signal<[RenderedChannelParticipant], NoError> - - switch mode { - case .searchMembers, .banAndPromoteActions: - foundGroupMembers = Signal { subscriber in - let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.recent(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in - if case .ready = state.loadingState { - subscriber.putNext(state.list) - subscriber.putCompletion() - } - }) - return disposable - } - |> runOn(Queue.mainQueue()) - foundMembers = .single([]) - case .inviteActions: - foundGroupMembers = .single([]) - foundMembers = channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, category: .recent(.search(query))) - |> map { $0 ?? [] } - case .searchAdmins: - foundGroupMembers = Signal { subscriber in - let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in - if case .ready = state.loadingState { - subscriber.putNext(state.list) - subscriber.putCompletion() - } - }) - return disposable - } |> runOn(Queue.mainQueue()) - foundMembers = .single([]) - case .searchBanned: - foundGroupMembers = Signal { subscriber in - let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.restrictedAndBanned(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in - if case .ready = state.loadingState { - subscriber.putNext(state.list) - subscriber.putCompletion() - } - }) - return disposable - } |> runOn(Queue.mainQueue()) - foundMembers = .single([]) + let statePromise = ValuePromise(ChannelMembersSearchContainerState(), ignoreRepeated: true) + let stateValue = Atomic(value: ChannelMembersSearchContainerState()) + let updateState: ((ChannelMembersSearchContainerState) -> ChannelMembersSearchContainerState) -> Void = { f in + statePromise.set(stateValue.modify { f($0) }) + } + + let removeMemberDisposable = self.removeMemberDisposable + let interaction = ChannelMembersSearchContainerInteraction(peerSelected: { peer, participant in + openPeer(peer, participant) + }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in + updateState { state in + var state = state + if (peerId == nil && fromPeerId == state.revealedPeerId) || (peerId != nil && fromPeerId == nil) { + state.revealedPeerId = peerId + } + return state + } + }, promotePeer: { participant in + present(channelAdminController(account: account, peerId: peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in + }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }, restrictPeer: { participant in + present(channelBannedMemberController(account: account, peerId: peerId, memberId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in + }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }, removePeer: { memberId in + let signal = account.postbox.loadedPeerWithId(memberId) + |> deliverOnMainQueue + |> mapToSignal { peer -> Signal in + let result = ValuePromise() + result.set(true) + return result.get() + } + |> mapToSignal { value -> Signal in + if value { + updateState { state in + var state = state + state.removingParticipantIds.insert(memberId) + return state } - let foundContacts: Signal<([Peer], [PeerId: PeerPresence]), NoError> - let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError> - switch mode { - case .inviteActions, .banAndPromoteActions: - foundContacts = account.postbox.searchContacts(query: query.lowercased()) - foundRemotePeers = .single(([], [])) |> then(searchPeers(account: account, query: query) - |> delay(0.2, queue: Queue.concurrentDefaultQueue())) - case .searchMembers, .searchBanned, .searchAdmins: - foundContacts = .single(([], [:])) - foundRemotePeers = .single(([], [])) + if peerId.namespace == Namespaces.Peer.CloudChannel { + return account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(account: account, peerId: peerId, memberId: memberId, bannedRights: TelegramChannelBannedRights(flags: [.banReadMessages], untilDate: Int32.max)) + |> afterDisposed { + Queue.mainQueue().async { + updateState { state in + var state = state + state.removingParticipantIds.remove(memberId) + return state + } + } + } } - return combineLatest(foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStringsPromise.get()) - |> map { foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStrings -> [ChannelMembersSearchEntry]? in - var entries: [ChannelMembersSearchEntry] = [] - - var existingPeerIds = Set() - for filter in filters { - switch filter { - case let .exclude(ids): - existingPeerIds = existingPeerIds.union(ids) - case .disable: - break - } - } - switch mode { - case .inviteActions, .banAndPromoteActions: - existingPeerIds.insert(account.peerId) - case .searchMembers, .searchAdmins, .searchBanned: - break - } - - var index = 0 - - for participant in foundGroupMembers { - if !existingPeerIds.contains(participant.peer.id) { - existingPeerIds.insert(participant.peer.id) - let section: ChannelMembersSearchSection - switch mode { - case .inviteActions, .banAndPromoteActions: - section = .members - case .searchMembers, .searchBanned, .searchAdmins: - section = .none - } - - var label: String? - var enabled = true - if case .banAndPromoteActions = mode { - if case .creator = participant.participant { - label = themeAndStrings.1.Channel_Management_LabelCreator - enabled = false - } - } - switch mode { - case .searchAdmins: - switch participant.participant { - case .creator: - label = themeAndStrings.1.Channel_Management_LabelCreator - case let .member(_, _, adminInfo, _): - if let adminInfo = adminInfo { - if let peer = participant.peers[adminInfo.promotedBy] { - label = themeAndStrings.1.Channel_Management_PromotedBy(peer.displayTitle).0 - } - } - } - case .searchBanned: - switch participant.participant { - case let .member(_, _, _, banInfo): - if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] { - label = themeAndStrings.1.Channel_Management_RestrictedBy(peer.displayTitle).0 - } - default: - break - } - default: - break - } - entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], enabled: enabled), section: section)) - index += 1 - } - } - - for participant in foundMembers { - if !existingPeerIds.contains(participant.peer.id) { - existingPeerIds.insert(participant.peer.id) - let section: ChannelMembersSearchSection - switch mode { - case .inviteActions, .banAndPromoteActions: - section = .members - case .searchMembers, .searchBanned, .searchAdmins: - section = .none - } - - var label: String? - var enabled = true - if case .banAndPromoteActions = mode { - if case .creator = participant.participant { - label = themeAndStrings.1.Channel_Management_LabelCreator - enabled = false - } - } - - - entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], enabled: enabled), section: section)) - index += 1 - } - } - - for peer in foundContacts.0 { - if !existingPeerIds.contains(peer.id) { - existingPeerIds.insert(peer.id) - entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .contacts)) - index += 1 - } - } - - for foundPeer in foundRemotePeers.0 { - let peer = foundPeer.peer - if !existingPeerIds.contains(peer.id) && peer is TelegramUser { - existingPeerIds.insert(peer.id) - entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global)) - index += 1 - } - } - - for foundPeer in foundRemotePeers.1 { - let peer = foundPeer.peer - if !existingPeerIds.contains(peer.id) && peer is TelegramUser { - existingPeerIds.insert(peer.id) - entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global)) - index += 1 - } - } - - return entries + return removePeerMember(account: account, peerId: peerId, memberId: memberId) + |> deliverOnMainQueue + |> afterDisposed { + updateState { state in + var state = state + state.removingParticipantIds.remove(memberId) + return state + } } } else { - return .single(nil) + return .complete() } + } + removeMemberDisposable.set(signal.start()) + }) + + let themeAndStringsPromise = self.themeAndStringsPromise + let foundItems = combineLatest(searchQuery.get(), account.postbox.multiplePeersView([peerId]) |> take(1)) + |> mapToSignal { query, peerView -> Signal<[ChannelMembersSearchEntry]?, NoError> in + guard let channel = peerView.peers[peerId] as? TelegramChannel else { + return .single(nil) + } + updateActivity(true) + if let query = query, !query.isEmpty { + let foundGroupMembers: Signal<[RenderedChannelParticipant], NoError> + let foundMembers: Signal<[RenderedChannelParticipant], NoError> + + switch mode { + case .searchMembers, .banAndPromoteActions: + foundGroupMembers = Signal { subscriber in + let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.recent(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in + if case .ready = state.loadingState { + subscriber.putNext(state.list) + } + }) + return disposable + } + |> runOn(Queue.mainQueue()) + foundMembers = .single([]) + case .inviteActions: + foundGroupMembers = .single([]) + foundMembers = channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, category: .recent(.search(query))) + |> map { $0 ?? [] } + case .searchAdmins: + foundGroupMembers = Signal { subscriber in + let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.admins(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in + if case .ready = state.loadingState { + subscriber.putNext(state.list) + subscriber.putCompletion() + } + }) + return disposable + } |> runOn(Queue.mainQueue()) + foundMembers = .single([]) + case .searchBanned: + foundGroupMembers = Signal { subscriber in + let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.restrictedAndBanned(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: peerId, searchQuery: query, updated: { state in + if case .ready = state.loadingState { + subscriber.putNext(state.list) + subscriber.putCompletion() + } + }) + return disposable + } + |> runOn(Queue.mainQueue()) + foundMembers = .single([]) + } + + let foundContacts: Signal<([Peer], [PeerId: PeerPresence]), NoError> + let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError> + switch mode { + case .inviteActions, .banAndPromoteActions: + foundContacts = account.postbox.searchContacts(query: query.lowercased()) + foundRemotePeers = .single(([], [])) |> then(searchPeers(account: account, query: query) + |> delay(0.2, queue: Queue.concurrentDefaultQueue())) + case .searchMembers, .searchBanned, .searchAdmins: + foundContacts = .single(([], [:])) + foundRemotePeers = .single(([], [])) + } + + return combineLatest(foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStringsPromise.get(), statePromise.get()) + |> map { foundGroupMembers, foundMembers, foundContacts, foundRemotePeers, themeAndStrings, state -> [ChannelMembersSearchEntry]? in + var entries: [ChannelMembersSearchEntry] = [] + + var existingPeerIds = Set() + for filter in filters { + switch filter { + case let .exclude(ids): + existingPeerIds = existingPeerIds.union(ids) + case .disable: + break + } + } + switch mode { + case .inviteActions, .banAndPromoteActions: + existingPeerIds.insert(account.peerId) + case .searchMembers, .searchAdmins, .searchBanned: + break + } + + var index = 0 + + for participant in foundGroupMembers { + if !existingPeerIds.contains(participant.peer.id) { + existingPeerIds.insert(participant.peer.id) + let section: ChannelMembersSearchSection + switch mode { + case .inviteActions, .banAndPromoteActions: + section = .members + case .searchMembers, .searchBanned, .searchAdmins: + section = .none + } + + var canPromote: Bool + var canRestrict: Bool + switch participant.participant { + case .creator: + canPromote = false + canRestrict = false + case let .member(_, _, adminRights, bannedRights): + if channel.hasAdminRights([.canAddAdmins]) { + canPromote = true + } else { + canPromote = false + } + if channel.hasAdminRights([.canBanUsers]) { + canRestrict = true + } else { + canRestrict = false + } + if canPromote { + if let bannedRights = bannedRights { + if bannedRights.restrictedBy != account.peerId && !channel.flags.contains(.isCreator) { + canPromote = false + } + } + } + if canRestrict { + if let adminRights = adminRights { + if adminRights.promotedBy != account.peerId && !channel.flags.contains(.isCreator) { + canRestrict = false + } + } + } + } + + var label: String? + var enabled = true + if case .banAndPromoteActions = mode { + if case .creator = participant.participant { + label = themeAndStrings.1.Channel_Management_LabelCreator + enabled = false + } + } else if case .searchMembers = mode { + switch participant.participant { + case .creator: + label = themeAndStrings.1.Channel_Management_LabelCreator + case let .member(member): + if member.adminInfo != nil { + label = themeAndStrings.1.Channel_Management_LabelEditor + } + } + } + + if state.removingParticipantIds.contains(participant.peer.id) { + enabled = false + } + + var peerActions: [ParticipantRevealAction] = [] + if case .searchMembers = mode { + if canPromote { + peerActions.append(ParticipantRevealAction(type: .neutral, title: themeAndStrings.1.GroupInfo_ActionPromote, action: .promote)) + } + if canRestrict { + peerActions.append(ParticipantRevealAction(type: .warning, title: themeAndStrings.1.GroupInfo_ActionRestrict, action: .restrict)) + peerActions.append(ParticipantRevealAction(type: .destructive, title: themeAndStrings.1.Common_Delete, action: .remove)) + } + } + + switch mode { + case .searchAdmins: + switch participant.participant { + case .creator: + label = themeAndStrings.1.Channel_Management_LabelCreator + case let .member(_, _, adminInfo, _): + if let adminInfo = adminInfo { + if let peer = participant.peers[adminInfo.promotedBy] { + label = themeAndStrings.1.Channel_Management_PromotedBy(peer.displayTitle).0 + } + } + } + case .searchBanned: + switch participant.participant { + case let .member(_, _, _, banInfo): + if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] { + label = themeAndStrings.1.Channel_Management_RestrictedBy(peer.displayTitle).0 + } + default: + break + } + default: + break + } + entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: peerActions, revealed: state.revealedPeerId == participant.peer.id, enabled: enabled), section: section)) + index += 1 + } + } + + for participant in foundMembers { + if !existingPeerIds.contains(participant.peer.id) { + existingPeerIds.insert(participant.peer.id) + let section: ChannelMembersSearchSection + switch mode { + case .inviteActions, .banAndPromoteActions: + section = .members + case .searchMembers, .searchBanned, .searchAdmins: + section = .none + } + + var label: String? + var enabled = true + if case .banAndPromoteActions = mode { + if case .creator = participant.participant { + label = themeAndStrings.1.Channel_Management_LabelCreator + enabled = false + } + } + + + entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], revealed: false, enabled: enabled), section: section)) + index += 1 + } + } + + for peer in foundContacts.0 { + if !existingPeerIds.contains(peer.id) { + existingPeerIds.insert(peer.id) + entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .contacts)) + index += 1 + } + } + + for foundPeer in foundRemotePeers.0 { + let peer = foundPeer.peer + if !existingPeerIds.contains(peer.id) && peer is TelegramUser { + existingPeerIds.insert(peer.id) + entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global)) + index += 1 + } + } + + for foundPeer in foundRemotePeers.1 { + let peer = foundPeer.peer + if !existingPeerIds.contains(peer.id) && peer is TelegramUser { + existingPeerIds.insert(peer.id) + entries.append(ChannelMembersSearchEntry(index: index, content: .peer(peer), section: .global)) + index += 1 + } + } + + return entries + } + } else { + return .single(nil) + } } let previousSearchItems = Atomic<[ChannelMembersSearchEntry]?>(value: nil) @@ -371,24 +532,24 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod let previousEntries = previousSearchItems.swap(entries) updateActivity(false) let firstTime = previousEntries == nil - let transition = channelMembersSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries ?? [], isSearching: entries != nil, account: account, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, peerSelected: openPeer) + let transition = channelMembersSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries ?? [], isSearching: entries != nil, account: account, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, interaction: interaction) strongSelf.enqueueTransition(transition, firstTime: firstTime) } })) self.presentationDataDisposable = (account.telegramApplicationContext.presentationData - |> deliverOnMainQueue).start(next: { [weak self] presentationData in - if let strongSelf = self { - let previousTheme = strongSelf.presentationData.theme - let previousStrings = strongSelf.presentationData.strings - - strongSelf.presentationData = presentationData - - if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { - strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings) - } + |> deliverOnMainQueue).start(next: { [weak self] presentationData in + if let strongSelf = self { + let previousTheme = strongSelf.presentationData.theme + let previousStrings = strongSelf.presentationData.strings + + strongSelf.presentationData = presentationData + + if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { + strongSelf.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings) } - }) + } + }) self.listNode.beganInteractiveDragging = { [weak self] in self?.dismissInput?() @@ -398,6 +559,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod deinit { self.searchDisposable.dispose() self.presentationDataDisposable?.dispose() + self.removeMemberDisposable.dispose() } override func didLoad() { diff --git a/TelegramUI/ChannelMembersSearchController.swift b/TelegramUI/ChannelMembersSearchController.swift index d7e4cde8f7..8c81ddea88 100644 --- a/TelegramUI/ChannelMembersSearchController.swift +++ b/TelegramUI/ChannelMembersSearchController.swift @@ -70,6 +70,9 @@ final class ChannelMembersSearchController: ViewController { self?.dismiss() self?.openPeer(peer, participant) } + self.controllerNode.present = { [weak self] c, a in + self?.present(c, in: .window(.root), with: a) + } self.displayNodeDidLoad() } diff --git a/TelegramUI/ChannelMembersSearchControllerNode.swift b/TelegramUI/ChannelMembersSearchControllerNode.swift index 9a39e7a9a5..50fc68e8b7 100644 --- a/TelegramUI/ChannelMembersSearchControllerNode.swift +++ b/TelegramUI/ChannelMembersSearchControllerNode.swift @@ -121,6 +121,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { var requestActivateSearch: (() -> Void)? var requestDeactivateSearch: (() -> Void)? var requestOpenPeerFromSearch: ((Peer, RenderedChannelParticipant?) -> Void)? + var present: ((ViewController, Any?) -> Void)? var themeAndStrings: (PresentationTheme, PresentationStrings) @@ -292,6 +293,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { self?.requestOpenPeerFromSearch?(peer, participant) }, updateActivity: { value in + }, present: { [weak self] c, a in + self?.present?(c, a) }), cancel: { [weak self] in if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() diff --git a/TelegramUI/ContactsPeerItem.swift b/TelegramUI/ContactsPeerItem.swift index f1e4acff77..69814b87df 100644 --- a/TelegramUI/ContactsPeerItem.swift +++ b/TelegramUI/ContactsPeerItem.swift @@ -115,6 +115,7 @@ class ContactsPeerItem: ListViewItem { let enabled: Bool let selection: ContactsPeerItemSelection let editing: ContactsPeerItemEditing + let options: [ItemListPeerItemRevealOption] let action: (ContactsPeerItemPeer) -> Void let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? let deletePeer: ((PeerId) -> Void)? @@ -125,7 +126,7 @@ class ContactsPeerItem: ListViewItem { let header: ListViewItemHeader? - init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) { + init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) { self.theme = theme self.strings = strings self.sortOrder = sortOrder @@ -138,6 +139,7 @@ class ContactsPeerItem: ListViewItem { self.enabled = enabled self.selection = selection self.editing = editing + self.options = options self.action = action self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.deletePeer = deletePeer @@ -562,6 +564,32 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { titleFrame = CGRect(origin: CGPoint(x: leftInset, y: 13.0), size: titleLayout.size) } + let peerRevealOptions: [ItemListRevealOption] + if item.enabled { + var mappedOptions: [ItemListRevealOption] = [] + var index: Int32 = 0 + for option in item.options { + let color: UIColor + let textColor: UIColor + switch option.type { + case .neutral: + color = item.theme.list.itemDisclosureActions.constructive.fillColor + textColor = item.theme.list.itemDisclosureActions.constructive.foregroundColor + case .warning: + color = item.theme.list.itemDisclosureActions.warning.fillColor + textColor = item.theme.list.itemDisclosureActions.warning.foregroundColor + case .destructive: + color = item.theme.list.itemDisclosureActions.destructive.fillColor + textColor = item.theme.list.itemDisclosureActions.destructive.foregroundColor + } + mappedOptions.append(ItemListRevealOption(key: index, title: option.title, icon: .none, color: color, textColor: textColor)) + index += 1 + } + peerRevealOptions = mappedOptions + } else { + peerRevealOptions = [] + } + return (nodeLayout, { [weak self] in if let strongSelf = self { return (.complete(), { [weak strongSelf] animated, synchronousLoads in @@ -710,12 +738,12 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.updateLayout(size: nodeLayout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) - if item.editing.editable { - strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) - strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated) + strongSelf.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: item.strings.Common_Delete, icon: .none, color: item.theme.list.itemDisclosureActions.destructive.fillColor, textColor: item.theme.list.itemDisclosureActions.destructive.foregroundColor)])) + strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated) } else { - strongSelf.setRevealOptions((left: [], right: [])) + strongSelf.setRevealOptions((left: [], right: peerRevealOptions)) + strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated) } } }) @@ -800,13 +828,17 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { override func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { if let item = self.item { - switch item.peer { - case let .peer(peer, chatPeer): - if let peer = chatPeer ?? peer { - item.deletePeer?(peer.id) - } - case .deviceContact: - break + if item.editing.editable { + switch item.peer { + case let .peer(peer, chatPeer): + if let peer = chatPeer ?? peer { + item.deletePeer?(peer.id) + } + case .deviceContact: + break + } + } else { + item.options[Int(option.key)].action() } } diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 96201232dc..ed3e17acef 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -1780,6 +1780,8 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl if let infoController = peerInfoController(account: account, peer: peer) { arguments.pushController(infoController) } + }, present: { c, a in + presentControllerImpl?(c, a) }) } diff --git a/TelegramUI/GroupInfoSearchItem.swift b/TelegramUI/GroupInfoSearchItem.swift index 99b3df5c34..65ba371924 100644 --- a/TelegramUI/GroupInfoSearchItem.swift +++ b/TelegramUI/GroupInfoSearchItem.swift @@ -10,15 +10,19 @@ final class ChannelMembersSearchItem: ItemListControllerSearch { let peerId: PeerId let cancel: () -> Void let openPeer: (Peer, RenderedChannelParticipant?) -> Void + let present: (ViewController, Any?) -> Void let searchMode: ChannelMembersSearchMode + private var updateActivity: ((Bool) -> Void)? private var activity: ValuePromise = ValuePromise(ignoreRepeated: false) private let activityDisposable = MetaDisposable() - init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { + + init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, present: @escaping (ViewController, Any?) -> Void) { self.account = account self.peerId = peerId self.cancel = cancel self.openPeer = openPeer + self.present = present self.searchMode = searchMode activityDisposable.set((activity.get() |> mapToSignal { value -> Signal in if value { @@ -32,7 +36,7 @@ final class ChannelMembersSearchItem: ItemListControllerSearch { } deinit { - activityDisposable.dispose() + self.activityDisposable.dispose() } func isEqual(to: ItemListControllerSearch) -> Bool { @@ -63,6 +67,8 @@ final class ChannelMembersSearchItem: ItemListControllerSearch { func node(current: ItemListControllerSearchNode?) -> ItemListControllerSearchNode { return ChannelMembersSearchItemNode(account: self.account, peerId: self.peerId, searchMode: self.searchMode, openPeer: self.openPeer, cancel: self.cancel, updateActivity: { [weak self] value in self?.activity.set(value) + }, present: { [weak self] c, a in + self?.present(c, a) }) } } @@ -70,10 +76,10 @@ final class ChannelMembersSearchItem: ItemListControllerSearch { private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode { private let containerNode: ChannelMembersSearchContainerNode - init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool)->Void) { + init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, filters: [], openPeer: { peer, participant in openPeer(peer, participant) - }, updateActivity: updateActivity) + }, updateActivity: updateActivity, present: present) self.containerNode.cancel = { cancel() } From 58d8d39a93532dd860c088580352b9f3315bcd70 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Tue, 11 Dec 2018 17:54:32 +0400 Subject: [PATCH 2/3] Various fixes --- TelegramUI.xcodeproj/project.pbxproj | 18 +- ...ationSequenceCodeEntryControllerNode.swift | 10 +- .../AuthorizationSequenceController.swift | 62 +-- ...rizationSequencePhoneEntryController.swift | 18 +- ...tionSequencePhoneEntryControllerNode.swift | 26 +- TelegramUI/BlockedPeersController.swift | 21 +- TelegramUI/CallController.swift | 2 +- TelegramUI/CallListControllerNode.swift | 1 + TelegramUI/ChangePhoneNumberController.swift | 16 +- .../ChangePhoneNumberControllerNode.swift | 9 +- TelegramUI/ChannelAdminsController.swift | 32 +- TelegramUI/ChannelBlacklistController.swift | 27 +- TelegramUI/ChannelMembersController.swift | 28 +- TelegramUI/ChannelVisibilityController.swift | 17 +- TelegramUI/ChatController.swift | 4 +- TelegramUI/ChatDocumentGalleryItem.swift | 9 +- TelegramUI/ChatHistoryGridNode.swift | 12 +- TelegramUI/ChatHistoryListNode.swift | 4 +- .../ChatInterfaceStateAccessoryPanels.swift | 4 +- .../ChatInterfaceStateContextQueries.swift | 2 +- .../ChatItemGalleryFooterContentNode.swift | 4 +- TelegramUI/ChatListItem.swift | 8 +- TelegramUI/ChatListItemStrings.swift | 6 +- TelegramUI/ChatMessageActionItemNode.swift | 18 +- .../ChatMessageAnimatedStickerItemNode.swift | 4 +- .../ChatMessageAttachedContentNode.swift | 2 +- TelegramUI/ChatMessageBubbleItemNode.swift | 12 +- .../ChatMessageCallBubbleContentNode.swift | 2 +- .../ChatMessageContactBubbleContentNode.swift | 2 +- TelegramUI/ChatMessageForwardInfoNode.swift | 12 +- .../ChatMessageInstantVideoItemNode.swift | 6 +- .../ChatMessageInteractiveFileNode.swift | 100 ++++- ...atMessageInteractiveInstantVideoNode.swift | 2 +- .../ChatMessageMapBubbleContentNode.swift | 2 +- .../ChatMessageMediaBubbleContentNode.swift | 2 +- TelegramUI/ChatMessageNotificationItem.swift | 28 +- TelegramUI/ChatMessageReplyInfoNode.swift | 22 +- TelegramUI/ChatMessageStickerItemNode.swift | 4 +- .../ChatMessageTextBubbleContentNode.swift | 2 +- .../ChatPinnedMessageTitlePanelNode.swift | 10 +- TelegramUI/ChatPresentationData.swift | 4 +- .../ChatPresentationInterfaceState.swift | 69 +-- .../ChatRecentActionsControllerNode.swift | 2 +- .../ChatRecentActionsFilterController.swift | 21 +- TelegramUI/ChatTitleView.swift | 6 +- TelegramUI/ContactListNode.swift | 187 ++++++-- .../ContactSynchronizationSettings.swift | 10 +- TelegramUI/ContactsPeerItem.swift | 13 +- TelegramUI/CountryList.swift | 13 + TelegramUI/CreateGroupController.swift | 17 +- .../DataPrivacySettingsController.swift | 4 +- TelegramUI/DeviceContactDataManager.swift | 46 +- TelegramUI/EditAccessoryPanelNode.swift | 8 +- TelegramUI/FFMpegAudioFrameDecoder.swift | 30 +- TelegramUI/FFMpegMediaFrameSource.swift | 10 +- .../FFMpegMediaFrameSourceContext.swift | 401 ++++++++---------- ...FFMpegMediaFrameSourceContextHelpers.swift | 42 +- ...pegMediaPassthroughVideoFrameDecoder.swift | 8 +- TelegramUI/FFMpegMediaVideoFrameDecoder.swift | 62 ++- TelegramUI/FFMpegPacket.swift | 26 -- TelegramUI/FeedGroupingControllerNode.swift | 29 +- TelegramUI/FetchVideoThumbnail.swift | 3 +- TelegramUI/GalleryController.swift | 8 +- TelegramUI/GroupAdminsController.swift | 21 +- TelegramUI/GroupInfoController.swift | 21 +- TelegramUI/GroupsInCommonController.swift | 21 +- TelegramUI/ItemListPeerItem.swift | 17 +- TelegramUI/ItemListWebsiteItem.swift | 6 +- TelegramUI/ListMessageFileItemNode.swift | 194 +++++---- TelegramUI/MediaPlayer.swift | 10 +- TelegramUI/MediaTrackDecodableFrame.swift | 2 +- TelegramUI/MentionChatInputPanelItem.swift | 4 +- TelegramUI/MessageContentKind.swift | 8 +- TelegramUI/MultiplexedVideoNode.swift | 87 ++-- TelegramUI/NativeVideoContent.swift | 12 +- .../NotificationExceptionControllerNode.swift | 38 +- TelegramUI/NotificationsAndSounds.swift | 2 +- TelegramUI/PasscodeOptionsController.swift | 12 +- .../PeerMediaCollectionControllerNode.swift | 20 +- TelegramUI/PeerTitle.swift | 11 +- TelegramUI/PhoneInputNode.swift | 34 +- TelegramUI/PhotoResources.swift | 10 +- TelegramUI/PresentationData.swift | 47 +- TelegramUI/RecentSessionsController.swift | 18 +- TelegramUI/ReplyAccessoryPanelNode.swift | 8 +- TelegramUI/SecretMediaPreviewController.swift | 59 ++- ...ectivePrivacySettingsPeersController.swift | 21 +- TelegramUI/ShareInputFieldNode.swift | 12 +- TelegramUI/SoftwareVideoSource.swift | 111 ++--- TelegramUI/StorageUsageController.swift | 17 +- .../StringForMessageTimestampStatus.swift | 4 +- TelegramUI/TelegramApplicationContext.swift | 8 + TelegramUI/TelegramUIPrivate/module.modulemap | 13 +- TelegramUI/ThemeSettingsChatPreviewItem.swift | 6 +- TelegramUI/ThemeSettingsController.swift | 12 +- third-party/FFmpeg-iOS/lib/libavcodec.a | Bin 16909632 -> 16768176 bytes third-party/FFmpeg-iOS/lib/libavformat.a | Bin 4264336 -> 4642864 bytes third-party/FFmpeg-iOS/lib/libavutil.a | Bin 3767736 -> 3767880 bytes third-party/FFmpeg-iOS/lib/libswresample.a | Bin 979184 -> 979328 bytes 99 files changed, 1383 insertions(+), 1072 deletions(-) delete mode 100644 TelegramUI/FFMpegPacket.swift diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index 584f90aff9..d02687d923 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -250,6 +250,7 @@ D056CD781FF2A6EE00880D28 /* ChatMessageSwipeToReplyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D056CD771FF2A6EE00880D28 /* ChatMessageSwipeToReplyNode.swift */; }; D056CD7A1FF3CC2A00880D28 /* ListMessagePlaybackOverlayNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D056CD791FF3CC2A00880D28 /* ListMessagePlaybackOverlayNode.swift */; }; D056CD7C1FF3E92C00880D28 /* DirectionalPanGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D056CD7B1FF3E92C00880D28 /* DirectionalPanGestureRecognizer.swift */; }; + D05B077421BFC38600B1D27C /* FFMpeg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05B077321BFC38600B1D27C /* FFMpeg.framework */; }; D05D8B3A2192FC460064586F /* LocalizationListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05D8B392192FC460064586F /* LocalizationListController.swift */; }; D05D8B3F2192FC6E0064586F /* LocalizationListControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05D8B3E2192FC6E0064586F /* LocalizationListControllerNode.swift */; }; D05D8B412192FC8A0064586F /* LocalizationListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05D8B402192FC8A0064586F /* LocalizationListItem.swift */; }; @@ -698,7 +699,6 @@ D0EC6D1A1EB9F58800EBF1C3 /* FFMpegMediaFrameSourceContextHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D161D6B87D30046BCD6 /* FFMpegMediaFrameSourceContextHelpers.swift */; }; D0EC6D1B1EB9F58800EBF1C3 /* FFMpegMediaVideoFrameDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D871D6B87EC0046BCD6 /* FFMpegMediaVideoFrameDecoder.swift */; }; D0EC6D1C1EB9F58800EBF1C3 /* FFMpegMediaPassthroughVideoFrameDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D6F1D6B87DE0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift */; }; - D0EC6D1D1EB9F58800EBF1C3 /* FFMpegPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69D171D6B87D30046BCD6 /* FFMpegPacket.swift */; }; D0EC6D1E1EB9F58800EBF1C3 /* MediaPlayerScrubbingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03922A61DF70E3F000F2CE9 /* MediaPlayerScrubbingNode.swift */; }; D0EC6D1F1EB9F58800EBF1C3 /* MediaPlayerTimeTextNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0177B7F1DFAE18500A5083A /* MediaPlayerTimeTextNode.swift */; }; D0EC6D201EB9F58800EBF1C3 /* PeerAvatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F69CDE1D6B87D30046BCD6 /* PeerAvatar.swift */; }; @@ -1049,10 +1049,6 @@ D0EC6E921EB9F5B200EBF1C3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452C1D5E340300A7428A /* SwiftSignalKit.framework */; }; D0EC6E931EB9F5B200EBF1C3 /* TelegramCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D08D452D1D5E340300A7428A /* TelegramCore.framework */; }; D0EC6E961EB9F5B300EBF1C3 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0EC6E941EB9F5B300EBF1C3 /* MtProtoKitDynamic.framework */; }; - D0EC6E981EB9F5D000EBF1C3 /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EA81D6B9BCB0046BCD6 /* libavcodec.a */; }; - D0EC6E991EB9F5D000EBF1C3 /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EA91D6B9BCB0046BCD6 /* libavformat.a */; }; - D0EC6E9A1EB9F5D000EBF1C3 /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EAA1D6B9BCB0046BCD6 /* libavutil.a */; }; - D0EC6E9B1EB9F5D000EBF1C3 /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EAB1D6B9BCB0046BCD6 /* libswresample.a */; }; D0EC6E9C1EB9F5E600EBF1C3 /* libwebp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0F69EA61D6B9BBC0046BCD6 /* libwebp.a */; }; D0EC6EA21EB9FAFA00EBF1C3 /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D03B251DECB26D00220C46 /* libopus.a */; }; D0EC6EA31EB9FB7A00EBF1C3 /* SSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D075518E1DDA4F9E0073E051 /* SSignalKit.framework */; }; @@ -1569,6 +1565,8 @@ D05A32E91E6F143C002760B4 /* RecentSessionsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentSessionsController.swift; sourceTree = ""; }; D05A32EB1E6F1462002760B4 /* BlockedPeersController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeersController.swift; sourceTree = ""; }; D05A32ED1E6F25A0002760B4 /* ItemListRecentSessionItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListRecentSessionItem.swift; sourceTree = ""; }; + D05B077121BFB9F600B1D27C /* FFMpeg.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FFMpeg.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D05B077321BFC38600B1D27C /* FFMpeg.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FFMpeg.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D05B724C1E720393000BD3AD /* SelectivePrivacySettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectivePrivacySettingsController.swift; sourceTree = ""; }; D05B724F1E720597000BD3AD /* PresentationData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationData.swift; sourceTree = ""; }; D05BFB5E1EAA22F900909D38 /* PresentationResourceKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationResourceKey.swift; sourceTree = ""; }; @@ -2145,7 +2143,6 @@ D0F69CFB1D6B87D30046BCD6 /* TouchDownGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchDownGestureRecognizer.swift; sourceTree = ""; }; D0F69D021D6B87D30046BCD6 /* MediaPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaPlayer.swift; sourceTree = ""; }; D0F69D161D6B87D30046BCD6 /* FFMpegMediaFrameSourceContextHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FFMpegMediaFrameSourceContextHelpers.swift; sourceTree = ""; }; - D0F69D171D6B87D30046BCD6 /* FFMpegPacket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FFMpegPacket.swift; sourceTree = ""; }; D0F69D1D1D6B87D30046BCD6 /* MediaTrackDecodableFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTrackDecodableFrame.swift; sourceTree = ""; }; D0F69D6F1D6B87DE0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FFMpegMediaPassthroughVideoFrameDecoder.swift; sourceTree = ""; }; D0F69D701D6B87DE0046BCD6 /* MediaTrackFrameBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTrackFrameBuffer.swift; sourceTree = ""; }; @@ -2270,6 +2267,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D05B077421BFC38600B1D27C /* FFMpeg.framework in Frameworks */, D045549A21B2F173007A6DD9 /* libturbojpeg.a in Frameworks */, 091BEAB3214552D9003AEA30 /* Vision.framework in Frameworks */, D0C45E9F213FFAFD00988156 /* Lottie.framework in Frameworks */, @@ -2286,10 +2284,6 @@ D0EC6EA31EB9FB7A00EBF1C3 /* SSignalKit.framework in Frameworks */, D0EC6EA21EB9FAFA00EBF1C3 /* libopus.a in Frameworks */, D0EC6E9C1EB9F5E600EBF1C3 /* libwebp.a in Frameworks */, - D0EC6E981EB9F5D000EBF1C3 /* libavcodec.a in Frameworks */, - D0EC6E991EB9F5D000EBF1C3 /* libavformat.a in Frameworks */, - D0EC6E9A1EB9F5D000EBF1C3 /* libavutil.a in Frameworks */, - D0EC6E9B1EB9F5D000EBF1C3 /* libswresample.a in Frameworks */, D0EC6E961EB9F5B300EBF1C3 /* MtProtoKitDynamic.framework in Frameworks */, D0EC6E8F1EB9F5B200EBF1C3 /* AsyncDisplayKit.framework in Frameworks */, D0EC6E901EB9F5B200EBF1C3 /* Display.framework in Frameworks */, @@ -3309,6 +3303,8 @@ D08D45281D5E340200A7428A /* Frameworks */ = { isa = PBXGroup; children = ( + D05B077321BFC38600B1D27C /* FFMpeg.framework */, + D05B077121BFB9F600B1D27C /* FFMpeg.framework */, D045549921B2F173007A6DD9 /* libturbojpeg.a */, D0C45E9E213FFAFD00988156 /* Lottie.framework */, D02DADBE2138D76F00116225 /* Vision.framework */, @@ -4145,7 +4141,6 @@ D0F69D161D6B87D30046BCD6 /* FFMpegMediaFrameSourceContextHelpers.swift */, D0F69D871D6B87EC0046BCD6 /* FFMpegMediaVideoFrameDecoder.swift */, D0F69D6F1D6B87DE0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift */, - D0F69D171D6B87D30046BCD6 /* FFMpegPacket.swift */, D03922A61DF70E3F000F2CE9 /* MediaPlayerScrubbingNode.swift */, D0177B7F1DFAE18500A5083A /* MediaPlayerTimeTextNode.swift */, ); @@ -5201,7 +5196,6 @@ D01C06AF1FBB461E001561AB /* JoinLinkPreviewController.swift in Sources */, D0EC6D1C1EB9F58800EBF1C3 /* FFMpegMediaPassthroughVideoFrameDecoder.swift in Sources */, D0D9DE0D20EFEA2E00F20B06 /* InstantPageMediaPlaylist.swift in Sources */, - D0EC6D1D1EB9F58800EBF1C3 /* FFMpegPacket.swift in Sources */, D01C06B11FBB4643001561AB /* JoinLinkPreviewControllerNode.swift in Sources */, D0EC6D1E1EB9F58800EBF1C3 /* MediaPlayerScrubbingNode.swift in Sources */, D0C0B59B1EE019E5000F4D2C /* ChatSearchNavigationContentNode.swift in Sources */, diff --git a/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift b/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift index 2914c024c7..b80f32d944 100644 --- a/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/TelegramUI/AuthorizationSequenceCodeEntryControllerNode.swift @@ -245,9 +245,17 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { self.layoutArguments = (layout, navigationBarHeight) - var insets = layout.insets(options: [.input]) + var insets = layout.insets(options: []) insets.top = navigationBarHeight + if let inputHeight = layout.inputHeight { + if inputHeight.isEqual(to: layout.standardInputHeight - 44.0) { + insets.bottom += layout.standardInputHeight + } else { + insets.bottom += inputHeight + } + } + if max(layout.size.width, layout.size.height) > 1023.0 { if let codeType = self.codeType, case .otherSession = codeType { self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_CheckOtherSessionMessages, font: Font.medium(32.0), textColor: self.theme.primaryColor) diff --git a/TelegramUI/AuthorizationSequenceController.swift b/TelegramUI/AuthorizationSequenceController.swift index 48cd5a89a8..244366aaee 100644 --- a/TelegramUI/AuthorizationSequenceController.swift +++ b/TelegramUI/AuthorizationSequenceController.swift @@ -34,8 +34,23 @@ public final class AuthorizationSequenceController: NavigationController { super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black, emptyDetailIcon: nil)) self.stateDisposable = (account.postbox.stateView() - |> deliverOnMainQueue).start(next: { [weak self] view in - self?.updateState(state: view.state ?? UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)) + |> filter { view in + if view.state is UnauthorizedAccountState || view.state == nil { + return true + } else { + return false + } + } + |> map { view -> UnauthorizedAccountStateContents in + if let state = view.state as? UnauthorizedAccountState { + return state.contents + } else { + return .empty + } + } + |> distinctUntilChanged + |> deliverOnMainQueue).start(next: { [weak self] state in + self?.updateState(state: state) }) } @@ -145,7 +160,7 @@ public final class AuthorizationSequenceController: NavigationController { } } } - controller.updateData(countryCode: countryCode, number: number) + controller.updateData(countryCode: countryCode, countryName: nil, number: number) return controller } @@ -606,28 +621,25 @@ public final class AuthorizationSequenceController: NavigationController { return controller } - private func updateState(state: PostboxCoding?) { - if let state = state as? UnauthorizedAccountState { - switch state.contents { - case .empty: - if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController { - } else { - self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty) - } - case let .phoneEntry(countryCode, number): - self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty) - case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService): - self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) - case let .passwordEntry(hint, _, _): - self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint)], animated: !self.viewControllers.isEmpty) - case let .passwordRecovery(_, _, _, emailPattern): - self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty) - case let .awaitingAccountReset(protectedUntil, number): - self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty) - case let .signUp(_, _, _, firstName, lastName, termsOfService): - self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) - } - } else if let _ = state as? AuthorizedAccountState { + private func updateState(state: UnauthorizedAccountStateContents) { + switch state { + case .empty: + if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController { + } else { + self.setViewControllers([self.splashController()], animated: !self.viewControllers.isEmpty) + } + case let .phoneEntry(countryCode, number): + self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: countryCode, number: number)], animated: !self.viewControllers.isEmpty) + case let .confirmationCodeEntry(number, type, _, timeout, nextType, termsOfService): + self.setViewControllers([self.splashController(), self.phoneEntryController(countryCode: defaultCountryCode(), number: ""), self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) + case let .passwordEntry(hint, _, _): + self.setViewControllers([self.splashController(), self.passwordEntryController(hint: hint)], animated: !self.viewControllers.isEmpty) + case let .passwordRecovery(_, _, _, emailPattern): + self.setViewControllers([self.splashController(), self.passwordRecoveryController(emailPattern: emailPattern)], animated: !self.viewControllers.isEmpty) + case let .awaitingAccountReset(protectedUntil, number): + self.setViewControllers([self.splashController(), self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number)], animated: !self.viewControllers.isEmpty) + case let .signUp(_, _, _, firstName, lastName, termsOfService): + self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService)], animated: !self.viewControllers.isEmpty) } } diff --git a/TelegramUI/AuthorizationSequencePhoneEntryController.swift b/TelegramUI/AuthorizationSequencePhoneEntryController.swift index 68c11e338b..c4b5320fc3 100644 --- a/TelegramUI/AuthorizationSequencePhoneEntryController.swift +++ b/TelegramUI/AuthorizationSequencePhoneEntryController.swift @@ -14,7 +14,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController { private let theme: AuthorizationTheme private let openUrl: (String) -> Void - private var currentData: (Int32, String)? + private var currentData: (Int32, String?, String)? var inProgress: Bool = false { didSet { @@ -58,25 +58,25 @@ final class AuthorizationSequencePhoneEntryController: ViewController { self.termsDisposable.dispose() } - func updateData(countryCode: Int32, number: String) { - self.currentData = (countryCode, number) + func updateData(countryCode: Int32, countryName: String?, number: String) { + self.currentData = (countryCode, countryName, number) if self.isNodeLoaded { - self.controllerNode.codeAndNumber = (countryCode, number) + self.controllerNode.codeAndNumber = (countryCode, countryName, number) } } override public func loadDisplayNode() { self.displayNode = AuthorizationSequencePhoneEntryControllerNode(strings: self.strings, theme: self.theme) - if let (code, number) = self.currentData { - self.controllerNode.codeAndNumber = (code, number) + if let (code, name, number) = self.currentData { + self.controllerNode.codeAndNumber = (code, name, number) } self.displayNodeDidLoad() self.controllerNode.selectCountryCode = { [weak self] in if let strongSelf = self { let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.strings, theme: AuthorizationSequenceCountrySelectionTheme(authorizationTheme: strongSelf.theme)) - controller.completeWithCountryCode = { code, _ in + controller.completeWithCountryCode = { code, name in if let strongSelf = self, let currentData = strongSelf.currentData { - strongSelf.updateData(countryCode: Int32(code), number: currentData.1) + strongSelf.updateData(countryCode: Int32(code), countryName: name, number: currentData.2) strongSelf.controllerNode.activateInput() } } @@ -111,7 +111,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController { } @objc func nextPressed() { - let (_, number) = self.controllerNode.codeAndNumber + let (_, _, number) = self.controllerNode.codeAndNumber if !number.isEmpty { self.loginWithNumber?(self.controllerNode.currentNumber) } else { diff --git a/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift b/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift index b06e0935c8..dd1dbc3951 100644 --- a/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/TelegramUI/AuthorizationSequencePhoneEntryControllerNode.swift @@ -117,9 +117,13 @@ private final class PhoneAndCountryNode: ASDisplayNode { self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside) - self.phoneInputNode.countryCodeUpdated = { [weak self] code in + self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in if let strongSelf = self { - if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] { + if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] { + let flagString = emojiFlagForISOCountryCode(name as NSString) + let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName + strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.primaryColor, for: []) + } else if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] { let flagString = emojiFlagForISOCountryCode(countryId as NSString) let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.primaryColor, for: []) @@ -171,7 +175,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { return self.phoneAndCountryNode.phoneInputNode.number } - var codeAndNumber: (Int32?, String) { + var codeAndNumber: (Int32?, String?, String) { get { return self.phoneAndCountryNode.phoneInputNode.codeAndNumber } set(value) { @@ -246,7 +250,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { return nil } } - self.termsOfServiceNode.tapAttributeAction = { [weak self] attributes in + self.termsOfServiceNode.tapAttributeAction = { attributes in if let _ = attributes[NSAttributedStringKey(rawValue: TelegramTextAttributes.URL)] as? String { } } @@ -254,9 +258,17 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - var insets = layout.insets(options: [.input]) + var insets = layout.insets(options: []) insets.top = navigationBarHeight + if let inputHeight = layout.inputHeight { + if inputHeight.isEqual(to: layout.standardInputHeight - 44.0) { + insets.bottom += layout.standardInputHeight + } else { + insets.bottom += inputHeight + } + } + if max(layout.size.width, layout.size.height) > 1023.0 { self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.light(40.0), textColor: self.theme.primaryColor) } else { @@ -265,13 +277,11 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) let noticeSize = self.noticeNode.measure(CGSize(width: min(274.0, layout.size.width - 28.0), height: CGFloat.greatestFiniteMagnitude)) - let termsOfServiceSize = self.termsOfServiceNode.updateLayout(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) var items: [AuthorizationLayoutItem] = [ AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), - AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 44.0, maxValue: 44.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), - //AuthorizationLayoutItem(node: self.termsOfServiceNode, size: termsOfServiceSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 90.0, maxValue: 90.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), + AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 44.0, maxValue: 44.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)) ] if layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 10.0)), items: items, transition: transition, failIfDoesNotFit: true) { diff --git a/TelegramUI/BlockedPeersController.swift b/TelegramUI/BlockedPeersController.swift index 0e93556b23..79d6f937fa 100644 --- a/TelegramUI/BlockedPeersController.swift +++ b/TelegramUI/BlockedPeersController.swift @@ -33,7 +33,7 @@ private enum BlockedPeersEntryStableId: Hashable { private enum BlockedPeersEntry: ItemListNodeEntry { case add(PresentationTheme, String) - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, ItemListPeerItemEditing, Bool) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool) var section: ItemListSectionId { switch self { @@ -48,7 +48,7 @@ private enum BlockedPeersEntry: ItemListNodeEntry { switch self { case .add: return .add - case let .peerItem(_, _, _, _, peer, _, _): + case let .peerItem(_, _, _, _, _, peer, _, _): return .peer(peer.id) } } @@ -61,8 +61,8 @@ private enum BlockedPeersEntry: ItemListNodeEntry { } else { return false } - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsEditing, lhsEnabled): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsEditing, rhsEnabled) = rhs { + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs { if lhsIndex != rhsIndex { return false } @@ -75,6 +75,9 @@ private enum BlockedPeersEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if !lhsPeer.isEqual(rhsPeer) { return false } @@ -99,11 +102,11 @@ private enum BlockedPeersEntry: ItemListNodeEntry { } else { return false } - case let .peerItem(index, _, _, _, _, _, _): + case let .peerItem(index, _, _, _, _, _, _, _): switch rhs { case .add: return false - case let .peerItem(rhsIndex, _, _, _, _, _, _): + case let .peerItem(rhsIndex, _, _, _, _, _, _, _): return index < rhsIndex } } @@ -115,8 +118,8 @@ private enum BlockedPeersEntry: ItemListNodeEntry { return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: false, action: { arguments.addPeer() }) - case let .peerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: { + case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: { arguments.openPeer(peer) }, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) @@ -179,7 +182,7 @@ private func blockedPeersControllerEntries(presentationData: PresentationData, s var index: Int32 = 0 for peer in peers { - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != peer.id)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != peer.id)) index += 1 } } diff --git a/TelegramUI/CallController.swift b/TelegramUI/CallController.swift index 4c0e2f067d..3046b3e256 100644 --- a/TelegramUI/CallController.swift +++ b/TelegramUI/CallController.swift @@ -146,7 +146,7 @@ public final class CallController: ViewController { }) ]) ]) - strongSelf.present(actionSheet, in: .window(.root)) + strongSelf.present(actionSheet, in: .window(.calls)) } } diff --git a/TelegramUI/CallListControllerNode.swift b/TelegramUI/CallListControllerNode.swift index 43bfc70ddd..fbde141dcc 100644 --- a/TelegramUI/CallListControllerNode.swift +++ b/TelegramUI/CallListControllerNode.swift @@ -203,6 +203,7 @@ final class CallListControllerNode: ASDisplayNode { self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.listNode = ListView() + self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor self.emptyTextNode = ASTextNode() self.emptyTextNode.alpha = 0.0 diff --git a/TelegramUI/ChangePhoneNumberController.swift b/TelegramUI/ChangePhoneNumberController.swift index cba4f82593..43b4df1647 100644 --- a/TelegramUI/ChangePhoneNumberController.swift +++ b/TelegramUI/ChangePhoneNumberController.swift @@ -11,7 +11,7 @@ final class ChangePhoneNumberController: ViewController { private let account: Account - private var currentData: (Int32, String)? + private var currentData: (Int32, String?, String)? private let requestDisposable = MetaDisposable() var inProgress: Bool = false { @@ -52,11 +52,11 @@ final class ChangePhoneNumberController: ViewController { self.requestDisposable.dispose() } - func updateData(countryCode: Int32, number: String) { - if self.currentData == nil || self.currentData! != (countryCode, number) { - self.currentData = (countryCode, number) + func updateData(countryCode: Int32, countryName: String, number: String) { + if self.currentData == nil || self.currentData! != (countryCode, countryName, number) { + self.currentData = (countryCode, countryName, number) if self.isNodeLoaded { - self.controllerNode.codeAndNumber = (countryCode, number) + self.controllerNode.codeAndNumber = (countryCode, countryName, number) } } } @@ -67,9 +67,9 @@ final class ChangePhoneNumberController: ViewController { self.controllerNode.selectCountryCode = { [weak self] in if let strongSelf = self { let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.presentationData.strings, theme: AuthorizationSequenceCountrySelectionTheme(presentationTheme: strongSelf.presentationData.theme)) - controller.completeWithCountryCode = { code, _ in + controller.completeWithCountryCode = { code, name in if let strongSelf = self { - strongSelf.updateData(countryCode: Int32(code), number: strongSelf.controllerNode.codeAndNumber.1) + strongSelf.updateData(countryCode: Int32(code), countryName: name, number: strongSelf.controllerNode.codeAndNumber.2) strongSelf.controllerNode.activateInput() } } @@ -98,7 +98,7 @@ final class ChangePhoneNumberController: ViewController { } @objc func nextPressed() { - let (code, number) = self.controllerNode.codeAndNumber + let (code, _, number) = self.controllerNode.codeAndNumber var phoneNumber = number if let code = code { phoneNumber = "\(code)\(phoneNumber)" diff --git a/TelegramUI/ChangePhoneNumberControllerNode.swift b/TelegramUI/ChangePhoneNumberControllerNode.swift index 498c0edb76..2f2375d418 100644 --- a/TelegramUI/ChangePhoneNumberControllerNode.swift +++ b/TelegramUI/ChangePhoneNumberControllerNode.swift @@ -78,7 +78,7 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { return self.phoneInputNode.number } - var codeAndNumber: (Int32?, String) { + var codeAndNumber: (Int32?, String?, String) { get { return self.phoneInputNode.codeAndNumber } set(value) { @@ -148,9 +148,12 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode { self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside) - self.phoneInputNode.countryCodeUpdated = { [weak self] code in + self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in if let strongSelf = self { - if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] { + if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] { + let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.presentationData.strings) ?? countryName + strongSelf.countryButton.setTitle(localizedName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) + } else if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] { strongSelf.countryButton.setTitle(countryName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) } else { strongSelf.countryButton.setTitle(strongSelf.presentationData.strings.Login_CountryCode, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) diff --git a/TelegramUI/ChannelAdminsController.swift b/TelegramUI/ChannelAdminsController.swift index e53c2fa1dd..89316be672 100644 --- a/TelegramUI/ChannelAdminsController.swift +++ b/TelegramUI/ChannelAdminsController.swift @@ -67,7 +67,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { case administrationInfo(PresentationTheme, String) case adminsHeader(PresentationTheme, String) - case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Bool, Int32, RenderedChannelParticipant, ItemListPeerItemEditing, Bool) + case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Bool, Int32, RenderedChannelParticipant, ItemListPeerItemEditing, Bool) case addAdmin(PresentationTheme, String, Bool) case adminsInfo(PresentationTheme, String) @@ -94,7 +94,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { return .index(4) case .adminsInfo: return .index(5) - case let .adminPeerItem(_, _, _, _, _, participant, _, _): + case let .adminPeerItem(_, _, _, _, _, _, participant, _, _): return .peer(participant.peer.id) } } @@ -125,8 +125,8 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { } else { return false } - case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIsGroup, lhsIndex, lhsParticipant, lhsEditing, lhsEnabled): - if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIsGroup, rhsIndex, rhsParticipant, rhsEditing, rhsEnabled) = rhs { + case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsIsGroup, lhsIndex, lhsParticipant, lhsEditing, lhsEnabled): + if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsIsGroup, rhsIndex, rhsParticipant, rhsEditing, rhsEnabled) = rhs { if lhsTheme !== rhsTheme { return false } @@ -136,6 +136,9 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if lhsIsGroup != rhsIsGroup { return false } @@ -195,11 +198,11 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { default: return true } - case let .adminPeerItem(_, _, _, _, index, _, _, _): + case let .adminPeerItem(_, _, _, _, _, index, _, _, _): switch rhs { case .recentActions, .administrationType, .administrationInfo, .adminsHeader, .addAdmin: return false - case let .adminPeerItem(_, _, _, _, rhsIndex, _, _, _): + case let .adminPeerItem(_, _, _, _, _, rhsIndex, _, _, _): return index < rhsIndex default: return true @@ -230,7 +233,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) case let .adminsHeader(theme, title): return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section) - case let .adminPeerItem(theme, strings, dateTimeFormat, isGroup, _, participant, editing, enabled): + case let .adminPeerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, participant, editing, enabled): let peerText: String let action: (() -> Void)? switch participant.participant { @@ -251,7 +254,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { arguments.openAdmin(participant.participant) } } - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: action, setPeerIdWithRevealedOptions: { previousId, id in + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: action, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) }, removePeer: { peerId in arguments.removeAdmin(peerId) @@ -356,6 +359,10 @@ private struct ChannelAdminsControllerState: Equatable { } private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: PeerId, view: PeerView, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?) -> [ChannelAdminsEntry] { + if participants == nil || participants?.count == nil { + return [] + } + var entries: [ChannelAdminsEntry] = [] if let peer = view.peers[view.peerId] as? TelegramChannel { @@ -450,7 +457,7 @@ private func channelAdminsControllerEntries(presentationData: PresentationData, editable = false } } - entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, isGroup, index, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), existingParticipantIds.contains(participant.peer.id))) + entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, isGroup, index, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), existingParticipantIds.contains(participant.peer.id))) index += 1 } } @@ -672,8 +679,13 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon }) } + var emptyStateItem: ItemListControllerEmptyStateItem? + if admins == nil || admins?.count == 0 { + emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) + } + let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) - let listState = ItemListNodeState(entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: account.peerId, view: view, state: state, participants: admins), style: .blocks, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) + let listState = ItemListNodeState(entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: account.peerId, view: view, state: state, participants: admins), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count) return (controllerState, (listState, arguments)) } |> afterDisposed { diff --git a/TelegramUI/ChannelBlacklistController.swift b/TelegramUI/ChannelBlacklistController.swift index afa60eaa34..c27a6c9f54 100644 --- a/TelegramUI/ChannelBlacklistController.swift +++ b/TelegramUI/ChannelBlacklistController.swift @@ -42,7 +42,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { case add(PresentationTheme, String) case restrictedHeader(PresentationTheme, String) case bannedHeader(PresentationTheme, String) - case peerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Int32, ChannelBlacklistPeerCategory, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool) + case peerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Int32, ChannelBlacklistPeerCategory, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool) var section: ItemListSectionId { switch self { @@ -52,7 +52,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { return ChannelBlacklistSection.restricted.rawValue case .bannedHeader: return ChannelBlacklistSection.banned.rawValue - case let .peerItem(_, _, _, _, category, _, _, _, _): + case let .peerItem(_, _, _, _, _, category, _, _, _, _): switch category { case .restricted: return ChannelBlacklistSection.restricted.rawValue @@ -70,7 +70,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { return .index(1) case .bannedHeader: return .index(2) - case let .peerItem(_, _, _, _, _, participant, _, _, _): + case let .peerItem(_, _, _, _, _, _, participant, _, _, _): return .peer(participant.peer.id) } } @@ -95,8 +95,8 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { } else { return false } - case let .peerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsCategory, lhsParticipant, lhsEditing, lhsEnabled, lhsCanOpen): - if case let .peerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsCategory, rhsParticipant, rhsEditing, rhsEnabled, rhsCanOpen) = rhs { + case let .peerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsIndex, lhsCategory, lhsParticipant, lhsEditing, lhsEnabled, lhsCanOpen): + if case let .peerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsIndex, rhsCategory, rhsParticipant, rhsEditing, rhsEnabled, rhsCanOpen) = rhs { if lhsTheme !== rhsTheme { return false } @@ -106,6 +106,9 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if lhsIndex != rhsIndex { return false } @@ -151,7 +154,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { switch rhs { case .add, .restrictedHeader, .bannedHeader: return false - case let .peerItem(_, _, _, _, category, _, _, _, _): + case let .peerItem(_, _, _, _, _, category, _, _, _, _): switch category { case .restricted: return false @@ -159,7 +162,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { return true } } - case let .peerItem(_, _, _, index, category, _, _, _, _): + case let .peerItem(_, _, _, _, index, category, _, _, _, _): switch rhs { case .add, .restrictedHeader: return false @@ -170,7 +173,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { case .banned: return false } - case let .peerItem(_, _, _, rhsIndex, _, _, _, _, _): + case let .peerItem(_, _, _, _, rhsIndex, _, _, _, _, _): return index < rhsIndex } } @@ -186,7 +189,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) case let .bannedHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) - case let .peerItem(theme, strings, dateTimeFormat, _, _, participant, editing, enabled, canOpen): + case let .peerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, participant, editing, enabled, canOpen): var text: ItemListPeerItemText = .none switch participant.participant { case let .member(_, _, _, banInfo): @@ -196,7 +199,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry { default: break } - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: canOpen ? { + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: nil, text: text, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: canOpen ? { arguments.openPeer(participant.participant) } : { arguments.openPeerInfo(participant.peer) @@ -285,7 +288,7 @@ private func channelBlacklistControllerEntries(presentationData: PresentationDat if case .creator = participant.participant { editable = false } - entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index, .restricted, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen)) + entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index, .restricted, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen)) index += 1 } if !blacklist.banned.isEmpty { @@ -296,7 +299,7 @@ private func channelBlacklistControllerEntries(presentationData: PresentationDat if case .creator = participant.participant { editable = false } - entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index, .banned, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen)) + entries.append(.peerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index, .banned, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, canOpen)) index += 1 } } diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index 10316c296d..578c257278 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -62,7 +62,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { case addMember(PresentationTheme, String) case addMemberInfo(PresentationTheme, String) case inviteLink(PresentationTheme, String) - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, RenderedChannelParticipant, ItemListPeerItemEditing, Bool) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool) var section: ItemListSectionId { switch self { @@ -81,7 +81,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { return .index(1) case .inviteLink: return .index(2) - case let .peerItem(_, _, _, _, participant, _, _): + case let .peerItem(_, _, _, _, _, participant, _, _): return .peer(participant.peer.id) } } @@ -106,8 +106,8 @@ private enum ChannelMembersEntry: ItemListNodeEntry { } else { return false } - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsParticipant, lhsEditing, lhsEnabled): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsParticipant, rhsEditing, rhsEnabled) = rhs { + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled) = rhs { if lhsIndex != rhsIndex { return false } @@ -120,6 +120,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if lhsParticipant != rhsParticipant { return false } @@ -155,9 +158,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry { return true } - case let .peerItem(index, _, _, _, _, _, _): + case let .peerItem(index, _, _, _, _, _, _, _): switch rhs { - case let .peerItem(rhsIndex, _, _, _, _, _, _): + case let .peerItem(rhsIndex, _, _, _, _, _, _, _): return index < rhsIndex case .addMember, .addMemberInfo, .inviteLink: return false @@ -177,9 +180,8 @@ private enum ChannelMembersEntry: ItemListNodeEntry { }) case let .addMemberInfo(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) - case let .peerItem(_, theme, strings, dateTimeFormat, participant, editing, enabled): - - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: participant.presences[participant.peer.id], text: .presence, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: { + case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: participant.presences[participant.peer.id], text: .presence, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: { arguments.openPeer(participant.peer) }, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) @@ -244,6 +246,10 @@ private struct ChannelMembersControllerState: Equatable { } private func ChannelMembersControllerEntries(account: Account, presentationData: PresentationData, view: PeerView, state: ChannelMembersControllerState, participants: [RenderedChannelParticipant]?) -> [ChannelMembersEntry] { + if participants == nil || participants?.count == nil { + return [] + } + var entries: [ChannelMembersEntry] = [] if let participants = participants { @@ -295,7 +301,7 @@ private func ChannelMembersControllerEntries(account: Account, presentationData: editable = canEditMembers } } - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id)) index += 1 } } @@ -448,7 +454,7 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo } var emptyStateItem: ItemListControllerEmptyStateItem? - if peers == nil { + if peers == nil || peers?.count == 0 { emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme) } diff --git a/TelegramUI/ChannelVisibilityController.swift b/TelegramUI/ChannelVisibilityController.swift index 74d045d1fe..132cc70d0e 100644 --- a/TelegramUI/ChannelVisibilityController.swift +++ b/TelegramUI/ChannelVisibilityController.swift @@ -67,7 +67,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry { case publicLinkStatus(PresentationTheme, String, AddressNameValidationStatus) case existingLinksInfo(PresentationTheme, String) - case existingLinkPeerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, ItemListPeerItemEditing, Bool) + case existingLinkPeerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool) var section: ItemListSectionId { switch self { @@ -113,7 +113,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry { return 12 case .existingLinksInfo: return 13 - case let .existingLinkPeerItem(index, _, _, _, _, _, _): + case let .existingLinkPeerItem(index, _, _, _, _, _, _, _): return 14 + index } } @@ -204,8 +204,8 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry { } else { return false } - case let .existingLinkPeerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsEditing, lhsEnabled): - if case let .existingLinkPeerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsEditing, rhsEnabled) = rhs { + case let .existingLinkPeerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled): + if case let .existingLinkPeerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs { if lhsIndex != rhsIndex { return false } @@ -218,6 +218,9 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if !lhsPeer.isEqual(rhsPeer) { return false } @@ -307,12 +310,12 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry { return ItemListActivityTextItem(displayActivity: displayActivity, theme: theme, text: NSAttributedString(string: text, textColor: color), sectionId: self.section) case let .existingLinksInfo(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) - case let .existingLinkPeerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled): + case let .existingLinkPeerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled): var label = "" if let addressName = peer.addressName { label = "t.me/" + addressName } - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .text(label), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .text(label), label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) }, removePeer: { peerId in arguments.revokePeerId(peerId) @@ -491,7 +494,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa } return lhsDate > rhsDate }) { - entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil)) + entries.append(.existingLinkPeerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: true, revealed: state.revealedRevokePeerId == peer.id), state.revokingPeerId == nil)) index += 1 } } else { diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index b189b0fa45..8c25bdb8c8 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -236,7 +236,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.presentationData = (account.applicationContext as! TelegramApplicationContext).currentPresentationData.with { $0 } self.automaticMediaDownloadSettings = (account.applicationContext as! TelegramApplicationContext).currentAutomaticMediaDownloadSettings.with { $0 } - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: mode, chatLocation: chatLocation) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: mode, chatLocation: chatLocation) var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none if case .standard = mode { @@ -1090,7 +1090,7 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal self.controllerInteraction = controllerInteraction - self.chatTitleView = ChatTitleView(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat) + self.chatTitleView = ChatTitleView(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder) self.navigationItem.titleView = self.chatTitleView self.chatTitleView?.pressed = { [weak self] in if let strongSelf = self { diff --git a/TelegramUI/ChatDocumentGalleryItem.swift b/TelegramUI/ChatDocumentGalleryItem.swift index 4ebd1c69ec..f2a093f650 100644 --- a/TelegramUI/ChatDocumentGalleryItem.swift +++ b/TelegramUI/ChatDocumentGalleryItem.swift @@ -128,10 +128,6 @@ class ChatDocumentGalleryItemNode: GalleryItemNode, WKNavigationDelegate { super.init() - if let webView = self.webView as? WKWebView { - //webView.navigationDelegate = self - } - self.view.addSubview(self.webView) self.statusNodeContainer.addSubnode(self.statusNode) @@ -170,6 +166,11 @@ class ChatDocumentGalleryItemNode: GalleryItemNode, WKNavigationDelegate { let updateFile = self.accountAndFile?.1.media != fileReference.media self.accountAndFile = (account, fileReference) if updateFile { + if fileReference.media.mimeType.hasPrefix("image/") { + if let webView = self.webView as? WKWebView { + webView.backgroundColor = .black + } + } self.maybeLoadContent() self.setupStatus(account: account, resource: fileReference.media.resource) } diff --git a/TelegramUI/ChatHistoryGridNode.swift b/TelegramUI/ChatHistoryGridNode.swift index 7a826ab103..d35e3d022d 100644 --- a/TelegramUI/ChatHistoryGridNode.swift +++ b/TelegramUI/ChatHistoryGridNode.swift @@ -246,22 +246,18 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode { super.init() - // self.chatPresentationDataPromise.set(.single(())) - - self.chatPresentationDataPromise.set(account.telegramApplicationContext.presentationData |> map { presentationData in - return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations) + return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations) }) self.floatingSections = true - //self.preloadPages = false let messageViewQueue = self.messageViewQueue let historyViewUpdate = self.chatHistoryLocation - |> distinctUntilChanged - |> mapToSignal { location in - return chatHistoryViewForLocation(location, account: account, chatLocation: .peer(peerId), fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: [], orderStatistics: [.locationWithinMonth]) + |> distinctUntilChanged + |> mapToSignal { location in + return chatHistoryViewForLocation(location, account: account, chatLocation: .peer(peerId), fixedCombinedReadStates: nil, tagMask: tagMask, additionalData: [], orderStatistics: [.locationWithinMonth]) } let previousView = Atomic(value: nil) diff --git a/TelegramUI/ChatHistoryListNode.swift b/TelegramUI/ChatHistoryListNode.swift index dbc70c2173..24ef29bad5 100644 --- a/TelegramUI/ChatHistoryListNode.swift +++ b/TelegramUI/ChatHistoryListNode.swift @@ -356,7 +356,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self.currentPresentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.currentPresentationData.theme, wallpaper: self.currentPresentationData.chatWallpaper), fontSize: self.currentPresentationData.fontSize, strings: self.currentPresentationData.strings, dateTimeFormat: self.currentPresentationData.dateTimeFormat, disableAnimations: self.currentPresentationData.disableAnimations)) + self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.currentPresentationData.theme, wallpaper: self.currentPresentationData.chatWallpaper), fontSize: self.currentPresentationData.fontSize, strings: self.currentPresentationData.strings, dateTimeFormat: self.currentPresentationData.dateTimeFormat, nameDisplayOrder: self.currentPresentationData.nameDisplayOrder, disableAnimations: self.currentPresentationData.disableAnimations)) super.init() @@ -680,7 +680,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousWallpaper != presentationData.chatWallpaper || previousDisableAnimations != presentationData.disableAnimations { let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper) - let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations) + let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations) strongSelf.dynamicBounceEnabled = !presentationData.disableAnimations diff --git a/TelegramUI/ChatInterfaceStateAccessoryPanels.swift b/TelegramUI/ChatInterfaceStateAccessoryPanels.swift index e998c8cc67..470a869735 100644 --- a/TelegramUI/ChatInterfaceStateAccessoryPanels.swift +++ b/TelegramUI/ChatInterfaceStateAccessoryPanels.swift @@ -29,7 +29,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS editPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) return editPanelNode } else { - let panelNode = EditAccessoryPanelNode(account: account, messageId: editMessage.messageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) + let panelNode = EditAccessoryPanelNode(account: account, messageId: editMessage.messageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder) panelNode.interfaceInteraction = interfaceInteraction return panelNode } @@ -60,7 +60,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS replyPanelNode.updateThemeAndStrings(theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) return replyPanelNode } else { - let panelNode = ReplyAccessoryPanelNode(account: account, messageId: replyMessageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings) + let panelNode = ReplyAccessoryPanelNode(account: account, messageId: replyMessageId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder) panelNode.interfaceInteraction = interfaceInteraction return panelNode } diff --git a/TelegramUI/ChatInterfaceStateContextQueries.swift b/TelegramUI/ChatInterfaceStateContextQueries.swift index a913a13dd4..49a10c319b 100644 --- a/TelegramUI/ChatInterfaceStateContextQueries.swift +++ b/TelegramUI/ChatInterfaceStateContextQueries.swift @@ -163,7 +163,7 @@ private func updatedContextQueryResultStateForQuery(account: Account, peer: Peer return result == .orderedAscending })) sortedPeers = sortedPeers.filter { peer in - return !peer.displayTitle.isEmpty + return !peer.debugDisplayTitle.isEmpty } return { _ in return .mentions(sortedPeers) } } diff --git a/TelegramUI/ChatItemGalleryFooterContentNode.swift b/TelegramUI/ChatItemGalleryFooterContentNode.swift index 989de9b2ec..3e280468fe 100644 --- a/TelegramUI/ChatItemGalleryFooterContentNode.swift +++ b/TelegramUI/ChatItemGalleryFooterContentNode.swift @@ -557,7 +557,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 } var generalMessageContentKind: MessageContentKind? for message in messages { - let currentKind = messageContentKind(message, strings: presentationData.strings, accountPeerId: strongSelf.account.peerId) + let currentKind = messageContentKind(message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: strongSelf.account.peerId) if generalMessageContentKind == nil || generalMessageContentKind == currentKind { generalMessageContentKind = currentKind } else { @@ -683,7 +683,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode { let presentationData = strongSelf.account.telegramApplicationContext.currentPresentationData.with { $0 } var generalMessageContentKind: MessageContentKind? for message in messages { - let currentKind = messageContentKind(message, strings: presentationData.strings, accountPeerId: strongSelf.account.peerId) + let currentKind = messageContentKind(message, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: strongSelf.account.peerId) if generalMessageContentKind == nil || generalMessageContentKind == currentKind { generalMessageContentKind = currentKind } else { diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index fcaf9b90a9..a354b009a5 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -520,7 +520,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let leftInset: CGFloat = params.leftInset + 78.0 - let (peer, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, message: message, chatPeer: itemPeer, accountPeerId: item.account.peerId) + let (peer, initialHideAuthor, messageText) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: message, chatPeer: itemPeer, accountPeerId: item.account.peerId) var hideAuthor = initialHideAuthor if isPeerGroup { hideAuthor = false @@ -540,11 +540,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let author = message.author as? TelegramUser, let peer = peer, !(peer is TelegramUser) { if let peer = peer as? TelegramChannel, case .broadcast = peer.info { } else { - peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings) + peerText = author.id == account.peerId ? item.presentationData.strings.DialogList_You : author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) } } else if case .groupReference = item.content { if let messagePeer = itemPeer.chatMainPeer { - peerText = messagePeer.displayTitle(strings: item.presentationData.strings) + peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) } } @@ -559,7 +559,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { case .peer: if peer?.id == item.account.peerId { titleAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_SavedMessages, font: titleFont, textColor: theme.titleColor) - } else if let displayTitle = peer?.displayTitle(strings: item.presentationData.strings) { + } else if let displayTitle = peer?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) { titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat ? theme.secretTitleColor : theme.titleColor) } case .groupReference: diff --git a/TelegramUI/ChatListItemStrings.swift b/TelegramUI/ChatListItemStrings.swift index 68ced077f3..8df33f4a6a 100644 --- a/TelegramUI/ChatListItemStrings.swift +++ b/TelegramUI/ChatListItemStrings.swift @@ -2,7 +2,7 @@ import Foundation import Postbox import TelegramCore -public func chatListItemStrings(strings: PresentationStrings, message: Message?, chatPeer: RenderedPeer, accountPeerId: PeerId) -> (peer: Peer?, hideAuthor: Bool, messageText: String) { +public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message?, chatPeer: RenderedPeer, accountPeerId: PeerId) -> (peer: Peer?, hideAuthor: Bool, messageText: String) { let peer: Peer? var hideAuthor = false @@ -130,12 +130,12 @@ public func chatListItemStrings(strings: PresentationStrings, message: Message?, } } default: - if let text = plainServiceMessageString(strings: strings, message: message, accountPeerId: accountPeerId) { + if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) { messageText = text } } case _ as TelegramMediaExpiredContent: - if let text = plainServiceMessageString(strings: strings, message: message, accountPeerId: accountPeerId) { + if let text = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) { messageText = text } default: diff --git a/TelegramUI/ChatMessageActionItemNode.swift b/TelegramUI/ChatMessageActionItemNode.swift index 44696746b9..108a2b64b2 100644 --- a/TelegramUI/ChatMessageActionItemNode.swift +++ b/TelegramUI/ChatMessageActionItemNode.swift @@ -22,15 +22,15 @@ private func peerMentionsAttributes(primaryTextColor: UIColor, peerIds: [(Int, P return result } -private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, message: Message, accountPeerId: PeerId) -> NSAttributedString? { - return universalServiceMessageString(theme: theme, strings: strings, message: message, accountPeerId: accountPeerId) +private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message, accountPeerId: PeerId) -> NSAttributedString? { + return universalServiceMessageString(theme: theme, strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) } -func plainServiceMessageString(strings: PresentationStrings, message: Message, accountPeerId: PeerId) -> String? { - return universalServiceMessageString(theme: nil, strings: strings, message: message, accountPeerId: accountPeerId)?.string +func plainServiceMessageString(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message, accountPeerId: PeerId) -> String? { + return universalServiceMessageString(theme: nil, strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId)?.string } -private func universalServiceMessageString(theme: ChatPresentationThemeData?, strings: PresentationStrings, message: Message, accountPeerId: PeerId) -> NSAttributedString? { +private func universalServiceMessageString(theme: ChatPresentationThemeData?, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, message: Message, accountPeerId: PeerId) -> NSAttributedString? { var attributedString: NSAttributedString? let primaryTextColor: UIColor @@ -44,7 +44,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st for media in message.media { if let action = media as? TelegramMediaAction { - let authorName = message.author?.displayTitle ?? "" + let authorName = message.author?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" var isChannel = false if message.id.peerId.namespace == Namespaces.Peer.CloudChannel, let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { @@ -70,7 +70,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st if peerIds.count == 1 { attributePeerIds.append((1, peerIds.first)) } - attributedString = addAttributesToStringWithRanges(strings.Notification_Invited(authorName, peerDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) + attributedString = addAttributesToStringWithRanges(strings.Notification_Invited(authorName, peerDebugDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) } case let .removedMembers(peerIds): if peerIds.first == message.author?.id { @@ -84,7 +84,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st if peerIds.count == 1 { attributePeerIds.append((1, peerIds.first)) } - attributedString = addAttributesToStringWithRanges(strings.Notification_Kicked(authorName, peerDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) + attributedString = addAttributesToStringWithRanges(strings.Notification_Kicked(authorName, peerDebugDisplayTitles(peerIds, message.peers)), body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: attributePeerIds)) } case let .photoUpdated(image): if authorName.isEmpty || isChannel { @@ -510,7 +510,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: true, headerSpacing: 0.0, hidesBackground: .always, forceFullCorners: false, forceAlignment: .center) return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in - let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, message: item.message, accountPeerId: item.account.peerId) + let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, message: item.message, accountPeerId: item.account.peerId) var image: TelegramMediaImage? for media in item.message.media { diff --git a/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/TelegramUI/ChatMessageAnimatedStickerItemNode.swift index c19eb03d22..68e4d1a6b0 100644 --- a/TelegramUI/ChatMessageAnimatedStickerItemNode.swift +++ b/TelegramUI/ChatMessageAnimatedStickerItemNode.swift @@ -295,7 +295,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { // } } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings, format: .minimal) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .minimal) let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude)) @@ -305,7 +305,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { for attribute in item.message.attributes { if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - imageSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) - replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) if let currentReplyBackgroundNode = currentReplyBackgroundNode { updatedReplyBackgroundNode = currentReplyBackgroundNode diff --git a/TelegramUI/ChatMessageAttachedContentNode.swift b/TelegramUI/ChatMessageAttachedContentNode.swift index 346aa31179..7c2fa5752b 100644 --- a/TelegramUI/ChatMessageAttachedContentNode.swift +++ b/TelegramUI/ChatMessageAttachedContentNode.swift @@ -304,7 +304,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { sentViaBot = true } - let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, strings: presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings) var webpageGalleryMediaCount: Int? for media in message.media { diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 2394f596d9..500e8d10f3 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -604,10 +604,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { if initialDisplayHeader && displayAuthorInfo { if let peer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .broadcast = peer.info { - authorNameString = peer.displayTitle + authorNameString = peer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) authorNameColor = chatMessagePeerIdColors[Int(peer.id.id % 7)] } else if let effectiveAuthor = effectiveAuthor { - authorNameString = effectiveAuthor.displayTitle + authorNameString = effectiveAuthor.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) authorNameColor = chatMessagePeerIdColors[Int(effectiveAuthor.id.id % 7)] } if let rawAuthorNameColor = authorNameColor { @@ -686,7 +686,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { sentViaBot = true } - let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) let statusType: ChatMessageDateAndStatusType if message.effectivelyIncoming(item.account.peerId) { @@ -774,7 +774,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { if let authorSignature = forwardInfo.authorSignature { forwardAuthorSignature = authorSignature } else if forwardInfo.author.id != source.id { - forwardAuthorSignature = forwardInfo.author.displayTitle + forwardAuthorSignature = forwardInfo.author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) } else { forwardAuthorSignature = nil } @@ -782,7 +782,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { forwardSource = forwardInfo.author forwardAuthorSignature = nil } - let sizeAndApply = forwardInfoLayout(item.presentationData.theme, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) + let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) forwardInfoSizeApply = (sizeAndApply.0, { sizeAndApply.1() }) forwardInfoOriginY = headerSize.height @@ -796,7 +796,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } else { headerSize.height += 2.0 } - let sizeAndApply = replyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .bubble(incoming: incoming), replyMessage, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) + let sizeAndApply = replyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .bubble(incoming: incoming), replyMessage, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) replyInfoSizeApply = (sizeAndApply.0, { sizeAndApply.1() }) replyInfoOriginY = headerSize.height diff --git a/TelegramUI/ChatMessageCallBubbleContentNode.swift b/TelegramUI/ChatMessageCallBubbleContentNode.swift index e713dc6168..339f6df553 100644 --- a/TelegramUI/ChatMessageCallBubbleContentNode.swift +++ b/TelegramUI/ChatMessageCallBubbleContentNode.swift @@ -132,7 +132,7 @@ class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { buttonImage = PresentationResourcesChat.chatBubbleOutgoingCallButtonImage(item.presentationData.theme.theme) } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) let statusText: String if let callDuration = callDuration, callDuration > 1 { diff --git a/TelegramUI/ChatMessageContactBubbleContentNode.swift b/TelegramUI/ChatMessageContactBubbleContentNode.swift index e63970305c..e8a2f0cb39 100644 --- a/TelegramUI/ChatMessageContactBubbleContentNode.swift +++ b/TelegramUI/ChatMessageContactBubbleContentNode.swift @@ -151,7 +151,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { sentViaBot = true } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/TelegramUI/ChatMessageForwardInfoNode.swift b/TelegramUI/ChatMessageForwardInfoNode.swift index c4a06948b6..009b15b826 100644 --- a/TelegramUI/ChatMessageForwardInfoNode.swift +++ b/TelegramUI/ChatMessageForwardInfoNode.swift @@ -18,15 +18,15 @@ class ChatMessageForwardInfoNode: ASDisplayNode { super.init() } - class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ theme: ChatPresentationThemeData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer, _ authorName: String?, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageForwardInfoNode) { + class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer, _ authorName: String?, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageForwardInfoNode) { let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode) - return { theme, strings, type, peer, authorName, constrainedSize in + return { presentationData, strings, type, peer, authorName, constrainedSize in let peerString: String if let authorName = authorName { - peerString = "\(peer.displayTitle(strings: strings)) (\(authorName))" + peerString = "\(peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)) (\(authorName))" } else { - peerString = peer.displayTitle(strings: strings) + peerString = peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) } let titleColor: UIColor @@ -34,10 +34,10 @@ class ChatMessageForwardInfoNode: ASDisplayNode { switch type { case let .bubble(incoming): - titleColor = incoming ? theme.theme.chat.bubble.incomingAccentTextColor : theme.theme.chat.bubble.outgoingAccentTextColor + titleColor = incoming ? presentationData.theme.theme.chat.bubble.incomingAccentTextColor : presentationData.theme.theme.chat.bubble.outgoingAccentTextColor completeSourceString = strings.Message_ForwardedMessage(peerString) case .standalone: - let serviceColor = serviceMessageColorComponents(theme: theme.theme, wallpaper: theme.wallpaper) + let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) titleColor = serviceColor.primaryText completeSourceString = strings.Message_ForwardedMessageShort(peerString) } diff --git a/TelegramUI/ChatMessageInstantVideoItemNode.swift b/TelegramUI/ChatMessageInstantVideoItemNode.swift index 8d5b78536d..a129f8426b 100644 --- a/TelegramUI/ChatMessageInstantVideoItemNode.swift +++ b/TelegramUI/ChatMessageInstantVideoItemNode.swift @@ -141,7 +141,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { for attribute in item.message.attributes { if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) - replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) if let currentReplyBackgroundNode = currentReplyBackgroundNode { updatedReplyBackgroundNode = currentReplyBackgroundNode @@ -176,7 +176,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { if let authorSignature = forwardInfo.authorSignature { forwardAuthorSignature = authorSignature } else if forwardInfo.author.id != source.id { - forwardAuthorSignature = forwardInfo.author.displayTitle + forwardAuthorSignature = forwardInfo.author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) } else { forwardAuthorSignature = nil } @@ -185,7 +185,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { forwardAuthorSignature = nil } let availableWidth = max(60.0, availableContentWidth - videoLayout.contentSize.width + 6.0) - forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData.theme, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) if let currentForwardBackgroundNode = currentForwardBackgroundNode { updatedForwardBackgroundNode = currentForwardBackgroundNode diff --git a/TelegramUI/ChatMessageInteractiveFileNode.swift b/TelegramUI/ChatMessageInteractiveFileNode.swift index 3ea2132437..945c0e2e4e 100644 --- a/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -17,6 +17,9 @@ private let durationFont = Font.regular(11.0) final class ChatMessageInteractiveFileNode: ASDisplayNode { private let titleNode: TextNode private let descriptionNode: TextNode + private let descriptionMeasuringNode: TextNode + private let fetchingTextNode: ImmediateTextNode + private let fetchingCompactTextNode: ImmediateTextNode private let waveformNode: AudioWaveformNode private let waveformForegroundNode: AudioWaveformNode private var waveformScrubbingNode: MediaPlayerScrubbingNode? @@ -57,6 +60,24 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { self.descriptionNode.displaysAsynchronously = true self.descriptionNode.isUserInteractionEnabled = false + self.descriptionMeasuringNode = TextNode() + + self.fetchingTextNode = ImmediateTextNode() + self.fetchingTextNode.displaysAsynchronously = true + self.fetchingTextNode.isUserInteractionEnabled = false + self.fetchingTextNode.maximumNumberOfLines = 1 + self.fetchingTextNode.contentMode = .left + self.fetchingTextNode.contentsScale = UIScreenScale + self.fetchingTextNode.isHidden = true + + self.fetchingCompactTextNode = ImmediateTextNode() + self.fetchingCompactTextNode.displaysAsynchronously = true + self.fetchingCompactTextNode.isUserInteractionEnabled = false + self.fetchingCompactTextNode.maximumNumberOfLines = 1 + self.fetchingCompactTextNode.contentMode = .left + self.fetchingCompactTextNode.contentsScale = UIScreenScale + self.fetchingCompactTextNode.isHidden = true + self.waveformNode = AudioWaveformNode() self.waveformNode.isLayerBacked = true self.waveformForegroundNode = AudioWaveformNode() @@ -70,6 +91,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { self.addSubnode(self.titleNode) self.addSubnode(self.descriptionNode) + self.addSubnode(self.fetchingTextNode) + self.addSubnode(self.fetchingCompactTextNode) } deinit { @@ -147,19 +170,13 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { let titleAsyncLayout = TextNode.asyncLayout(self.titleNode) let descriptionAsyncLayout = TextNode.asyncLayout(self.descriptionNode) + let descriptionMeasuringAsyncLayout = TextNode.asyncLayout(self.descriptionMeasuringNode) let statusLayout = self.dateAndStatusNode.asyncLayout() let currentMessage = self.message let currentTheme = self.themeAndStrings?.0 - let currentResourceStatus = self.resourceStatus return { account, presentationData, message, file, automaticDownload, incoming, isRecentActions, dateAndStatusType, constrainedSize in - var updatedTheme: ChatPresentationThemeData? - - if presentationData.theme != currentTheme { - updatedTheme = presentationData.theme - } - return (CGFloat.greatestFiniteMagnitude, { constrainedSize in var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? var updatedStatusSignal: Signal? @@ -233,7 +250,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { sentViaBot = true } - let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, strings: presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: message, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, strings: presentationData.strings) let (size, apply) = statusLayout(presentationData.theme, presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, constrainedSize) statusSize = size @@ -273,7 +290,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { if voice { isVoice = true let durationString = stringForDuration(audioDuration) - candidateDescriptionString = NSAttributedString(string: durationString, font: durationFont, textColor:incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor) + candidateDescriptionString = NSAttributedString(string: durationString, font: durationFont, textColor: incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor) if let waveform = waveform { waveform.withDataNoCopy { data in audioWaveform = AudioWaveform(bitstream: data, bitsPerSample: 5) @@ -334,6 +351,16 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { let (titleLayout, titleApply) = titleAsyncLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (descriptionLayout, descriptionApply) = descriptionAsyncLayout(TextNodeLayoutArguments(attributedString: descriptionString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let fileSizeString: String + if let _ = file.size { + fileSizeString = "000.0 MB" + } else { + fileSizeString = "" + } + let (descriptionMeasuringLayout, descriptionMeasuringApply) = descriptionMeasuringAsyncLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "\(fileSizeString) / \(fileSizeString)", font: descriptionFont, textColor: .black), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let descriptionMaxWidth = max(descriptionLayout.size.width, descriptionMeasuringLayout.size.width) + let minVoiceWidth: CGFloat = 120.0 let maxVoiceWidth = constrainedSize.width let maxVoiceLength: CGFloat = 30.0 @@ -341,12 +368,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { var minLayoutWidth: CGFloat if hasThumbnail { - minLayoutWidth = max(titleLayout.size.width, descriptionLayout.size.width) + 86.0 + minLayoutWidth = max(titleLayout.size.width, descriptionMaxWidth) + 86.0 } else if isVoice { let calcDuration = max(minVoiceLength, min(maxVoiceLength, CGFloat(audioDuration))) minLayoutWidth = minVoiceWidth + (maxVoiceWidth - minVoiceWidth) * (calcDuration - minVoiceLength) / (maxVoiceLength - minVoiceLength) } else { - minLayoutWidth = max(titleLayout.size.width, descriptionLayout.size.width) + 44.0 + 8.0 + minLayoutWidth = max(titleLayout.size.width, descriptionMaxWidth) + 44.0 + 8.0 } if let statusSize = statusSize { @@ -445,9 +472,11 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { let _ = titleApply() let _ = descriptionApply() + let _ = descriptionMeasuringApply() strongSelf.titleNode.frame = titleFrame strongSelf.descriptionNode.frame = descriptionFrame + strongSelf.descriptionMeasuringNode.frame = CGRect(origin: CGPoint(), size: descriptionMeasuringLayout.size) if let consumableContentIcon = consumableContentIcon { if strongSelf.consumableContentNode.supernode == nil { @@ -625,6 +654,25 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { let isSending = message.flags.isSending + var downloadingStrings: (String, String)? + + if !isAudio { + switch resourceStatus.mediaStatus { + case let .fetchStatus(fetchStatus): + switch fetchStatus { + case let .Fetching(_, progress): + if let size = file.size { + let compactString = dataSizeString(Int(Float(size) * progress), forceDecimal: true) + downloadingStrings = ("\(compactString) / \(dataSizeString(size, forceDecimal: true))", compactString) + } + default: + break + } + default: + break + } + } + if isAudio && !isVoice && !isSending { let streamingStatusForegroundColor: UIColor = incoming ? bubbleTheme.incomingAccentControlColor : bubbleTheme.outgoingAccentControlColor let streamingStatusBackgroundColor: UIColor = incoming ? bubbleTheme.incomingMediaInactiveControlColor : bubbleTheme.outgoingMediaInactiveControlColor @@ -741,6 +789,36 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { }) } } + + if let (expandedString, compactString) = downloadingStrings { + self.fetchingTextNode.attributedText = NSAttributedString(string: expandedString, font: descriptionFont, textColor: incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor) + self.fetchingCompactTextNode.attributedText = NSAttributedString(string: compactString, font: descriptionFont, textColor: incoming ? bubbleTheme.incomingFileDurationColor : bubbleTheme.outgoingFileDurationColor) + } else { + self.fetchingTextNode.attributedText = nil + self.fetchingCompactTextNode.attributedText = nil + } + + let maxFetchingStatusWidth = max(self.titleNode.frame.width, self.descriptionMeasuringNode.frame.width) + 2.0 + let fetchingInfo = self.fetchingTextNode.updateLayoutInfo(CGSize(width: maxFetchingStatusWidth, height: CGFloat.greatestFiniteMagnitude)) + let fetchingCompactSize = self.fetchingCompactTextNode.updateLayout(CGSize(width: maxFetchingStatusWidth, height: CGFloat.greatestFiniteMagnitude)) + + if downloadingStrings != nil { + self.descriptionNode.isHidden = true + if fetchingInfo.truncated { + self.fetchingTextNode.isHidden = true + self.fetchingCompactTextNode.isHidden = false + } else { + self.fetchingTextNode.isHidden = false + self.fetchingCompactTextNode.isHidden = true + } + } else { + self.descriptionNode.isHidden = false + self.fetchingTextNode.isHidden = true + self.fetchingCompactTextNode.isHidden = true + } + + self.fetchingTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingInfo.size) + self.fetchingCompactTextNode.frame = CGRect(origin: self.descriptionNode.frame.origin, size: fetchingCompactSize) } static func asyncLayout(_ node: ChatMessageInteractiveFileNode?) -> (_ account: Account, _ presentationData: ChatPresentationData, _ message: Message, _ file: TelegramMediaFile, _ automaticDownload: Bool, _ incoming: Bool, _ isRecentActions: Bool, _ dateAndStatusType: ChatMessageDateAndStatusType?, _ constrainedSize: CGSize) -> (CGFloat, (CGSize) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageInteractiveFileNode))) { diff --git a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index dabb53c8c4..64adabeb1c 100644 --- a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -244,7 +244,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { // sentViaBot = true // } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings, format: .regular) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular) let maxDateAndStatusWidth: CGFloat if case .bubble = statusDisplayType { diff --git a/TelegramUI/ChatMessageMapBubbleContentNode.swift b/TelegramUI/ChatMessageMapBubbleContentNode.swift index 326109923d..0c9bb9fa1c 100644 --- a/TelegramUI/ChatMessageMapBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMapBubbleContentNode.swift @@ -188,7 +188,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { } } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/TelegramUI/ChatMessageMediaBubbleContentNode.swift b/TelegramUI/ChatMessageMediaBubbleContentNode.swift index ecf76193dc..447264b57b 100644 --- a/TelegramUI/ChatMessageMediaBubbleContentNode.swift +++ b/TelegramUI/ChatMessageMediaBubbleContentNode.swift @@ -119,7 +119,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { sentViaBot = true } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/TelegramUI/ChatMessageNotificationItem.swift b/TelegramUI/ChatMessageNotificationItem.swift index 149c197c12..f739aa6e12 100644 --- a/TelegramUI/ChatMessageNotificationItem.swift +++ b/TelegramUI/ChatMessageNotificationItem.swift @@ -8,6 +8,7 @@ import SwiftSignalKit public final class ChatMessageNotificationItem: NotificationItem { let account: Account let strings: PresentationStrings + let nameDisplayOrder: PresentationPersonNameOrder let messages: [Message] let tapAction: () -> Bool let expandAction: (@escaping () -> (ASDisplayNode?, () -> Void)) -> Void @@ -16,9 +17,10 @@ public final class ChatMessageNotificationItem: NotificationItem { return messages.first?.id.peerId } - public init(account: Account, strings: PresentationStrings, messages: [Message], tapAction: @escaping () -> Bool, expandAction: @escaping (() -> (ASDisplayNode?, () -> Void)) -> Void) { + public init(account: Account, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, messages: [Message], tapAction: @escaping () -> Bool, expandAction: @escaping (() -> (ASDisplayNode?, () -> Void)) -> Void) { self.account = account self.strings = strings + self.nameDisplayOrder = nameDisplayOrder self.messages = messages self.tapAction = tapAction self.expandAction = expandAction @@ -101,11 +103,11 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { self.avatarNode.setPeer(account: item.account, peer: peer, emptyColor: presentationData.theme.list.mediaPlaceholderColor) if let channel = peer as? TelegramChannel, case .broadcast = channel.info { - title = peer.displayTitle + title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) } else if let author = firstMessage.author, author.id != peer.id { - title = author.displayTitle + "@" + peer.displayTitle + title = author.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) + "@" + peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) } else { - title = peer.displayTitle + title = peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) } } @@ -138,7 +140,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { if message.containsSecretMedia { imageDimensions = nil } - messageText = descriptionStringForMessage(message, strings: item.strings, accountPeerId: item.account.peerId).0 + messageText = descriptionStringForMessage(message, strings: item.strings, nameDisplayOrder: item.nameDisplayOrder, accountPeerId: item.account.peerId).0 } else if item.messages.count > 1, let peer = item.messages[0].peers[item.messages[0].id.peerId] { var displayAuthor = true if let channel = peer as? TelegramChannel { @@ -155,15 +157,15 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { if item.messages[0].forwardInfo != nil { if let author = item.messages[0].author, displayAuthor { title = nil - messageText = presentationData.strings.CHAT_MESSAGE_FWDS(author.compactDisplayTitle, peer.displayTitle, "\(item.messages.count)").0 + messageText = presentationData.strings.CHAT_MESSAGE_FWDS(author.compactDisplayTitle, peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0 } else { title = nil - messageText = presentationData.strings.MESSAGE_FWDS(peer.displayTitle, "\(item.messages.count)").0 + messageText = presentationData.strings.MESSAGE_FWDS(peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0 } } else if item.messages[0].groupingKey != nil { - var kind = messageContentKind(item.messages[0], strings: presentationData.strings, accountPeerId: item.account.peerId).key + var kind = messageContentKind(item.messages[0], strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: item.account.peerId).key for i in 1 ..< item.messages.count { - let nextKind = messageContentKind(item.messages[i], strings: presentationData.strings, accountPeerId: item.account.peerId) + let nextKind = messageContentKind(item.messages[i], strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: item.account.peerId) if kind != nextKind.key { kind = .text break @@ -191,16 +193,16 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { } else if isGroup, let author = item.messages[0].author { switch kind { case .image: - messageText = presentationData.strings.CHAT_MESSAGE_PHOTOS(author.compactDisplayTitle, peer.displayTitle, "\(item.messages.count)").0 + messageText = presentationData.strings.CHAT_MESSAGE_PHOTOS(author.compactDisplayTitle, peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0 default: - messageText = presentationData.strings.CHAT_MESSAGES(author.compactDisplayTitle, peer.displayTitle, "\(item.messages.count)").0 + messageText = presentationData.strings.CHAT_MESSAGES(author.compactDisplayTitle, peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0 } } else { switch kind { case .image: - messageText = presentationData.strings.MESSAGE_PHOTOS(peer.displayTitle, "\(item.messages.count)").0 + messageText = presentationData.strings.MESSAGE_PHOTOS(peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0 default: - messageText = presentationData.strings.MESSAGES(peer.displayTitle, "\(item.messages.count)").0 + messageText = presentationData.strings.MESSAGES(peer.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), "\(item.messages.count)").0 } } } else { diff --git a/TelegramUI/ChatMessageReplyInfoNode.swift b/TelegramUI/ChatMessageReplyInfoNode.swift index 112f133f45..15be0c3cae 100644 --- a/TelegramUI/ChatMessageReplyInfoNode.swift +++ b/TelegramUI/ChatMessageReplyInfoNode.swift @@ -39,35 +39,35 @@ class ChatMessageReplyInfoNode: ASDisplayNode { self.contentNode.addSubnode(self.lineNode) } - class func asyncLayout(_ maybeNode: ChatMessageReplyInfoNode?) -> (_ theme: ChatPresentationThemeData, _ strings: PresentationStrings, _ account: Account, _ type: ChatMessageReplyInfoType, _ message: Message, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode) { + class func asyncLayout(_ maybeNode: ChatMessageReplyInfoNode?) -> (_ theme: ChatPresentationData, _ strings: PresentationStrings, _ account: Account, _ type: ChatMessageReplyInfoType, _ message: Message, _ constrainedSize: CGSize) -> (CGSize, () -> ChatMessageReplyInfoNode) { let titleNodeLayout = TextNode.asyncLayout(maybeNode?.titleNode) let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode) let imageNodeLayout = TransformImageNode.asyncLayout(maybeNode?.imageNode) let previousMediaReference = maybeNode?.previousMediaReference - return { theme, strings, account, type, message, constrainedSize in - let titleString = message.author?.displayTitle(strings: strings) ?? strings.User_DeletedAccount - let (textString, isMedia) = descriptionStringForMessage(message, strings: strings, accountPeerId: account.peerId) + return { presentationData, strings, account, type, message, constrainedSize in + let titleString = message.author?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? strings.User_DeletedAccount + let (textString, isMedia) = descriptionStringForMessage(message, strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: account.peerId) - let placeholderColor: UIColor = message.effectivelyIncoming(account.peerId) ? theme.theme.chat.bubble.incomingMediaPlaceholderColor : theme.theme.chat.bubble.outgoingMediaPlaceholderColor + let placeholderColor: UIColor = message.effectivelyIncoming(account.peerId) ? presentationData.theme.theme.chat.bubble.incomingMediaPlaceholderColor : presentationData.theme.theme.chat.bubble.outgoingMediaPlaceholderColor let titleColor: UIColor let lineImage: UIImage? let textColor: UIColor switch type { case let .bubble(incoming): - titleColor = incoming ? theme.theme.chat.bubble.incomingAccentTextColor : theme.theme.chat.bubble.outgoingAccentTextColor - lineImage = incoming ? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(theme.theme) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(theme.theme) + titleColor = incoming ? presentationData.theme.theme.chat.bubble.incomingAccentTextColor : presentationData.theme.theme.chat.bubble.outgoingAccentTextColor + lineImage = incoming ? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(presentationData.theme.theme) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(presentationData.theme.theme) if isMedia { - textColor = incoming ? theme.theme.chat.bubble.incomingSecondaryTextColor : theme.theme.chat.bubble.outgoingSecondaryTextColor + textColor = incoming ? presentationData.theme.theme.chat.bubble.incomingSecondaryTextColor : presentationData.theme.theme.chat.bubble.outgoingSecondaryTextColor } else { - textColor = incoming ? theme.theme.chat.bubble.incomingPrimaryTextColor : theme.theme.chat.bubble.outgoingPrimaryTextColor + textColor = incoming ? presentationData.theme.theme.chat.bubble.incomingPrimaryTextColor : presentationData.theme.theme.chat.bubble.outgoingPrimaryTextColor } case .standalone: - let serviceColor = serviceMessageColorComponents(theme: theme.theme, wallpaper: theme.wallpaper) + let serviceColor = serviceMessageColorComponents(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) titleColor = serviceColor.primaryText - lineImage = PresentationResourcesChat.chatServiceVerticalLineImage(theme.theme) + lineImage = PresentationResourcesChat.chatServiceVerticalLineImage(presentationData.theme.theme) textColor = titleColor } diff --git a/TelegramUI/ChatMessageStickerItemNode.swift b/TelegramUI/ChatMessageStickerItemNode.swift index 3ef0d713ee..c5da1cc6df 100644 --- a/TelegramUI/ChatMessageStickerItemNode.swift +++ b/TelegramUI/ChatMessageStickerItemNode.swift @@ -227,7 +227,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { // } } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings, format: .minimal) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .minimal) let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.presentationData.theme, item.presentationData.strings, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude)) @@ -239,7 +239,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { for attribute in item.message.attributes { if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - imageSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) - replyInfoApply = makeReplyInfoLayout(item.presentationData.theme, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.account, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) if let currentReplyBackgroundNode = currentReplyBackgroundNode { updatedReplyBackgroundNode = currentReplyBackgroundNode diff --git a/TelegramUI/ChatMessageTextBubbleContentNode.swift b/TelegramUI/ChatMessageTextBubbleContentNode.swift index 0baab2d140..7a7c813849 100644 --- a/TelegramUI/ChatMessageTextBubbleContentNode.swift +++ b/TelegramUI/ChatMessageTextBubbleContentNode.swift @@ -87,7 +87,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { sentViaBot = true } - let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, strings: item.presentationData.strings) + let dateText = stringForMessageTimestampStatus(message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings) let statusType: ChatMessageDateAndStatusType? switch position { diff --git a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift index 49a35e181e..a947fd2f06 100644 --- a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift +++ b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift @@ -115,7 +115,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.currentMessage = interfaceState.pinnedMessage if let currentMessage = currentMessage, let currentLayout = self.currentLayout { - self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, accountPeerId: self.account.peerId, firstTime: previousMessageWasNil) + self.enqueueTransition(width: currentLayout.0, leftInset: currentLayout.1, rightInset: currentLayout.2, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: self.account.peerId, firstTime: previousMessageWasNil) } } @@ -134,14 +134,14 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.currentLayout = (width, leftInset, rightInset) if let currentMessage = self.currentMessage { - self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, accountPeerId: interfaceState.accountPeerId, firstTime: true) + self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, message: currentMessage, theme: interfaceState.theme, strings: interfaceState.strings, nameDisplayOrder: interfaceState.nameDisplayOrder, accountPeerId: interfaceState.accountPeerId, firstTime: true) } } return panelHeight } - private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, message: Message, theme: PresentationTheme, strings: PresentationStrings, accountPeerId: PeerId, firstTime: Bool) { + private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, message: Message, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId, firstTime: Bool) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTextLayout = TextNode.asyncLayout(self.textNode) let imageNodeLayout = self.imageNode.asyncLayout() @@ -214,9 +214,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: strings.Conversation_PinnedMessage, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) - - - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) Queue.mainQueue().async { if let strongSelf = self { diff --git a/TelegramUI/ChatPresentationData.swift b/TelegramUI/ChatPresentationData.swift index 5c5e32c3a8..d9a1c7fd10 100644 --- a/TelegramUI/ChatPresentationData.swift +++ b/TelegramUI/ChatPresentationData.swift @@ -61,6 +61,7 @@ public final class ChatPresentationData { let fontSize: PresentationFontSize let strings: PresentationStrings let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder let disableAnimations: Bool let messageFont: UIFont @@ -68,11 +69,12 @@ public final class ChatPresentationData { let messageItalicFont: UIFont let messageFixedFont: UIFont - init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) { + init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) { self.theme = theme self.fontSize = fontSize self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.disableAnimations = disableAnimations let baseFontSize = fontSize.baseDisplaySize diff --git a/TelegramUI/ChatPresentationInterfaceState.swift b/TelegramUI/ChatPresentationInterfaceState.swift index 66a32165f7..4259ee11fb 100644 --- a/TelegramUI/ChatPresentationInterfaceState.swift +++ b/TelegramUI/ChatPresentationInterfaceState.swift @@ -337,11 +337,12 @@ final class ChatPresentationInterfaceState: Equatable { let theme: PresentationTheme let strings: PresentationStrings let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder let fontSize: PresentationFontSize let accountPeerId: PeerId let mode: ChatControllerPresentationMode - init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation) { + init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation) { self.interfaceState = ChatInterfaceState() self.inputTextPanelState = ChatTextInputPanelState() self.editMessageState = nil @@ -373,12 +374,13 @@ final class ChatPresentationInterfaceState: Equatable { self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.fontSize = fontSize self.accountPeerId = accountPeerId self.mode = mode } - init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, isContact: Bool, hasBots: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, canReportPeer: Bool, callsAvailable: Bool, callsPrivate: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode) { + init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, isContact: Bool, hasBots: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, recordedMediaPreview: ChatRecordedMediaPreview?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: Message?, peerIsBlocked: Bool, peerIsMuted: Bool, canReportPeer: Bool, callsAvailable: Bool, callsPrivate: Bool, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: (String, TelegramMediaWebpage)?, editingUrlPreview: (String, TelegramMediaWebpage)?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, fontSize: PresentationFontSize, accountPeerId: PeerId, mode: ChatControllerPresentationMode) { self.interfaceState = interfaceState self.chatLocation = chatLocation self.renderedPeer = renderedPeer @@ -410,6 +412,7 @@ final class ChatPresentationInterfaceState: Equatable { self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.fontSize = fontSize self.accountPeerId = accountPeerId self.mode = mode @@ -575,27 +578,27 @@ final class ChatPresentationInterfaceState: Equatable { } func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedIsContact(_ isContact: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { @@ -606,103 +609,103 @@ final class ChatPresentationInterfaceState: Equatable { } else { inputQueryResults.removeValue(forKey: queryKind) } - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedRecordedMediaPreview(_ recordedMediaPreview: ChatRecordedMediaPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPinnedMessage(_ pinnedMessage: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedCanReportPeer(_ canReportPeer: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedUrlPreview(_ urlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedEditingUrlPreview(_ editingUrlPreview: (String, TelegramMediaWebpage)?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: mode) } func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, isContact: self.isContact, hasBots: self.hasBots, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, recordedMediaPreview: self.recordedMediaPreview, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, canReportPeer: self.canReportPeer, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, fontSize: self.fontSize, accountPeerId: self.accountPeerId, mode: self.mode) } } diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index d74218a9c1..1124c888f7 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -97,7 +97,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.state = ChatRecentActionsControllerState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.fontSize) - self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, disableAnimations: self.presentationData.disableAnimations)) + self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)) self.context = ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: self.peer.id) diff --git a/TelegramUI/ChatRecentActionsFilterController.swift b/TelegramUI/ChatRecentActionsFilterController.swift index c3780ffbe5..429a5137be 100644 --- a/TelegramUI/ChatRecentActionsFilterController.swift +++ b/TelegramUI/ChatRecentActionsFilterController.swift @@ -64,7 +64,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { case adminsTitle(PresentationTheme, String) case allAdmins(PresentationTheme, String, Bool) - case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Int32, RenderedChannelParticipant, Bool) + case adminPeerItem(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Int32, RenderedChannelParticipant, Bool) var section: ItemListSectionId { switch self { @@ -87,7 +87,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { return .index(200) case .allAdmins: return .index(201) - case let .adminPeerItem(_, _, _, _, participant, _): + case let .adminPeerItem(_, _, _, _, _, participant, _): return .peer(participant.peer.id) } } @@ -124,8 +124,8 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { } else { return false } - case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsParticipant, lhsChecked): - if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsParticipant, rhsChecked) = rhs { + case let .adminPeerItem(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsIndex, lhsParticipant, lhsChecked): + if case let .adminPeerItem(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsIndex, rhsParticipant, rhsChecked) = rhs { if lhsTheme !== rhsTheme { return false } @@ -135,6 +135,9 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameDisplayOrder != rhsNameDisplayOrder { + return false + } if lhsIndex != rhsIndex { return false } @@ -185,9 +188,9 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { default: return false } - case let .adminPeerItem(_, _, _, lhsIndex, _, _): + case let .adminPeerItem(_, _, _, _, lhsIndex, _, _): switch rhs { - case let .adminPeerItem(_, _, _, rhsIndex, _, _): + case let .adminPeerItem(_, _, _, _, rhsIndex, _, _): return lhsIndex < rhsIndex default: return false @@ -213,7 +216,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { return ItemListSwitchItem(theme: theme, title: text, value: value, enabled: true, sectionId: self.section, style: .blocks, updated: { _ in arguments.toggleAllAdmins() }) - case let .adminPeerItem(theme, strings, dateTimeFormat, _, participant, checked): + case let .adminPeerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, participant, checked): let peerText: String switch participant.participant { case .creator: @@ -221,7 +224,7 @@ private enum ChatRecentActionsFilterEntry: ItemListNodeEntry { case .member: peerText = strings.ChatAdmins_AdminLabel.capitalized } - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: checked, style: .check), enabled: true, sectionId: self.section, action: { + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: participant.peer, presence: nil, text: .text(peerText), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: checked, style: .check), enabled: true, sectionId: self.section, action: { arguments.toggleAdmin(participant.peer.id) }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }) @@ -345,7 +348,7 @@ private func channelRecentActionsFilterControllerEntries(presentationData: Prese } else { adminSelected = true } - entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index, participant, adminSelected)) + entries.append(.adminPeerItem(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index, participant, adminSelected)) index += 1 } } diff --git a/TelegramUI/ChatTitleView.swift b/TelegramUI/ChatTitleView.swift index 2565535cef..537a843e3f 100644 --- a/TelegramUI/ChatTitleView.swift +++ b/TelegramUI/ChatTitleView.swift @@ -78,6 +78,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { private var theme: PresentationTheme private var strings: PresentationStrings private var dateTimeFormat: PresentationDateTimeFormat + private var nameDisplayOrder: PresentationPersonNameOrder private let contentContainer: ASDisplayNode private let titleNode: ImmediateTextNode @@ -255,7 +256,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { if peerView.peerId == self.account.peerId { string = NSAttributedString(string: self.strings.Conversation_SavedMessages, font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) } else { - string = NSAttributedString(string: peer.displayTitle(strings: self.strings), font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) + string = NSAttributedString(string: peer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: Font.medium(17.0), textColor: self.theme.rootController.navigationBar.primaryTextColor) } } if peerView.peerId.namespace == Namespaces.Peer.SecretChat { @@ -446,11 +447,12 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } } - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) { + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { self.account = account self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.contentContainer = ASDisplayNode() diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index 5a9415556e..126a23bc71 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -304,44 +304,64 @@ private extension PeerIndexNameRepresentation { func isLessThan(other: PeerIndexNameRepresentation, ordering: PresentationPersonNameOrder) -> ComparisonResult { switch self { case let .title(lhsTitle, _): + let rhsString: String switch other { case let .title(title, _): - return lhsTitle.compare(title) - case let .personName(_, last, _, _): - let lastResult = lhsTitle.compare(last) - if lastResult == .orderedSame { - return .orderedAscending - } else { - return lastResult - } - } - case let .personName(lhsFirst, lhsLast, _, _): - switch other { - case let .title(title, _): - let lastResult = lhsFirst.compare(title) - if lastResult == .orderedSame { - return .orderedDescending - } else { - return lastResult - } + rhsString = title case let .personName(first, last, _, _): switch ordering { case .firstLast: - let firstResult = lhsFirst.compare(first) - if firstResult == .orderedSame { - return lhsLast.compare(last) + if first.isEmpty { + rhsString = last } else { - return firstResult + rhsString = first + last } case .lastFirst: - let lastResult = lhsLast.compare(last) - if lastResult == .orderedSame { - return lhsFirst.compare(first) + if last.isEmpty { + rhsString = first } else { - return lastResult + rhsString = last + first } } } + return lhsTitle.caseInsensitiveCompare(rhsString) + case let .personName(lhsFirst, lhsLast, _, _): + let lhsString: String + switch ordering { + case .firstLast: + if lhsFirst.isEmpty { + lhsString = lhsLast + } else { + lhsString = lhsFirst + lhsLast + } + case .lastFirst: + if lhsLast.isEmpty { + lhsString = lhsFirst + } else { + lhsString = lhsLast + lhsFirst + } + } + let rhsString: String + switch other { + case let .title(title, _): + rhsString = title + case let .personName(first, last, _, _): + switch ordering { + case .firstLast: + if first.isEmpty { + rhsString = last + } else { + rhsString = first + last + } + case .lastFirst: + if last.isEmpty { + rhsString = first + } else { + rhsString = last + first + } + } + } + return lhsString.caseInsensitiveCompare(rhsString) } } } @@ -424,21 +444,21 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer] var indexHeader: unichar = 35 switch peer.indexName { case let .title(title, _): - if let c = title.utf16.first { + if let c = title.uppercased().utf16.first { indexHeader = c } case let .personName(first, last, _, _): switch sortOrder { case .firstLast: - if let c = first.utf16.first { + if let c = first.uppercased().utf16.first { indexHeader = c - } else if let c = last.utf16.first { + } else if let c = last.uppercased().utf16.first { indexHeader = c } case .lastFirst: - if let c = last.utf16.first { + if let c = last.uppercased().utf16.first { indexHeader = c - } else if let c = first.utf16.first { + } else if let c = first.uppercased().utf16.first { indexHeader = c } } @@ -511,20 +531,45 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer] return entries } -private func preparedContactListNodeTransition(account: Account, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, animated: Bool) -> ContactsListNodeTransition { +private func preparedContactListNodeTransition(account: Account, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, generateIndexSections: Bool, animated: Bool) -> ContactsListNodeTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) } - return ContactsListNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, isEmpty: isEmpty, animated: animated) + var indexSections: [String] = [] + if generateIndexSections { + var existingSections = Set() + for entry in toEntries { + switch entry { + case .search: + //indexSections.apend(CollectionIndexNode.searchIndex) + break + case let .peer(_, _, _, header, _, _, _, _, _, _, _): + if let header = header as? ContactListNameIndexHeader { + if !existingSections.contains(header.letter) { + existingSections.insert(header.letter) + if let scalar = UnicodeScalar(header.letter) { + let title = "\(Character(scalar))" + indexSections.append(title) + } + } + } + default: + break + } + } + } + + return ContactsListNodeTransition(deletions: deletions, insertions: insertions, updates: updates, indexSections: indexSections, firstTime: firstTime, isEmpty: isEmpty, animated: animated) } private struct ContactsListNodeTransition { let deletions: [ListViewDeleteItem] let insertions: [ListViewInsertItem] let updates: [ListViewUpdateItem] + let indexSections: [String] let firstTime: Bool let isEmpty: Bool let animated: Bool @@ -584,9 +629,11 @@ final class ContactListNode: ASDisplayNode { private let filters: [ContactListFilter] let listNode: ListView + private var indexNode: CollectionIndexNode? + private var indexSections: [String]? private var queuedTransitions: [ContactsListNodeTransition] = [] - private var hasValidLayout = false + private var validLayout: ContainerViewLayout? private var _ready = ValuePromise() var ready: Signal { @@ -647,6 +694,14 @@ final class ContactListNode: ASDisplayNode { self.listNode = ListView() self.listNode.dynamicBounceEnabled = !self.presentationData.disableAnimations + var generateSections = false + if case .natural = presentation { + generateSections = true + self.indexNode = CollectionIndexNode() + } else { + self.indexNode = nil + } + self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.dateTimeFormat, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder, self.presentationData.disableAnimations)) self.authorizationPromise = Promise(AccessType.allowed) @@ -664,12 +719,15 @@ final class ContactListNode: ASDisplayNode { super.init() self.backgroundColor = self.presentationData.theme.chatList.backgroundColor - self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor + if self.indexNode == nil { + self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor + } self.selectionStateValue = selectionState self.selectionStatePromise.set(.single(selectionState)) self.addSubnode(self.listNode) + self.indexNode.flatMap(self.addSubnode) self.addSubnode(self.authorizationNode) let processingQueue = Queue() @@ -683,6 +741,37 @@ final class ContactListNode: ASDisplayNode { self?.openPeer?(peer) }) + self.indexNode?.indexSelected = { [weak self] section in + guard let strongSelf = self, let entries = previousEntries.with({ $0 }) else { + return + } + var index = 0 + var peerIndex = 0 + loop: for entry in entries { + switch entry { + case .search: + if section == CollectionIndexNode.searchIndex { + strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + break loop + } + case let .peer(_, _, _, header, _, _, _, _, _, _, _): + if let header = header as? ContactListNameIndexHeader { + if let scalar = UnicodeScalar(header.letter) { + let title = "\(Character(scalar))" + if title == section { + strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: peerIndex == 0 ? 0 : index, position: .top(0.0), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + break loop + } + } + } + peerIndex += 1 + default: + break + } + index += 1 + } + } + let account = self.account var firstTime: Int32 = 1 let selectionStateSignal = self.selectionStatePromise.get() @@ -795,7 +884,7 @@ final class ContactListNode: ASDisplayNode { let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, dateTimeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed) let previous = previousEntries.swap(entries) - return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, animated: false)) + return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animated: false)) } if OSAtomicCompareAndSwap32(1, 0, &firstTime) { @@ -844,7 +933,7 @@ final class ContactListNode: ASDisplayNode { } else { animated = false } - return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, animated: animated)) + return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, generateIndexSections: generateSections, animated: animated)) } if OSAtomicCompareAndSwap32(1, 0, &firstTime) { @@ -953,6 +1042,9 @@ final class ContactListNode: ASDisplayNode { } func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + let hadValidLayout = self.validLayout != nil + self.validLayout = layout + var insets = layout.insets(options: [.input]) insets.left += layout.safeInsets.left insets.right += layout.safeInsets.right @@ -985,14 +1077,18 @@ final class ContactListNode: ASDisplayNode { let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve) self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + if let indexNode = self.indexNode, let indexSections = self.indexSections { + let indexNodeFrame = CGRect(origin: CGPoint(x: layout.size.width - insets.right - 20.0, y: insets.top), size: CGSize(width: 20.0, height: layout.size.height - insets.top - insets.bottom)) + transition.updateFrame(node: indexNode, frame: indexNodeFrame) + indexNode.update(size: indexNodeFrame.size, color: self.presentationData.theme.list.itemAccentColor, sections: indexSections, transition: transition) + } //if let authorizationNode = self.authorizationNode { authorizationNode.updateLayout(size: layout.size, insets: insets, transition: transition) transition.updateFrame(node: authorizationNode, frame: self.bounds) //} - if !self.hasValidLayout { - self.hasValidLayout = true + if !hadValidLayout { self.dequeueTransitions() } } @@ -1000,13 +1096,13 @@ final class ContactListNode: ASDisplayNode { private func enqueueTransition(_ transition: ContactsListNodeTransition) { self.queuedTransitions.append(transition) - if self.hasValidLayout { + if self.validLayout != nil { self.dequeueTransitions() } } private func dequeueTransitions() { - if self.hasValidLayout { + if self.validLayout != nil { while !self.queuedTransitions.isEmpty { let transition = self.queuedTransitions.removeFirst() @@ -1019,6 +1115,15 @@ final class ContactListNode: ASDisplayNode { options.insert(.AnimateCrossfade) } } + if let indexNode = self.indexNode, let layout = self.validLayout { + self.indexSections = transition.indexSections + + var insets = layout.insets(options: [.input]) + insets.left += layout.safeInsets.left + insets.right += layout.safeInsets.right + + indexNode.update(size: CGSize(width: 20.0, height: layout.size.height - insets.top - insets.bottom), color: self.presentationData.theme.list.itemAccentColor, sections: transition.indexSections, transition: .immediate) + } self.listNode.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { [weak self] _ in if let strongSelf = self { if !strongSelf.didSetReady { diff --git a/TelegramUI/ContactSynchronizationSettings.swift b/TelegramUI/ContactSynchronizationSettings.swift index b82ababcef..2ce55817c8 100644 --- a/TelegramUI/ContactSynchronizationSettings.swift +++ b/TelegramUI/ContactSynchronizationSettings.swift @@ -4,21 +4,25 @@ import SwiftSignalKit public struct ContactSynchronizationSettings: Equatable, PreferencesEntry { public var synchronizeDeviceContacts: Bool + public var nameDisplayOrder: PresentationPersonNameOrder public static var defaultSettings: ContactSynchronizationSettings { - return ContactSynchronizationSettings(synchronizeDeviceContacts: true) + return ContactSynchronizationSettings(synchronizeDeviceContacts: true, nameDisplayOrder: .firstLast) } - public init(synchronizeDeviceContacts: Bool) { + public init(synchronizeDeviceContacts: Bool, nameDisplayOrder: PresentationPersonNameOrder) { self.synchronizeDeviceContacts = synchronizeDeviceContacts + self.nameDisplayOrder = nameDisplayOrder } public init(decoder: PostboxDecoder) { self.synchronizeDeviceContacts = decoder.decodeInt32ForKey("synchronizeDeviceContacts", orElse: 0) != 0 + self.nameDisplayOrder = PresentationPersonNameOrder(rawValue: decoder.decodeInt32ForKey("nameDisplayOrder", orElse: 0)) ?? .firstLast } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.synchronizeDeviceContacts ? 1 : 0, forKey: "synchronizeDeviceContacts") + encoder.encodeInt32(self.nameDisplayOrder.rawValue, forKey: "synchronizeDeviceContacts") } public func isEqual(to: PreferencesEntry) -> Bool { @@ -30,7 +34,7 @@ public struct ContactSynchronizationSettings: Equatable, PreferencesEntry { } } -func updateContactSynchronizationSettingsInteractively(postbox: Postbox, _ f: @escaping (ContactSynchronizationSettings) -> ContactSynchronizationSettings) -> Signal { +func updateContactSettingsInteractively(postbox: Postbox, _ f: @escaping (ContactSynchronizationSettings) -> ContactSynchronizationSettings) -> Signal { return postbox.transaction { transaction -> Void in transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.contactSynchronizationSettings, { entry in let currentSettings: ContactSynchronizationSettings diff --git a/TelegramUI/ContactsPeerItem.swift b/TelegramUI/ContactsPeerItem.swift index f1e4acff77..cf595bf07f 100644 --- a/TelegramUI/ContactsPeerItem.swift +++ b/TelegramUI/ContactsPeerItem.swift @@ -451,9 +451,16 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { titleAttributedString = NSAttributedString(string: item.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor) } else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty { let string = NSMutableAttributedString() - string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor)) - string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor)) - string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor)) + switch item.displayOrder { + case .firstLast: + string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor)) + string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor)) + string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor)) + case .lastFirst: + string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor)) + string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor)) + string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor)) + } titleAttributedString = string } else if let firstName = user.firstName, !firstName.isEmpty { titleAttributedString = NSAttributedString(string: firstName, font: titleBoldFont, textColor: textColor) diff --git a/TelegramUI/CountryList.swift b/TelegramUI/CountryList.swift index 9aa3ba1266..6a792cee1e 100644 --- a/TelegramUI/CountryList.swift +++ b/TelegramUI/CountryList.swift @@ -73,3 +73,16 @@ let countryCodeToIdAndName: [Int: (String, String)] = { } return dict }() + +struct CountryCodeAndId: Hashable { + let code: Int + let id: String +} + +let countryCodeAndIdToName: [CountryCodeAndId: String] = { + var dict: [CountryCodeAndId: String] = [:] + for (code, id, name) in phoneCountriesInfo { + dict[CountryCodeAndId(code: code, id: id)] = name + } + return dict +}() diff --git a/TelegramUI/CreateGroupController.swift b/TelegramUI/CreateGroupController.swift index 90468943cc..0db2f17906 100644 --- a/TelegramUI/CreateGroupController.swift +++ b/TelegramUI/CreateGroupController.swift @@ -42,7 +42,7 @@ private enum CreateGroupEntry: ItemListNodeEntry { case groupInfo(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer?, ItemListAvatarAndNameInfoItemState, ItemListAvatarAndNameInfoItemUpdatingAvatar?) case setProfilePhoto(PresentationTheme, String) - case member(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, PeerPresence?) + case member(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, PeerPresence?) var section: ItemListSectionId { switch self { @@ -59,7 +59,7 @@ private enum CreateGroupEntry: ItemListNodeEntry { return 0 case .setProfilePhoto: return 1 - case let .member(index, _, _, _, _, _): + case let .member(index, _, _, _, _, _, _): return 2 + index } } @@ -100,8 +100,8 @@ private enum CreateGroupEntry: ItemListNodeEntry { } else { return false } - case let .member(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsPresence): - if case let .member(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPresence) = rhs { + case let .member(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsPeer, lhsPresence): + if case let .member(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsPeer, rhsPresence) = rhs { if lhsIndex != rhsIndex { return false } @@ -114,6 +114,9 @@ private enum CreateGroupEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameDisplayOrder != rhsNameDisplayOrder { + return false + } if !lhsPeer.isEqual(rhsPeer) { return false } @@ -146,8 +149,8 @@ private enum CreateGroupEntry: ItemListNodeEntry { return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { arguments.changeProfilePhoto() }) - case let .member(_, theme, strings, dateTimeFormat, peer, presence): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: presence, text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }) + case let .member(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, presence): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: presence, text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }) } } } @@ -210,7 +213,7 @@ private func createGroupEntries(presentationData: PresentationData, state: Creat }) for i in 0 ..< peers.count { - entries.append(.member(Int32(i), presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peers[i], view.presences[peers[i].id])) + entries.append(.member(Int32(i), presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peers[i], view.presences[peers[i].id])) } return entries diff --git a/TelegramUI/DataPrivacySettingsController.swift b/TelegramUI/DataPrivacySettingsController.swift index c92678f722..c2b28344f7 100644 --- a/TelegramUI/DataPrivacySettingsController.swift +++ b/TelegramUI/DataPrivacySettingsController.swift @@ -394,7 +394,7 @@ public func dataPrivacyController(account: Account) -> ViewController { return } - let _ = updateContactSynchronizationSettingsInteractively(postbox: account.postbox, { settings in + let _ = updateContactSettingsInteractively(postbox: account.postbox, { settings in var settings = settings settings.synchronizeDeviceContacts = false return settings @@ -413,7 +413,7 @@ public func dataPrivacyController(account: Account) -> ViewController { }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})])) } }, updateSyncContacts: { value in - let _ = updateContactSynchronizationSettingsInteractively(postbox: account.postbox, { settings in + let _ = updateContactSettingsInteractively(postbox: account.postbox, { settings in var settings = settings settings.synchronizeDeviceContacts = value return settings diff --git a/TelegramUI/DeviceContactDataManager.swift b/TelegramUI/DeviceContactDataManager.swift index c7f79dccfa..50dbc1f557 100644 --- a/TelegramUI/DeviceContactDataManager.swift +++ b/TelegramUI/DeviceContactDataManager.swift @@ -7,6 +7,7 @@ import AddressBook public typealias DeviceContactStableId = String private protocol DeviceContactDataContext { + func personNameDisplayOrder() -> PresentationPersonNameOrder func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData? func appendContactData(_ contactData: DeviceContactExtendedData, to stableId: DeviceContactStableId) -> DeviceContactExtendedData? func createContactWithData(_ contactData: DeviceContactExtendedData) -> (DeviceContactStableId, DeviceContactExtendedData)? @@ -64,6 +65,15 @@ private final class DeviceContactDataModernContext: DeviceContactDataContext { return (contact.identifier, DeviceContactBasicData(firstName: contact.givenName, lastName: contact.familyName, phoneNumbers: phoneNumbers)) } + func personNameDisplayOrder() -> PresentationPersonNameOrder { + switch CNContactFormatter.nameOrder(for: CNContact()) { + case .givenNameFirst: + return .firstLast + default: + return .lastFirst + } + } + func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData? { let keysToFetch: [CNKeyDescriptor] = [ CNContactFormatter.descriptorForRequiredKeys(for: .fullName), @@ -318,6 +328,14 @@ private final class DeviceContactDataLegacyContext: DeviceContactDataContext { return (stableId, DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: phoneNumbers)) } + func personNameDisplayOrder() -> PresentationPersonNameOrder { + if ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst { + return .firstLast + } else { + return .lastFirst + } + } + func getExtendedContactData(stableId: DeviceContactStableId) -> DeviceContactExtendedData? { if let contact = self.getContactById(stableId: stableId) { let basicData = DeviceContactDataLegacyContext.parseContact(contact).1 @@ -378,6 +396,7 @@ private final class DeviceContactDataManagerImpl { private var accessInitialized = false private var dataContext: DeviceContactDataContext? + let personNameDisplayOrder = ValuePromise() private var extendedContexts: [DeviceContactStableId: ExtendedContactDataContext] = [:] private var stableIdToBasicContactData: [DeviceContactStableId: DeviceContactBasicData] = [:] @@ -402,27 +421,24 @@ private final class DeviceContactDataManagerImpl { strongSelf.accessInitialized = true if authorizationStatus { if #available(iOSApplicationExtension 9.0, *) { - strongSelf.dataContext = DeviceContactDataModernContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in + let dataContext = DeviceContactDataModernContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in guard let strongSelf = self else { return } strongSelf.updateAll(stableIdToBasicContactData) }) + strongSelf.dataContext = dataContext + strongSelf.personNameDisplayOrder.set(dataContext.personNameDisplayOrder()) } else { - strongSelf.dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in + let dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in guard let strongSelf = self else { return } strongSelf.updateAll(stableIdToBasicContactData) }) + strongSelf.dataContext = dataContext + strongSelf.personNameDisplayOrder.set(dataContext.personNameDisplayOrder()) } - - /*strongSelf.dataContext = DeviceContactDataLegacyContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in - guard let strongSelf = self else { - return - } - strongSelf.updateAll(stableIdToBasicContactData) - })*/ } else { strongSelf.updateAll([:]) } @@ -626,6 +642,18 @@ public final class DeviceContactDataManager { }) } + public func personNameDisplayOrder() -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with({ impl in + disposable.set(impl.personNameDisplayOrder.get().start(next: { value in + subscriber.putNext(value) + })) + }) + return disposable + } + } + public func basicData() -> Signal<[DeviceContactStableId: DeviceContactBasicData], NoError> { return Signal { subscriber in let disposable = MetaDisposable() diff --git a/TelegramUI/EditAccessoryPanelNode.swift b/TelegramUI/EditAccessoryPanelNode.swift index 7984558673..04c91d8202 100644 --- a/TelegramUI/EditAccessoryPanelNode.swift +++ b/TelegramUI/EditAccessoryPanelNode.swift @@ -51,12 +51,14 @@ final class EditAccessoryPanelNode: AccessoryPanelNode { private let account: Account var theme: PresentationTheme var strings: PresentationStrings + var nameDisplayOrder: PresentationPersonNameOrder - init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings) { + init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) { self.account = account self.messageId = messageId self.theme = theme self.strings = strings + self.nameDisplayOrder = nameDisplayOrder self.closeButton = ASButtonNode() self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: []) @@ -127,7 +129,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode { if let currentEditMediaReference = self.currentEditMediaReference { effectiveMessage = effectiveMessage.withUpdatedMedia([currentEditMediaReference.media]) } - (text, _) = descriptionStringForMessage(effectiveMessage, strings: self.strings, accountPeerId: self.account.peerId) + (text, _) = descriptionStringForMessage(effectiveMessage, strings: self.strings, nameDisplayOrder: self.nameDisplayOrder, accountPeerId: self.account.peerId) } var updatedMediaReference: AnyMediaReference? @@ -196,7 +198,7 @@ final class EditAccessoryPanelNode: AccessoryPanelNode { if let currentEditMediaReference = self.currentEditMediaReference { effectiveMessage = effectiveMessage.withUpdatedMedia([currentEditMediaReference.media]) } - switch messageContentKind(effectiveMessage, strings: strings, accountPeerId: self.account.peerId) { + switch messageContentKind(effectiveMessage, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: self.account.peerId) { case .text: isMedia = false default: diff --git a/TelegramUI/FFMpegAudioFrameDecoder.swift b/TelegramUI/FFMpegAudioFrameDecoder.swift index f839b537f7..52a5687f9e 100644 --- a/TelegramUI/FFMpegAudioFrameDecoder.swift +++ b/TelegramUI/FFMpegAudioFrameDecoder.swift @@ -1,33 +1,25 @@ import Foundation -import TelegramUIPrivateModule import CoreMedia +import FFMpeg final class FFMpegAudioFrameDecoder: MediaTrackFrameDecoder { - private let codecContext: UnsafeMutablePointer - private let swrContext: FFMpegSwResample + private let codecContext: FFMpegAVCodecContext + private let swrContext: FFMpegSWResample - private let audioFrame: UnsafeMutablePointer + private let audioFrame: FFMpegAVFrame private var resetDecoderOnNextFrame = true - init(codecContext: UnsafeMutablePointer) { + init(codecContext: FFMpegAVCodecContext) { self.codecContext = codecContext - self.audioFrame = av_frame_alloc() + self.audioFrame = FFMpegAVFrame() - self.swrContext = FFMpegSwResample(sourceChannelCount: Int(codecContext.pointee.channels), sourceSampleRate: Int(codecContext.pointee.sample_rate), sourceSampleFormat: codecContext.pointee.sample_fmt, destinationChannelCount: 2, destinationSampleRate: 44100, destinationSampleFormat: AV_SAMPLE_FMT_S16) - } - - deinit { - av_frame_unref(self.audioFrame) - - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) + self.swrContext = FFMpegSWResample(sourceChannelCount: Int(codecContext.channels()), sourceSampleRate: Int(codecContext.sampleRate()), sourceSampleFormat: codecContext.sampleFormat(), destinationChannelCount: 2, destinationSampleRate: 44100, destinationSampleFormat: FFMPEG_AV_SAMPLE_FMT_S16) } func decode(frame: MediaTrackDecodableFrame) -> MediaTrackFrame? { - var status = frame.packet.sendToDecoder(self.codecContext) + let status = frame.packet.send(toDecoder: self.codecContext) if status == 0 { - status = avcodec_receive_frame(self.codecContext, self.audioFrame) - if status == 0 { + if self.codecContext.receive(into: self.audioFrame) { return convertAudioFrame(self.audioFrame, pts: frame.pts, duration: frame.duration) } } @@ -39,7 +31,7 @@ final class FFMpegAudioFrameDecoder: MediaTrackFrameDecoder { return nil } - private func convertAudioFrame(_ frame: UnsafeMutablePointer, pts: CMTime, duration: CMTime) -> MediaTrackFrame? { + private func convertAudioFrame(_ frame: FFMpegAVFrame, pts: CMTime, duration: CMTime) -> MediaTrackFrame? { guard let data = self.swrContext.resample(frame) else { return nil } @@ -67,7 +59,7 @@ final class FFMpegAudioFrameDecoder: MediaTrackFrameDecoder { } func reset() { - avcodec_flush_buffers(self.codecContext) + self.codecContext.flushBuffers() self.resetDecoderOnNextFrame = true } } diff --git a/TelegramUI/FFMpegMediaFrameSource.swift b/TelegramUI/FFMpegMediaFrameSource.swift index 345b75022f..365a44d3cd 100644 --- a/TelegramUI/FFMpegMediaFrameSource.swift +++ b/TelegramUI/FFMpegMediaFrameSource.swift @@ -69,6 +69,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { private let queue: Queue private let postbox: Postbox private let resourceReference: MediaResourceReference + private let tempFilePath: String? private let streamable: Bool private let video: Bool private let preferSoftwareDecoding: Bool @@ -91,10 +92,11 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { } } - init(queue: Queue, postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) { + init(queue: Queue, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) { self.queue = queue self.postbox = postbox self.resourceReference = resourceReference + self.tempFilePath = tempFilePath self.streamable = streamable self.video = video self.preferSoftwareDecoding = preferSoftwareDecoding @@ -146,6 +148,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { let postbox = self.postbox let resourceReference = self.resourceReference + let tempFilePath = self.tempFilePath let queue = self.queue let streamable = self.streamable let video = self.video @@ -153,7 +156,7 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { let fetchAutomatically = self.fetchAutomatically self.performWithContext { [weak self] context in - context.initializeState(postbox: postbox, resourceReference: resourceReference, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically) + context.initializeState(postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically) let (frames, endOfStream) = context.takeFrames(until: timestamp) @@ -195,13 +198,14 @@ final class FFMpegMediaFrameSource: NSObject, MediaFrameSource { let queue = self.queue let postbox = self.postbox let resourceReference = self.resourceReference + let tempFilePath = self.tempFilePath let streamable = self.streamable let video = self.video let preferSoftwareDecoding = self.preferSoftwareDecoding let fetchAutomatically = self.fetchAutomatically self.performWithContext { [weak self] context in - context.initializeState(postbox: postbox, resourceReference: resourceReference, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically) + context.initializeState(postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, fetchAutomatically: fetchAutomatically) context.seek(timestamp: timestamp, completed: { streamDescriptions, timestamp in queue.async { diff --git a/TelegramUI/FFMpegMediaFrameSourceContext.swift b/TelegramUI/FFMpegMediaFrameSourceContext.swift index f08788b7ec..9b7e75d745 100644 --- a/TelegramUI/FFMpegMediaFrameSourceContext.swift +++ b/TelegramUI/FFMpegMediaFrameSourceContext.swift @@ -4,10 +4,11 @@ import Postbox import CoreMedia import TelegramUIPrivateModule import TelegramCore +import FFMpeg private struct StreamContext { let index: Int - let codecContext: UnsafeMutablePointer? + let codecContext: FFMpegAVCodecContext? let fps: CMTime let timebase: CMTime let duration: CMTime @@ -30,29 +31,18 @@ struct FFMpegMediaFrameSourceDescriptionSet { } private final class InitializedState { - fileprivate let avIoContext: UnsafeMutablePointer - fileprivate let avFormatContext: UnsafeMutablePointer + fileprivate let avIoContext: FFMpegAVIOContext + fileprivate let avFormatContext: FFMpegAVFormatContext fileprivate let audioStream: StreamContext? fileprivate let videoStream: StreamContext? - init(avIoContext: UnsafeMutablePointer, avFormatContext: UnsafeMutablePointer, audioStream: StreamContext?, videoStream: StreamContext?) { + init(avIoContext: FFMpegAVIOContext, avFormatContext: FFMpegAVFormatContext, audioStream: StreamContext?, videoStream: StreamContext?) { self.avIoContext = avIoContext self.avFormatContext = avFormatContext self.audioStream = audioStream self.videoStream = videoStream } - - deinit { - let avIoContext = Optional(self.avIoContext) - if self.avIoContext.pointee.buffer != nil { - av_free(self.avIoContext.pointee.buffer) - } - av_free(avIoContext) - - var avFormatContext = Optional(self.avFormatContext) - avformat_close_input(&avFormatContext) - } } struct FFMpegMediaFrameSourceStreamContextInfo { @@ -81,44 +71,66 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa let readCount = min(resourceSize - context.readingOffset, Int(bufferSize)) let requestRange: Range = context.readingOffset ..< (context.readingOffset + readCount) data = postbox.mediaBox.resourceData(resourceReference.resource, size: resourceSize, in: requestRange, mode: .complete) - let semaphore = DispatchSemaphore(value: 0) if readCount == 0 { fetchedData = Data() } else { - let disposable = data.start(next: { data in - if data.count == readCount { - fetchedData = data + if let tempFilePath = context.tempFilePath, let fileData = (try? Data(contentsOf: URL(fileURLWithPath: tempFilePath), options: .mappedRead))?.subdata(in: requestRange) { + fetchedData = fileData + } else { + let semaphore = DispatchSemaphore(value: 0) + let disposable = data.start(next: { data in + if data.count == readCount { + fetchedData = data + semaphore.signal() + } + }) + semaphore.wait() + disposable.dispose() + } + } + } else { + if let tempFilePath = context.tempFilePath, let fileSize = fileSize(tempFilePath) { + let fd = open(tempFilePath, O_RDONLY, S_IRUSR) + if fd >= 0 { + let readingOffset = context.readingOffset + let readCount = max(0, min(fileSize - readingOffset, Int(bufferSize))) + let range = readingOffset ..< (readingOffset + readCount) + + lseek(fd, off_t(range.lowerBound), SEEK_SET) + var data = Data(count: readCount) + data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + let readBytes = read(fd, bytes, readCount) + assert(readBytes <= readCount) + } + fetchedData = data + close(fd) + } + } else { + let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)) + let semaphore = DispatchSemaphore(value: 0) + let readingOffset = context.readingOffset + let disposable = data.start(next: { next in + if next.complete { + let readCount = max(0, min(next.size - readingOffset, Int(bufferSize))) + let range = readingOffset ..< (readingOffset + readCount) + + let fd = open(next.path, O_RDONLY, S_IRUSR) + if fd >= 0 { + lseek(fd, off_t(range.lowerBound), SEEK_SET) + var data = Data(count: readCount) + data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + let readBytes = read(fd, bytes, readCount) + assert(readBytes <= readCount) + } + fetchedData = data + close(fd) + } semaphore.signal() } }) semaphore.wait() disposable.dispose() } - } else { - let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)) - let semaphore = DispatchSemaphore(value: 0) - let readingOffset = context.readingOffset - let disposable = data.start(next: { next in - if next.complete { - let readCount = max(0, min(next.size - readingOffset, Int(bufferSize))) - let range = readingOffset ..< (readingOffset + readCount) - - let fd = open(next.path, O_RDONLY, S_IRUSR) - if fd >= 0 { - lseek(fd, off_t(range.lowerBound), SEEK_SET) - var data = Data(count: readCount) - data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in - let readBytes = read(fd, bytes, readCount) - assert(readBytes <= readCount) - } - fetchedData = data - close(fd) - } - semaphore.signal() - } - }) - semaphore.wait() - disposable.dispose() } if let fetchedData = fetchedData { fetchedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in @@ -144,24 +156,28 @@ private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whe resourceSize = size } else { if !streamable { - var resultSize: Int = Int(Int32.max - 1) - let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)) - let semaphore = DispatchSemaphore(value: 0) - let disposable = data.start(next: { next in - if next.complete { - resultSize = Int(next.size) - semaphore.signal() - } - }) - semaphore.wait() - disposable.dispose() - resourceSize = resultSize + if let tempFilePath = context.tempFilePath, let fileSize = fileSize(tempFilePath) { + resourceSize = fileSize + } else { + var resultSize: Int = Int(Int32.max - 1) + let data = postbox.mediaBox.resourceData(resourceReference.resource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)) + let semaphore = DispatchSemaphore(value: 0) + let disposable = data.start(next: { next in + if next.complete { + resultSize = Int(next.size) + semaphore.signal() + } + }) + semaphore.wait() + disposable.dispose() + resourceSize = resultSize + } } else { resourceSize = Int(Int32.max - 1) } } - if (whence & AVSEEK_SIZE) != 0 { + if (whence & FFMPEG_AVSEEK_SIZE) != 0 { result = Int64(resourceSize == Int(Int32.max - 1) ? -1 : resourceSize) } else { context.readingOffset = Int(min(Int64(resourceSize), offset)) @@ -173,11 +189,15 @@ private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whe context.fetchedDataDisposable.set(nil) } else { if streamable { - let fetchRange: Range = context.readingOffset ..< Int(Int32.max) - context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (fetchRange, .elevated), statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) + if context.tempFilePath == nil { + let fetchRange: Range = context.readingOffset ..< Int(Int32.max) + context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (fetchRange, .elevated), statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) + } } else if !context.requestedCompleteFetch && context.fetchAutomatically { context.requestedCompleteFetch = true - context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) + if context.tempFilePath == nil { + context.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: statsCategory, preferBackgroundReferenceRevalidation: streamable).start()) + } } } } @@ -193,6 +213,7 @@ final class FFMpegMediaFrameSourceContext: NSObject { fileprivate var postbox: Postbox? fileprivate var resourceReference: MediaResourceReference? + fileprivate var tempFilePath: String? fileprivate var streamable: Bool? fileprivate var statsCategory: MediaResourceStatsCategory? @@ -223,7 +244,7 @@ final class FFMpegMediaFrameSourceContext: NSObject { self.fetchedFullDataDisposable.dispose() } - func initializeState(postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) { + func initializeState(postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, fetchAutomatically: Bool) { if self.readingError || self.initializedState != nil { return } @@ -232,173 +253,119 @@ final class FFMpegMediaFrameSourceContext: NSObject { self.postbox = postbox self.resourceReference = resourceReference + self.tempFilePath = tempFilePath self.streamable = streamable self.statsCategory = video ? .video : .audio self.preferSoftwareDecoding = preferSoftwareDecoding self.fetchAutomatically = fetchAutomatically + var preferSoftwareAudioDecoding = false + if case let .media(media, _) = resourceReference, let file = media.media as? TelegramMediaFile { + if file.isInstantVideo { + preferSoftwareAudioDecoding = true + } + } + if streamable { - self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .elevated), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + if self.tempFilePath == nil { + self.fetchedDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .elevated), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + } } else if !self.requestedCompleteFetch && self.fetchAutomatically { self.requestedCompleteFetch = true - self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + if self.tempFilePath == nil { + self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + } } - var avFormatContextRef = avformat_alloc_context() - guard let avFormatContext = avFormatContextRef else { + let avFormatContext = FFMpegAVFormatContext() + + guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(self.ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else { self.readingError = true return } - let avIoBuffer = av_malloc(self.ioBufferSize)! - let avIoContextRef = avio_alloc_context(avIoBuffer.assumingMemoryBound(to: UInt8.self), Int32(self.ioBufferSize), 0, Unmanaged.passUnretained(self).toOpaque(), readPacketCallback, nil, seekCallback) + avFormatContext.setIO(avIoContext) - guard let avIoContext = avIoContextRef else { + if !avFormatContext.openInput() { self.readingError = true return } - avFormatContext.pointee.pb = avIoContext - - //avFormatContext.pointee.flags |= AVFMT_FLAG_FAST_SEEK - //print(String.init(cString: avutil_configuration())) - - var options: OpaquePointer? - av_dict_set(&options, "usetoc", "1", 0) - - guard avformat_open_input(&avFormatContextRef, nil, nil, &options) >= 0 else { - self.readingError = true - return - } - - av_dict_free(&options) - - guard avformat_find_stream_info(avFormatContext, nil) >= 0 else { - self.readingError = true + if !avFormatContext.findStreamInfo() { + self.readingError = true; return } var videoStream: StreamContext? var audioStream: StreamContext? - for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_VIDEO) { - if (avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.disposition & Int32(AV_DISPOSITION_ATTACHED_PIC)) == 0 { - - let codecPar = avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.codecpar! - - if self.preferSoftwareDecoding { - if let codec = avcodec_find_decoder(codecPar.pointee.codec_id) { - if let codecContext = avcodec_alloc_context3(codec) { - if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 { - if avcodec_open2(codecContext, codec, nil) >= 0 { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 24)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - var rotationAngle: Double = 0.0 - if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { - if strcmp(value, "0") != 0 { - if let angle = Double(String(cString: value)) { - rotationAngle = angle * Double.pi / 180.0 - } - } - } - - let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) - - videoStream = StreamContext(index: streamIndex, codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect) - break - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } + for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeVideo) { + let streamIndex = streamIndexNumber.int32Value + if avFormatContext.isAttachedPic(atStreamIndex: streamIndex) { + continue + } + + let codecId = avFormatContext.codecId(atStreamIndex: streamIndex) + + let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000)) + let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase) + + let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale) + + let metrics = avFormatContext.metricsForStream(at: streamIndex) + + let rotationAngle: Double = metrics.rotationAngle + let aspect = Double(metrics.width) / Double(metrics.height) + + if self.preferSoftwareDecoding { + if let codec = FFMpegAVCodec.find(forId: codecId) { + let codecContext = FFMpegAVCodecContext(codec: codec) + if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) { + if codecContext.open() { + videoStream = StreamContext(index: Int(streamIndex), codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect) + break } } - } else if codecPar.pointee.codec_id == AV_CODEC_ID_MPEG4 { - if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromMpeg4CodecData(UInt32(kCMVideoCodecType_MPEG4Video), codecPar.pointee.width, codecPar.pointee.height, codecPar.pointee.extradata, codecPar.pointee.extradata_size) { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 1000)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - var rotationAngle: Double = 0.0 - if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { - if strcmp(value, "0") != 0 { - if let angle = Double(String(cString: value)) { - rotationAngle = angle * Double.pi / 180.0 - } - } - } - - let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) - - videoStream = StreamContext(index: streamIndex, codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect) - break - } - } else if codecPar.pointee.codec_id == AV_CODEC_ID_H264 { - if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromAVCCodecData(UInt32(kCMVideoCodecType_H264), codecPar.pointee.width, codecPar.pointee.height, codecPar.pointee.extradata, codecPar.pointee.extradata_size) { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 1000)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - var rotationAngle: Double = 0.0 - if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { - if strcmp(value, "0") != 0 { - if let angle = Double(String(cString: value)) { - rotationAngle = angle * Double.pi / 180.0 - } - } - } - - let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) - - videoStream = StreamContext(index: streamIndex, codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect) - break - } - } else if codecPar.pointee.codec_id == AV_CODEC_ID_HEVC { - if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromHEVCCodecData(UInt32(kCMVideoCodecType_HEVC), codecPar.pointee.width, codecPar.pointee.height, codecPar.pointee.extradata, codecPar.pointee.extradata_size) { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 1000)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - var rotationAngle: Double = 0.0 - if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { - if strcmp(value, "0") != 0 { - if let angle = Double(String(cString: value)) { - rotationAngle = angle * Double.pi / 180.0 - } - } - } - - let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) - - videoStream = StreamContext(index: streamIndex, codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect) - break - } + } + } else if codecId == FFMpegCodecIdMPEG4 { + if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromMpeg4CodecData(UInt32(kCMVideoCodecType_MPEG4Video), metrics.width, metrics.height, metrics.extradata, metrics.extradataSize) { + videoStream = StreamContext(index: Int(streamIndex), codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect) + break + } + } else if codecId == FFMpegCodecIdH264 { + if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromAVCCodecData(UInt32(kCMVideoCodecType_H264), metrics.width, metrics.height, metrics.extradata, metrics.extradataSize) { + videoStream = StreamContext(index: Int(streamIndex), codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect) + break + } + } else if codecId == FFMpegCodecIdHEVC { + if let videoFormat = FFMpegMediaFrameSourceContextHelpers.createFormatDescriptionFromHEVCCodecData(UInt32(kCMVideoCodecType_HEVC), metrics.width, metrics.height, metrics.extradata, metrics.extradataSize) { + videoStream = StreamContext(index: Int(streamIndex), codecContext: nil, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaPassthroughVideoFrameDecoder(videoFormat: videoFormat, rotationAngle: rotationAngle), rotationAngle: rotationAngle, aspect: aspect) + break } } } - for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_AUDIO) { - if let codec = avcodec_find_decoder(avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar.pointee.codec_id) { - if let codecContext = avcodec_alloc_context3(codec) { - if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 { - if avcodec_open2(codecContext, codec, nil) >= 0 { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 40000)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - audioStream = StreamContext(index: streamIndex, codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegAudioFrameDecoder(codecContext: codecContext), rotationAngle: 0.0, aspect: 1.0) - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) + for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeAudio) { + let streamIndex = streamIndexNumber.int32Value + let codecId = avFormatContext.codecId(atStreamIndex: streamIndex) + + var codec: FFMpegAVCodec? + + if codec == nil { + codec = FFMpegAVCodec.find(forId: codecId) + } + + if let codec = codec { + let codecContext = FFMpegAVCodecContext(codec: codec) + if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) { + if codecContext.open() { + let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000)) + let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase) + + let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale) + + audioStream = StreamContext(index: Int(streamIndex), codecContext: codecContext, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegAudioFrameDecoder(codecContext: codecContext), rotationAngle: 0.0, aspect: 1.0) + break } } } @@ -407,7 +374,9 @@ final class FFMpegMediaFrameSourceContext: NSObject { self.initializedState = InitializedState(avIoContext: avIoContext, avFormatContext: avFormatContext, audioStream: audioStream, videoStream: videoStream) if streamable { - self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .default), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + if self.tempFilePath == nil { + self.fetchedFullDataDisposable.set(fetchedMediaResource(postbox: postbox, reference: resourceReference, range: (0 ..< Int(Int32.max), .default), statsCategory: self.statsCategory ?? .generic, preferBackgroundReferenceRevalidation: streamable).start()) + } self.requestedCompleteFetch = true } } @@ -426,10 +395,10 @@ final class FFMpegMediaFrameSourceContext: NSObject { } let packet = FFMpegPacket() - if av_read_frame(initializedState.avFormatContext, &packet.packet) < 0 { - return nil - } else { + if initializedState.avFormatContext.readFrame(into: packet) { return packet + } else { + return nil } } @@ -457,24 +426,22 @@ final class FFMpegMediaFrameSourceContext: NSObject { while !self.readingError && ((videoTimestamp == nil || videoTimestamp!.isLess(than: until)) || (audioTimestamp == nil || audioTimestamp!.isLess(than: until))) { if let packet = self.readPacket() { - if let videoStream = initializedState.videoStream, Int(packet.packet.stream_index) == videoStream.index { + if let videoStream = initializedState.videoStream, Int(packet.streamIndex) == videoStream.index { let frame = videoFrameFromPacket(packet, videoStream: videoStream) frames.append(frame) if videoTimestamp == nil || videoTimestamp! < CMTimeGetSeconds(frame.pts) { videoTimestamp = CMTimeGetSeconds(frame.pts) } - } else if let audioStream = initializedState.audioStream, Int(packet.packet.stream_index) == audioStream.index { - let avNoPtsRawValue: UInt64 = 0x8000000000000000 - let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue) - let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts + } else if let audioStream = initializedState.audioStream, Int(packet.streamIndex) == audioStream.index { + let packetPts = packet.pts let pts = CMTimeMake(packetPts, audioStream.timebase.timescale) - let dts = CMTimeMake(packet.packet.dts, audioStream.timebase.timescale) + let dts = CMTimeMake(packet.dts, audioStream.timebase.timescale) let duration: CMTime - let frameDuration = packet.packet.duration + let frameDuration = packet.duration if frameDuration != 0 { duration = CMTimeMake(frameDuration * audioStream.timebase.value, audioStream.timebase.timescale) } else { @@ -522,7 +489,7 @@ final class FFMpegMediaFrameSourceContext: NSObject { for stream in [initializedState.videoStream, initializedState.audioStream] { if let stream = stream { let pts = CMTimeMakeWithSeconds(timestamp, stream.timebase.timescale) - av_seek_frame(initializedState.avFormatContext, Int32(stream.index), pts.value, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME) + initializedState.avFormatContext.seekFrame(forStreamIndex: Int32(stream.index), pts: pts.value) break } } @@ -543,12 +510,12 @@ final class FFMpegMediaFrameSourceContext: NSObject { if timestamp.isZero || initializedState.videoStream == nil { for _ in 0 ..< 24 { if let packet = self.readPacketInternal() { - if let videoStream = initializedState.videoStream, Int(packet.packet.stream_index) == videoStream.index { + if let videoStream = initializedState.videoStream, Int(packet.streamIndex) == videoStream.index { self.packetQueue.append(packet) let pts = CMTimeMake(packet.pts, videoStream.timebase.timescale) actualPts = pts break - } else if let audioStream = initializedState.audioStream, Int(packet.packet.stream_index) == audioStream.index { + } else if let audioStream = initializedState.audioStream, Int(packet.streamIndex) == audioStream.index { self.packetQueue.append(packet) let pts = CMTimeMake(packet.pts, audioStream.timebase.timescale) actualPts = pts @@ -564,14 +531,14 @@ final class FFMpegMediaFrameSourceContext: NSObject { var audioPackets: [FFMpegPacket] = [] while !self.readingError { if let packet = self.readPacket() { - if let videoStream = initializedState.videoStream, Int(packet.packet.stream_index) == videoStream.index { + if let videoStream = initializedState.videoStream, Int(packet.streamIndex) == videoStream.index { let frame = videoFrameFromPacket(packet, videoStream: videoStream) extraVideoFrames.append(frame) if CMTimeCompare(frame.dts, limitPts) >= 0 && CMTimeCompare(frame.pts, limitPts) >= 0 { break } - } else if let audioStream = initializedState.audioStream, Int(packet.packet.stream_index) == audioStream.index { + } else if let audioStream = initializedState.audioStream, Int(packet.streamIndex) == audioStream.index { audioPackets.append(packet) } } else { @@ -613,16 +580,14 @@ final class FFMpegMediaFrameSourceContext: NSObject { } private func videoFrameFromPacket(_ packet: FFMpegPacket, videoStream: StreamContext) -> MediaTrackDecodableFrame { - let avNoPtsRawValue: UInt64 = 0x8000000000000000 - let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue) - let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts + let packetPts = packet.pts let pts = CMTimeMake(packetPts, videoStream.timebase.timescale) - let dts = CMTimeMake(packet.packet.dts, videoStream.timebase.timescale) + let dts = CMTimeMake(packet.dts, videoStream.timebase.timescale) let duration: CMTime - let frameDuration = packet.packet.duration + let frameDuration = packet.duration if frameDuration != 0 { duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale) } else { diff --git a/TelegramUI/FFMpegMediaFrameSourceContextHelpers.swift b/TelegramUI/FFMpegMediaFrameSourceContextHelpers.swift index f05866ce91..3fd1681e8c 100644 --- a/TelegramUI/FFMpegMediaFrameSourceContextHelpers.swift +++ b/TelegramUI/FFMpegMediaFrameSourceContextHelpers.swift @@ -1,15 +1,10 @@ import Foundation import CoreMedia -import TelegramUIPrivateModule +import FFMpeg final class FFMpegMediaFrameSourceContextHelpers { static let registerFFMpegGlobals: Void = { - #if DEBUG - av_log_set_level(AV_LOG_ERROR) - #else - av_log_set_level(AV_LOG_QUIET) - #endif - av_register_all() + FFMpegGlobals.initializeGlobals() return }() @@ -95,37 +90,4 @@ final class FFMpegMediaFrameSourceContextHelpers { return formatDescription } - - static func streamIndices(formatContext: UnsafeMutablePointer, codecType: AVMediaType) -> [Int] { - var indices: [Int] = [] - for i in 0 ..< Int(formatContext.pointee.nb_streams) { - if codecType == formatContext.pointee.streams.advanced(by: i).pointee!.pointee.codecpar!.pointee.codec_type { - indices.append(i) - } - } - return indices - } - - static func streamFpsAndTimeBase(stream: UnsafePointer, defaultTimeBase: CMTime) -> (fps: CMTime, timebase: CMTime) { - let timebase: CMTime - var fps: CMTime - - if stream.pointee.time_base.den != 0 && stream.pointee.time_base.num != 0 { - timebase = CMTimeMake(Int64(stream.pointee.time_base.num), stream.pointee.time_base.den) - } else if stream.pointee.codec.pointee.time_base.den != 0 && stream.pointee.codec.pointee.time_base.num != 0 { - timebase = CMTimeMake(Int64(stream.pointee.codec.pointee.time_base.num), stream.pointee.codec.pointee.time_base.den) - } else { - timebase = defaultTimeBase - } - - if stream.pointee.avg_frame_rate.den != 0 && stream.pointee.avg_frame_rate.num != 0 { - fps = CMTimeMake(Int64(stream.pointee.avg_frame_rate.num), stream.pointee.avg_frame_rate.den) - } else if stream.pointee.r_frame_rate.den != 0 && stream.pointee.r_frame_rate.num != 0 { - fps = CMTimeMake(Int64(stream.pointee.r_frame_rate.num), stream.pointee.r_frame_rate.den) - } else { - fps = CMTimeMake(1, 24) - } - - return (fps, timebase) - } } diff --git a/TelegramUI/FFMpegMediaPassthroughVideoFrameDecoder.swift b/TelegramUI/FFMpegMediaPassthroughVideoFrameDecoder.swift index 2ae5f11ede..f4291ba09f 100644 --- a/TelegramUI/FFMpegMediaPassthroughVideoFrameDecoder.swift +++ b/TelegramUI/FFMpegMediaPassthroughVideoFrameDecoder.swift @@ -13,16 +13,16 @@ final class FFMpegMediaPassthroughVideoFrameDecoder: MediaTrackFrameDecoder { func decode(frame: MediaTrackDecodableFrame) -> MediaTrackFrame? { var blockBuffer: CMBlockBuffer? - let bytes = malloc(Int(frame.packet.packet.size))! - memcpy(bytes, frame.packet.packet.data, Int(frame.packet.packet.size)) - guard CMBlockBufferCreateWithMemoryBlock(nil, bytes, Int(frame.packet.packet.size), nil, nil, 0, Int(frame.packet.packet.size), 0, &blockBuffer) == noErr else { + let bytes = malloc(Int(frame.packet.size))! + memcpy(bytes, frame.packet.data, Int(frame.packet.size)) + guard CMBlockBufferCreateWithMemoryBlock(nil, bytes, Int(frame.packet.size), nil, nil, 0, Int(frame.packet.size), 0, &blockBuffer) == noErr else { free(bytes) return nil } var timingInfo = CMSampleTimingInfo(duration: frame.duration, presentationTimeStamp: frame.pts, decodeTimeStamp: frame.dts) var sampleBuffer: CMSampleBuffer? - var sampleSize = Int(frame.packet.packet.size) + var sampleSize = Int(frame.packet.size) guard CMSampleBufferCreate(nil, blockBuffer, true, nil, nil, self.videoFormat, 1, 1, &timingInfo, 1, &sampleSize, &sampleBuffer) == noErr else { return nil } diff --git a/TelegramUI/FFMpegMediaVideoFrameDecoder.swift b/TelegramUI/FFMpegMediaVideoFrameDecoder.swift index 2d965ff3fa..b1b49df020 100644 --- a/TelegramUI/FFMpegMediaVideoFrameDecoder.swift +++ b/TelegramUI/FFMpegMediaVideoFrameDecoder.swift @@ -1,22 +1,22 @@ -import TelegramUIPrivateModule import CoreMedia import Accelerate +import FFMpeg private let bufferCount = 32 final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { - private let codecContext: UnsafeMutablePointer + private let codecContext: FFMpegAVCodecContext - private let videoFrame: UnsafeMutablePointer + private let videoFrame: FFMpegAVFrame private var resetDecoderOnNextFrame = true private var pixelBufferPool: CVPixelBufferPool? private var delayedFrames: [MediaTrackFrame] = [] - init(codecContext: UnsafeMutablePointer) { + init(codecContext: FFMpegAVCodecContext) { self.codecContext = codecContext - self.videoFrame = av_frame_alloc() + self.videoFrame = FFMpegAVFrame() /*var sourcePixelBufferOptions: [String: Any] = [:] sourcePixelBufferOptions[kCVPixelBufferPixelFormatTypeKey as String] = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange as NSNumber @@ -40,13 +40,6 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { self.pixelBufferPool = pixelBufferPool*/ } - deinit { - av_frame_unref(self.videoFrame) - - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } - func decodeInternal(frame: MediaTrackDecodableFrame) { } @@ -56,11 +49,10 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } func decode(frame: MediaTrackDecodableFrame, ptsOffset: CMTime?) -> MediaTrackFrame? { - var status = frame.packet.sendToDecoder(self.codecContext) + var status = frame.packet.send(toDecoder: self.codecContext) if status == 0 { - status = avcodec_receive_frame(self.codecContext, self.videoFrame) - if status == 0 { - var pts = CMTimeMake(self.videoFrame.pointee.pts, frame.pts.timescale) + if self.codecContext.receive(into: self.videoFrame) { + var pts = CMTimeMake(self.videoFrame.pts, frame.pts.timescale) if let ptsOffset = ptsOffset { pts = CMTimeAdd(pts, ptsOffset) } @@ -87,11 +79,11 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } } - private func convertVideoFrame(_ frame: UnsafeMutablePointer, pts: CMTime, dts: CMTime, duration: CMTime) -> MediaTrackFrame? { - if frame.pointee.data.0 == nil { + private func convertVideoFrame(_ frame: FFMpegAVFrame, pts: CMTime, dts: CMTime, duration: CMTime) -> MediaTrackFrame? { + if frame.data[0] == nil { return nil } - if frame.pointee.linesize.1 != frame.pointee.linesize.2 { + if frame.lineSize[1] != frame.lineSize[2] { return nil } @@ -107,15 +99,15 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { let ioSurfaceProperties = NSMutableDictionary() ioSurfaceProperties["IOSurfaceIsGlobal"] = true as NSNumber - var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.pointee.linesize.0 as NSNumber] + var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.lineSize[0] as NSNumber] /*if #available(iOSApplicationExtension 9.0, *) { options[kCVPixelBufferOpenGLESTextureCacheCompatibilityKey as String] = true as NSNumber }*/ options[kCVPixelBufferIOSurfacePropertiesKey as String] = ioSurfaceProperties CVPixelBufferCreate(kCFAllocatorDefault, - Int(frame.pointee.width), - Int(frame.pointee.height), + Int(frame.width), + Int(frame.height), kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, options as CFDictionary, &pixelBufferRef) @@ -125,7 +117,7 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { return nil } - let srcPlaneSize = Int(frame.pointee.linesize.1) * Int(frame.pointee.height / 2) + let srcPlaneSize = Int(frame.lineSize[1]) * Int(frame.height / 2) let dstPlaneSize = srcPlaneSize * 2 let dstPlane = malloc(dstPlaneSize)!.assumingMemoryBound(to: UInt8.self) @@ -134,8 +126,8 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } for i in 0 ..< srcPlaneSize { - dstPlane[2 * i] = frame.pointee.data.1![i] - dstPlane[2 * i + 1] = frame.pointee.data.2![i] + dstPlane[2 * i] = frame.data[1]![i] + dstPlane[2 * i + 1] = frame.data[2]![i] } let status = CVPixelBufferLockBaseAddress(pixelBuffer, []) @@ -148,13 +140,13 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { let bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1) var base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)! - if bytePerRowY == frame.pointee.linesize.0 { - memcpy(base, frame.pointee.data.0!, bytePerRowY * Int(frame.pointee.height)) + if bytePerRowY == frame.lineSize[0] { + memcpy(base, frame.data[0]!, bytePerRowY * Int(frame.height)) } else { var dest = base - var src = frame.pointee.data.0! - let linesize = Int(frame.pointee.linesize.0) - for _ in 0 ..< Int(frame.pointee.height) { + var src = frame.data[0]! + let linesize = Int(frame.lineSize[0]) + for _ in 0 ..< Int(frame.height) { memcpy(dest, src, linesize) dest = dest.advanced(by: bytePerRowY) src = src.advanced(by: linesize) @@ -162,13 +154,13 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } base = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)! - if bytesPerRowUV == frame.pointee.linesize.1 * 2 { - memcpy(base, dstPlane, bytesPerRowUV * Int(frame.pointee.height) / 2) + if bytesPerRowUV == frame.lineSize[1] * 2 { + memcpy(base, dstPlane, bytesPerRowUV * Int(frame.height) / 2) } else { var dest = base var src = dstPlane - let linesize = Int(frame.pointee.linesize.1) * 2 - for _ in 0 ..< Int(frame.pointee.height) / 2 { + let linesize = Int(frame.lineSize[1]) * 2 + for _ in 0 ..< Int(frame.height) / 2 { memcpy(dest, src, linesize) dest = dest.advanced(by: bytesPerRowUV) src = src.advanced(by: linesize) @@ -229,7 +221,7 @@ final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder { } func reset() { - avcodec_flush_buffers(self.codecContext) + self.codecContext.flushBuffers() self.resetDecoderOnNextFrame = true } } diff --git a/TelegramUI/FFMpegPacket.swift b/TelegramUI/FFMpegPacket.swift deleted file mode 100644 index 56e4c40baa..0000000000 --- a/TelegramUI/FFMpegPacket.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation -import TelegramUIPrivateModule - -final class FFMpegPacket { - var packet = AVPacket() - - init() { - av_init_packet(&self.packet) - } - - deinit { - av_packet_unref(&self.packet) - } - - var pts: Int64 { - let avNoPtsRawValue: UInt64 = 0x8000000000000000 - let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue) - let packetPts = self.packet.pts == avNoPtsValue ? self.packet.dts : self.packet.pts - - return packetPts - } - - func sendToDecoder(_ codecContext: UnsafeMutablePointer) -> Int32 { - return avcodec_send_packet(codecContext, &self.packet) - } -} diff --git a/TelegramUI/FeedGroupingControllerNode.swift b/TelegramUI/FeedGroupingControllerNode.swift index a2db9c03f6..36eeb38ece 100644 --- a/TelegramUI/FeedGroupingControllerNode.swift +++ b/TelegramUI/FeedGroupingControllerNode.swift @@ -80,7 +80,7 @@ private enum FeedGroupingSection: ItemListSectionId { private enum FeedGroupingEntry: ItemListNodeEntry { case groupHeader(PresentationTheme, String) - case peer(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Int, Peer, Bool) + case peer(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Int, Peer, Bool) case ungroup(PresentationTheme, String) var section: ItemListSectionId { @@ -96,7 +96,7 @@ private enum FeedGroupingEntry: ItemListNodeEntry { switch self { case .groupHeader: return .index(0) - case let .peer(_, _, _, _, peer, _): + case let .peer(_, _, _, _, _, peer, _): return .peer(peer.id) case .ungroup: return .index(1) @@ -111,8 +111,8 @@ private enum FeedGroupingEntry: ItemListNodeEntry { } else { return false } - case let .peer(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsPeer, lhsValue): - if case let .peer(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsPeer, rhsValue) = rhs { + case let .peer(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsIndex, lhsPeer, lhsValue): + if case let .peer(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsIndex, rhsPeer, rhsValue) = rhs { if lhsTheme !== rhsTheme { return false } @@ -122,6 +122,9 @@ private enum FeedGroupingEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if lhsIndex != rhsIndex { return false } @@ -148,11 +151,11 @@ private enum FeedGroupingEntry: ItemListNodeEntry { switch lhs { case .groupHeader: return true - case let .peer(_, _, _, index, _, _): + case let .peer(_, _, _, _, index, _, _): switch rhs { case .groupHeader: return false - case let .peer(_, _, _, rhsIndex, _, _): + case let .peer(_, _, _, _, rhsIndex, _, _): return index < rhsIndex default: return true @@ -166,8 +169,8 @@ private enum FeedGroupingEntry: ItemListNodeEntry { switch self { case let .groupHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) - case let .peer(theme, strings, dateTimeFormat, _, peer, value): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: value, style: .standard), enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in + case let .peer(theme, strings, dateTimeFormat, nameDisplayOrder, _, peer, value): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: value, style: .standard), enabled: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in arguments.togglePeer(peer, value) }) case let .ungroup(theme, text): @@ -200,17 +203,19 @@ private final class FeedGroupingState { let theme: PresentationTheme let strings: PresentationStrings let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder let peers: [FeedGroupingPeerState] - init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, peers: [FeedGroupingPeerState]) { + init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, peers: [FeedGroupingPeerState]) { self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.peers = peers } func withUpdatedPeers(_ peers: [FeedGroupingPeerState]) -> FeedGroupingState { - return FeedGroupingState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, peers: peers) + return FeedGroupingState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, peers: peers) } } @@ -220,7 +225,7 @@ private func entriesStateFromState(_ state: FeedGroupingState) -> FeedGroupingEn entries.append(.groupHeader(state.theme, "GROUP CHANNELS")) var index = 0 for peer in state.peers { - entries.append(.peer(state.theme, state.strings, state.dateTimeFormat, index, peer.peer, peer.included)) + entries.append(.peer(state.theme, state.strings, state.dateTimeFormat, state.nameDisplayOrder, index, peer.peer, peer.included)) index += 1 } entries.append(.ungroup(state.theme, "Ungroup All Channels")) @@ -284,7 +289,7 @@ final class FeedGroupingControllerNode: ViewControllerTracingNode { self.blockingOverlay = ASDisplayNode() self.blockingOverlay.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor - self._stateValue = FeedGroupingState(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, peers: []) + self._stateValue = FeedGroupingState(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, peers: []) self.statePromise.set(.single(self._stateValue)) super.init() diff --git a/TelegramUI/FetchVideoThumbnail.swift b/TelegramUI/FetchVideoThumbnail.swift index c15a800039..b206c9be41 100644 --- a/TelegramUI/FetchVideoThumbnail.swift +++ b/TelegramUI/FetchVideoThumbnail.swift @@ -7,7 +7,7 @@ import TelegramUIPrivateModule import Display import UIKit import VideoToolbox - +/* private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer?, bufferSize: Int32) -> Int32 { guard let buffer = buffer else { return 0 @@ -404,3 +404,4 @@ func streamingVideoThumbnail(postbox: Postbox, fileReference: FileMediaReference } } } +*/ diff --git a/TelegramUI/GalleryController.swift b/TelegramUI/GalleryController.swift index 1735305e19..4422cf80d3 100644 --- a/TelegramUI/GalleryController.swift +++ b/TelegramUI/GalleryController.swift @@ -131,7 +131,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String { return message.text } -func galleryItemForEntry(account: Account, presentationData: PresentationData, entry: MessageHistoryEntry, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? { +func galleryItemForEntry(account: Account, presentationData: PresentationData, entry: MessageHistoryEntry, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }) -> GalleryItem? { switch entry { case let .MessageEntry(message, _, location, _): if let (media, mediaImage) = mediaForMessage(message: message) { @@ -141,10 +141,10 @@ func galleryItemForEntry(account: Account, presentationData: PresentationData, e if file.isVideo { let content: UniversalVideoContent if file.isAnimated { - content = NativeVideoContent(id: .message(message.id, message.stableId + 1, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: false, loopVideo: true, enableSound: false) + content = NativeVideoContent(id: .message(message.id, message.stableId + 1, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: false, loopVideo: true, enableSound: false, tempFilePath: tempFilePath) } else { if true || (file.mimeType == "video/mpeg4" || file.mimeType == "video/mov" || file.mimeType == "video/mp4") { - content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, loopVideo: loopVideos) + content = NativeVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: true, loopVideo: loopVideos, tempFilePath: tempFilePath) } else { content = PlatformVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), streamVideo: streamVideos, loopVideo: loopVideos) } @@ -161,7 +161,7 @@ func galleryItemForEntry(account: Account, presentationData: PresentationData, e return UniversalVideoGalleryItem(account: account, presentationData: presentationData, content: content, originData: GalleryItemOriginData(title: message.author?.displayTitle, timestamp: message.timestamp), indexData: location.flatMap { GalleryItemIndexData(position: Int32($0.index), totalCount: Int32($0.count)) }, contentInfo: .message(message), caption: caption, hideControls: hideControls, playbackCompleted: playbackCompleted, performAction: performAction, openActionOptions: openActionOptions) } else { if file.mimeType.hasPrefix("image/") && file.mimeType != "image/gif" { - if file.size == nil || file.size! < 5 * 1024 * 1024 { + if file.size == nil || file.size! < 2 * 1024 * 1024 { return ChatImageGalleryItem(account: account, presentationData: presentationData, message: message, location: location, performAction: performAction, openActionOptions: openActionOptions) } else { return ChatDocumentGalleryItem(account: account, presentationData: presentationData, message: message, location: location) diff --git a/TelegramUI/GroupAdminsController.swift b/TelegramUI/GroupAdminsController.swift index 3cc3ba0aa2..b3909fe23a 100644 --- a/TelegramUI/GroupAdminsController.swift +++ b/TelegramUI/GroupAdminsController.swift @@ -56,7 +56,7 @@ private enum GroupAdminsEntryStableId: Hashable { private enum GroupAdminsEntry: ItemListNodeEntry { case allAdmins(PresentationTheme, String, Bool) case allAdminsInfo(PresentationTheme, String) - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, String, Bool, Bool) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, String, Bool, Bool) var section: ItemListSectionId { switch self { @@ -73,7 +73,7 @@ private enum GroupAdminsEntry: ItemListNodeEntry { return .index(0) case .allAdminsInfo: return .index(1) - case let .peerItem(_, _, _, _, peer, _, _, _): + case let .peerItem(_, _, _, _, _, peer, _, _, _): return .peer(peer.id) } } @@ -92,8 +92,8 @@ private enum GroupAdminsEntry: ItemListNodeEntry { } else { return false } - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsLabel, lhsToggled, lhsEnabled): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsLabel, rhsToggled, rhsEnabled) = rhs { + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsLabel, lhsToggled, lhsEnabled): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsLabel, rhsToggled, rhsEnabled) = rhs { if lhsIndex != rhsIndex { return false } @@ -106,6 +106,9 @@ private enum GroupAdminsEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if !lhsPeer.isEqual(rhsPeer) { return false } @@ -136,9 +139,9 @@ private enum GroupAdminsEntry: ItemListNodeEntry { default: return true } - case let .peerItem(index, _, _, _, _, _, _, _): + case let .peerItem(index, _, _, _, _, _, _, _, _): switch rhs { - case let .peerItem(rhsIndex, _, _, _, _, _, _, _): + case let .peerItem(rhsIndex, _, _, _, _, _, _, _, _): return index < rhsIndex case .allAdmins, .allAdminsInfo: return false @@ -154,8 +157,8 @@ private enum GroupAdminsEntry: ItemListNodeEntry { }) case let .allAdminsInfo(theme, text): return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section) - case let .peerItem(_, theme, strings, dateTimeFormat, peer, label, toggled, enabled): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: toggled, style: .standard), enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in + case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, label, toggled, enabled): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: ItemListPeerItemSwitch(value: toggled, style: .standard), enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, toggleUpdated: { value in arguments.updatePeerIsAdmin(peer.id, value) }) } @@ -276,7 +279,7 @@ private func groupAdminsControllerEntries(account: Account, presentationData: Pr } } } - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, label, isAdmin, isEnabled)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, label, isAdmin, isEnabled)) index += 1 } } diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 96201232dc..e65db24254 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -149,7 +149,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { case membersAdmins(PresentationTheme, String, String) case membersBlacklist(PresentationTheme, String, String) case addMember(PresentationTheme, String, editing: Bool) - case member(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, index: Int, peerId: PeerId, peer: Peer, participant: RenderedChannelParticipant?, presence: PeerPresence?, memberStatus: GroupInfoMemberStatus, editing: ItemListPeerItemEditing, revealActions: [ParticipantRevealAction], enabled: Bool) + case member(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, index: Int, peerId: PeerId, peer: Peer, participant: RenderedChannelParticipant?, presence: PeerPresence?, memberStatus: GroupInfoMemberStatus, editing: ItemListPeerItemEditing, revealActions: [ParticipantRevealAction], enabled: Bool) case convertToSupergroup(PresentationTheme, String) case leave(PresentationTheme, String) @@ -343,8 +343,8 @@ private enum GroupInfoEntry: ItemListNodeEntry { } else { return false } - case let .member(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsIndex, lhsPeerId, lhsPeer, lhsParticipant, lhsPresence, lhsMemberStatus, lhsEditing, lhsActions, lhsEnabled): - if case let .member(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsIndex, rhsPeerId, rhsPeer, rhsParticipant, rhsPresence, rhsMemberStatus, rhsEditing, rhsActions, rhsEnabled) = rhs { + case let .member(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameDisplayOrder, lhsIndex, lhsPeerId, lhsPeer, lhsParticipant, lhsPresence, lhsMemberStatus, lhsEditing, lhsActions, lhsEnabled): + if case let .member(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameDisplayOrder, rhsIndex, rhsPeerId, rhsPeer, rhsParticipant, rhsPresence, rhsMemberStatus, rhsEditing, rhsActions, rhsEnabled) = rhs { if lhsTheme !== rhsTheme { return false } @@ -354,6 +354,9 @@ private enum GroupInfoEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameDisplayOrder != rhsNameDisplayOrder { + return false + } if lhsIndex != rhsIndex { return false } @@ -394,7 +397,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { var stableId: GroupEntryStableId { switch self { - case let .member(_, _, _, _, peerId, _, _, _, _, _, _, _): + case let .member(_, _, _, _, _, peerId, _, _, _, _, _, _, _): return .peer(peerId) default: return .index(self.sortIndex) @@ -439,7 +442,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { return 16 case .addMember: return 17 - case let .member(_, _, _, index, _, _, _, _, _, _, _, _): + case let .member(_, _, _, _, index, _, _, _, _, _, _, _, _): return 20 + index case .convertToSupergroup: return 100000 @@ -526,7 +529,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { return ItemListDisclosureItem(theme: theme, title: title, label: text, sectionId: self.section, style: .blocks, action: { arguments.pushController(channelBlacklistController(account: arguments.account, peerId: arguments.peerId)) }) - case let .member(theme, strings, dateTimeFormat, _, _, peer, participant, presence, memberStatus, editing, actions, enabled): + case let .member(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, peer, participant, presence, memberStatus, editing, actions, enabled): let label: String? switch memberStatus { case .admin: @@ -551,7 +554,7 @@ private enum GroupInfoEntry: ItemListNodeEntry { } })) } - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, sectionId: self.section, action: { + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: presence, text: .presence, label: label == nil ? .none : .text(label!), editing: editing, revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: enabled, sectionId: self.section, action: { if let infoController = peerInfoController(account: arguments.account, peer: peer) { arguments.pushController(infoController) } @@ -957,7 +960,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa } else { memberStatus = .member } - entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index: i, peerId: peer.id, peer: peer, participant: nil, presence: peerPresences[peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: canRemoveParticipant(account: account, isAdmin: canEditMembers, participantId: peer.id, invitedBy: sortedParticipants[i].invitedBy), editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == peer.id), revealActions: [ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)], enabled: !disabledPeerIds.contains(peer.id))) + entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: peer.id, peer: peer, participant: nil, presence: peerPresences[peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: canRemoveParticipant(account: account, isAdmin: canEditMembers, participantId: peer.id, invitedBy: sortedParticipants[i].invitedBy), editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == peer.id), revealActions: [ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)], enabled: !disabledPeerIds.contains(peer.id))) } } } else if let channel = view.peers[view.peerId] as? TelegramChannel, let cachedChannelData = view.cachedData as? CachedChannelData, let memberCount = cachedChannelData.participantsSummary.memberCount { @@ -1079,7 +1082,7 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa peerActions.append(ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)) } - entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, index: i, peerId: participant.peer.id, peer: participant.peer, participant: participant, presence: participant.presences[participant.peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: !peerActions.isEmpty, editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == participant.peer.id), revealActions: peerActions, enabled: !disabledPeerIds.contains(participant.peer.id))) + entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: participant.peer.id, peer: participant.peer, participant: participant, presence: participant.presences[participant.peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: !peerActions.isEmpty, editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == participant.peer.id), revealActions: peerActions, enabled: !disabledPeerIds.contains(participant.peer.id))) } } diff --git a/TelegramUI/GroupsInCommonController.swift b/TelegramUI/GroupsInCommonController.swift index a6fdc213c5..b1861eab8d 100644 --- a/TelegramUI/GroupsInCommonController.swift +++ b/TelegramUI/GroupsInCommonController.swift @@ -42,7 +42,7 @@ private enum GroupsInCommonEntryStableId: Hashable { } private enum GroupsInCommonEntry: ItemListNodeEntry { - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer) var section: ItemListSectionId { switch self { @@ -53,15 +53,15 @@ private enum GroupsInCommonEntry: ItemListNodeEntry { var stableId: GroupsInCommonEntryStableId { switch self { - case let .peerItem(_, _, _, _, peer): + case let .peerItem(_, _, _, _, _, peer): return .peer(peer.id) } } static func ==(lhs: GroupsInCommonEntry, rhs: GroupsInCommonEntry) -> Bool { switch lhs { - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer) = rhs { + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer) = rhs { if lhsIndex != rhsIndex { return false } @@ -77,6 +77,9 @@ private enum GroupsInCommonEntry: ItemListNodeEntry { if !lhsPeer.isEqual(rhsPeer) { return false } + if lhsNameOrder != rhsNameOrder { + return false + } return true } else { return false @@ -86,9 +89,9 @@ private enum GroupsInCommonEntry: ItemListNodeEntry { static func <(lhs: GroupsInCommonEntry, rhs: GroupsInCommonEntry) -> Bool { switch lhs { - case let .peerItem(index, _, _, _, _): + case let .peerItem(index, _, _, _, _, _): switch rhs { - case let .peerItem(rhsIndex, _, _, _, _): + case let .peerItem(rhsIndex, _, _, _, _, _): return index < rhsIndex } } @@ -96,8 +99,8 @@ private enum GroupsInCommonEntry: ItemListNodeEntry { func item(_ arguments: GroupsInCommonControllerArguments) -> ListViewItem { switch self { - case let .peerItem(_, theme, strings, dateTimeFormat, peer): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: { + case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: { arguments.openPeer(peer.id) }, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in @@ -118,7 +121,7 @@ private func groupsInCommonControllerEntries(presentationData: PresentationData, if let peers = peers { var index: Int32 = 0 for peer in peers { - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer)) index += 1 } } diff --git a/TelegramUI/ItemListPeerItem.swift b/TelegramUI/ItemListPeerItem.swift index e2e8c59533..e0ba8211f3 100644 --- a/TelegramUI/ItemListPeerItem.swift +++ b/TelegramUI/ItemListPeerItem.swift @@ -63,6 +63,7 @@ final class ItemListPeerItem: ListViewItem, ItemListItem { let theme: PresentationTheme let strings: PresentationStrings let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder let account: Account let peer: Peer let aliasHandling: ItemListPeerItemAliasHandling @@ -82,10 +83,11 @@ final class ItemListPeerItem: ListViewItem, ItemListItem { let hasTopStripe: Bool let hasTopGroupInset: Bool - init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, account: Account, peer: Peer, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true) { + init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, account: Account, peer: Peer, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true) { self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.account = account self.peer = peer self.aliasHandling = aliasHandling @@ -332,9 +334,16 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { } else if let user = item.peer as? TelegramUser { if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty { let string = NSMutableAttributedString() - string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor)) - string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor)) - string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: titleColor)) + switch item.nameDisplayOrder { + case .firstLast: + string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor)) + string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor)) + string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: titleColor)) + case .lastFirst: + string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: titleColor)) + string.append(NSAttributedString(string: " ", font: titleFont, textColor: titleColor)) + string.append(NSAttributedString(string: firstName, font: titleFont, textColor: titleColor)) + } titleAttributedString = string } else if let firstName = user.firstName, !firstName.isEmpty { titleAttributedString = NSAttributedString(string: firstName, font: titleBoldFont, textColor: titleColor) diff --git a/TelegramUI/ItemListWebsiteItem.swift b/TelegramUI/ItemListWebsiteItem.swift index d59c38a476..3ff62be725 100644 --- a/TelegramUI/ItemListWebsiteItem.swift +++ b/TelegramUI/ItemListWebsiteItem.swift @@ -24,6 +24,7 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { let theme: PresentationTheme let strings: PresentationStrings let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder let website: WebAuthorization let peer: Peer? let enabled: Bool @@ -33,10 +34,11 @@ final class ItemListWebsiteItem: ListViewItem, ItemListItem { let setSessionIdWithRevealedOptions: (Int64?, Int64?) -> Void let removeSession: (Int64) -> Void - init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool, sectionId: ItemListSectionId, setSessionIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, removeSession: @escaping (Int64) -> Void) { self.theme = theme self.strings = strings self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder self.website = website self.peer = peer self.enabled = enabled @@ -174,7 +176,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { let rightInset: CGFloat = params.rightInset if let user = item.peer as? TelegramUser { - titleAttributedString = NSAttributedString(string: user.displayTitle, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor) + titleAttributedString = NSAttributedString(string: user.displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder), font: titleFont, textColor: item.theme.list.itemPrimaryTextColor) } var appString = "" diff --git a/TelegramUI/ListMessageFileItemNode.swift b/TelegramUI/ListMessageFileItemNode.swift index 577b76ca12..500e69af22 100644 --- a/TelegramUI/ListMessageFileItemNode.swift +++ b/TelegramUI/ListMessageFileItemNode.swift @@ -144,6 +144,7 @@ final class ListMessageFileItemNode: ListMessageNode { private let titleNode: TextNode private let descriptionNode: TextNode + private let descriptionProgressNode: ImmediateTextNode private let extensionIconNode: ASImageNode private let extensionIconText: TextNode @@ -171,6 +172,7 @@ final class ListMessageFileItemNode: ListMessageNode { private var appliedItem: ListMessageItem? private var layoutParams: ListViewItemLayoutParams? + private var contentSizeValue: CGSize? private var currentLeftOffet: CGFloat = 0.0 override var canBeLongTapped: Bool { @@ -191,6 +193,10 @@ final class ListMessageFileItemNode: ListMessageNode { self.descriptionNode = TextNode() self.descriptionNode.isUserInteractionEnabled = false + self.descriptionProgressNode = ImmediateTextNode() + self.descriptionProgressNode.isUserInteractionEnabled = false + self.descriptionProgressNode.maximumNumberOfLines = 1 + self.extensionIconNode = ASImageNode() self.extensionIconNode.isLayerBacked = true self.extensionIconNode.displaysAsynchronously = false @@ -223,6 +229,7 @@ final class ListMessageFileItemNode: ListMessageNode { self.addSubnode(self.separatorNode) self.addSubnode(self.titleNode) self.addSubnode(self.descriptionNode) + self.addSubnode(self.descriptionProgressNode) self.addSubnode(self.extensionIconNode) self.addSubnode(self.extensionIconText) self.addSubnode(self.statusNode) @@ -492,6 +499,7 @@ final class ListMessageFileItemNode: ListMessageNode { strongSelf.account = item.account strongSelf.appliedItem = item strongSelf.layoutParams = params + strongSelf.contentSizeValue = nodeLayout.contentSize strongSelf.currentLeftOffet = leftOffset if let _ = updatedTheme { @@ -604,96 +612,105 @@ final class ListMessageFileItemNode: ListMessageNode { if let updatedStatusSignal = updatedStatusSignal { strongSelf.statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak strongSelf] fileStatus in - let status = fileStatus.mediaStatus - displayLinkDispatcher.dispatch { - if let strongSelf = strongSelf { - strongSelf.fetchStatus = fileStatus.fetchStatus - strongSelf.resourceStatus = status - var musicIsPlaying: Bool? - var statusState: RadialStatusNodeState = .none - if !isAudio { - if let layoutParams = strongSelf.layoutParams { - strongSelf.updateProgressFrame(size: nodeLayout.contentSize, leftInset: layoutParams.leftInset, rightInset: layoutParams.rightInset, transition: .immediate) - } - } else { - switch fileStatus.fetchStatus { - case let .Fetching(isActive, progress): - var adjustedProgress = progress - if isActive { - adjustedProgress = max(adjustedProgress, 0.027) - } - statusState = .cloudProgress(color: item.theme.list.itemAccentColor, strokeBackgroundColor: item.theme.list.itemAccentColor.withAlphaComponent(0.5), lineWidth: 2.0, value: CGFloat(adjustedProgress)) - case .Local: - break - case .Remote: - if let image = PresentationResourcesItemList.cloudFetchIcon(item.theme) { - statusState = .customIcon(image) - } - } - strongSelf.statusNode.transitionToState(statusState, completion: {}) - strongSelf.statusButtonNode.isUserInteractionEnabled = statusState != .none - - switch status { - case let .fetchStatus(fetchStatus): - switch fetchStatus { - case let .Fetching(isActive, progress): - var adjustedProgress = progress - if isActive { - adjustedProgress = max(adjustedProgress, 0.027) - } - strongSelf.progressNode.state = .Fetching(progress: adjustedProgress) - case .Local: - if isAudio { - strongSelf.progressNode.state = .Play - } else { - strongSelf.progressNode.state = .Icon - } - case .Remote: - if isAudio { - strongSelf.progressNode.state = .Play - } else { - strongSelf.progressNode.state = .Remote - } - } - case let .playbackStatus(playbackStatus): - switch playbackStatus { - case .playing: - musicIsPlaying = true - strongSelf.progressNode.state = .Pause - case .paused: - musicIsPlaying = false - strongSelf.progressNode.state = .Play - } - } - } - if let musicIsPlaying = musicIsPlaying { - if strongSelf.playbackOverlayNode == nil { - let playbackOverlayNode = ListMessagePlaybackOverlayNode() - playbackOverlayNode.frame = strongSelf.iconImageNode.frame - strongSelf.playbackOverlayNode = playbackOverlayNode - strongSelf.addSubnode(playbackOverlayNode) - } - strongSelf.playbackOverlayNode?.isPlaying = musicIsPlaying - } else if let playbackOverlayNode = strongSelf.playbackOverlayNode { - strongSelf.playbackOverlayNode = nil - playbackOverlayNode.removeFromSupernode() - } - } + if let strongSelf = strongSelf { + strongSelf.fetchStatus = fileStatus.fetchStatus + strongSelf.resourceStatus = fileStatus.mediaStatus + strongSelf.updateStatus(transition: .immediate) } })) } - strongSelf.updateProgressFrame(size: CGSize(width: params.width, height: 52.0), leftInset: params.leftInset, rightInset: params.rightInset, transition: transition) transition.updateFrame(node: strongSelf.downloadStatusIconNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 31.0), size: CGSize(width: 11.0, height: 11.0))) if let updatedFetchControls = updatedFetchControls { let _ = strongSelf.fetchControls.swap(updatedFetchControls) } + + strongSelf.updateStatus(transition: transition) } }) } } + private func updateStatus(transition: ContainedViewLayoutTransition) { + guard let item = self.item, let media = self.currentMedia, let fetchStatus = self.fetchStatus, let status = self.resourceStatus, let layoutParams = self.layoutParams, let contentSize = self.contentSizeValue else { + return + } + + var isAudio = false + if let file = media as? TelegramMediaFile { + isAudio = file.isMusic || file.isVoice + } + + var musicIsPlaying: Bool? + var statusState: RadialStatusNodeState = .none + if !isAudio { + self.updateProgressFrame(size: contentSize, leftInset: layoutParams.leftInset, rightInset: layoutParams.rightInset, transition: .immediate) + } else { + switch fetchStatus { + case let .Fetching(isActive, progress): + var adjustedProgress = progress + if isActive { + adjustedProgress = max(adjustedProgress, 0.027) + } + statusState = .cloudProgress(color: item.theme.list.itemAccentColor, strokeBackgroundColor: item.theme.list.itemAccentColor.withAlphaComponent(0.5), lineWidth: 2.0, value: CGFloat(adjustedProgress)) + case .Local: + break + case .Remote: + if let image = PresentationResourcesItemList.cloudFetchIcon(item.theme) { + statusState = .customIcon(image) + } + } + self.statusNode.transitionToState(statusState, completion: {}) + self.statusButtonNode.isUserInteractionEnabled = statusState != .none + + switch status { + case let .fetchStatus(fetchStatus): + switch fetchStatus { + case let .Fetching(isActive, progress): + var adjustedProgress = progress + if isActive { + adjustedProgress = max(adjustedProgress, 0.027) + } + self.progressNode.state = .Fetching(progress: adjustedProgress) + case .Local: + if isAudio { + self.progressNode.state = .Play + } else { + self.progressNode.state = .Icon + } + case .Remote: + if isAudio { + self.progressNode.state = .Play + } else { + self.progressNode.state = .Remote + } + } + case let .playbackStatus(playbackStatus): + switch playbackStatus { + case .playing: + musicIsPlaying = true + self.progressNode.state = .Pause + case .paused: + musicIsPlaying = false + self.progressNode.state = .Play + } + } + } + if let musicIsPlaying = musicIsPlaying { + if self.playbackOverlayNode == nil { + let playbackOverlayNode = ListMessagePlaybackOverlayNode() + playbackOverlayNode.frame = self.iconImageNode.frame + self.playbackOverlayNode = playbackOverlayNode + self.addSubnode(playbackOverlayNode) + } + self.playbackOverlayNode?.isPlaying = musicIsPlaying + } else if let playbackOverlayNode = self.playbackOverlayNode { + self.playbackOverlayNode = nil + playbackOverlayNode.removeFromSupernode() + } + } + override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { super.setHighlighted(highlighted, at: point, animated: animated) @@ -742,9 +759,13 @@ final class ListMessageFileItemNode: ListMessageNode { } private func updateProgressFrame(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + guard let item = self.appliedItem else { + return + } var descriptionOffset: CGFloat = 0.0 - if let resourceStatus = self.resourceStatus, let item = self.appliedItem { + var downloadingString: String? + if let resourceStatus = self.resourceStatus { var maybeFetchStatus: MediaResourceStatus = .Local switch resourceStatus { case .playbackStatus: @@ -752,7 +773,12 @@ final class ListMessageFileItemNode: ListMessageNode { case let .fetchStatus(fetchStatus): maybeFetchStatus = fetchStatus switch fetchStatus { - case .Remote, .Fetching: + case let .Fetching(_, progress): + if let file = self.currentMedia as? TelegramMediaFile, let size = file.size { + downloadingString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true)) / \(dataSizeString(size, forceDecimal: true))" + } + descriptionOffset = 14.0 + case .Remote: descriptionOffset = 14.0 case .Local: break @@ -802,6 +828,18 @@ final class ListMessageFileItemNode: ListMessageNode { descriptionFrame.origin.x = originX transition.updateFrame(node: self.descriptionNode, frame: descriptionFrame) } + + if downloadingString != nil { + self.descriptionProgressNode.isHidden = false + self.descriptionNode.isHidden = true + } else { + self.descriptionProgressNode.isHidden = true + self.descriptionNode.isHidden = false + } + self.descriptionProgressNode.attributedText = NSAttributedString(string: downloadingString ?? "", font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor) + let descriptionSize = self.descriptionProgressNode.updateLayout(CGSize(width: size.width - 14.0, height: size.height)) + transition.updateFrame(node: self.descriptionProgressNode, frame: CGRect(origin: self.descriptionNode.frame.origin, size: descriptionSize)) + } func activateMedia() { diff --git a/TelegramUI/MediaPlayer.swift b/TelegramUI/MediaPlayer.swift index a5c4151a49..6bbfe7cc6e 100644 --- a/TelegramUI/MediaPlayer.swift +++ b/TelegramUI/MediaPlayer.swift @@ -63,6 +63,7 @@ private final class MediaPlayerContext { private let postbox: Postbox private let resourceReference: MediaResourceReference + private let tempFilePath: String? private let streamable: Bool private let video: Bool private let preferSoftwareDecoding: Bool @@ -88,7 +89,7 @@ private final class MediaPlayerContext { private var stoppedAtEnd = false - init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise, postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool) { + init(queue: Queue, audioSessionManager: ManagedAudioSession, playerStatus: Promise, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String?, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, playAndRecord: Bool, keepAudioSessionWhilePaused: Bool) { assert(queue.isCurrent()) self.queue = queue @@ -96,6 +97,7 @@ private final class MediaPlayerContext { self.playerStatus = playerStatus self.postbox = postbox self.resourceReference = resourceReference + self.tempFilePath = tempFilePath self.streamable = streamable self.video = video self.preferSoftwareDecoding = preferSoftwareDecoding @@ -253,7 +255,7 @@ private final class MediaPlayerContext { self.playerStatus.set(.single(status)) } - let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, streamable: self.streamable, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically) + let frameSource = FFMpegMediaFrameSource(queue: self.queue, postbox: self.postbox, resourceReference: self.resourceReference, tempFilePath: self.tempFilePath, streamable: self.streamable, video: self.video, preferSoftwareDecoding: self.preferSoftwareDecoding, fetchAutomatically: self.fetchAutomatically) let disposable = MetaDisposable() let updatedSeekState: MediaPlayerSeekState? if let loadedDuration = loadedDuration { @@ -827,9 +829,9 @@ final class MediaPlayer { } } - init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = true) { + init(audioSessionManager: ManagedAudioSession, postbox: Postbox, resourceReference: MediaResourceReference, tempFilePath: String? = nil, streamable: Bool, video: Bool, preferSoftwareDecoding: Bool, playAutomatically: Bool = false, enableSound: Bool, baseRate: Double = 1.0, fetchAutomatically: Bool, playAndRecord: Bool = false, keepAudioSessionWhilePaused: Bool = true) { self.queue.async { - let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, postbox: postbox, resourceReference: resourceReference, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused) + let context = MediaPlayerContext(queue: self.queue, audioSessionManager: audioSessionManager, playerStatus: self.statusValue, postbox: postbox, resourceReference: resourceReference, tempFilePath: tempFilePath, streamable: streamable, video: video, preferSoftwareDecoding: preferSoftwareDecoding, playAutomatically: playAutomatically, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically, playAndRecord: playAndRecord, keepAudioSessionWhilePaused: keepAudioSessionWhilePaused) self.contextRef = Unmanaged.passRetained(context) } } diff --git a/TelegramUI/MediaTrackDecodableFrame.swift b/TelegramUI/MediaTrackDecodableFrame.swift index 72e2073f2a..d3c0991667 100644 --- a/TelegramUI/MediaTrackDecodableFrame.swift +++ b/TelegramUI/MediaTrackDecodableFrame.swift @@ -1,6 +1,6 @@ import Foundation import CoreMedia -import TelegramUIPrivateModule +import FFMpeg enum MediaTrackFrameType { case video diff --git a/TelegramUI/MentionChatInputPanelItem.swift b/TelegramUI/MentionChatInputPanelItem.swift index 164ec888b0..188bc5b89e 100644 --- a/TelegramUI/MentionChatInputPanelItem.swift +++ b/TelegramUI/MentionChatInputPanelItem.swift @@ -128,8 +128,6 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { let previousItem = self.item return { [weak self] item, params, mergedTop, mergedBottom in - let baseWidth = params.width - params.leftInset - params.rightInset - let leftInset: CGFloat = 55.0 + params.leftInset let rightInset: CGFloat = 10.0 + params.rightInset @@ -139,7 +137,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { } let string = NSMutableAttributedString() - string.append(NSAttributedString(string: item.peer.displayTitle, font: primaryFont, textColor: item.theme.list.itemPrimaryTextColor)) + string.append(NSAttributedString(string: item.peer.debugDisplayTitle, font: primaryFont, textColor: item.theme.list.itemPrimaryTextColor)) if let addressName = item.peer.addressName, !addressName.isEmpty { string.append(NSAttributedString(string: " @\(addressName)", font: secondaryFont, textColor: item.theme.list.itemSecondaryTextColor)) } diff --git a/TelegramUI/MessageContentKind.swift b/TelegramUI/MessageContentKind.swift index d988b868d4..4266bc44f1 100644 --- a/TelegramUI/MessageContentKind.swift +++ b/TelegramUI/MessageContentKind.swift @@ -69,7 +69,7 @@ public enum MessageContentKind: Equatable { } } -public func messageContentKind(_ message: Message, strings: PresentationStrings, accountPeerId: PeerId) -> MessageContentKind { +public func messageContentKind(_ message: Message, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId) -> MessageContentKind { for media in message.media { switch media { case let expiredMedia as TelegramMediaExpiredContent: @@ -127,7 +127,7 @@ public func messageContentKind(_ message: Message, strings: PresentationStrings, return .location } case _ as TelegramMediaAction: - return .text(plainServiceMessageString(strings: strings, message: message, accountPeerId: accountPeerId) ?? "") + return .text(plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, message: message, accountPeerId: accountPeerId) ?? "") default: break } @@ -135,11 +135,11 @@ public func messageContentKind(_ message: Message, strings: PresentationStrings, return .text(message.text) } -func descriptionStringForMessage(_ message: Message, strings: PresentationStrings, accountPeerId: PeerId) -> (String, Bool) { +func descriptionStringForMessage(_ message: Message, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, accountPeerId: PeerId) -> (String, Bool) { if !message.text.isEmpty { return (message.text, false) } - switch messageContentKind(message, strings: strings, accountPeerId: accountPeerId) { + switch messageContentKind(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: accountPeerId) { case let .text(text): return (text, false) case .image: diff --git a/TelegramUI/MultiplexedVideoNode.swift b/TelegramUI/MultiplexedVideoNode.swift index b6965e44f7..d36d950dbb 100644 --- a/TelegramUI/MultiplexedVideoNode.swift +++ b/TelegramUI/MultiplexedVideoNode.swift @@ -223,61 +223,56 @@ final class MultiplexedVideoNode: UIScrollView, UIScrollViewDelegate { let progressSize = CGSize(width: 24.0, height: 24.0) let progressFrame = CGRect(origin: CGPoint(x: item.frame.midX - progressSize.width / 2.0, y: item.frame.midY - progressSize.height / 2.0), size: progressSize) - let updatedStatusSignal = account.postbox.mediaBox.resourceStatus(item.fileReference.media.resource) - - let statusDisposable: MetaDisposable - if let disposable = self.statusDisposable[item.fileReference.media.fileId] { - statusDisposable = disposable - } else { - statusDisposable = MetaDisposable() - self.statusDisposable[item.fileReference.media.fileId] = statusDisposable + if item.frame.maxY < minVisibleY { + continue + } + if item.frame.minY > maxVisibleY { + continue } - statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak self] status in - displayLinkDispatcher.dispatch { + if self.statusDisposable[item.fileReference.media.fileId] == nil { + let statusDisposable = MetaDisposable() + let updatedStatusSignal = account.postbox.mediaBox.resourceStatus(item.fileReference.media.resource) + self.statusDisposable[item.fileReference.media.fileId] = statusDisposable + statusDisposable.set((updatedStatusSignal + |> deliverOnMainQueue).start(next: { [weak self] status in guard let `self` = self else {return} let state: RadialStatusNodeState switch status { - case let .Fetching(_, progress): - state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false) - case .Remote: - state = .progress(color: .white, lineWidth: nil, value: 0, cancelEnabled: false) - case .Local: - state = .none + case let .Fetching(_, progress): + state = .progress(color: .white, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false) + case .Remote: + state = .progress(color: .white, lineWidth: nil, value: 0, cancelEnabled: false) + case .Local: + state = .none } - if state == .none { - if let statusNode = self.visibleProgressNodes[item.fileReference.media.fileId] { + if let statusNode = self.visibleProgressNodes[item.fileReference.media.fileId] { + if state == .none { self.visibleProgressNodes.removeValue(forKey: item.fileReference.media.fileId) statusNode.transitionToState(state, completion: { [weak statusNode] in - statusNode?.removeFromSupernode() + statusNode?.isHidden = true }) - } - } else { - if let visibleProgressNode = self.visibleProgressNodes[item.fileReference.media.fileId] { - if ensureFrames { - visibleProgressNode.frame = progressFrame - } } else { - let visibleProgressNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) - visibleProgressNode.frame = progressFrame - self.visibleProgressNodes[item.fileReference.media.fileId] = visibleProgressNode + statusNode.isHidden = false + statusNode.transitionToState(state, completion: {}) } - - let statusNode = self.visibleProgressNodes[item.fileReference.media.fileId]! - statusNode.transitionToState(state, completion: {}) - self.addSubnode(statusNode) } - } - })) - - if item.frame.maxY < minVisibleY { - continue; + })) } - if item.frame.minY > maxVisibleY { - continue; + + if let visibleProgressNode = self.visibleProgressNodes[item.fileReference.media.fileId] { + if ensureFrames { + visibleProgressNode.frame = progressFrame + } + } else { + let statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) + statusNode.isHidden = true + statusNode.frame = progressFrame + self.visibleProgressNodes[item.fileReference.media.fileId] = statusNode + self.addSubnode(statusNode) } visibleIds.insert(item.fileReference.media.fileId) @@ -315,6 +310,13 @@ final class MultiplexedVideoNode: UIScrollView, UIScrollViewDelegate { } } + var removeProgressIds: [MediaId] = [] + for id in self.visibleProgressNodes.keys { + if !visibleIds.contains(id) { + removeProgressIds.append(id) + } + } + for id in removeIds { let (_, layerHolder) = self.visibleLayers[id]! layerHolder.layer.removeFromSuperlayer() @@ -326,6 +328,13 @@ final class MultiplexedVideoNode: UIScrollView, UIScrollViewDelegate { thumbnailLayer.removeFromSuperlayer() self.visibleThumbnailLayers.removeValue(forKey: id) } + + for id in removeProgressIds { + let progressNode = self.visibleProgressNodes[id]! + progressNode.removeFromSupernode() + self.visibleProgressNodes.removeValue(forKey: id) + self.statusDisposable.removeValue(forKey: id)?.dispose() + } } private func updateVisibleItems(transition: ContainedViewLayoutTransition = .immediate) { diff --git a/TelegramUI/NativeVideoContent.swift b/TelegramUI/NativeVideoContent.swift index 8a125d23bd..02f05c7e2d 100644 --- a/TelegramUI/NativeVideoContent.swift +++ b/TelegramUI/NativeVideoContent.swift @@ -49,8 +49,9 @@ final class NativeVideoContent: UniversalVideoContent { let baseRate: Double let fetchAutomatically: Bool let placeholderColor: UIColor + let tempFilePath: String? - init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, placeholderColor: UIColor = .white) { + init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, placeholderColor: UIColor = .white, tempFilePath: String? = nil) { self.id = id self.nativeId = id self.fileReference = fileReference @@ -75,10 +76,11 @@ final class NativeVideoContent: UniversalVideoContent { self.baseRate = baseRate self.fetchAutomatically = fetchAutomatically self.placeholderColor = placeholderColor + self.tempFilePath = tempFilePath } func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode { - return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, placeholderColor: self.placeholderColor) + return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath) } func isEqual(to other: UniversalVideoContent) -> Bool { @@ -128,14 +130,14 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent private var validLayout: CGSize? - init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, placeholderColor: UIColor) { + init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, placeholderColor: UIColor, tempFilePath: String?) { self.postbox = postbox self.fileReference = fileReference self.placeholderColor = placeholderColor self.imageNode = TransformImageNode() - self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically) + self.player = MediaPlayer(audioSessionManager: audioSessionManager, postbox: postbox, resourceReference: fileReference.resourceReference(fileReference.media.resource), tempFilePath: tempFilePath, streamable: streamVideo, video: true, preferSoftwareDecoding: false, playAutomatically: false, enableSound: enableSound, baseRate: baseRate, fetchAutomatically: fetchAutomatically) var actionAtEndImpl: (() -> Void)? if enableSound && !loopVideo { self.player.actionAtEnd = .action({ @@ -214,7 +216,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent } self.imageNode.frame = CGRect(origin: CGPoint(), size: size) - self.playerNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -0.0002, dy: -0.0002) + self.playerNode.frame = CGRect(origin: CGPoint(), size: size).insetBy(dx: -1.0, dy: -1.0) } func play() { diff --git a/TelegramUI/NotificationExceptionControllerNode.swift b/TelegramUI/NotificationExceptionControllerNode.swift index 7098f35485..8332fe48d9 100644 --- a/TelegramUI/NotificationExceptionControllerNode.swift +++ b/TelegramUI/NotificationExceptionControllerNode.swift @@ -5,15 +5,12 @@ import Postbox import TelegramCore import SwiftSignalKit - - - private final class NotificationExceptionState : Equatable { - let mode:NotificationExceptionMode let isSearchMode: Bool let revealedPeerId: PeerId? let editing: Bool + init(mode: NotificationExceptionMode, isSearchMode: Bool = false, revealedPeerId: PeerId? = nil, editing: Bool = false) { self.mode = mode self.isSearchMode = isSearchMode @@ -294,7 +291,7 @@ private func notificationsExceptionEntries(presentationData: PresentationData, s let soundName = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: value.settings.messageSound) title += (title.isEmpty ? presentationData.strings.Notification_Exceptions_Sound(soundName).0 : ", \(presentationData.strings.Notification_Exceptions_Sound(soundName).0)") } - entries.append(.peer(index: index, peer: value.peer, theme: presentationData.theme, strings: presentationData.strings, dateFormat: presentationData.dateTimeFormat, description: title, notificationSettings: value.settings, revealed: state.revealedPeerId == value.peer.id, editing: state.editing)) + entries.append(.peer(index: index, peer: value.peer, theme: presentationData.theme, strings: presentationData.strings, dateFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, description: title, notificationSettings: value.settings, revealed: state.revealedPeerId == value.peer.id, editing: state.editing)) index += 1 } } @@ -324,14 +321,15 @@ private enum NotificationExceptionEntryId: Hashable { case search case peerId(Int64) case addException + var hashValue: Int { switch self { - case .search: - return 0 - case .addException: - return 1 - case let .peerId(peerId): - return peerId.hashValue + case .search: + return 0 + case .addException: + return 1 + case let .peerId(peerId): + return peerId.hashValue } } @@ -380,7 +378,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { typealias ItemGenerationArguments = NotificationExceptionArguments case search(PresentationTheme, PresentationStrings) - case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool) + case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool) case addException(PresentationTheme, PresentationStrings, Bool) func item(_ arguments: NotificationExceptionArguments) -> ListViewItem { @@ -393,8 +391,8 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addExceptionIcon(theme), title: strings.Notification_Exceptions_AddException, sectionId: self.section, editing: editing, action: { arguments.selectPeer() }) - case let .peer(_, peer, theme, strings, dateTimeFormat, value, _, revealed, editing): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .text(value), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, sectionId: self.section, action: { + case let .peer(_, peer, theme, strings, dateTimeFormat, nameDisplayOrder, value, _, revealed, editing): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .text(value), label: .none, editing: ItemListPeerItemEditing(editable: true, editing: editing, revealed: revealed), switchValue: nil, enabled: true, sectionId: self.section, action: { arguments.openPeer(peer) }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in arguments.updateRevealedPeerId(peerId) @@ -410,7 +408,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { return .search case .addException: return .addException - case let .peer(_, peer, _, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _, _): return .peerId(peer.id.toInt64()) } } @@ -431,10 +429,10 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { default: return false } - case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsValue, lhsSettings, lhsRevealed, lhsEditing): + case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsValue, lhsSettings, lhsRevealed, lhsEditing): switch rhs { - case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsValue, rhsSettings, rhsRevealed, rhsEditing): - return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsDateTimeFormat == rhsDateTimeFormat && lhsIndex == rhsIndex && lhsPeer.isEqual(rhsPeer) && lhsValue == rhsValue && lhsSettings == rhsSettings && lhsRevealed == rhsRevealed && lhsEditing == rhsEditing + case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsValue, rhsSettings, rhsRevealed, rhsEditing): + return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsDateTimeFormat == rhsDateTimeFormat && lhsNameOrder == rhsNameOrder && lhsIndex == rhsIndex && lhsPeer.isEqual(rhsPeer) && lhsValue == rhsValue && lhsSettings == rhsSettings && lhsRevealed == rhsRevealed && lhsEditing == rhsEditing default: return false } @@ -452,11 +450,11 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { default: return true } - case let .peer(lhsIndex, _, _, _, _, _, _, _ , _): + case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _): switch rhs { case .search, .addException: return false - case let .peer(rhsIndex, _, _, _, _, _, _, _, _): + case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex } } diff --git a/TelegramUI/NotificationsAndSounds.swift b/TelegramUI/NotificationsAndSounds.swift index 3004b1dca1..5127b3cedc 100644 --- a/TelegramUI/NotificationsAndSounds.swift +++ b/TelegramUI/NotificationsAndSounds.swift @@ -813,7 +813,7 @@ public func notificationsAndSoundsController(account: Account, exceptionsList: N var channels:[PeerId : NotificationExceptionWrapper] = [:] if let list = list { for (key, value) in list.settings { - if let peer = list.peers[key], !peer.displayTitle.isEmpty, peer.id != account.peerId { + if let peer = list.peers[key], !peer.debugDisplayTitle.isEmpty, peer.id != account.peerId { switch value.muteState { case .default: switch value.messageSound { diff --git a/TelegramUI/PasscodeOptionsController.swift b/TelegramUI/PasscodeOptionsController.swift index 6d05060c6e..9ebcd4cfb2 100644 --- a/TelegramUI/PasscodeOptionsController.swift +++ b/TelegramUI/PasscodeOptionsController.swift @@ -541,9 +541,11 @@ public func passcodeEntryController(account: Account, animateIn: Bool = true, co completion(value != nil) dismissImpl?() })! - controller.touchIdCompletion = { - completion(true) - dismissImpl?() + if passcodeSettings?.enableBiometrics ?? false { + controller.touchIdCompletion = { + completion(true) + dismissImpl?() + } } controller.checkCurrentPasscode = { value in if let value = value { @@ -578,7 +580,9 @@ public func passcodeEntryController(account: Account, animateIn: Bool = true, co }).start() } legacyController.presentationCompleted = { [weak controller] in - controller?.refreshTouchId() + if passcodeSettings?.enableBiometrics ?? false { + controller?.refreshTouchId() + } } legacyController.bind(controller: controller) legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait) diff --git a/TelegramUI/PeerMediaCollectionControllerNode.swift b/TelegramUI/PeerMediaCollectionControllerNode.swift index 4dbbe91011..45e79c2ebb 100644 --- a/TelegramUI/PeerMediaCollectionControllerNode.swift +++ b/TelegramUI/PeerMediaCollectionControllerNode.swift @@ -10,12 +10,20 @@ struct PeerMediaCollectionMessageForGallery { let fromSearchResults: Bool } -private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Account, peerId: PeerId, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal?, NoError>) -> ChatHistoryNode & ASDisplayNode { +private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Account, theme: PresentationTheme, peerId: PeerId, messageId: MessageId?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal?, NoError>) -> ChatHistoryNode & ASDisplayNode { switch mode { case .photoOrVideo: - return ChatHistoryGridNode(account: account, peerId: peerId, messageId: messageId, tagMask: .photoOrVideo, controllerInteraction: controllerInteraction) + let node = ChatHistoryGridNode(account: account, peerId: peerId, messageId: messageId, tagMask: .photoOrVideo, controllerInteraction: controllerInteraction) + node.showVerticalScrollIndicator = true + if theme.list.plainBackgroundColor.argb == 0xffffffff { + node.indicatorStyle = .default + } else { + node.indicatorStyle = .white + } + return node case .file: let node = ChatHistoryListNode(account: account, chatLocation: .peer(peerId), tagMask: .file, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false)) + node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor node.didEndScrolling = { [weak node] in guard let node = node else { return @@ -26,6 +34,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Ac return node case .music: let node = ChatHistoryListNode(account: account, chatLocation: .peer(peerId), tagMask: .music, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false)) + node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor node.didEndScrolling = { [weak node] in guard let node = node else { return @@ -36,6 +45,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, account: Ac return node case .webpage: let node = ChatHistoryListNode(account: account, chatLocation: .peer(peerId), tagMask: .webPage, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false)) + node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor node.didEndScrolling = { [weak node] in guard let node = node else { return @@ -134,11 +144,11 @@ class PeerMediaCollectionControllerNode: ASDisplayNode { self.sectionsNode = PeerMediaCollectionSectionsNode(theme: self.presentationData.theme, strings: self.presentationData.strings) - self.historyNode = historyNodeImplForMode(self.mediaCollectionInterfaceState.mode, account: account, peerId: peerId, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get()) + self.historyNode = historyNodeImplForMode(self.mediaCollectionInterfaceState.mode, account: account, theme: self.presentationData.theme, peerId: peerId, messageId: messageId, controllerInteraction: controllerInteraction, selectedMessages: self.selectedMessagesPromise.get()) self.historyEmptyNode = PeerMediaCollectionEmptyNode(mode: self.mediaCollectionInterfaceState.mode, theme: self.presentationData.theme, strings: self.presentationData.strings) self.historyEmptyNode.isHidden = true - self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId)) + self.chatPresentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId)) super.init() @@ -397,7 +407,7 @@ class PeerMediaCollectionControllerNode: ASDisplayNode { if self.mediaCollectionInterfaceState.mode != mediaCollectionInterfaceState.mode { let previousMode = self.mediaCollectionInterfaceState.mode if let containerLayout = self.containerLayout, self.candidateHistoryNode == nil || self.candidateHistoryNode!.1 != mediaCollectionInterfaceState.mode { - let node = historyNodeImplForMode(mediaCollectionInterfaceState.mode, account: self.account, peerId: self.peerId, messageId: nil, controllerInteraction: self.controllerInteraction, selectedMessages: self.selectedMessagesPromise.get()) + let node = historyNodeImplForMode(mediaCollectionInterfaceState.mode, account: self.account, theme: self.presentationData.theme, peerId: self.peerId, messageId: nil, controllerInteraction: self.controllerInteraction, selectedMessages: self.selectedMessagesPromise.get()) node.backgroundColor = mediaCollectionInterfaceState.theme.list.plainBackgroundColor self.candidateHistoryNode = (node, mediaCollectionInterfaceState.mode) diff --git a/TelegramUI/PeerTitle.swift b/TelegramUI/PeerTitle.swift index 6899244da0..363b23637f 100644 --- a/TelegramUI/PeerTitle.swift +++ b/TelegramUI/PeerTitle.swift @@ -3,15 +3,16 @@ import TelegramCore import Postbox extension Peer { - func displayTitle(strings: PresentationStrings) -> String { + func displayTitle(strings: PresentationStrings, displayOrder: PresentationPersonNameOrder) -> String { switch self { case let user as TelegramUser: if let firstName = user.firstName { if let lastName = user.lastName { - if strings.lc == 0x6b6f { - return "\(lastName) \(firstName)" - } else { - return "\(firstName) \(lastName)" + switch displayOrder { + case .firstLast: + return "\(firstName) \(lastName)" + case .lastFirst: + return "\(lastName) \(firstName)" } } else { return firstName diff --git a/TelegramUI/PhoneInputNode.swift b/TelegramUI/PhoneInputNode.swift index 523e740dc0..a5badf6f9b 100644 --- a/TelegramUI/PhoneInputNode.swift +++ b/TelegramUI/PhoneInputNode.swift @@ -113,23 +113,35 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { } } - var codeAndNumber: (Int32?, String) { + private var countryNameForCode: (Int32, String)? + + var codeAndNumber: (Int32?, String?, String) { get { var code: Int32? if let text = self.countryCodeField.textField.text, text.count <= 4, let number = Int(removePlus(text)) { code = Int32(number) - return (code, cleanPhoneNumber(self.numberField.textField.text)) + var countryName: String? + if self.countryNameForCode?.0 == code { + countryName = self.countryNameForCode?.1 + } + return (code, countryName, cleanPhoneNumber(self.numberField.textField.text)) } else if let text = self.countryCodeField.textField.text { - return (nil, cleanPhoneNumber(text + (self.numberField.textField.text ?? ""))) + return (nil, nil, cleanPhoneNumber(text + (self.numberField.textField.text ?? ""))) } else { - return (nil, "") + return (nil, nil, "") } } set(value) { - self.updateNumber("+" + (value.0 == nil ? "" : "\(value.0!)") + value.1) + let updatedCountryName = self.countryNameForCode?.0 != value.0 || self.countryNameForCode?.1 != value.1 + if let code = value.0, let name = value.1 { + self.countryNameForCode = (code, name) + } else { + self.countryNameForCode = nil + } + self.updateNumber("+" + (value.0 == nil ? "" : "\(value.0!)") + value.2, forceNotifyCountryCodeUpdated: updatedCountryName) } } - var countryCodeUpdated: ((String) -> Void)? + var countryCodeUpdated: ((String, String?) -> Void)? var countryCodeTextUpdated: ((String) -> Void)? var numberTextUpdated: ((String) -> Void)? @@ -204,7 +216,7 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { self.updateNumber(inputText) } - private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true) { + private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true, forceNotifyCountryCodeUpdated: Bool = false) { let (regionPrefix, text) = self.phoneFormatter.updateText(inputText) var realRegionPrefix: String let numberText: String @@ -226,10 +238,14 @@ final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { if realRegionPrefix != self.countryCodeField.textField.text { self.countryCodeField.textField.text = realRegionPrefix } - if self.previousCountryCodeText != realRegionPrefix { + if self.previousCountryCodeText != realRegionPrefix || forceNotifyCountryCodeUpdated { self.previousCountryCodeText = realRegionPrefix let code = removePlus(realRegionPrefix).trimmingCharacters(in: CharacterSet.whitespaces) - self.countryCodeUpdated?(code) + var countryName: String? + if self.countryNameForCode?.0 == Int32(code) { + countryName = self.countryNameForCode?.1 + } + self.countryCodeUpdated?(code, countryName) } self.countryCodeTextUpdated?(realRegionPrefix) diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index a58417bf73..015097e427 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -88,9 +88,13 @@ private func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaR return thumbnail |> mapToSignal { thumbnailData in - return fullSizeData - |> map { (fullSizeData, complete) in - return (thumbnailData, fullSizeData, complete) + if let thumbnailData = thumbnailData { + return fullSizeData + |> map { (fullSizeData, complete) in + return (thumbnailData, fullSizeData, complete) + } + } else { + return .single((thumbnailData, nil, false)) } } } diff --git a/TelegramUI/PresentationData.swift b/TelegramUI/PresentationData.swift index e48f742712..18a8391690 100644 --- a/TelegramUI/PresentationData.swift +++ b/TelegramUI/PresentationData.swift @@ -31,9 +31,9 @@ public enum PresentationDateFormat { case dayFirst } -public enum PresentationPersonNameOrder { - case firstLast - case lastFirst +public enum PresentationPersonNameOrder: Int32 { + case firstLast = 0 + case lastFirst = 1 } extension PresentationStrings : Equatable { @@ -41,12 +41,6 @@ extension PresentationStrings : Equatable { return lhs === rhs } } -// -//extension PresentationTheme : Equatable { -// public static func ==(lhs: PresentationTheme, rhs: PresentationTheme) -> Bool { -// return lhs === rhs -// } -//} public final class PresentationData: Equatable { public let strings: PresentationStrings @@ -167,23 +161,6 @@ private func currentPersonNameSortOrder() -> PresentationPersonNameOrder { } } -private func currentPersonNameDisplayOrder() -> PresentationPersonNameOrder { - if #available(iOSApplicationExtension 9.0, *) { - switch CNContactFormatter.nameOrder(for: CNContact()) { - case .givenNameFirst: - return .firstLast - default: - return .lastFirst - } - } else { - if ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst { - return .firstLast - } else { - return .lastFirst - } - } -} - public final class InitialPresentationDataAndSettings { public let presentationData: PresentationData public let automaticMediaDownloadSettings: AutomaticMediaDownloadSettings @@ -203,7 +180,7 @@ public final class InitialPresentationDataAndSettings { } public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal { - return postbox.transaction { transaction -> (PresentationThemeSettings, LocalizationSettings?, AutomaticMediaDownloadSettings, CallListSettings, InAppNotificationSettings, MediaInputSettings, ExperimentalUISettings) in + return postbox.transaction { transaction -> (PresentationThemeSettings, LocalizationSettings?, AutomaticMediaDownloadSettings, CallListSettings, InAppNotificationSettings, MediaInputSettings, ExperimentalUISettings, ContactSynchronizationSettings) in let themeSettings: PresentationThemeSettings if let current = transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.presentationThemeSettings) as? PresentationThemeSettings { themeSettings = current @@ -248,9 +225,11 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal map { (themeSettings, localizationSettings, automaticMediaDownloadSettings, callListSettings, inAppNotificationSettings, mediaInputSettings, experimentalUISettings) -> InitialPresentationDataAndSettings in + |> map { (themeSettings, localizationSettings, automaticMediaDownloadSettings, callListSettings, inAppNotificationSettings, mediaInputSettings, experimentalUISettings, contactSettings) -> InitialPresentationDataAndSettings in let themeValue: PresentationTheme let effectiveTheme: PresentationThemeReference @@ -295,7 +274,7 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal Signal { - let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings])) + let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings, ApplicationSpecificPreferencesKeys.contactSynchronizationSettings])) return postbox.combinedView(keys: [preferencesKey]) |> mapToSignal { view -> Signal in let themeSettings: PresentationThemeSettings @@ -377,6 +356,8 @@ public func updatedPresentationData(postbox: Postbox) -> Signal distinctUntilChanged |> map { shouldSwitch in @@ -430,7 +411,7 @@ public func updatedPresentationData(postbox: Postbox) -> Signal Signal PresentationData { let dateTimeFormat = currentDateTimeFormat() - let nameDisplayOrder = currentPersonNameDisplayOrder() + let nameDisplayOrder: PresentationPersonNameOrder = .firstLast let nameSortOrder = currentPersonNameSortOrder() let themeSettings = PresentationThemeSettings.defaultSettings diff --git a/TelegramUI/RecentSessionsController.swift b/TelegramUI/RecentSessionsController.swift index 9c7d919efa..989627d72d 100644 --- a/TelegramUI/RecentSessionsController.swift +++ b/TelegramUI/RecentSessionsController.swift @@ -78,7 +78,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry { case pendingSessionsInfo(PresentationTheme, String) case otherSessionsHeader(PresentationTheme, String) case session(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool) - case website(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool) + case website(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool) var section: ItemListSectionId { switch self { @@ -113,7 +113,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry { return .index(7) case let .session(_, _, _, _, session, _, _, _): return .session(session.hash) - case let .website(_, _, _, _, website, _, _, _, _): + case let .website(_, _, _, _, _, website, _, _, _, _): return .session(website.hash) } } @@ -180,8 +180,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry { } else { return false } - case let .website(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed): - if case let .website(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed { + case let .website(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed): + if case let .website(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameOrder == rhsNameOrder, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed { return true } else { return false @@ -221,8 +221,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry { return false } } - case let .website(lhsIndex, _, _, _, _, _, _, _, _): - if case let .website(rhsIndex, _, _, _, _, _, _, _, _) = rhs { + case let .website(lhsIndex, _, _, _, _, _, _, _, _, _): + if case let .website(rhsIndex, _, _, _, _, _, _, _, _, _) = rhs { return lhsIndex <= rhsIndex } else { return false @@ -269,8 +269,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry { }, removeSession: { id in arguments.removeSession(id) }) - case let .website(_, theme, strings, dateTimeFormat, website, peer, enabled, editing, revealed): - return ItemListWebsiteItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in + case let .website(_, theme, strings, dateTimeFormat, nameDisplayOrder, website, peer, enabled, editing, revealed): + return ItemListWebsiteItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, website: website, peer: peer, enabled: enabled, editing: editing, revealed: revealed, sectionId: self.section, setSessionIdWithRevealedOptions: { previousId, id in arguments.setSessionIdWithRevealedOptions(previousId, id) }, removeSession: { id in arguments.removeWebSession(id) @@ -397,7 +397,7 @@ private func recentSessionsControllerEntries(presentationData: PresentationData, let website = websites[i] if !existingSessionIds.contains(website.hash) { existingSessionIds.insert(website.hash) - entries.append(.website(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash)) + entries.append(.website(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash)) } } } diff --git a/TelegramUI/ReplyAccessoryPanelNode.swift b/TelegramUI/ReplyAccessoryPanelNode.swift index e0a38d8b3c..15d8785839 100644 --- a/TelegramUI/ReplyAccessoryPanelNode.swift +++ b/TelegramUI/ReplyAccessoryPanelNode.swift @@ -19,7 +19,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { var theme: PresentationTheme - init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings) { + init(account: Account, messageId: MessageId, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) { self.messageId = messageId self.theme = theme @@ -62,10 +62,10 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { var authorName = "" var text = "" if let author = message?.author { - authorName = author.displayTitle + authorName = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder) } if let message = message { - (text, _) = descriptionStringForMessage(message, strings: strings, accountPeerId: account.peerId) + (text, _) = descriptionStringForMessage(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: account.peerId) } var updatedMediaReference: AnyMediaReference? @@ -131,7 +131,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { let isMedia: Bool if let message = message { - switch messageContentKind(message, strings: strings, accountPeerId: account.peerId) { + switch messageContentKind(message, strings: strings, nameDisplayOrder: nameDisplayOrder, accountPeerId: account.peerId) { case .text: isMedia = false default: diff --git a/TelegramUI/SecretMediaPreviewController.swift b/TelegramUI/SecretMediaPreviewController.swift index 647a0f5a5b..4c1aff9559 100644 --- a/TelegramUI/SecretMediaPreviewController.swift +++ b/TelegramUI/SecretMediaPreviewController.swift @@ -130,6 +130,8 @@ public final class SecretMediaPreviewController: ViewController { private var messageView: MessageView? private var currentNodeMessageId: MessageId? + private var currentNodeMessageIsVideo = false + private var tempFile: TempBoxFile? private let _hiddenMedia = Promise<(MessageId, Media)?>(nil) private var hiddenMediaManagerIndex: Int? @@ -149,6 +151,8 @@ public final class SecretMediaPreviewController: ViewController { self.statusBar.statusBarStyle = .White + + self.disposable.set((account.postbox.messageView(messageId) |> deliverOnMainQueue).start(next: { [weak self] view in if let strongSelf = self { strongSelf.messageView = view @@ -192,6 +196,9 @@ public final class SecretMediaPreviewController: ViewController { mediaManager.galleryHiddenMediaManager.removeSource(hiddenMediaManagerIndex) } self.screenCaptureEventsDisposable?.dispose() + if let tempFile = self.tempFile { + TempBox.shared.dispose(tempFile) + } } @objc func donePressed() { @@ -205,7 +212,7 @@ public final class SecretMediaPreviewController: ViewController { } }, dismissController: { [weak self] in self?.dismiss(forceAway: true) - }, replaceRootController: { [weak self] _, _ in + }, replaceRootController: { _, _ in }) self.displayNode = SecretMediaPreviewControllerNode(controllerInteraction: controllerInteraction) self.displayNodeDidLoad() @@ -215,7 +222,7 @@ public final class SecretMediaPreviewController: ViewController { self.controllerNode.transitionDataForCentralItem = { [weak self] in if let strongSelf = self { - if let centralItemNode = strongSelf.controllerNode.pager.centralItemNode(), let presentationArguments = strongSelf.presentationArguments as? GalleryControllerPresentationArguments { + if let _ = strongSelf.controllerNode.pager.centralItemNode(), let presentationArguments = strongSelf.presentationArguments as? GalleryControllerPresentationArguments { if let message = strongSelf.messageView?.message { if let media = mediaForMessage(message: message), let transitionArguments = presentationArguments.transitionArguments(message.id, media) { return (transitionArguments.transitionNode, transitionArguments.addToTransitionSurface) @@ -234,7 +241,7 @@ public final class SecretMediaPreviewController: ViewController { if let strongSelf = self { strongSelf._hiddenMedia.set(.single(nil)) - var animatedOutNode = true + let animatedOutNode = true var animatedOutInterface = false let completion = { @@ -261,10 +268,20 @@ public final class SecretMediaPreviewController: ViewController { if let _ = index { if let message = strongSelf.messageView?.message, let media = mediaForMessage(message: message) { var beginTimeAndTimeout: (Double, Double)? + var videoDuration: Int32? + for media in message.media { + if let file = media as? TelegramMediaFile { + videoDuration = file.duration + } + } for attribute in message.attributes { if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { if let countdownBeginTime = attribute.countdownBeginTime { - beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) + if let videoDuration = videoDuration { + beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration)) + } else { + beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) + } } break } @@ -302,7 +319,7 @@ public final class SecretMediaPreviewController: ViewController { } else { text = strongSelf.presentationData.strings.SecretImage_NotViewedYet(peerTitle).0 } - contentNode.setText(text) + contentNode.setText(text) strongSelf.controllerNode.updatePresentationState({ $0.withUpdatedFooterContentNode(contentNode) }, transition: .immediate) @@ -328,7 +345,6 @@ public final class SecretMediaPreviewController: ViewController { var nodeAnimatesItself = false if let centralItemNode = self.controllerNode.pager.centralItemNode(), let message = self.messageView?.message { - if let media = mediaForMessage(message: message) { if let presentationArguments = self.presentationArguments as? GalleryControllerPresentationArguments, let transitionArguments = presentationArguments.transitionArguments(message.id, media) { nodeAnimatesItself = true @@ -394,7 +410,20 @@ public final class SecretMediaPreviewController: ViewController { if let message = message { if self.currentNodeMessageId != message.id { self.currentNodeMessageId = message.id - guard let item = galleryItemForEntry(account: account, presentationData: self.presentationData, entry: .MessageEntry(message, false, nil, nil), streamVideos: false, hideControls: true, playbackCompleted: { [weak self] in + var tempFilePath: String? + for media in message.media { + if let file = media as? TelegramMediaFile { + if let path = self.account.postbox.mediaBox.completedResourcePath(file.resource) { + let tempFile = TempBox.shared.file(path: path, fileName: file.fileName ?? "file") + self.tempFile = tempFile + tempFilePath = tempFile.path + self.currentNodeMessageIsVideo = true + } + break + } + } + + guard let item = galleryItemForEntry(account: self.account, presentationData: self.presentationData, entry: .MessageEntry(message, false, nil, nil), streamVideos: false, hideControls: true, tempFilePath: tempFilePath, playbackCompleted: { [weak self] in self?.dismiss(forceAway: false) }) else { self._ready.set(.single(true)) @@ -409,10 +438,20 @@ public final class SecretMediaPreviewController: ViewController { self.markMessageAsConsumedDisposable.set(markMessageContentAsConsumedInteractively(postbox: self.account.postbox, messageId: message.id).start()) } else { var beginTimeAndTimeout: (Double, Double)? + var videoDuration: Int32? + for media in message.media { + if let file = media as? TelegramMediaFile { + videoDuration = file.duration + } + } for attribute in message.attributes { if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { if let countdownBeginTime = attribute.countdownBeginTime { - beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) + if let videoDuration = videoDuration { + beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration)) + } else { + beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout)) + } } break } @@ -428,7 +467,9 @@ public final class SecretMediaPreviewController: ViewController { if !self.didSetReady { self._ready.set(.single(true)) } - self.dismiss() + if !self.currentNodeMessageIsVideo { + self.dismiss() + } } } diff --git a/TelegramUI/SelectivePrivacySettingsPeersController.swift b/TelegramUI/SelectivePrivacySettingsPeersController.swift index e8cd7af072..a48963a038 100644 --- a/TelegramUI/SelectivePrivacySettingsPeersController.swift +++ b/TelegramUI/SelectivePrivacySettingsPeersController.swift @@ -56,7 +56,7 @@ private enum SelectivePrivacyPeersEntryStableId: Hashable { } private enum SelectivePrivacyPeersEntry: ItemListNodeEntry { - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, ItemListPeerItemEditing, Bool) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, ItemListPeerItemEditing, Bool) case addItem(PresentationTheme, String, Bool) var section: ItemListSectionId { @@ -70,7 +70,7 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry { var stableId: SelectivePrivacyPeersEntryStableId { switch self { - case let .peerItem(_, _, _, _, peer, _, _): + case let .peerItem(_, _, _, _, _, peer, _, _): return .peer(peer.id) case .addItem: return .add @@ -79,8 +79,8 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry { static func ==(lhs: SelectivePrivacyPeersEntry, rhs: SelectivePrivacyPeersEntry) -> Bool { switch lhs { - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsEditing, lhsEnabled): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsEditing, rhsEnabled) = rhs { + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsEditing, lhsEnabled): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsEditing, rhsEnabled) = rhs { if lhsIndex != rhsIndex { return false } @@ -96,6 +96,9 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if lhsEditing != rhsEditing { return false } @@ -117,9 +120,9 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry { static func <(lhs: SelectivePrivacyPeersEntry, rhs: SelectivePrivacyPeersEntry) -> Bool { switch lhs { - case let .peerItem(index, _, _, _, _, _, _): + case let .peerItem(index, _, _, _, _, _, _, _): switch rhs { - case let .peerItem(rhsIndex, _, _, _, _, _, _): + case let .peerItem(rhsIndex, _, _, _, _, _, _, _): return index < rhsIndex case .addItem: return true @@ -131,8 +134,8 @@ private enum SelectivePrivacyPeersEntry: ItemListNodeEntry { func item(_ arguments: SelectivePrivacyPeersControllerArguments) -> ListViewItem { switch self { - case let .peerItem(_, theme, strings, dateTimeFormat, peer, editing, enabled): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in + case let .peerItem(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, editing, enabled): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, presence: nil, text: .none, label: .none, editing: editing, switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) }, removePeer: { peerId in arguments.removePeer(peerId) @@ -183,7 +186,7 @@ private func selectivePrivacyPeersControllerEntries(presentationData: Presentati var index: Int32 = 0 for peer in peers { - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), true)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, peer, ItemListPeerItemEditing(editable: true, editing: state.editing, revealed: peer.id == state.peerIdWithRevealedOptions), true)) index += 1 } diff --git a/TelegramUI/ShareInputFieldNode.swift b/TelegramUI/ShareInputFieldNode.swift index fd4e69c67c..c6db863f04 100644 --- a/TelegramUI/ShareInputFieldNode.swift +++ b/TelegramUI/ShareInputFieldNode.swift @@ -34,13 +34,11 @@ final class ShareInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { self.textInputNode = ASEditableTextNode() let textColor: UIColor = theme.actionSheet.inputTextColor - let keyboardAppearance: UIKeyboardAppearance = UIKeyboardAppearance.default - textInputNode.typingAttributes = [NSAttributedStringKey.font.rawValue: Font.regular(17.0), NSAttributedStringKey.foregroundColor.rawValue: textColor] - textInputNode.clipsToBounds = true - textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - textInputNode.keyboardAppearance = keyboardAppearance - textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) - textInputNode.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance + self.textInputNode.typingAttributes = [NSAttributedStringKey.font.rawValue: Font.regular(17.0), NSAttributedStringKey.foregroundColor.rawValue: textColor] + self.textInputNode.clipsToBounds = true + self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) + self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) + self.textInputNode.keyboardAppearance = theme.chatList.searchBarKeyboardColor.keyboardAppearance self.placeholderNode = ASTextNode() self.placeholderNode.isUserInteractionEnabled = false diff --git a/TelegramUI/SoftwareVideoSource.swift b/TelegramUI/SoftwareVideoSource.swift index ce7f658b0b..207066774a 100644 --- a/TelegramUI/SoftwareVideoSource.swift +++ b/TelegramUI/SoftwareVideoSource.swift @@ -1,7 +1,7 @@ import Foundation import CoreMedia -import TelegramUIPrivateModule import SwiftSignalKit +import FFMpeg private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer?, bufferSize: Int32) -> Int32 { let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() @@ -14,7 +14,7 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whence: Int32) -> Int64 { let context = Unmanaged.fromOpaque(userData!).takeUnretainedValue() if let fd = context.fd { - if (whence & AVSEEK_SIZE) != 0 { + if (whence & FFMPEG_AVSEEK_SIZE) != 0 { return Int64(context.size) } else { lseek(fd, off_t(offset), SEEK_SET) @@ -47,8 +47,8 @@ private final class SoftwareVideoStream { final class SoftwareVideoSource { private var readingError = false private var videoStream: SoftwareVideoStream? - private var avIoContext: UnsafeMutablePointer? - private var avFormatContext: UnsafeMutablePointer? + private var avIoContext: FFMpegAVIOContext? + private var avFormatContext: FFMpegAVFormatContext? private let path: String fileprivate let fd: Int32? fileprivate let size: Int32 @@ -69,25 +69,21 @@ final class SoftwareVideoSource { self.path = path - var avFormatContextRef = avformat_alloc_context() - guard let avFormatContext = avFormatContextRef else { - self.readingError = true - return - } + let avFormatContext = FFMpegAVFormatContext() let ioBufferSize = 64 * 1024 - let avIoBuffer = av_malloc(ioBufferSize)! - let avIoContextRef = avio_alloc_context(avIoBuffer.assumingMemoryBound(to: UInt8.self), Int32(ioBufferSize), 0, Unmanaged.passUnretained(self).toOpaque(), readPacketCallback, nil, seekCallback) - self.avIoContext = avIoContextRef - avFormatContext.pointee.pb = self.avIoContext + let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) + self.avIoContext = avIoContext - guard avformat_open_input(&avFormatContextRef, nil, nil, nil) >= 0 else { + avFormatContext.setIO(self.avIoContext!) + + if !avFormatContext.openInput() { self.readingError = true return } - guard avformat_find_stream_info(avFormatContext, nil) >= 0 else { + if !avFormatContext.findStreamInfo() { self.readingError = true return } @@ -96,40 +92,30 @@ final class SoftwareVideoSource { var videoStream: SoftwareVideoStream? - for streamIndex in FFMpegMediaFrameSourceContextHelpers.streamIndices(formatContext: avFormatContext, codecType: AVMEDIA_TYPE_VIDEO) { - if (avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.disposition & Int32(AV_DISPOSITION_ATTACHED_PIC)) == 0 { - - let codecPar = avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.codecpar! - - if let codec = avcodec_find_decoder(codecPar.pointee.codec_id) { - if let codecContext = avcodec_alloc_context3(codec) { - if avcodec_parameters_to_context(codecContext, avFormatContext.pointee.streams[streamIndex]!.pointee.codecpar) >= 0 { - if avcodec_open2(codecContext, codec, nil) >= 0 { - let (fps, timebase) = FFMpegMediaFrameSourceContextHelpers.streamFpsAndTimeBase(stream: avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!, defaultTimeBase: CMTimeMake(1, 24)) - - let duration = CMTimeMake(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.duration, timebase.timescale) - - var rotationAngle: Double = 0.0 - if let rotationInfo = av_dict_get(avFormatContext.pointee.streams.advanced(by: streamIndex).pointee!.pointee.metadata, "rotate", nil, 0), let value = rotationInfo.pointee.value { - if strcmp(value, "0") != 0 { - if let angle = Double(String(cString: value)) { - rotationAngle = angle * Double.pi / 180.0 - } - } - } - - let aspect = Double(codecPar.pointee.width) / Double(codecPar.pointee.height) - - videoStream = SoftwareVideoStream(index: streamIndex, fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect) - break - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } - } else { - var codecContextRef: UnsafeMutablePointer? = codecContext - avcodec_free_context(&codecContextRef) - } + for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeVideo) { + let streamIndex = streamIndexNumber.int32Value + if avFormatContext.isAttachedPic(atStreamIndex: streamIndex) { + continue + } + + let codecId = avFormatContext.codecId(atStreamIndex: streamIndex) + + let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000)) + let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase) + + let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale) + + let metrics = avFormatContext.metricsForStream(at: streamIndex) + + let rotationAngle: Double = metrics.rotationAngle + let aspect = Double(metrics.width) / Double(metrics.height) + + if let codec = FFMpegAVCodec.find(forId: codecId) { + let codecContext = FFMpegAVCodecContext(codec: codec) + if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) { + if codecContext.open() { + videoStream = SoftwareVideoStream(index: Int(streamIndex), fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect) + break } } } @@ -139,15 +125,6 @@ final class SoftwareVideoSource { } deinit { - if let avIoContext = self.avIoContext { - if avIoContext.pointee.buffer != nil { - av_free(avIoContext.pointee.buffer) - } - av_free(avIoContext) - } - if let avFormatContext = self.avFormatContext { - avformat_free_context(avFormatContext) - } if let fd = self.fd { close(fd) } @@ -159,10 +136,10 @@ final class SoftwareVideoSource { } let packet = FFMpegPacket() - if av_read_frame(avFormatContext, &packet.packet) < 0 { - return nil - } else { + if avFormatContext.readFrame(into: packet) { return packet + } else { + return nil } } @@ -172,17 +149,15 @@ final class SoftwareVideoSource { while !self.readingError && frames.isEmpty { if let packet = self.readPacketInternal() { - if let videoStream = videoStream, Int(packet.packet.stream_index) == videoStream.index { - let avNoPtsRawValue: UInt64 = 0x8000000000000000 - let avNoPtsValue = Int64(bitPattern: avNoPtsRawValue) - let packetPts = packet.packet.pts == avNoPtsValue ? packet.packet.dts : packet.packet.pts + if let videoStream = videoStream, Int(packet.streamIndex) == videoStream.index { + let packetPts = packet.pts let pts = CMTimeMake(packetPts, videoStream.timebase.timescale) - let dts = CMTimeMake(packet.packet.dts, videoStream.timebase.timescale) + let dts = CMTimeMake(packet.dts, videoStream.timebase.timescale) let duration: CMTime - let frameDuration = packet.packet.duration + let frameDuration = packet.duration if frameDuration != 0 { duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale) } else { @@ -198,7 +173,7 @@ final class SoftwareVideoSource { } else { if let avFormatContext = self.avFormatContext, let videoStream = self.videoStream { endOfStream = true - av_seek_frame(avFormatContext, Int32(videoStream.index), 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME) + avFormatContext.seekFrame(forStreamIndex: Int32(videoStream.index), pts: 0) } else { endOfStream = true break diff --git a/TelegramUI/StorageUsageController.swift b/TelegramUI/StorageUsageController.swift index d166900bdc..69fb4e64f5 100644 --- a/TelegramUI/StorageUsageController.swift +++ b/TelegramUI/StorageUsageController.swift @@ -36,7 +36,7 @@ private enum StorageUsageEntry: ItemListNodeEntry { case clearAll(PresentationTheme, String, String, Bool) case peersHeader(PresentationTheme, String) - case peer(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, Peer?, String) + case peer(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, Peer, Peer?, String) var section: ItemListSectionId { switch self { @@ -65,7 +65,7 @@ private enum StorageUsageEntry: ItemListNodeEntry { return 4 case .peersHeader: return 5 - case let .peer(index, _, _, _, _, _, _): + case let .peer(index, _, _, _, _, _, _, _): return 6 + index } } @@ -108,8 +108,8 @@ private enum StorageUsageEntry: ItemListNodeEntry { } else { return false } - case let .peer(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsChatPeer, lhsValue): - if case let .peer(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsChatPeer, rhsValue) = rhs { + case let .peer(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsPeer, lhsChatPeer, lhsValue): + if case let .peer(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsPeer, rhsChatPeer, rhsValue) = rhs { if lhsIndex != rhsIndex { return false } @@ -122,6 +122,9 @@ private enum StorageUsageEntry: ItemListNodeEntry { if lhsDateTimeFormat != rhsDateTimeFormat { return false } + if lhsNameOrder != rhsNameOrder { + return false + } if !arePeersEqual(lhsPeer, rhsPeer) { return false } @@ -160,8 +163,8 @@ private enum StorageUsageEntry: ItemListNodeEntry { return ItemListDisclosureItem(theme: theme, icon: nil, title: text, kind: enabled ? .generic : .disabled, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.openClearAll() }) - case let .peer(_, theme, strings, dateTimeFormat, peer, chatPeer, value): - return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, account: arguments.account, peer: peer, aliasHandling: .threatSelfAsSaved, nameColor: chatPeer == nil ? .primary : .secret, presence: nil, text: .none, label: .disclosure(value), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: { + case let .peer(_, theme, strings, dateTimeFormat, nameDisplayOrder, peer, chatPeer, value): + return ItemListPeerItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, account: arguments.account, peer: peer, aliasHandling: .threatSelfAsSaved, nameColor: chatPeer == nil ? .primary : .secret, presence: nil, text: .none, label: .disclosure(value), editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, sectionId: self.section, action: { let resolvedPeer = chatPeer ?? peer arguments.openPeerMedia(resolvedPeer.id) }, setPeerIdWithRevealedOptions: { previousId, id in @@ -223,7 +226,7 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c chatPeer = mainPeer mainPeer = associatedPeer } - entries.append(.peer(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, mainPeer, chatPeer, dataSizeString(size))) + entries.append(.peer(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, mainPeer, chatPeer, dataSizeString(size))) index += 1 } } diff --git a/TelegramUI/StringForMessageTimestampStatus.swift b/TelegramUI/StringForMessageTimestampStatus.swift index fcebfcb945..3b8b979805 100644 --- a/TelegramUI/StringForMessageTimestampStatus.swift +++ b/TelegramUI/StringForMessageTimestampStatus.swift @@ -7,13 +7,13 @@ enum MessageTimestampStatusFormat { case minimal } -func stringForMessageTimestampStatus(message: Message, dateTimeFormat: PresentationDateTimeFormat, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular) -> String { +func stringForMessageTimestampStatus(message: Message, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, strings: PresentationStrings, format: MessageTimestampStatusFormat = .regular) -> String { var dateText = stringForMessageTimestamp(timestamp: message.timestamp, dateTimeFormat: dateTimeFormat) var authorTitle: String? if let author = message.author as? TelegramUser { if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { - authorTitle = author.displayTitle + authorTitle = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder) } } else { if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { diff --git a/TelegramUI/TelegramApplicationContext.swift b/TelegramUI/TelegramApplicationContext.swift index e0ecda28b4..e4e31a7f03 100644 --- a/TelegramUI/TelegramApplicationContext.swift +++ b/TelegramUI/TelegramApplicationContext.swift @@ -229,6 +229,14 @@ public final class TelegramApplicationContext { let _ = immediateExperimentalUISettingsValue.swap(settings) } }) + + let _ = self.contactDataManager.personNameDisplayOrder().start(next: { order in + let _ = updateContactSettingsInteractively(postbox: postbox, { settings in + var settings = settings + settings.nameDisplayOrder = order + return settings + }).start() + }) } deinit { diff --git a/TelegramUI/TelegramUIPrivate/module.modulemap b/TelegramUI/TelegramUIPrivate/module.modulemap index cab446b48e..0fd83698be 100644 --- a/TelegramUI/TelegramUIPrivate/module.modulemap +++ b/TelegramUI/TelegramUIPrivate/module.modulemap @@ -1,10 +1,11 @@ module TelegramUIPrivateModule { - private header "../../third-party/FFmpeg-iOS/include/libavcodec/avcodec.h" - private header "../../third-party/FFmpeg-iOS/include/libavformat/avformat.h" - private header "../../third-party/FFmpeg-iOS/include/libavformat/avio.h" - private header "../../third-party/FFmpeg-iOS/include/libavutil/avutil.h" - private header "../../third-party/FFmpeg-iOS/include/libavutil/pixdesc.h" - private header "../../third-party/FFmpeg-iOS/include/libswresample/swresample.h" + //private header "../../third-party/FFmpeg-iOS/include/libavcodec/avcodec.h" + //private header "../../third-party/FFmpeg-iOS/include/libavformat/avformat.h" + //private header "../../third-party/FFmpeg-iOS/include/libavformat/avio.h" + //private header "../../third-party/FFmpeg-iOS/include/libavutil/avutil.h" + //private header "../../third-party/FFmpeg-iOS/include/libavutil/pixdesc.h" + //private header "../../third-party/FFmpeg-iOS/include/libswresample/swresample.h" + header "../../third-party/opusenc/opusenc.h" header "../TGDataItem.h" header "../FFMpegSwResample.h" diff --git a/TelegramUI/ThemeSettingsChatPreviewItem.swift b/TelegramUI/ThemeSettingsChatPreviewItem.swift index e51dbae6cc..2c447370cc 100644 --- a/TelegramUI/ThemeSettingsChatPreviewItem.swift +++ b/TelegramUI/ThemeSettingsChatPreviewItem.swift @@ -14,8 +14,9 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem { let fontSize: PresentationFontSize let wallpaper: TelegramWallpaper let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder - init(account: Account, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat) { + init(account: Account, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) { self.account = account self.theme = theme self.componentTheme = componentTheme @@ -24,6 +25,7 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem { self.fontSize = fontSize self.wallpaper = wallpaper self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder } func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { @@ -151,7 +153,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: item.strings.Appearance_PreviewReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) - let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, disableAnimations: false) + let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, nameDisplayOrder: item.nameDisplayOrder, disableAnimations: false) let item2: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: item.strings.Appearance_PreviewIncomingText, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true) let item1: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: TelegramUser(id: item.account.peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), text: item.strings.Appearance_PreviewOutgoingText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true) diff --git a/TelegramUI/ThemeSettingsController.swift b/TelegramUI/ThemeSettingsController.swift index 8a95de01ec..ac8596f355 100644 --- a/TelegramUI/ThemeSettingsController.swift +++ b/TelegramUI/ThemeSettingsController.swift @@ -35,7 +35,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { case fontSizeHeader(PresentationTheme, String) case fontSize(PresentationTheme, PresentationFontSize) case chatPreviewHeader(PresentationTheme, String) - case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat) + case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder) case wallpaper(PresentationTheme, String) case accentColor(PresentationTheme, String, Int32) case autoNightTheme(PresentationTheme, String, String) @@ -95,8 +95,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { } else { return false } - case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat): - if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat { + case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder): + if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder { return true } else { return false @@ -178,8 +178,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry { }) case let .chatPreviewHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) - case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat): - return ThemeSettingsChatPreviewItem(account: arguments.account, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat) + case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder): + return ThemeSettingsChatPreviewItem(account: arguments.account, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder) case let .wallpaper(theme, text): return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: { arguments.openWallpaperSettings() @@ -216,7 +216,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData, entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize)) entries.append(.fontSize(presentationData.theme, fontSize)) entries.append(.chatPreviewHeader(presentationData.theme, strings.Appearance_Preview)) - entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat)) + entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder)) entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground)) if theme.name == .builtin(.day) { entries.append(.accentColor(presentationData.theme, strings.Appearance_AccentColor, themeAccentColor ?? defaultDayAccentColor)) diff --git a/third-party/FFmpeg-iOS/lib/libavcodec.a b/third-party/FFmpeg-iOS/lib/libavcodec.a index f522a21d97e56043cc88a3e11bb577ca5f62005b..260d47d0c286a93516570f030999a41d781e45f3 100644 GIT binary patch delta 52380 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�IN#vXICH51is9Pqg?yax!6hAB2o5O^G9RUy0j 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 diff --git a/third-party/FFmpeg-iOS/lib/libavformat.a b/third-party/FFmpeg-iOS/lib/libavformat.a index 25d5b11d6af39e88100dfd6ef8b2f566ef42dc85..46a855c6300f4203fff0160f7b51c96ca8a04af5 100644 GIT binary patch delta 358058 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
1anBLPcubqP3n5PKYzp=-M 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 M8B@e{-1FuC0hC-i{Qv*} diff --git a/third-party/FFmpeg-iOS/lib/libavutil.a b/third-party/FFmpeg-iOS/lib/libavutil.a index 767ce34fc99c643d538fc5884cb53df703c74991..f59375aa3cab1ebad902f89c8f7aaf6b077b9792 100644 GIT binary patch delta 10977 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 delta 10938 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 Po?@Goxap&aWRL#?>~Q)W diff --git a/third-party/FFmpeg-iOS/lib/libswresample.a b/third-party/FFmpeg-iOS/lib/libswresample.a index 8ac11e47cf9f16cb65eefd298b7a3f7e97d2b045..dbbcf0362d5d510a0987b658e8bba287a1fafbfc 100644 GIT binary patch delta 13740 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 O)V2~&{EyPJ^Zx=WhSoy> delta 13573 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 RsWQ#5ZN?2gs~phv{{ir9SUCUy From 78c8d2e565367e8bef2674c55f1cac856dbf4ddb Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 11 Dec 2018 21:59:07 +0400 Subject: [PATCH 3/3] Update project --- TelegramUI.xcodeproj/project.pbxproj | 150 +++++++++++++-------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index d02687d923..3fe52f0ba5 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -5918,7 +5918,7 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - D021D510219CB2240064BEBA /* Debug Fork */ = { + D021D510219CB2240064BEBA /* DebugFork */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -5979,9 +5979,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Debug Fork"; + name = DebugFork; }; - D021D511219CB2240064BEBA /* Debug Fork */ = { + D021D511219CB2240064BEBA /* DebugFork */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -5995,9 +5995,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Debug Fork"; + name = DebugFork; }; - D021D512219CB2240064BEBA /* Debug Fork */ = { + D021D512219CB2240064BEBA /* DebugFork */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6035,9 +6035,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Debug Fork"; + name = DebugFork; }; - D0400EDB1D5B900A007931CE /* Release AppStore */ = { + D0400EDB1D5B900A007931CE /* ReleaseAppStore */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6091,9 +6091,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Release AppStore"; + name = ReleaseAppStore; }; - D0400EDD1D5B900A007931CE /* Release AppStore */ = { + D0400EDD1D5B900A007931CE /* ReleaseAppStore */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6108,9 +6108,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Release AppStore"; + name = ReleaseAppStore; }; - D079FD261F06BEF70038FADE /* Debug AppStore */ = { + D079FD261F06BEF70038FADE /* DebugAppStore */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6171,9 +6171,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Debug AppStore"; + name = DebugAppStore; }; - D079FD271F06BEF70038FADE /* Debug AppStore */ = { + D079FD271F06BEF70038FADE /* DebugAppStore */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6187,9 +6187,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Debug AppStore"; + name = DebugAppStore; }; - D079FD281F06BEF70038FADE /* Debug AppStore */ = { + D079FD281F06BEF70038FADE /* DebugAppStore */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6227,9 +6227,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Debug AppStore"; + name = DebugAppStore; }; - D0924FEE1FE52C29003F693F /* Release Hockeyapp Internal */ = { + D0924FEE1FE52C29003F693F /* ReleaseHockeyappInternal */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6283,9 +6283,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Release Hockeyapp Internal"; + name = ReleaseHockeyappInternal; }; - D0924FEF1FE52C29003F693F /* Release Hockeyapp Internal */ = { + D0924FEF1FE52C29003F693F /* ReleaseHockeyappInternal */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6300,9 +6300,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Release Hockeyapp Internal"; + name = ReleaseHockeyappInternal; }; - D0924FF01FE52C29003F693F /* Release Hockeyapp Internal */ = { + D0924FF01FE52C29003F693F /* ReleaseHockeyappInternal */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6340,9 +6340,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Release Hockeyapp Internal"; + name = ReleaseHockeyappInternal; }; - D0ADF948212B3B0000310BBC /* Debug AppStore LLC */ = { + D0ADF948212B3B0000310BBC /* DebugAppStoreLLC */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6403,9 +6403,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Debug AppStore LLC"; + name = DebugAppStoreLLC; }; - D0ADF949212B3B0000310BBC /* Debug AppStore LLC */ = { + D0ADF949212B3B0000310BBC /* DebugAppStoreLLC */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6419,9 +6419,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Debug AppStore LLC"; + name = DebugAppStoreLLC; }; - D0ADF94A212B3B0000310BBC /* Debug AppStore LLC */ = { + D0ADF94A212B3B0000310BBC /* DebugAppStoreLLC */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6459,9 +6459,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Debug AppStore LLC"; + name = DebugAppStoreLLC; }; - D0CE6F02213DC32300BCD44B /* Release AppStore LLC */ = { + D0CE6F02213DC32300BCD44B /* ReleaseAppStoreLLC */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6515,9 +6515,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Release AppStore LLC"; + name = ReleaseAppStoreLLC; }; - D0CE6F03213DC32300BCD44B /* Release AppStore LLC */ = { + D0CE6F03213DC32300BCD44B /* ReleaseAppStoreLLC */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6532,9 +6532,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Release AppStore LLC"; + name = ReleaseAppStoreLLC; }; - D0CE6F04213DC32300BCD44B /* Release AppStore LLC */ = { + D0CE6F04213DC32300BCD44B /* ReleaseAppStoreLLC */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6570,9 +6570,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Release AppStore LLC"; + name = ReleaseAppStoreLLC; }; - D0EC6E9E1EB9F79800EBF1C3 /* Debug Hockeyapp */ = { + D0EC6E9E1EB9F79800EBF1C3 /* DebugHockeyapp */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6610,9 +6610,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Debug Hockeyapp"; + name = DebugHockeyapp; }; - D0EC6E9F1EB9F79800EBF1C3 /* Release Hockeyapp */ = { + D0EC6E9F1EB9F79800EBF1C3 /* ReleaseHockeyapp */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6650,9 +6650,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Release Hockeyapp"; + name = ReleaseHockeyapp; }; - D0EC6EA01EB9F79800EBF1C3 /* Release AppStore */ = { + D0EC6EA01EB9F79800EBF1C3 /* ReleaseAppStore */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -6688,9 +6688,9 @@ SWIFT_VERSION = 4.0; USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/third-party/FFmpeg-iOS/include"; }; - name = "Release AppStore"; + name = ReleaseAppStore; }; - D0FC40911D5B8E7500261D9D /* Debug Hockeyapp */ = { + D0FC40911D5B8E7500261D9D /* DebugHockeyapp */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6751,9 +6751,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Debug Hockeyapp"; + name = DebugHockeyapp; }; - D0FC40921D5B8E7500261D9D /* Release Hockeyapp */ = { + D0FC40921D5B8E7500261D9D /* ReleaseHockeyapp */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6807,9 +6807,9 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = "Release Hockeyapp"; + name = ReleaseHockeyapp; }; - D0FC40971D5B8E7500261D9D /* Debug Hockeyapp */ = { + D0FC40971D5B8E7500261D9D /* DebugHockeyapp */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6823,9 +6823,9 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Debug Hockeyapp"; + name = DebugHockeyapp; }; - D0FC40981D5B8E7500261D9D /* Release Hockeyapp */ = { + D0FC40981D5B8E7500261D9D /* ReleaseHockeyapp */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0F69DB91D6B88190046BCD6 /* TelegramUI.xcconfig */; buildSettings = { @@ -6840,7 +6840,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; }; - name = "Release Hockeyapp"; + name = ReleaseHockeyapp; }; /* End XCBuildConfiguration section */ @@ -6848,47 +6848,47 @@ D0EC6EA11EB9F79800EBF1C3 /* Build configuration list for PBXNativeTarget "TelegramUI" */ = { isa = XCConfigurationList; buildConfigurations = ( - D0EC6E9E1EB9F79800EBF1C3 /* Debug Hockeyapp */, - D021D512219CB2240064BEBA /* Debug Fork */, - D079FD281F06BEF70038FADE /* Debug AppStore */, - D0ADF94A212B3B0000310BBC /* Debug AppStore LLC */, - D0EC6E9F1EB9F79800EBF1C3 /* Release Hockeyapp */, - D0924FF01FE52C29003F693F /* Release Hockeyapp Internal */, - D0EC6EA01EB9F79800EBF1C3 /* Release AppStore */, - D0CE6F04213DC32300BCD44B /* Release AppStore LLC */, + D0EC6E9E1EB9F79800EBF1C3 /* DebugHockeyapp */, + D021D512219CB2240064BEBA /* DebugFork */, + D079FD281F06BEF70038FADE /* DebugAppStore */, + D0ADF94A212B3B0000310BBC /* DebugAppStoreLLC */, + D0EC6E9F1EB9F79800EBF1C3 /* ReleaseHockeyapp */, + D0924FF01FE52C29003F693F /* ReleaseHockeyappInternal */, + D0EC6EA01EB9F79800EBF1C3 /* ReleaseAppStore */, + D0CE6F04213DC32300BCD44B /* ReleaseAppStoreLLC */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Release Hockeyapp"; + defaultConfigurationName = ReleaseHockeyapp; }; D0FC40791D5B8E7400261D9D /* Build configuration list for PBXProject "TelegramUI" */ = { isa = XCConfigurationList; buildConfigurations = ( - D0FC40911D5B8E7500261D9D /* Debug Hockeyapp */, - D021D510219CB2240064BEBA /* Debug Fork */, - D079FD261F06BEF70038FADE /* Debug AppStore */, - D0ADF948212B3B0000310BBC /* Debug AppStore LLC */, - D0FC40921D5B8E7500261D9D /* Release Hockeyapp */, - D0924FEE1FE52C29003F693F /* Release Hockeyapp Internal */, - D0400EDB1D5B900A007931CE /* Release AppStore */, - D0CE6F02213DC32300BCD44B /* Release AppStore LLC */, + D0FC40911D5B8E7500261D9D /* DebugHockeyapp */, + D021D510219CB2240064BEBA /* DebugFork */, + D079FD261F06BEF70038FADE /* DebugAppStore */, + D0ADF948212B3B0000310BBC /* DebugAppStoreLLC */, + D0FC40921D5B8E7500261D9D /* ReleaseHockeyapp */, + D0924FEE1FE52C29003F693F /* ReleaseHockeyappInternal */, + D0400EDB1D5B900A007931CE /* ReleaseAppStore */, + D0CE6F02213DC32300BCD44B /* ReleaseAppStoreLLC */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Release Hockeyapp"; + defaultConfigurationName = ReleaseHockeyapp; }; D0FC40961D5B8E7500261D9D /* Build configuration list for PBXNativeTarget "TelegramUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( - D0FC40971D5B8E7500261D9D /* Debug Hockeyapp */, - D021D511219CB2240064BEBA /* Debug Fork */, - D079FD271F06BEF70038FADE /* Debug AppStore */, - D0ADF949212B3B0000310BBC /* Debug AppStore LLC */, - D0FC40981D5B8E7500261D9D /* Release Hockeyapp */, - D0924FEF1FE52C29003F693F /* Release Hockeyapp Internal */, - D0400EDD1D5B900A007931CE /* Release AppStore */, - D0CE6F03213DC32300BCD44B /* Release AppStore LLC */, + D0FC40971D5B8E7500261D9D /* DebugHockeyapp */, + D021D511219CB2240064BEBA /* DebugFork */, + D079FD271F06BEF70038FADE /* DebugAppStore */, + D0ADF949212B3B0000310BBC /* DebugAppStoreLLC */, + D0FC40981D5B8E7500261D9D /* ReleaseHockeyapp */, + D0924FEF1FE52C29003F693F /* ReleaseHockeyappInternal */, + D0400EDD1D5B900A007931CE /* ReleaseAppStore */, + D0CE6F03213DC32300BCD44B /* ReleaseAppStoreLLC */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Release Hockeyapp"; + defaultConfigurationName = ReleaseHockeyapp; }; /* End XCConfigurationList section */ };