mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Chat List Filter improvements
This commit is contained in:
parent
309a8b112b
commit
77826e91d4
@ -1822,7 +1822,23 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.push(chatListFilterPresetListController(context: strongSelf.context))
|
||||
strongSelf.push(chatListFilterPresetListController(context: strongSelf.context, updated: { presets in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let currentPreset = strongSelf.chatListDisplayNode.chatListNode.chatListFilter {
|
||||
var found = false
|
||||
if let index = presets.index(where: { $0.id == currentPreset.id }) {
|
||||
found = true
|
||||
if currentPreset != presets[index] {
|
||||
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = presets[index]
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
strongSelf.chatListDisplayNode.chatListNode.chatListFilter = nil
|
||||
}
|
||||
}
|
||||
}))
|
||||
}, updatePreset: { value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -53,9 +53,11 @@ private func filterEntry(presentationData: ItemListPresentationData, arguments:
|
||||
private enum ChatListFilterPresetEntryStableId: Hashable {
|
||||
case index(Int)
|
||||
case peer(PeerId)
|
||||
case additionalPeerInfo
|
||||
}
|
||||
|
||||
private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
case nameHeader(String)
|
||||
case name(placeholder: String, value: String)
|
||||
case filterPrivateChats(title: String, value: Bool)
|
||||
case filterSecretChats(title: String, value: Bool)
|
||||
@ -68,46 +70,51 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
case additionalPeersHeader(String)
|
||||
case addAdditionalPeer(title: String)
|
||||
case additionalPeer(index: Int, peer: RenderedPeer, isRevealed: Bool)
|
||||
case additionalPeerInfo(String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .name:
|
||||
case .nameHeader, .name:
|
||||
return ChatListFilterPresetControllerSection.name.rawValue
|
||||
case .filterPrivateChats, .filterSecretChats, .filterPrivateGroups, .filterBots, .filterPublicGroups, .filterChannels:
|
||||
return ChatListFilterPresetControllerSection.categories.rawValue
|
||||
case .filterMuted, .filterRead:
|
||||
return ChatListFilterPresetControllerSection.excludeCategories.rawValue
|
||||
case .additionalPeersHeader, .addAdditionalPeer, .additionalPeer:
|
||||
case .additionalPeersHeader, .addAdditionalPeer, .additionalPeer, .additionalPeerInfo:
|
||||
return ChatListFilterPresetControllerSection.additionalPeers.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: ChatListFilterPresetEntryStableId {
|
||||
switch self {
|
||||
case .name:
|
||||
case .nameHeader:
|
||||
return .index(0)
|
||||
case .filterPrivateChats:
|
||||
case .name:
|
||||
return .index(1)
|
||||
case .filterSecretChats:
|
||||
case .filterPrivateChats:
|
||||
return .index(2)
|
||||
case .filterPrivateGroups:
|
||||
case .filterSecretChats:
|
||||
return .index(3)
|
||||
case .filterBots:
|
||||
case .filterPrivateGroups:
|
||||
return .index(4)
|
||||
case .filterPublicGroups:
|
||||
case .filterBots:
|
||||
return .index(5)
|
||||
case .filterChannels:
|
||||
case .filterPublicGroups:
|
||||
return .index(6)
|
||||
case .filterMuted:
|
||||
case .filterChannels:
|
||||
return .index(7)
|
||||
case .filterRead:
|
||||
case .filterMuted:
|
||||
return .index(8)
|
||||
case .additionalPeersHeader:
|
||||
case .filterRead:
|
||||
return .index(9)
|
||||
case .addAdditionalPeer:
|
||||
case .additionalPeersHeader:
|
||||
return .index(10)
|
||||
case .addAdditionalPeer:
|
||||
return .index(11)
|
||||
case let .additionalPeer(additionalPeer):
|
||||
return .peer(additionalPeer.peer.peerId)
|
||||
case .additionalPeerInfo:
|
||||
return .additionalPeerInfo
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +126,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
return lhsIndex < rhsIndex
|
||||
case .peer:
|
||||
return true
|
||||
case .additionalPeerInfo:
|
||||
return true
|
||||
}
|
||||
case .peer:
|
||||
switch lhs {
|
||||
@ -126,6 +135,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
switch rhs.stableId {
|
||||
case .index:
|
||||
return false
|
||||
case .additionalPeerInfo:
|
||||
return true
|
||||
case .peer:
|
||||
switch rhs {
|
||||
case let .additionalPeer(rhsIndex, _, _):
|
||||
@ -137,12 +148,23 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
default:
|
||||
preconditionFailure()
|
||||
}
|
||||
case .additionalPeerInfo:
|
||||
switch rhs.stableId {
|
||||
case .index:
|
||||
return false
|
||||
case .peer:
|
||||
return false
|
||||
case .additionalPeerInfo:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! ChatListFilterPresetControllerArguments
|
||||
switch self {
|
||||
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), sectionId: self.section, textUpdated: { value in
|
||||
arguments.updateState { current in
|
||||
@ -201,6 +223,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
|
||||
}, removePeer: { id in
|
||||
arguments.deleteAdditionalPeer(id)
|
||||
})
|
||||
case let .additionalPeerInfo(text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,6 +250,7 @@ private struct ChatListFilterPresetControllerState: Equatable {
|
||||
private func chatListFilterPresetControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetControllerState, peers: [RenderedPeer]) -> [ChatListFilterPresetEntry] {
|
||||
var entries: [ChatListFilterPresetEntry] = []
|
||||
|
||||
entries.append(.nameHeader("NAME"))
|
||||
entries.append(.name(placeholder: "Preset Name", value: state.name))
|
||||
|
||||
entries.append(.filterPrivateChats(title: "Private Chats", value: state.includeCategories.contains(.privateChats)))
|
||||
@ -245,10 +270,12 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio
|
||||
entries.append(.additionalPeer(index: entries.count, peer: peer, isRevealed: state.revealedPeerId == peer.peerId))
|
||||
}
|
||||
|
||||
entries.append(.additionalPeerInfo("These chats will always be included in the list."))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func chatListFilterPresetController(context: AccountContext, currentPreset: ChatListFilterPreset?) -> ViewController {
|
||||
func chatListFilterPresetController(context: AccountContext, currentPreset: ChatListFilterPreset?, updated: @escaping ([ChatListFilterPreset]) -> Void) -> ViewController {
|
||||
let initialName: String
|
||||
if let currentPreset = currentPreset {
|
||||
switch currentPreset.name {
|
||||
@ -352,14 +379,15 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
})
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: {
|
||||
let state = stateValue.with { $0 }
|
||||
let preset = ChatListFilterPreset(name: .custom(state.name), includeCategories: state.includeCategories, additionallyIncludePeers: state.additionallyIncludePeers)
|
||||
let preset = ChatListFilterPreset(id: currentPreset?.id ?? arc4random64(), name: .custom(state.name), includeCategories: state.includeCategories, additionallyIncludePeers: state.additionallyIncludePeers)
|
||||
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
settings.presets = settings.presets.filter { $0 != preset && $0 != currentPreset }
|
||||
settings.presets.append(preset)
|
||||
return settings
|
||||
})
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
updated(settings.presets)
|
||||
dismissImpl?()
|
||||
})
|
||||
})
|
||||
|
@ -46,7 +46,7 @@ private func stringForUserCount(_ peers: [PeerId: SelectivePrivacyPeer], strings
|
||||
|
||||
private enum ChatListFilterPresetListEntryStableId: Hashable {
|
||||
case listHeader
|
||||
case preset(ChatListFilterPresetName)
|
||||
case preset(Int64)
|
||||
case addItem
|
||||
case listFooter
|
||||
}
|
||||
@ -82,7 +82,7 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
case .listHeader:
|
||||
return .listHeader
|
||||
case let .preset(preset):
|
||||
return .preset(preset.preset.name)
|
||||
return .preset(preset.preset.id)
|
||||
case .addItem:
|
||||
return .addItem
|
||||
case .listFooter:
|
||||
@ -141,7 +141,7 @@ private func chatListFilterPresetListControllerEntries(presentationData: Present
|
||||
return entries
|
||||
}
|
||||
|
||||
func chatListFilterPresetListController(context: AccountContext) -> ViewController {
|
||||
func chatListFilterPresetListController(context: AccountContext, updated: @escaping ([ChatListFilterPreset]) -> Void) -> ViewController {
|
||||
let initialState = ChatListFilterPresetListControllerState()
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
@ -154,9 +154,9 @@ func chatListFilterPresetListController(context: AccountContext) -> ViewControll
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
|
||||
let arguments = ChatListFilterPresetListControllerArguments(context: context, openPreset: { preset in
|
||||
pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: preset))
|
||||
pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: preset, updated: updated))
|
||||
}, addNew: {
|
||||
pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: nil))
|
||||
pushControllerImpl?(chatListFilterPresetController(context: context, currentPreset: nil, updated: updated))
|
||||
}, setItemWithRevealedOptions: { preset, fromPreset in
|
||||
updateState { state in
|
||||
var state = state
|
||||
@ -166,13 +166,16 @@ func chatListFilterPresetListController(context: AccountContext) -> ViewControll
|
||||
return state
|
||||
}
|
||||
}, removePreset: { preset in
|
||||
let _ = updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var settings = settings
|
||||
if let index = settings.presets.index(of: preset) {
|
||||
settings.presets.remove(at: index)
|
||||
}
|
||||
return settings
|
||||
}).start()
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
updated(settings.presets)
|
||||
})
|
||||
})
|
||||
|
||||
let preferences = context.account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.chatListFilterSettings])
|
||||
|
@ -370,6 +370,12 @@ public final class ChatListNode: ListView {
|
||||
didSet {
|
||||
if self.chatListFilter != oldValue {
|
||||
self.chatListFilterValue.set(self.chatListFilter)
|
||||
|
||||
if self.chatListFilter?.includeCategories != oldValue?.includeCategories || self.chatListFilter?.additionallyIncludePeers != oldValue?.additionallyIncludePeers {
|
||||
if let currentLocation = self.currentLocation {
|
||||
self.setChatListLocation(.initial(count: 50, filter: self.chatListFilter))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -533,20 +539,12 @@ public final class ChatListNode: ListView {
|
||||
|
||||
let viewProcessingQueue = self.viewProcessingQueue
|
||||
|
||||
let chatListViewUpdate = combineLatest(self.chatListLocation.get(), self.chatListFilterValue.get())
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
if lhs.0 != rhs.0 {
|
||||
return false
|
||||
}
|
||||
if lhs.1 != rhs.1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|> mapToSignal { location, filter -> Signal<(ChatListNodeViewUpdate, ChatListFilterPreset?), NoError> in
|
||||
return chatListViewForLocation(groupId: groupId, filter: filter, location: location, account: context.account)
|
||||
let chatListViewUpdate = self.chatListLocation.get()
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { location -> Signal<(ChatListNodeViewUpdate, ChatListFilterPreset?), NoError> in
|
||||
return chatListViewForLocation(groupId: groupId, location: location, account: context.account)
|
||||
|> map { update in
|
||||
return (update, filter)
|
||||
return (update, location.filter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -792,9 +790,9 @@ public final class ChatListNode: ListView {
|
||||
if let range = range.loadedRange {
|
||||
var location: ChatListNodeLocation?
|
||||
if range.firstIndex < 5 && originalView.laterIndex != nil {
|
||||
location = .navigation(index: originalView.entries[originalView.entries.count - 1].index)
|
||||
location = .navigation(index: originalView.entries[originalView.entries.count - 1].index, filter: strongSelf.chatListFilter)
|
||||
} else if range.firstIndex >= 5 && range.lastIndex >= originalView.entries.count - 5 && originalView.earlierIndex != nil {
|
||||
location = .navigation(index: originalView.entries[0].index)
|
||||
location = .navigation(index: originalView.entries[0].index, filter: strongSelf.chatListFilter)
|
||||
}
|
||||
|
||||
if let location = location, location != strongSelf.currentLocation {
|
||||
@ -850,10 +848,10 @@ public final class ChatListNode: ListView {
|
||||
|
||||
let initialLocation: ChatListNodeLocation
|
||||
switch mode {
|
||||
case .chatList:
|
||||
initialLocation = .initial(count: 50)
|
||||
case .peers:
|
||||
initialLocation = .initial(count: 200)
|
||||
case .chatList:
|
||||
initialLocation = .initial(count: 50, filter: self.chatListFilter)
|
||||
case .peers:
|
||||
initialLocation = .initial(count: 200, filter: self.chatListFilter)
|
||||
}
|
||||
self.setChatListLocation(initialLocation)
|
||||
|
||||
@ -1332,12 +1330,12 @@ public final class ChatListNode: ListView {
|
||||
if view.laterIndex == nil {
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
} else {
|
||||
let location: ChatListNodeLocation = .scroll(index: .absoluteUpperBound, sourceIndex: .absoluteLowerBound, scrollPosition: .top(0.0), animated: true)
|
||||
let location: ChatListNodeLocation = .scroll(index: .absoluteUpperBound, sourceIndex: .absoluteLowerBound, scrollPosition: .top(0.0), animated: true, filter: self.chatListFilter)
|
||||
self.setChatListLocation(location)
|
||||
}
|
||||
} else {
|
||||
let location: ChatListNodeLocation = .scroll(index: .absoluteUpperBound, sourceIndex: .absoluteLowerBound
|
||||
, scrollPosition: .top(0.0), animated: true)
|
||||
, scrollPosition: .top(0.0), animated: true, filter: self.chatListFilter)
|
||||
self.setChatListLocation(location)
|
||||
}
|
||||
}
|
||||
@ -1373,11 +1371,11 @@ public final class ChatListNode: ListView {
|
||||
|
||||
if let index = index {
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: self?.currentlyVisibleLatestChatListIndex() ?? .absoluteUpperBound
|
||||
, scrollPosition: .center(.top), animated: true)
|
||||
, scrollPosition: .center(.top), animated: true, filter: strongSelf.chatListFilter)
|
||||
strongSelf.setChatListLocation(location)
|
||||
} else {
|
||||
let location: ChatListNodeLocation = .scroll(index: .absoluteUpperBound, sourceIndex: .absoluteLowerBound
|
||||
, scrollPosition: .top(0.0), animated: true)
|
||||
, scrollPosition: .top(0.0), animated: true, filter: strongSelf.chatListFilter)
|
||||
strongSelf.setChatListLocation(location)
|
||||
}
|
||||
})
|
||||
@ -1433,7 +1431,7 @@ public final class ChatListNode: ListView {
|
||||
guard let strongSelf = self, let index = index else {
|
||||
return
|
||||
}
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: strongSelf.currentlyVisibleLatestChatListIndex() ?? .absoluteUpperBound, scrollPosition: .center(.top), animated: true)
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: strongSelf.currentlyVisibleLatestChatListIndex() ?? .absoluteUpperBound, scrollPosition: .center(.top), animated: true, filter: strongSelf.chatListFilter)
|
||||
strongSelf.setChatListLocation(location)
|
||||
strongSelf.peerSelected?(index.messageIndex.id.peerId, false, false)
|
||||
})
|
||||
@ -1457,7 +1455,7 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
if let target = target {
|
||||
let location: ChatListNodeLocation = .scroll(index: target.0, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true)
|
||||
let location: ChatListNodeLocation = .scroll(index: target.0, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true, filter: self.chatListFilter)
|
||||
self.setChatListLocation(location)
|
||||
self.peerSelected?(target.1, false, false)
|
||||
}
|
||||
@ -1473,12 +1471,12 @@ public final class ChatListNode: ListView {
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
let _ = (chatListViewForLocation(groupId: self.groupId, filter: filter, location: .initial(count: 10), account: self.context.account)
|
||||
let _ = (chatListViewForLocation(groupId: self.groupId, location: .initial(count: 10, filter: filter), account: self.context.account)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { update in
|
||||
let entries = update.view.entries
|
||||
if entries.count > index, case let .MessageEntry(index, _, _, _, _, renderedPeer, _, _, _) = entries[10 - index - 1] {
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true)
|
||||
let location: ChatListNodeLocation = .scroll(index: index, sourceIndex: .absoluteLowerBound, scrollPosition: .center(.top), animated: true, filter: filter)
|
||||
self.setChatListLocation(location)
|
||||
self.peerSelected?(renderedPeer.peerId, false, false)
|
||||
}
|
||||
|
@ -7,21 +7,18 @@ import Display
|
||||
import TelegramUIPreferences
|
||||
|
||||
enum ChatListNodeLocation: Equatable {
|
||||
case initial(count: Int)
|
||||
case navigation(index: ChatListIndex)
|
||||
case scroll(index: ChatListIndex, sourceIndex: ChatListIndex, scrollPosition: ListViewScrollPosition, animated: Bool)
|
||||
case initial(count: Int, filter: ChatListFilterPreset?)
|
||||
case navigation(index: ChatListIndex, filter: ChatListFilterPreset?)
|
||||
case scroll(index: ChatListIndex, sourceIndex: ChatListIndex, scrollPosition: ListViewScrollPosition, animated: Bool, filter: ChatListFilterPreset?)
|
||||
|
||||
static func ==(lhs: ChatListNodeLocation, rhs: ChatListNodeLocation) -> Bool {
|
||||
switch lhs {
|
||||
case let .navigation(index):
|
||||
switch rhs {
|
||||
case .navigation(index):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
var filter: ChatListFilterPreset? {
|
||||
switch self {
|
||||
case let .initial(initial):
|
||||
return initial.filter
|
||||
case let .navigation(navigation):
|
||||
return navigation.filter
|
||||
case let .scroll(scroll):
|
||||
return scroll.filter
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -32,9 +29,9 @@ struct ChatListNodeViewUpdate {
|
||||
let scrollPosition: ChatListNodeViewScrollPosition?
|
||||
}
|
||||
|
||||
func chatListViewForLocation(groupId: PeerGroupId, filter: ChatListFilterPreset?, location: ChatListNodeLocation, account: Account) -> Signal<ChatListNodeViewUpdate, NoError> {
|
||||
func chatListViewForLocation(groupId: PeerGroupId, location: ChatListNodeLocation, account: Account) -> Signal<ChatListNodeViewUpdate, NoError> {
|
||||
let filterPredicate: ((Peer, PeerNotificationSettings?, Bool) -> Bool)?
|
||||
if let filter = filter {
|
||||
if let filter = location.filter {
|
||||
let includePeers = Set(filter.additionallyIncludePeers)
|
||||
filterPredicate = { peer, notificationSettings, isUnread in
|
||||
if includePeers.contains(peer.id) {
|
||||
@ -107,14 +104,14 @@ func chatListViewForLocation(groupId: PeerGroupId, filter: ChatListFilterPreset?
|
||||
}
|
||||
|
||||
switch location {
|
||||
case let .initial(count):
|
||||
case let .initial(count, _):
|
||||
let signal: Signal<(ChatListView, ViewUpdateType), NoError>
|
||||
signal = account.viewTracker.tailChatListView(groupId: groupId, filterPredicate: filterPredicate, count: count)
|
||||
return signal
|
||||
|> map { view, updateType -> ChatListNodeViewUpdate in
|
||||
return ChatListNodeViewUpdate(view: view, type: updateType, scrollPosition: nil)
|
||||
}
|
||||
case let .navigation(index):
|
||||
case let .navigation(index, _):
|
||||
var first = true
|
||||
return account.viewTracker.aroundChatListView(groupId: groupId, filterPredicate: filterPredicate, index: index, count: 80)
|
||||
|> map { view, updateType -> ChatListNodeViewUpdate in
|
||||
@ -127,7 +124,7 @@ func chatListViewForLocation(groupId: PeerGroupId, filter: ChatListFilterPreset?
|
||||
}
|
||||
return ChatListNodeViewUpdate(view: view, type: genericType, scrollPosition: nil)
|
||||
}
|
||||
case let .scroll(index, sourceIndex, scrollPosition, animated):
|
||||
case let .scroll(index, sourceIndex, scrollPosition, animated, _):
|
||||
let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up
|
||||
let chatScrollPosition: ChatListNodeViewScrollPosition = .index(index: index, position: scrollPosition, directionHint: directionHint, animated: animated)
|
||||
var first = true
|
||||
|
@ -167,7 +167,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
||||
|
||||
var fromEmptyView = false
|
||||
if let fromView = fromView {
|
||||
if fromView.filteredEntries.isEmpty {
|
||||
if fromView.filteredEntries.isEmpty || fromView.filter != toView.filter {
|
||||
options.remove(.AnimateInsertion)
|
||||
options.remove(.AnimateAlpha)
|
||||
fromEmptyView = true
|
||||
|
@ -462,7 +462,7 @@ private final class TabBarChatListFilterControllerNode: ViewControllerTracingNod
|
||||
if preset.includeCategories.contains(.publicGroups) {
|
||||
tags.append(.publicGroup)
|
||||
}
|
||||
if preset.includeCategories.contains(.privateChats) {
|
||||
if preset.includeCategories.contains(.channels) {
|
||||
tags.append(.channel)
|
||||
}
|
||||
|
||||
|
63
submodules/Postbox/Sources/AllChatListHolesView.swift
Normal file
63
submodules/Postbox/Sources/AllChatListHolesView.swift
Normal file
@ -0,0 +1,63 @@
|
||||
import Foundation
|
||||
|
||||
final class MutableAllChatListHolesView: MutablePostboxView {
|
||||
fileprivate let groupId: PeerGroupId
|
||||
private var holes = Set<ChatListHole>()
|
||||
fileprivate var latestHole: ChatListHole?
|
||||
|
||||
init(postbox: Postbox, groupId: PeerGroupId) {
|
||||
self.groupId = groupId
|
||||
self.holes = Set(postbox.chatListTable.allHoles(groupId: groupId))
|
||||
self.latestHole = self.holes.max(by: { $0.index < $1.index })
|
||||
}
|
||||
|
||||
func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
|
||||
if let operations = transaction.chatListOperations[self.groupId] {
|
||||
var updated = false
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case let .InsertHole(hole):
|
||||
if !self.holes.contains(hole) {
|
||||
self.holes.insert(hole)
|
||||
updated = true
|
||||
}
|
||||
case let .RemoveHoles(indices):
|
||||
for index in indices {
|
||||
if self.holes.contains(ChatListHole(index: index.messageIndex)) {
|
||||
self.holes.remove(ChatListHole(index: index.messageIndex))
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if updated {
|
||||
let updatedLatestHole = self.holes.max(by: { $0.index < $1.index })
|
||||
if updatedLatestHole != self.latestHole {
|
||||
self.latestHole = updatedLatestHole
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func immutableView() -> PostboxView {
|
||||
return AllChatListHolesView(self)
|
||||
}
|
||||
}
|
||||
|
||||
public final class AllChatListHolesView: PostboxView {
|
||||
public let latestHole: ChatListHole?
|
||||
|
||||
init(_ view: MutableAllChatListHolesView) {
|
||||
self.latestHole = view.latestHole
|
||||
}
|
||||
}
|
@ -570,13 +570,10 @@ final class ChatListIndexTable: Table {
|
||||
|
||||
func debugReindexUnreadCounts(postbox: Postbox) -> (ChatListTotalUnreadState, [PeerGroupId: PeerGroupUnreadCountersCombinedSummary]) {
|
||||
var peerIds: [PeerId] = []
|
||||
self.valueBox.scanInt64(self.table, values: { key, _ in
|
||||
let peerId = PeerId(key)
|
||||
if peerId.namespace != Int32.max {
|
||||
peerIds.append(peerId)
|
||||
}
|
||||
return true
|
||||
})
|
||||
for groupId in postbox.chatListTable.existingGroups() + [.root] {
|
||||
let groupPeerIds = postbox.chatListTable.allPeerIds(groupId: groupId)
|
||||
peerIds.append(contentsOf: groupPeerIds)
|
||||
}
|
||||
var rootState = ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:])
|
||||
var summaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] = [:]
|
||||
for peerId in peerIds {
|
||||
|
@ -680,6 +680,35 @@ final class ChatListTable: Table {
|
||||
return entries
|
||||
}
|
||||
|
||||
func allPeerIds(groupId: PeerGroupId) -> [PeerId] {
|
||||
var peerIds: [PeerId] = []
|
||||
self.valueBox.range(self.table, start: self.upperBound(groupId: groupId), end: self.lowerBound(groupId: groupId), keys: { key in
|
||||
let (keyGroupId, pinningIndex, messageIndex, type) = extractKey(key)
|
||||
assert(groupId == keyGroupId)
|
||||
|
||||
let index = ChatListIndex(pinningIndex: pinningIndex, messageIndex: messageIndex)
|
||||
if type == ChatListEntryType.message.rawValue {
|
||||
peerIds.append(messageIndex.id.peerId)
|
||||
}
|
||||
return true
|
||||
}, limit: 0)
|
||||
return peerIds
|
||||
}
|
||||
|
||||
func allHoles(groupId: PeerGroupId) -> [ChatListHole] {
|
||||
var entries: [ChatListHole] = []
|
||||
self.valueBox.range(self.table, start: self.upperBound(groupId: groupId), end: self.lowerBound(groupId: groupId), keys: { key in
|
||||
let (keyGroupId, pinningIndex, messageIndex, type) = extractKey(key)
|
||||
assert(groupId == keyGroupId)
|
||||
if type == ChatListEntryType.hole.rawValue {
|
||||
let index = ChatListIndex(pinningIndex: pinningIndex, messageIndex: messageIndex)
|
||||
entries.append(ChatListHole(index: index.messageIndex))
|
||||
}
|
||||
return true
|
||||
}, limit: 0)
|
||||
return entries
|
||||
}
|
||||
|
||||
func entriesInRange(groupId: PeerGroupId, upperBound: ChatListIndex, lowerBound: ChatListIndex) -> [ChatListEntryInfo] {
|
||||
var entries: [ChatListEntryInfo] = []
|
||||
let upperBoundKey: ValueBoxKey
|
||||
|
@ -27,6 +27,7 @@ public enum PostboxViewKey: Hashable {
|
||||
case peerNotificationSettingsBehaviorTimestampView
|
||||
case peerChatInclusion(PeerId)
|
||||
case basicPeer(PeerId)
|
||||
case allChatListHoles(PeerGroupId)
|
||||
|
||||
public var hashValue: Int {
|
||||
switch self {
|
||||
@ -82,6 +83,8 @@ public enum PostboxViewKey: Hashable {
|
||||
return peerId.hashValue
|
||||
case let .basicPeer(peerId):
|
||||
return peerId.hashValue
|
||||
case let .allChatListHoles(groupId):
|
||||
return groupId.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +246,12 @@ public enum PostboxViewKey: Hashable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .allChatListHoles(groupId):
|
||||
if case .allChatListHoles(groupId) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -301,5 +310,7 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV
|
||||
return MutablePeerChatInclusionView(postbox: postbox, peerId: peerId)
|
||||
case let .basicPeer(peerId):
|
||||
return MutableBasicPeerView(postbox: postbox, peerId: peerId)
|
||||
case let .allChatListHoles(groupId):
|
||||
return MutableAllChatListHolesView(postbox: postbox, groupId: groupId)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import SwiftSignalKit
|
||||
|
||||
private final class ManagedChatListHolesState {
|
||||
private var holeDisposables: [ChatListHolesEntry: Disposable] = [:]
|
||||
private var additionalLatestHoleDisposable: (ChatListHole, Disposable)?
|
||||
|
||||
func clearDisposables() -> [Disposable] {
|
||||
let disposables = Array(self.holeDisposables.values)
|
||||
@ -11,7 +12,7 @@ private final class ManagedChatListHolesState {
|
||||
return disposables
|
||||
}
|
||||
|
||||
func update(entries: Set<ChatListHolesEntry>) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable]) {
|
||||
func update(entries: Set<ChatListHolesEntry>, additionalLatestHole: ChatListHole?) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable], addedAdditionalLatestHole: (ChatListHole, MetaDisposable)?) {
|
||||
var removed: [Disposable] = []
|
||||
var added: [ChatListHolesEntry: MetaDisposable] = [:]
|
||||
|
||||
@ -30,7 +31,21 @@ private final class ManagedChatListHolesState {
|
||||
}
|
||||
}
|
||||
|
||||
return (removed, added)
|
||||
var addedAdditionalLatestHole: (ChatListHole, MetaDisposable)?
|
||||
if self.holeDisposables.isEmpty {
|
||||
if self.additionalLatestHoleDisposable?.0 != additionalLatestHole {
|
||||
if let (_, disposable) = self.additionalLatestHoleDisposable {
|
||||
removed.append(disposable)
|
||||
}
|
||||
if let additionalLatestHole = additionalLatestHole {
|
||||
let disposable = MetaDisposable()
|
||||
self.additionalLatestHoleDisposable = (additionalLatestHole, disposable)
|
||||
addedAdditionalLatestHole = (additionalLatestHole, disposable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (removed, added, addedAdditionalLatestHole)
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,9 +53,17 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
|
||||
return Signal { _ in
|
||||
let state = Atomic(value: ManagedChatListHolesState())
|
||||
|
||||
let disposable = postbox.chatListHolesView().start(next: { view in
|
||||
let (removed, added) = state.with { state -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable]) in
|
||||
return state.update(entries: view.entries)
|
||||
let topRootHoleKey = PostboxViewKey.allChatListHoles(.root)
|
||||
let topRootHole = postbox.combinedView(keys: [topRootHoleKey])
|
||||
|
||||
let disposable = combineLatest(postbox.chatListHolesView(), topRootHole).start(next: { view, topRootHoleView in
|
||||
var additionalLatestHole: ChatListHole?
|
||||
if let topRootHole = topRootHoleView.views[topRootHoleKey] as? AllChatListHolesView {
|
||||
additionalLatestHole = topRootHole.latestHole
|
||||
}
|
||||
|
||||
let (removed, added, addedAdditionalLatestHole) = state.with { state in
|
||||
return state.update(entries: view.entries, additionalLatestHole: additionalLatestHole)
|
||||
}
|
||||
|
||||
for disposable in removed {
|
||||
@ -50,6 +73,10 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
|
||||
for (entry, disposable) in added {
|
||||
disposable.set(fetchChatListHole(postbox: postbox, network: network, accountPeerId: accountPeerId, groupId: entry.groupId, hole: entry.hole).start())
|
||||
}
|
||||
|
||||
if let (hole, disposable) = addedAdditionalLatestHole {
|
||||
disposable.set(fetchChatListHole(postbox: postbox, network: network, accountPeerId: accountPeerId, groupId: .root, hole: hole).start())
|
||||
}
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
|
@ -59,23 +59,27 @@ public enum ChatListFilterPresetName: Equatable, Hashable, PostboxCoding {
|
||||
}
|
||||
|
||||
public struct ChatListFilterPreset: Equatable, PostboxCoding {
|
||||
public var id: Int64
|
||||
public var name: ChatListFilterPresetName
|
||||
public var includeCategories: ChatListIncludeCategoryFilter
|
||||
public var additionallyIncludePeers: [PeerId]
|
||||
|
||||
public init(name: ChatListFilterPresetName, includeCategories: ChatListIncludeCategoryFilter, additionallyIncludePeers: [PeerId]) {
|
||||
public init(id: Int64, name: ChatListFilterPresetName, includeCategories: ChatListIncludeCategoryFilter, additionallyIncludePeers: [PeerId]) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.includeCategories = includeCategories
|
||||
self.additionallyIncludePeers = additionallyIncludePeers
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.id = decoder.decodeInt64ForKey("id", orElse: 0)
|
||||
self.name = decoder.decodeObjectForKey("name", decoder: { ChatListFilterPresetName(decoder: $0) }) as? ChatListFilterPresetName ?? ChatListFilterPresetName.custom("Preset")
|
||||
self.includeCategories = ChatListIncludeCategoryFilter(rawValue: decoder.decodeInt32ForKey("includeCategories", orElse: 0))
|
||||
self.additionallyIncludePeers = decoder.decodeInt64ArrayForKey("additionallyIncludePeers").map(PeerId.init)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt64(self.id, forKey: "id")
|
||||
encoder.encodeObject(self.name, forKey: "name")
|
||||
encoder.encodeInt32(self.includeCategories.rawValue, forKey: "includeCategories")
|
||||
encoder.encodeInt64Array(self.additionallyIncludePeers.map { $0.toInt64() }, forKey: "additionallyIncludePeers")
|
||||
@ -88,6 +92,7 @@ public struct ChatListFilterSettings: PreferencesEntry, Equatable {
|
||||
public static var `default`: ChatListFilterSettings {
|
||||
return ChatListFilterSettings(presets: [
|
||||
ChatListFilterPreset(
|
||||
id: Int64(arc4random()),
|
||||
name: .unread,
|
||||
includeCategories: ChatListIncludeCategoryFilter.all.subtracting(.read),
|
||||
additionallyIncludePeers: []
|
||||
@ -116,12 +121,15 @@ public struct ChatListFilterSettings: PreferencesEntry, Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateChatListFilterSettingsInteractively(postbox: Postbox, _ f: @escaping (ChatListFilterSettings) -> ChatListFilterSettings) -> Signal<Never, NoError> {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
public func updateChatListFilterSettingsInteractively(postbox: Postbox, _ f: @escaping (ChatListFilterSettings) -> ChatListFilterSettings) -> Signal<ChatListFilterSettings, NoError> {
|
||||
return postbox.transaction { transaction -> ChatListFilterSettings in
|
||||
var result: ChatListFilterSettings?
|
||||
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatListFilterSettings, { entry in
|
||||
var settings = entry as? ChatListFilterSettings ?? ChatListFilterSettings.default
|
||||
return f(settings)
|
||||
let updated = f(settings)
|
||||
result = updated
|
||||
return updated
|
||||
})
|
||||
return result ?? .default
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user