mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Folder improvements
This commit is contained in:
parent
46ea78b6c3
commit
ceccb98af7
1
.bazelrc
1
.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
|
||||
|
@ -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<PeerId>)?, NoError> = self.chatListDisplayNode.containerNode.currentItemState
|
||||
|> map { state -> Set<PeerId>? in
|
||||
|> map { state, filterId -> (Set<PeerId>, Int32?)? in
|
||||
if !state.editing {
|
||||
return nil
|
||||
}
|
||||
return state.selectedPeerIds
|
||||
return (state.selectedPeerIds, filterId)
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { selectedPeerIds -> Signal<(ChatListSelectionOptions, Set<PeerId>)?, 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<PeerId>)?, NoError> in
|
||||
if let (selectedPeerIds, filterId) = selectedPeerIdsAndFilterId {
|
||||
return chatListSelectionOptions(postbox: context.account.postbox, peerIds: selectedPeerIds, filterId: filterId)
|
||||
|> map { options -> (ChatListSelectionOptions, Set<PeerId>)? 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
|
||||
|
@ -431,8 +431,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
return self.currentItemNodeValue!.listNode
|
||||
}
|
||||
|
||||
private let currentItemStateValue = Promise<ChatListNodeState>()
|
||||
var currentItemState: Signal<ChatListNodeState, NoError> {
|
||||
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())
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -15,23 +15,36 @@ struct ChatListSelectionOptions: Equatable {
|
||||
let delete: Bool
|
||||
}
|
||||
|
||||
func chatListSelectionOptions(postbox: Postbox, peerIds: Set<PeerId>) -> Signal<ChatListSelectionOptions, NoError> {
|
||||
func chatListSelectionOptions(postbox: Postbox, peerIds: Set<PeerId>, filterId: Int32?) -> Signal<ChatListSelectionOptions, NoError> {
|
||||
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)
|
||||
|
@ -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<Bool, NoError>.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 {
|
||||
|
@ -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<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil) {
|
||||
public init(context: AccountContext, presentation: Signal<ContactListPresentation, NoError>, 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<ContactsListNodeTransition, NoError> 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<ContactsListNodeTransition, NoError> in
|
||||
let signal = deferred { () -> Signal<ContactsListNodeTransition, NoError> in
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
var disabledPeerIds = Set<PeerId>()
|
||||
|
||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||
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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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()),
|
||||
|
Loading…
x
Reference in New Issue
Block a user