mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Support expand/collapse in chat search results
This commit is contained in:
parent
264189bf48
commit
1d1e976328
@ -5158,3 +5158,6 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Map.HomeAndWorkTitle" = "Home & Work Addresses";
|
||||
"Map.HomeAndWorkInfo" = "Telegram uses the Home and Work addresses from your Contact Card.\n\nKeep your Contact Card up to date for quick access to sending Home and Work addresses.";
|
||||
"Map.SearchNoResultsDescription" = "There were no results for \"%@\".\nTry a new search.";
|
||||
|
||||
"ChatList.Search.ShowMore" = "Show more";
|
||||
"ChatList.Search.ShowLess" = "Show less";
|
||||
|
@ -46,16 +46,18 @@ public final class ChatListSearchItemHeader: ListViewItemHeader {
|
||||
}
|
||||
|
||||
public func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
|
||||
|
||||
(node as? ChatListSearchItemHeaderNode)?.update(type: self.type, actionTitle: self.actionTitle, action: self.action)
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode {
|
||||
private let type: ChatListSearchItemHeaderType
|
||||
private var type: ChatListSearchItemHeaderType
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
private let actionTitle: String?
|
||||
private let action: (() -> Void)?
|
||||
private var actionTitle: String?
|
||||
private var action: (() -> Void)?
|
||||
|
||||
private var validLayout: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)?
|
||||
|
||||
private let sectionHeaderNode: ListSectionHeaderNode
|
||||
|
||||
@ -71,34 +73,34 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode {
|
||||
super.init()
|
||||
|
||||
switch type {
|
||||
case .localPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionDialogs.uppercased()
|
||||
case .members:
|
||||
self.sectionHeaderNode.title = strings.Channel_Info_Members.uppercased()
|
||||
case .contacts:
|
||||
self.sectionHeaderNode.title = strings.Contacts_TopSection.uppercased()
|
||||
case .bots:
|
||||
self.sectionHeaderNode.title = strings.MemberSearch_BotSection.uppercased()
|
||||
case .admins:
|
||||
self.sectionHeaderNode.title = strings.Channel_Management_Title.uppercased()
|
||||
case .globalPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionGlobal.uppercased()
|
||||
case .deviceContacts:
|
||||
self.sectionHeaderNode.title = strings.Contacts_NotRegisteredSection.uppercased()
|
||||
case .messages:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionMessages.uppercased()
|
||||
case .recentPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionRecent.uppercased()
|
||||
case .phoneNumber:
|
||||
self.sectionHeaderNode.title = strings.Contacts_PhoneNumber.uppercased()
|
||||
case .exceptions:
|
||||
self.sectionHeaderNode.title = strings.GroupInfo_Permissions_Exceptions.uppercased()
|
||||
case .addToExceptions:
|
||||
self.sectionHeaderNode.title = strings.Exceptions_AddToExceptions.uppercased()
|
||||
case .mapAddress:
|
||||
self.sectionHeaderNode.title = strings.Map_AddressOnMap.uppercased()
|
||||
case .nearbyVenues:
|
||||
self.sectionHeaderNode.title = strings.Map_PlacesNearby.uppercased()
|
||||
case .localPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionDialogs.uppercased()
|
||||
case .members:
|
||||
self.sectionHeaderNode.title = strings.Channel_Info_Members.uppercased()
|
||||
case .contacts:
|
||||
self.sectionHeaderNode.title = strings.Contacts_TopSection.uppercased()
|
||||
case .bots:
|
||||
self.sectionHeaderNode.title = strings.MemberSearch_BotSection.uppercased()
|
||||
case .admins:
|
||||
self.sectionHeaderNode.title = strings.Channel_Management_Title.uppercased()
|
||||
case .globalPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionGlobal.uppercased()
|
||||
case .deviceContacts:
|
||||
self.sectionHeaderNode.title = strings.Contacts_NotRegisteredSection.uppercased()
|
||||
case .messages:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionMessages.uppercased()
|
||||
case .recentPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionRecent.uppercased()
|
||||
case .phoneNumber:
|
||||
self.sectionHeaderNode.title = strings.Contacts_PhoneNumber.uppercased()
|
||||
case .exceptions:
|
||||
self.sectionHeaderNode.title = strings.GroupInfo_Permissions_Exceptions.uppercased()
|
||||
case .addToExceptions:
|
||||
self.sectionHeaderNode.title = strings.Exceptions_AddToExceptions.uppercased()
|
||||
case .mapAddress:
|
||||
self.sectionHeaderNode.title = strings.Map_AddressOnMap.uppercased()
|
||||
case .nearbyVenues:
|
||||
self.sectionHeaderNode.title = strings.Map_PlacesNearby.uppercased()
|
||||
}
|
||||
|
||||
self.sectionHeaderNode.action = actionTitle
|
||||
@ -112,7 +114,51 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode {
|
||||
self.sectionHeaderNode.updateTheme(theme: theme)
|
||||
}
|
||||
|
||||
public func update(type: ChatListSearchItemHeaderType, actionTitle: String?, action: (() -> Void)?) {
|
||||
self.actionTitle = actionTitle
|
||||
self.action = action
|
||||
|
||||
switch type {
|
||||
case .localPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionDialogs.uppercased()
|
||||
case .members:
|
||||
self.sectionHeaderNode.title = strings.Channel_Info_Members.uppercased()
|
||||
case .contacts:
|
||||
self.sectionHeaderNode.title = strings.Contacts_TopSection.uppercased()
|
||||
case .bots:
|
||||
self.sectionHeaderNode.title = strings.MemberSearch_BotSection.uppercased()
|
||||
case .admins:
|
||||
self.sectionHeaderNode.title = strings.Channel_Management_Title.uppercased()
|
||||
case .globalPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionGlobal.uppercased()
|
||||
case .deviceContacts:
|
||||
self.sectionHeaderNode.title = strings.Contacts_NotRegisteredSection.uppercased()
|
||||
case .messages:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionMessages.uppercased()
|
||||
case .recentPeers:
|
||||
self.sectionHeaderNode.title = strings.DialogList_SearchSectionRecent.uppercased()
|
||||
case .phoneNumber:
|
||||
self.sectionHeaderNode.title = strings.Contacts_PhoneNumber.uppercased()
|
||||
case .exceptions:
|
||||
self.sectionHeaderNode.title = strings.GroupInfo_Permissions_Exceptions.uppercased()
|
||||
case .addToExceptions:
|
||||
self.sectionHeaderNode.title = strings.Exceptions_AddToExceptions.uppercased()
|
||||
case .mapAddress:
|
||||
self.sectionHeaderNode.title = strings.Map_AddressOnMap.uppercased()
|
||||
case .nearbyVenues:
|
||||
self.sectionHeaderNode.title = strings.Map_PlacesNearby.uppercased()
|
||||
}
|
||||
|
||||
self.sectionHeaderNode.action = actionTitle
|
||||
self.sectionHeaderNode.activateAction = action
|
||||
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
self.sectionHeaderNode.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset)
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
|
||||
self.validLayout = (size, leftInset, rightInset)
|
||||
self.sectionHeaderNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.sectionHeaderNode.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset)
|
||||
}
|
||||
|
@ -228,17 +228,23 @@ public enum ChatListSearchEntryStableId: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChatListSearchSectionExpandType {
|
||||
case none
|
||||
case expand
|
||||
case collapse
|
||||
}
|
||||
|
||||
public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
case localPeer(Peer, Peer?, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)
|
||||
case globalPeer(FoundPeer, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)
|
||||
case localPeer(Peer, Peer?, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType)
|
||||
case globalPeer(FoundPeer, (Int32, Bool)?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, ChatListSearchSectionExpandType)
|
||||
case message(Message, RenderedPeer, CombinedPeerReadState?, ChatListPresentationData)
|
||||
case addContact(String, PresentationTheme, PresentationStrings)
|
||||
|
||||
public var stableId: ChatListSearchEntryStableId {
|
||||
switch self {
|
||||
case let .localPeer(peer, _, _, _, _, _, _, _):
|
||||
case let .localPeer(peer, _, _, _, _, _, _, _, _):
|
||||
return .localPeerId(peer.id)
|
||||
case let .globalPeer(peer, _, _, _, _, _, _):
|
||||
case let .globalPeer(peer, _, _, _, _, _, _, _):
|
||||
return .globalPeerId(peer.peer.id)
|
||||
case let .message(message, _, _, _):
|
||||
return .messageId(message.id)
|
||||
@ -249,14 +255,14 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
|
||||
public static func ==(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .localPeer(lhsPeer, lhsAssociatedPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder):
|
||||
if case let .localPeer(rhsPeer, rhsAssociatedPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder) = rhs, lhsPeer.isEqual(rhsPeer) && arePeersEqual(lhsAssociatedPeer, rhsAssociatedPeer) && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge?.0 == rhsUnreadBadge?.0 && lhsUnreadBadge?.1 == rhsUnreadBadge?.1 {
|
||||
case let .localPeer(lhsPeer, lhsAssociatedPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder, lhsExpandType):
|
||||
if case let .localPeer(rhsPeer, rhsAssociatedPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder, rhsExpandType) = rhs, lhsPeer.isEqual(rhsPeer) && arePeersEqual(lhsAssociatedPeer, rhsAssociatedPeer) && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge?.0 == rhsUnreadBadge?.0 && lhsUnreadBadge?.1 == rhsUnreadBadge?.1 && lhsExpandType == rhsExpandType {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .globalPeer(lhsPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder):
|
||||
if case let .globalPeer(rhsPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge?.0 == rhsUnreadBadge?.0 && lhsUnreadBadge?.1 == rhsUnreadBadge?.1 {
|
||||
case let .globalPeer(lhsPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder, lhsExpandType):
|
||||
if case let .globalPeer(rhsPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder, rhsExpandType) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge?.0 == rhsUnreadBadge?.0 && lhsUnreadBadge?.1 == rhsUnreadBadge?.1 && lhsExpandType == rhsExpandType {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -302,17 +308,17 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
|
||||
public static func <(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .localPeer(_, _, _, lhsIndex, _, _, _, _):
|
||||
if case let .localPeer(_, _, _, rhsIndex, _, _, _, _) = rhs {
|
||||
case let .localPeer(_, _, _, lhsIndex, _, _, _, _, _):
|
||||
if case let .localPeer(_, _, _, rhsIndex, _, _, _, _, _) = rhs {
|
||||
return lhsIndex <= rhsIndex
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
case let .globalPeer(_, _, lhsIndex, _, _, _, _):
|
||||
case let .globalPeer(_, _, lhsIndex, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case .localPeer:
|
||||
return false
|
||||
case let .globalPeer(_, _, rhsIndex, _, _, _, _):
|
||||
case let .globalPeer(_, _, rhsIndex, _, _, _, _, _):
|
||||
return lhsIndex <= rhsIndex
|
||||
case .message, .addContact:
|
||||
return true
|
||||
@ -330,9 +336,9 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
||||
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void) -> ListViewItem {
|
||||
switch self {
|
||||
case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
|
||||
case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType):
|
||||
let primaryPeer: Peer
|
||||
var chatPeer: Peer?
|
||||
if let associatedPeer = associatedPeer {
|
||||
@ -377,11 +383,22 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
badge = ContactsPeerItemBadge(count: unreadBadge.0, type: unreadBadge.1 ? .inactive : .active)
|
||||
}
|
||||
|
||||
let header:ChatListSearchItemHeader?
|
||||
let header: ChatListSearchItemHeader?
|
||||
if filter.contains(.removeSearchHeader) {
|
||||
header = nil
|
||||
} else {
|
||||
header = ChatListSearchItemHeader(type: .localPeers, theme: theme, strings: strings, actionTitle: nil, action: nil)
|
||||
let actionTitle: String?
|
||||
switch expandType {
|
||||
case .none:
|
||||
actionTitle = nil
|
||||
case .expand:
|
||||
actionTitle = strings.ChatList_Search_ShowMore
|
||||
case .collapse:
|
||||
actionTitle = strings.ChatList_Search_ShowLess
|
||||
}
|
||||
header = ChatListSearchItemHeader(type: .localPeers, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : {
|
||||
toggleExpandLocalResults()
|
||||
})
|
||||
}
|
||||
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: context.account, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
@ -395,7 +412,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
})
|
||||
case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
|
||||
case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder, expandType):
|
||||
var enabled = true
|
||||
if filter.contains(.onlyWriteable) {
|
||||
enabled = canSendMessagesToPeer(peer.peer)
|
||||
@ -429,11 +446,22 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
badge = ContactsPeerItemBadge(count: unreadBadge.0, type: unreadBadge.1 ? .inactive : .active)
|
||||
}
|
||||
|
||||
let header:ChatListSearchItemHeader?
|
||||
let header: ChatListSearchItemHeader?
|
||||
if filter.contains(.removeSearchHeader) {
|
||||
header = nil
|
||||
} else {
|
||||
header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil)
|
||||
let actionTitle: String?
|
||||
switch expandType {
|
||||
case .none:
|
||||
actionTitle = nil
|
||||
case .expand:
|
||||
actionTitle = strings.ChatList_Search_ShowMore
|
||||
case .collapse:
|
||||
actionTitle = strings.ChatList_Search_ShowLess
|
||||
}
|
||||
header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: actionTitle, action: actionTitle == nil ? nil : {
|
||||
toggleExpandGlobalResults()
|
||||
})
|
||||
}
|
||||
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: context.account, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
@ -483,12 +511,12 @@ private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [
|
||||
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
|
||||
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) -> ChatListSearchContainerTransition {
|
||||
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void) -> ChatListSearchContainerTransition {
|
||||
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, enableHeaders: enableHeaders, filter: filter, interaction: interaction, peerContextAction: peerContextAction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, interaction: interaction, peerContextAction: peerContextAction), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, interaction: interaction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enableHeaders: enableHeaders, filter: filter, interaction: interaction, peerContextAction: peerContextAction, toggleExpandLocalResults: toggleExpandLocalResults, toggleExpandGlobalResults: toggleExpandGlobalResults), directionHint: nil) }
|
||||
|
||||
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults)
|
||||
}
|
||||
@ -512,6 +540,11 @@ private struct ChatListSearchContainerNodeState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private struct ChatListSearchContainerNodeSearchState: Equatable {
|
||||
var expandLocalSearch: Bool = false
|
||||
var expandGlobalSearch: Bool = false
|
||||
}
|
||||
|
||||
private func doesPeerMatchFilter(peer: Peer, filter: ChatListNodePeersFilter) -> Bool {
|
||||
var enabled = true
|
||||
if filter.contains(.onlyWriteable), !canSendMessagesToPeer(peer) {
|
||||
@ -572,6 +605,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
private let presentationDataPromise: Promise<ChatListPresentationData>
|
||||
private var stateValue = ChatListSearchContainerNodeState()
|
||||
private let statePromise: ValuePromise<ChatListSearchContainerNodeState>
|
||||
private var searchStateValue = ChatListSearchContainerNodeSearchState()
|
||||
private let searchStatePromise: ValuePromise<ChatListSearchContainerNodeSearchState>
|
||||
|
||||
private let _isSearching = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
override public var isSearching: Signal<Bool, NoError> {
|
||||
@ -610,6 +645,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
|
||||
|
||||
self.statePromise = ValuePromise(self.stateValue, ignoreRepeated: true)
|
||||
self.searchStatePromise = ValuePromise(self.searchStateValue, ignoreRepeated: true)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -662,6 +698,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
let currentRemotePeers = Atomic<([FoundPeer], [FoundPeer])?>(value: nil)
|
||||
|
||||
let presentationDataPromise = self.presentationDataPromise
|
||||
let searchStatePromise = self.searchStatePromise
|
||||
let foundItems = self.searchQuery.get()
|
||||
|> mapToSignal { query -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
|
||||
guard let query = query, !query.isEmpty else {
|
||||
@ -767,8 +804,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
)
|
||||
}
|
||||
|
||||
return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationDataPromise.get())
|
||||
|> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationData -> ([ChatListSearchEntry], Bool)? in
|
||||
return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationDataPromise.get(), searchStatePromise.get())
|
||||
|> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationData, searchState -> ([ChatListSearchEntry], Bool)? in
|
||||
var entries: [ChatListSearchEntry] = []
|
||||
let isSearching = foundRemotePeers.2 || foundRemoteMessages.1
|
||||
var index = 0
|
||||
@ -805,16 +842,59 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
|
||||
var totalNumberOfLocalPeers = 0
|
||||
for renderedPeer in foundLocalPeers.peers {
|
||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != context.account.peerId, filteredPeer(peer, accountPeer) {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
totalNumberOfLocalPeers += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
for peer in foundRemotePeers.0 {
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
totalNumberOfLocalPeers += 1
|
||||
}
|
||||
}
|
||||
|
||||
var totalNumberOfGlobalPeers = 0
|
||||
for peer in foundRemotePeers.1 {
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
totalNumberOfGlobalPeers += 1
|
||||
}
|
||||
}
|
||||
|
||||
existingPeerIds.removeAll()
|
||||
|
||||
let localExpandType: ChatListSearchSectionExpandType
|
||||
let globalExpandType: ChatListSearchSectionExpandType
|
||||
if totalNumberOfLocalPeers > 5 {
|
||||
localExpandType = searchState.expandLocalSearch ? .collapse : .expand
|
||||
} else {
|
||||
localExpandType = .none
|
||||
}
|
||||
if totalNumberOfGlobalPeers > 5 {
|
||||
globalExpandType = searchState.expandGlobalSearch ? .collapse : .expand
|
||||
} else {
|
||||
globalExpandType = .none
|
||||
}
|
||||
|
||||
let lowercasedQuery = query.lowercased()
|
||||
if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(lowercasedQuery) || "saved messages".hasPrefix(lowercasedQuery) {
|
||||
if !existingPeerIds.contains(accountPeer.id), filteredPeer(accountPeer, accountPeer) {
|
||||
existingPeerIds.insert(accountPeer.id)
|
||||
entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
|
||||
entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfLocalPeers = 0
|
||||
for renderedPeer in foundLocalPeers.peers {
|
||||
if case .expand = localExpandType, numberOfLocalPeers >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
if let peer = renderedPeer.peers[renderedPeer.peerId], peer.id != context.account.peerId, filteredPeer(peer, accountPeer) {
|
||||
if !existingPeerIds.contains(peer.id) {
|
||||
existingPeerIds.insert(peer.id)
|
||||
@ -822,26 +902,38 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if let associatedPeerId = peer.associatedPeerId {
|
||||
associatedPeer = renderedPeer.peers[associatedPeerId]
|
||||
}
|
||||
entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
|
||||
entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
numberOfLocalPeers += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for peer in foundRemotePeers.0 {
|
||||
if case .expand = localExpandType, numberOfLocalPeers >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
|
||||
entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, localExpandType))
|
||||
index += 1
|
||||
numberOfLocalPeers += 1
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfGlobalPeers = 0
|
||||
index = 0
|
||||
for peer in foundRemotePeers.1 {
|
||||
if case .expand = globalExpandType, numberOfGlobalPeers >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id), filteredPeer(peer.peer, accountPeer) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
|
||||
entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalExpandType))
|
||||
index += 1
|
||||
numberOfGlobalPeers += 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -1026,7 +1118,26 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
let previousEntries = previousSearchItems.swap(entriesAndFlags?.0)
|
||||
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entriesAndFlags?.0 ?? [], displayingResults: entriesAndFlags?.0 != nil, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: filter, interaction: interaction, peerContextAction: peerContextAction)
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entriesAndFlags?.0 ?? [], displayingResults: entriesAndFlags?.0 != nil, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: filter, interaction: interaction, peerContextAction: peerContextAction,
|
||||
toggleExpandLocalResults: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateSearchState { state in
|
||||
var state = state
|
||||
state.expandLocalSearch = !state.expandLocalSearch
|
||||
return state
|
||||
}
|
||||
}, toggleExpandGlobalResults: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateSearchState { state in
|
||||
var state = state
|
||||
state.expandGlobalSearch = !state.expandGlobalSearch
|
||||
return state
|
||||
}
|
||||
})
|
||||
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
}))
|
||||
@ -1087,6 +1198,14 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSearchState(_ f: (ChatListSearchContainerNodeSearchState) -> ChatListSearchContainerNodeSearchState) {
|
||||
let state = f(self.searchStateValue)
|
||||
if state != self.searchStateValue {
|
||||
self.searchStateValue = state
|
||||
self.searchStatePromise.set(state)
|
||||
}
|
||||
}
|
||||
|
||||
override public func searchTextUpdated(text: String) {
|
||||
let searchQuery: String? = !text.isEmpty ? text : nil
|
||||
self.interaction?.searchTextHighightState = searchQuery
|
||||
|
@ -3041,6 +3041,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
}
|
||||
|
||||
if headerNode.item !== item {
|
||||
item.updateNode(headerNode, previous: nil, next: nil)
|
||||
headerNode.item = item
|
||||
}
|
||||
headerNode.updateLayoutInternal(size: headerFrame.size, leftInset: leftInset, rightInset: rightInset)
|
||||
headerNode.updateInternalStickLocationDistanceFactor(stickLocationDistanceFactor, animated: true)
|
||||
headerNode.internalStickLocationDistance = stickLocationDistance
|
||||
@ -3058,6 +3062,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: transition.0)
|
||||
} else {
|
||||
let headerNode = item.node()
|
||||
if headerNode.item !== item {
|
||||
item.updateNode(headerNode, previous: nil, next: nil)
|
||||
headerNode.item = item
|
||||
}
|
||||
headerNode.updateFlashingOnScrolling(flashing, animated: false)
|
||||
headerNode.frame = headerFrame
|
||||
headerNode.updateLayoutInternal(size: headerFrame.size, leftInset: leftInset, rightInset: rightInset)
|
||||
|
@ -26,6 +26,8 @@ open class ListViewItemHeaderNode: ASDisplayNode {
|
||||
final var internalStickLocationDistance: CGFloat = 0.0
|
||||
private var isFlashingOnScrolling = false
|
||||
|
||||
var item: ListViewItemHeader?
|
||||
|
||||
func updateInternalStickLocationDistanceFactor(_ factor: CGFloat, animated: Bool) {
|
||||
self.internalStickLocationDistanceFactor = factor
|
||||
}
|
||||
|
@ -79,7 +79,9 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
let previousEntries = previousSearchItems.swap(entries)
|
||||
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], interaction: interaction, peerContextAction: nil)
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, context: strongSelf.context, presentationData: strongSelf.presentationData, enableHeaders: false, filter: [], interaction: interaction, peerContextAction: nil, toggleExpandLocalResults: {
|
||||
}, toggleExpandGlobalResults: {
|
||||
})
|
||||
strongSelf.controllerNode.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
})
|
||||
|
@ -9,6 +9,7 @@ private let actionFont = Font.medium(13.0)
|
||||
|
||||
public final class ListSectionHeaderNode: ASDisplayNode {
|
||||
private let label: ImmediateTextNode
|
||||
private var actionButtonLabel: ImmediateTextNode?
|
||||
private var actionButton: HighlightableButtonNode?
|
||||
private var theme: PresentationTheme
|
||||
|
||||
@ -28,17 +29,26 @@ public final class ListSectionHeaderNode: ASDisplayNode {
|
||||
didSet {
|
||||
if (self.action != nil) != (self.actionButton != nil) {
|
||||
if let _ = self.action {
|
||||
let actionButtonLabel = ImmediateTextNode()
|
||||
self.addSubnode(actionButtonLabel)
|
||||
self.actionButtonLabel = actionButtonLabel
|
||||
let actionButton = HighlightableButtonNode()
|
||||
self.addSubnode(actionButton)
|
||||
self.actionButton = actionButton
|
||||
actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside)
|
||||
} else if let actionButton = self.actionButton {
|
||||
self.actionButton = nil
|
||||
actionButton.removeFromSupernode()
|
||||
} else {
|
||||
if let actionButtonLabel = self.actionButtonLabel {
|
||||
self.actionButtonLabel = nil
|
||||
actionButtonLabel.removeFromSupernode()
|
||||
}
|
||||
if let actionButton = self.actionButton {
|
||||
self.actionButton = nil
|
||||
actionButton.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
if let action = self.action {
|
||||
self.actionButton?.setAttributedTitle(NSAttributedString(string: action, font: actionFont, textColor: self.theme.chatList.sectionHeaderTextColor), for: [])
|
||||
self.actionButtonLabel?.attributedText = NSAttributedString(string: action, font: actionFont, textColor: self.theme.chatList.sectionHeaderTextColor)
|
||||
}
|
||||
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
@ -70,7 +80,7 @@ public final class ListSectionHeaderNode: ASDisplayNode {
|
||||
|
||||
self.backgroundColor = theme.chatList.sectionHeaderFillColor
|
||||
if let action = self.action {
|
||||
self.actionButton?.setAttributedTitle(NSAttributedString(string: action, font: actionFont, textColor: self.theme.chatList.sectionHeaderTextColor), for: [])
|
||||
self.actionButtonLabel?.attributedText = NSAttributedString(string: action, font: actionFont, textColor: self.theme.chatList.sectionHeaderTextColor)
|
||||
}
|
||||
|
||||
if let (size, leftInset, rightInset) = self.validLayout {
|
||||
@ -84,8 +94,9 @@ public final class ListSectionHeaderNode: ASDisplayNode {
|
||||
let labelSize = self.label.updateLayout(CGSize(width: max(0.0, size.width - leftInset - rightInset - 18.0), height: size.height))
|
||||
self.label.frame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0 + UIScreenPixel), size: labelSize)
|
||||
|
||||
if let actionButton = self.actionButton {
|
||||
let buttonSize = actionButton.measure(CGSize(width: size.width, height: size.height))
|
||||
if let actionButton = self.actionButton, let actionButtonLabel = self.actionButtonLabel {
|
||||
let buttonSize = actionButtonLabel.updateLayout(CGSize(width: size.width, height: size.height))
|
||||
actionButtonLabel.frame = CGRect(origin: CGPoint(x: size.width - rightInset - 16.0 - buttonSize.width, y: 6.0 + UIScreenPixel), size: buttonSize)
|
||||
actionButton.frame = CGRect(origin: CGPoint(x: size.width - rightInset - 16.0 - buttonSize.width, y: 6.0 + UIScreenPixel), size: buttonSize)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -448,12 +448,12 @@ public final class WalletStrings: Equatable {
|
||||
public var Wallet_SecureStorageReset_Title: String { return self._s[218]! }
|
||||
public var Wallet_Receive_CommentHeader: String { return self._s[219]! }
|
||||
public var Wallet_Info_ReceiveGrams: String { return self._s[220]! }
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
}
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
|
Loading…
x
Reference in New Issue
Block a user