diff --git a/.bazelrc b/.bazelrc index 1627a59000..1ffa5c2f15 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,4 +1,5 @@ build --experimental_guard_against_concurrent_changes +build --action_env=ZERO_AR_DATE=1 build --strategy=Genrule=local build --apple_platform_type=ios diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 64935ccb08..29fc42f289 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -279,7 +279,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, passcode, self.chatListDisplayNode.containerNode.currentItemState, self.isReorderingTabsValue.get() - ).start(next: { [weak self] networkState, proxy, passcode, state, isReorderingTabs in + ).start(next: { [weak self] networkState, proxy, passcode, stateAndFilterId, isReorderingTabs in if let strongSelf = self { let defaultTitle: String if strongSelf.groupId == .root { @@ -287,12 +287,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } else { defaultTitle = strongSelf.presentationData.strings.ChatList_ArchivedChatsTitle } - if state.editing { + if stateAndFilterId.state.editing { if strongSelf.groupId == .root { strongSelf.navigationItem.rightBarButtonItem = nil } - let title = !state.selectedPeerIds.isEmpty ? strongSelf.presentationData.strings.ChatList_SelectedChats(Int32(state.selectedPeerIds.count)) : defaultTitle + let title = !stateAndFilterId.state.selectedPeerIds.isEmpty ? strongSelf.presentationData.strings.ChatList_SelectedChats(Int32(stateAndFilterId.state.selectedPeerIds.count)) : defaultTitle strongSelf.titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false) } else if isReorderingTabs { if strongSelf.groupId == .root { @@ -334,7 +334,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, strongSelf.navigationItem.leftBarButtonItem = leftBarButtonItem } else { let editItem: UIBarButtonItem - if state.editing { + if stateAndFilterId.state.editing { editItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(strongSelf.donePressed)) editItem.accessibilityLabel = strongSelf.presentationData.strings.Common_Done } else { @@ -798,16 +798,24 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let context = self.context let peerIdsAndOptions: Signal<(ChatListSelectionOptions, Set)?, NoError> = self.chatListDisplayNode.containerNode.currentItemState - |> map { state -> Set? in + |> map { state, filterId -> (Set, Int32?)? in if !state.editing { return nil } - return state.selectedPeerIds + return (state.selectedPeerIds, filterId) } - |> distinctUntilChanged - |> mapToSignal { selectedPeerIds -> Signal<(ChatListSelectionOptions, Set)?, NoError> in - if let selectedPeerIds = selectedPeerIds { - return chatListSelectionOptions(postbox: context.account.postbox, peerIds: selectedPeerIds) + |> distinctUntilChanged(isEqual: { lhs, rhs in + if lhs?.0 != rhs?.0 { + return false + } + if lhs?.1 != rhs?.1 { + return false + } + return true + }) + |> mapToSignal { selectedPeerIdsAndFilterId -> Signal<(ChatListSelectionOptions, Set)?, NoError> in + if let (selectedPeerIds, filterId) = selectedPeerIdsAndFilterId { + return chatListSelectionOptions(postbox: context.account.postbox, peerIds: selectedPeerIds, filterId: filterId) |> map { options -> (ChatListSelectionOptions, Set)? in return (options, selectedPeerIds) } @@ -1381,7 +1389,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, } |> distinctUntilChanged - let filterItems = chatListFilterItems(context: self.context) + let filterItems = chatListFilterItems(postbox: self.context.account.postbox) var notifiedFirstUpdate = false self.filterDisposable.set((combineLatest(queue: .mainQueue(), context.account.postbox.combinedView(keys: [ @@ -2428,7 +2436,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) { let _ = (combineLatest(queue: .mainQueue(), currentChatListFilters(postbox: self.context.account.postbox), - chatListFilterItems(context: self.context) + chatListFilterItems(postbox: self.context.account.postbox) |> take(1) ) |> deliverOnMainQueue).start(next: { [weak self] presetList, filterItemsAndTotalCount in diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 45803845a2..a05cf1d705 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -431,8 +431,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { return self.currentItemNodeValue!.listNode } - private let currentItemStateValue = Promise() - var currentItemState: Signal { + private let currentItemStateValue = Promise<(state: ChatListNodeState, filterId: Int32?)>() + var currentItemState: Signal<(state: ChatListNodeState, filterId: Int32?), NoError> { return self.currentItemStateValue.get() } @@ -502,7 +502,16 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate { self?.didBeginSelectingChats?() } - self.currentItemStateValue.set(itemNode.listNode.state) + self.currentItemStateValue.set(itemNode.listNode.state |> map { state in + let filterId: Int32? + switch id { + case .all: + filterId = nil + case let .filter(filter): + filterId = filter + } + return (state, filterId) + }) if self.controlsHistoryPreload { self.context.account.viewTracker.chatListPreloadItems.set(itemNode.listNode.preloadItems.get()) diff --git a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift index cd96d2c51a..35624cda4e 100644 --- a/submodules/ChatListUI/Sources/ChatListEmptyNode.swift +++ b/submodules/ChatListUI/Sources/ChatListEmptyNode.swift @@ -95,12 +95,22 @@ final class ChatListEmptyNode: ASDisplayNode { } self.updateThemeAndStrings(theme: theme, strings: strings) + + self.animationNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.animationTapGesture(_:)))) } @objc private func buttonPressed() { self.action() } + @objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + if !self.animationNode.isPlaying { + self.animationNode.play() + } + } + } + func restartAnimation() { self.animationNode.play() } @@ -189,6 +199,9 @@ final class ChatListEmptyNode: ASDisplayNode { if self.buttonNode.frame.contains(point) { return self.buttonNode.view.hitTest(self.view.convert(point, to: self.buttonNode.view), with: event) } + if self.animationNode.frame.contains(point) { + return self.animationNode.view.hitTest(self.view.convert(point, to: self.animationNode.view), with: event) + } return nil } } diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index bd832ebb9c..b8995512ad 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -330,7 +330,7 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { case let .nameHeader(title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) case let .name(placeholder, value): - return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: value, placeholder: placeholder, type: .regular(capitalization: true, autocorrection: false), clearType: .always, maxLength: 20, sectionId: self.section, textUpdated: { value in + return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(), text: value, placeholder: placeholder, type: .regular(capitalization: true, autocorrection: false), clearType: .always, maxLength: 12, sectionId: self.section, textUpdated: { value in arguments.updateState { current in var state = current state.name = value @@ -480,7 +480,7 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio for peer in includePeers { entries.append(.includePeer(index: entries.count, peer: peer, isRevealed: state.revealedItemId == .peer(peer.peerId))) count += 1 - if includePeers.count >= 6 && count == 5 && !state.expandedSections.contains(.include) { + if includePeers.count >= 7 && count == 5 && !state.expandedSections.contains(.include) { break } } @@ -517,7 +517,7 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio for peer in excludePeers { entries.append(.excludePeer(index: entries.count, peer: peer, isRevealed: state.revealedItemId == .peer(peer.peerId))) count += 1 - if excludePeers.count >= 6 && count == 5 && !state.expandedSections.contains(.exclude) { + if excludePeers.count >= 7 && count == 5 && !state.expandedSections.contains(.exclude) { break } } @@ -592,7 +592,7 @@ private func internalChatListFilterAddChatsController(context: AccountContext, f } } - let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_IncludeChatsTitle, selectedChats: Set(filter.data.includePeers.peers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories)), options: [], alwaysEnabled: true)) + let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_IncludeChatsTitle, selectedChats: Set(filter.data.includePeers.peers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories)), options: [], filters: [], alwaysEnabled: true)) controller.navigationPresentation = .modal let _ = (controller.result |> take(1) @@ -679,7 +679,7 @@ private func internalChatListFilterExcludeChatsController(context: AccountContex selectedCategories.insert(AdditionalExcludeCategoryId.archived.rawValue) } - let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_ExcludeChatsTitle, selectedChats: Set(filter.data.excludePeers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories)), options: [], alwaysEnabled: true)) + let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(title: presentationData.strings.ChatListFolder_ExcludeChatsTitle, selectedChats: Set(filter.data.excludePeers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories)), options: [], filters: [], alwaysEnabled: true)) controller.navigationPresentation = .modal let _ = (controller.result |> take(1) diff --git a/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift b/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift index 006a39ce43..b80cceed46 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterSettingsHeaderItem.swift @@ -94,7 +94,9 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode { @objc private func animationTapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { - self.animationNode.play() + if !self.animationNode.isPlaying { + self.animationNode.play() + } } } diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift index 784f4d99d4..56e49f5360 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabContainerNode.swift @@ -254,10 +254,6 @@ private final class ItemNode: ASDisplayNode { width = badgeBackgroundFrame.maxX } - let extractedBackgroundHeight: CGFloat = 36.0 - let extractedBackgroundInset: CGFloat = 14.0 - self.extractedBackgroundNode.frame = CGRect(origin: CGPoint(x: -extractedBackgroundInset, y: floor((height - extractedBackgroundHeight) / 2.0)), size: CGSize(width: width + extractedBackgroundInset * 2.0, height: extractedBackgroundHeight)) - return (width, shortTitleSize.width - self.shortTitleNode.insets.left - self.shortTitleNode.insets.right + 5.0) } @@ -276,6 +272,10 @@ private final class ItemNode: ASDisplayNode { self.extractedContainerNode.hitTestSlop = self.hitTestSlop self.extractedContainerNode.contentNode.hitTestSlop = self.hitTestSlop self.containerNode.hitTestSlop = self.hitTestSlop + + let extractedBackgroundHeight: CGFloat = 36.0 + let extractedBackgroundInset: CGFloat = 14.0 + self.extractedBackgroundNode.frame = CGRect(origin: CGPoint(x: -extractedBackgroundInset, y: floor((size.height - extractedBackgroundHeight) / 2.0)), size: CGSize(width: size.width + extractedBackgroundInset * 2.0, height: extractedBackgroundHeight)) } func animateBadgeIn() { diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift index b26e2012fa..eace33b6c6 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift @@ -254,10 +254,6 @@ private final class ItemNode: ASDisplayNode { width = badgeBackgroundFrame.maxX } - let extractedBackgroundHeight: CGFloat = 32.0 - let extractedBackgroundInset: CGFloat = 14.0 - self.extractedBackgroundNode.frame = CGRect(origin: CGPoint(x: -extractedBackgroundInset, y: floor((height - extractedBackgroundHeight) / 2.0)), size: CGSize(width: width + extractedBackgroundInset * 2.0, height: extractedBackgroundHeight)) - return (width, shortTitleSize.width - self.shortTitleNode.insets.left - self.shortTitleNode.insets.right) } @@ -276,6 +272,10 @@ private final class ItemNode: ASDisplayNode { self.extractedContainerNode.hitTestSlop = self.hitTestSlop self.extractedContainerNode.contentNode.hitTestSlop = self.hitTestSlop self.containerNode.hitTestSlop = self.hitTestSlop + + let extractedBackgroundHeight: CGFloat = 32.0 + let extractedBackgroundInset: CGFloat = 14.0 + self.extractedBackgroundNode.frame = CGRect(origin: CGPoint(x: -extractedBackgroundInset, y: floor((size.height - extractedBackgroundHeight) / 2.0)), size: CGSize(width: size.width + extractedBackgroundInset * 2.0, height: extractedBackgroundHeight)) } func animateBadgeIn() { diff --git a/submodules/ChatListUI/Sources/ChatListSelection.swift b/submodules/ChatListUI/Sources/ChatListSelection.swift index 039a91e555..869a2fae27 100644 --- a/submodules/ChatListUI/Sources/ChatListSelection.swift +++ b/submodules/ChatListUI/Sources/ChatListSelection.swift @@ -15,23 +15,36 @@ struct ChatListSelectionOptions: Equatable { let delete: Bool } -func chatListSelectionOptions(postbox: Postbox, peerIds: Set) -> Signal { +func chatListSelectionOptions(postbox: Postbox, peerIds: Set, filterId: Int32?) -> Signal { if peerIds.isEmpty { - let key = PostboxViewKey.unreadCounts(items: [.total(nil)]) - return postbox.combinedView(keys: [key]) - |> map { view -> ChatListSelectionOptions in - var hasUnread = false - if let unreadCounts = view.views[key] as? UnreadMessageCountsView, let total = unreadCounts.total() { - for (_, counter) in total.1.absoluteCounters { - if counter.messageCount != 0 { - hasUnread = true - break + if let filterId = filterId { + return chatListFilterItems(postbox: postbox) + |> map { filterItems -> ChatListSelectionOptions in + for (filter, unreadCount, _) in filterItems.1 { + if filter.id == filterId { + return ChatListSelectionOptions(read: .all(enabled: unreadCount != 0), delete: false) } } + return ChatListSelectionOptions(read: .all(enabled: false), delete: false) } - return ChatListSelectionOptions(read: .all(enabled: hasUnread), delete: false) + |> distinctUntilChanged + } else { + let key = PostboxViewKey.unreadCounts(items: [.total(nil)]) + return postbox.combinedView(keys: [key]) + |> map { view -> ChatListSelectionOptions in + var hasUnread = false + if let unreadCounts = view.views[key] as? UnreadMessageCountsView, let total = unreadCounts.total() { + for (_, counter) in total.1.absoluteCounters { + if counter.messageCount != 0 { + hasUnread = true + break + } + } + } + return ChatListSelectionOptions(read: .all(enabled: hasUnread), delete: false) + } + |> distinctUntilChanged } - |> distinctUntilChanged } else { let items: [UnreadMessageCountsItem] = peerIds.map(UnreadMessageCountsItem.peer) let key = PostboxViewKey.unreadCounts(items: items) diff --git a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift index e6d8c68c8d..18d16492de 100644 --- a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift +++ b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift @@ -10,8 +10,8 @@ import Postbox import TelegramUIPreferences import TelegramCore -func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> { - return updatedChatListFilters(postbox: context.account.postbox) +func chatListFilterItems(postbox: Postbox) -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> { + return updatedChatListFilters(postbox: postbox) |> distinctUntilChanged |> mapToSignal { filters -> Signal<(Int, [(ChatListFilter, Int, Bool)]), NoError> in var unreadCountItems: [UnreadMessageCountsItem] = [] @@ -40,8 +40,8 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt keys.append(.basicPeer(peerId)) } - return combineLatest(queue: context.account.postbox.queue, - context.account.postbox.combinedView(keys: keys), + return combineLatest(queue: postbox.queue, + postbox.combinedView(keys: keys), Signal.single(true) ) |> map { view, _ -> (Int, [(ChatListFilter, Int, Bool)]) in @@ -63,7 +63,7 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt case let .peer(peerId, state): if let state = state, state.isUnread { if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer { - let tag = context.account.postbox.seedConfiguration.peerSummaryCounterTags(peer, peerView.isContact) + let tag = postbox.seedConfiguration.peerSummaryCounterTags(peer, peerView.isContact) var peerCount = Int(state.count) if state.isUnread { diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 9c0c08d47d..3a327286f3 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -152,7 +152,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } } - func item(context: AccountContext, presentationData: PresentationData, interaction: ContactListNodeInteraction) -> ListViewItem { + func item(context: AccountContext, presentationData: PresentationData, interaction: ContactListNodeInteraction, isSearch: Bool) -> ListViewItem { switch self { case let .search(theme, strings): return ChatListSearchItem(theme: theme, placeholder: strings.Contacts_SearchLabel, activate: { @@ -177,7 +177,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { case let .option(_, option, header, theme, _): return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action) case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, enabled): - let status: ContactsPeerItemStatus + var status: ContactsPeerItemStatus let itemPeer: ContactsPeerItemPeer var isContextActionEnabled = false switch peer { @@ -214,6 +214,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable { status = .none itemPeer = .deviceContact(stableId: id, contact: contact) } + if isSearch { + status = .none + } var itemContextAction: ((ASDisplayNode, ContextGesture?) -> Void)? if isContextActionEnabled, let contextAction = interaction.contextAction { itemContextAction = { node, gesture in @@ -227,7 +230,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } } } - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: isSearch ? .generalSearch : .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in interaction.openPeer(peer) }, itemHighlighting: interaction.itemHighlighting, contextAction: itemContextAction) } @@ -608,12 +611,12 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer] return entries } -private func preparedContactListNodeTransition(context: AccountContext, presentationData: PresentationData, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, generateIndexSections: Bool, animation: ContactListAnimation) -> ContactsListNodeTransition { +private func preparedContactListNodeTransition(context: AccountContext, presentationData: PresentationData, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, generateIndexSections: Bool, animation: ContactListAnimation, isSearch: 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(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction, isSearch: isSearch), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, interaction: interaction, isSearch: isSearch), directionHint: nil) } var shouldFixScroll = false var indexSections: [String] = [] @@ -783,7 +786,7 @@ public final class ContactListNode: ASDisplayNode { private var authorizationNode: PermissionContentNode private let displayPermissionPlaceholder: Bool - public init(context: AccountContext, presentation: Signal, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil) { + public init(context: AccountContext, presentation: Signal, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil, isSearch: Bool = false) { self.context = context self.filters = filters self.displayPermissionPlaceholder = displayPermissionPlaceholder @@ -926,6 +929,7 @@ public final class ContactListNode: ASDisplayNode { foundLocalContacts = foundChatListPeers |> mapToSignal { peers -> Signal<([FoundPeer], [PeerId: PeerPresence]), NoError> in var resultPeers: [FoundPeer] = [] + for peer in peers { if searchGroups || searchChannels { let mainPeer = peer.chatMainPeer @@ -991,16 +995,21 @@ public final class ContactListNode: ASDisplayNode { foundDeviceContacts = .single([:]) } - return combineLatest(foundLocalContacts, foundRemoteContacts, foundDeviceContacts, selectionStateSignal, presentationDataPromise.get()) - |> mapToQueue { localPeersAndStatuses, remotePeers, deviceContacts, selectionState, presentationData -> Signal in + let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) + |> take(1) + + return combineLatest(accountPeer, foundLocalContacts, foundRemoteContacts, foundDeviceContacts, selectionStateSignal, presentationDataPromise.get()) + |> mapToQueue { accountPeer, localPeersAndStatuses, remotePeers, deviceContacts, selectionState, presentationData -> Signal in let signal = deferred { () -> Signal in var existingPeerIds = Set() var disabledPeerIds = Set() var existingNormalizedPhoneNumbers = Set() + var excludeSelf = false for filter in filters { switch filter { case .excludeSelf: + excludeSelf = true existingPeerIds.insert(context.account.peerId) case let .exclude(peerIds): existingPeerIds = existingPeerIds.union(peerIds) @@ -1010,6 +1019,15 @@ public final class ContactListNode: ASDisplayNode { } var peers: [ContactListPeer] = [] + + if !excludeSelf && !existingPeerIds.contains(accountPeer.id) { + let lowercasedQuery = query.lowercased() + if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) { + existingPeerIds.insert(accountPeer.id) + peers.append(.peer(peer: accountPeer, isGlobal: false, participantCount: nil)) + } + } + for peer in localPeersAndStatuses.0 { if !existingPeerIds.contains(peer.peer.id) { existingPeerIds.insert(peer.peer.id) @@ -1097,7 +1115,7 @@ public final class ContactListNode: ASDisplayNode { let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false) let previous = previousEntries.swap(entries) - return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none)) + return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none, isSearch: isSearch)) } if OSAtomicCompareAndSwap32(1, 0, &firstTime) { @@ -1203,7 +1221,7 @@ public final class ContactListNode: ASDisplayNode { animation = .none } - return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, generateIndexSections: generateSections, animation: animation)) + return .single(preparedContactListNodeTransition(context: context, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, generateIndexSections: generateSections, animation: animation, isSearch: isSearch)) } if OSAtomicCompareAndSwap32(1, 0, &firstTime) { diff --git a/submodules/TelegramCore/Sources/RemovePeerChat.swift b/submodules/TelegramCore/Sources/RemovePeerChat.swift index 34e4b76797..dbe8bbdc10 100644 --- a/submodules/TelegramCore/Sources/RemovePeerChat.swift +++ b/submodules/TelegramCore/Sources/RemovePeerChat.swift @@ -34,6 +34,18 @@ public func removePeerChat(account: Account, transaction: Transaction, mediaBox: } }) } + updateChatListFiltersInteractively(transaction: transaction, { filters in + var filters = filters + for i in 0 ..< filters.count { + if filters[i].data.includePeers.peers.contains(peerId) { + filters[i].data.includePeers.setPeers(filters[i].data.includePeers.peers.filter { $0 != peerId }) + } + if filters[i].data.excludePeers.contains(peerId) { + filters[i].data.excludePeers = filters[i].data.excludePeers.filter { $0 != peerId } + } + } + return filters + }) if peerId.namespace == Namespaces.Peer.SecretChat { if let state = transaction.getPeerChatState(peerId) as? SecretChatState, state.embeddedState != .terminated { let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.terminate(reportSpam: reportChatSpam), state: state).withUpdatedEmbeddedState(.terminated) diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index e1d476a34c..2562517f08 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -7,6 +7,7 @@ import SwiftSignalKit import TelegramCore import SyncCore import TelegramPresentationData +import TelegramUIPreferences import ProgressNavigationButtonNode import AccountContext import AlertUI @@ -14,6 +15,14 @@ import PresentationDataUtils import ContactListUI import CounterContollerTitleView +private func peerTokenTitle(accountPeerId: PeerId, peer: Peer, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) -> String { + if peer.id == accountPeerId { + return strings.DialogList_SavedMessages + } else { + return peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) + } +} + class ContactMultiselectionControllerImpl: ViewController, ContactMultiselectionController { private let params: ContactMultiselectionControllerParams private let context: AccountContext @@ -132,7 +141,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection } } strongSelf.contactsNode.editableTokens.append(contentsOf: peers.map { peer -> EditableTokenListToken in - return EditableTokenListToken(id: peer.id, title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) + return EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: params.context.account.peerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) }) strongSelf._peersReady.set(.single(true)) if strongSelf.isNodeLoaded { @@ -203,7 +212,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection } override func loadDisplayNode() { - self.displayNode = ContactMultiselectionControllerNode(context: self.context, mode: self.mode, options: self.options, filters: filters) + self.displayNode = ContactMultiselectionControllerNode(context: self.context, mode: self.mode, options: self.options, filters: self.filters) switch self.contactsNode.contentNode { case let .contacts(contactsNode): self._listReady.set(contactsNode.ready) @@ -211,6 +220,8 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection self._listReady.set(chatsNode.ready) } + let accountPeerId = self.context.account.peerId + self.contactsNode.dismiss = { [weak self] in self?.presentingViewController?.dismiss(animated: true, completion: nil) } @@ -237,7 +248,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection displayCountAlert = true updatedState = updatedState.withToggledPeerId(.peer(peer.id)) } else { - addedToken = EditableTokenListToken(id: peer.id, title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) + addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) } } updatedCount = updatedState.selectedPeerIndices.count @@ -254,7 +265,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection state.selectedPeerIds.remove(peer.id) removedTokenId = peer.id } else { - addedToken = EditableTokenListToken(id: peer.id, title: peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) + addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) state.selectedPeerIds.insert(peer.id) } updatedCount = state.selectedPeerIds.count diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index 4db44a8d9d..bbc71b2474 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -86,7 +86,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { if case let .chatSelection(_, selectedChats, additionalCategories) = mode { placeholder = self.presentationData.strings.ChatListFilter_AddChatsTitle - let chatListNode = ChatListNode(context: context, groupId: .root, previewing: false, fillPreloadItems: false, mode: .peers(filter: [.excludeSavedMessages], isSelecting: true, additionalCategories: additionalCategories?.categories ?? []), theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) + let chatListNode = ChatListNode(context: context, groupId: .root, previewing: false, fillPreloadItems: false, mode: .peers(filter: [], isSelecting: true, additionalCategories: additionalCategories?.categories ?? []), theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true) chatListNode.updateState { state in var state = state for peerId in selectedChats { @@ -182,7 +182,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { searchChannels = true globalSearch = false } - let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false, searchGroups: searchGroups, searchChannels: searchChannels, globalSearch: globalSearch)), filters: filters, selectionState: selectionState) + let searchResultsNode = ContactListNode(context: context, presentation: .single(.search(signal: searchText.get(), searchChatList: searchChatList, searchDeviceContacts: false, searchGroups: searchGroups, searchChannels: searchChannels, globalSearch: globalSearch)), filters: filters, selectionState: selectionState, isSearch: true) searchResultsNode.openPeer = { peer in self?.tokenListNode.setText("") self?.openPeer?(peer) diff --git a/submodules/rlottie/BUCK b/submodules/rlottie/BUCK index c9a5e016b3..6a595b8462 100644 --- a/submodules/rlottie/BUCK +++ b/submodules/rlottie/BUCK @@ -17,6 +17,7 @@ static_library( compiler_flags = [ "-Dpixman_region_selfcheck(x)=1", "-DLOTTIE_DISABLE_ARM_NEON=1", + "-DLOTTIE_IMAGE_MODULE_DISABLED=1", ], headers = glob([ "rlottie/src/**/*.h", diff --git a/submodules/rlottie/BUILD b/submodules/rlottie/BUILD index 3e765bd718..a0a038abbb 100644 --- a/submodules/rlottie/BUILD +++ b/submodules/rlottie/BUILD @@ -24,6 +24,7 @@ objc_library( copts = [ "-Dpixman_region_selfcheck(x)=1", "-DLOTTIE_DISABLE_ARM_NEON=1", + "-DLOTTIE_IMAGE_MODULE_DISABLED=1", "-I{}".format(package_name()), "-I{}/rlottie/inc".format(package_name()), "-I{}/rlottie/src/vector".format(package_name()),