Chat List Filter improvements

This commit is contained in:
Ali 2020-01-28 19:46:28 +04:00
parent 309a8b112b
commit 77826e91d4
13 changed files with 266 additions and 89 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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