Don't collapse local chat list search results; collapse global search results at 3 items

This commit is contained in:
Ali 2019-12-03 12:08:35 +04:00
parent b2674c22ce
commit 25125ebb0f
6 changed files with 252 additions and 69 deletions

View File

@ -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)
}

View File

@ -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, context: context, 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, context: context, 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,54 @@ 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 = .none
let globalExpandType: ChatListSearchSectionExpandType
if totalNumberOfGlobalPeers > 3 {
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 +897,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 >= 3 {
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 +1113,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 +1193,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

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}
})

View File

@ -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)
}
}