Folder improvements

This commit is contained in:
Ali 2023-04-03 01:30:06 +04:00
parent e10f30d3eb
commit 0b59cd2864
17 changed files with 437 additions and 62 deletions

View File

@ -91,6 +91,7 @@ swift_library(
"//submodules/InviteLinksUI",
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
"//submodules/ItemListUI",
"//submodules/QrCodeUI",
],
visibility = [
"//visibility:public",

View File

@ -3217,7 +3217,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let text = strongSelf.presentationData.strings.ChatList_DeletedThreads(Int32(threadIds.count))
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: text), elevatedLayout: false, animateInAsReplacement: true, action: { value in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: text, text: nil), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else {
return false
}
@ -3301,7 +3301,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let text = strongSelf.presentationData.strings.ChatList_DeletedChats(Int32(peerIds.count))
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: text), elevatedLayout: false, animateInAsReplacement: true, action: { value in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: text, text: nil), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else {
return false
}
@ -3646,7 +3646,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return true
})
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { value in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: strongSelf.presentationData.strings.Undo_ChatCleared, text: nil), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else {
return false
}
@ -3868,7 +3868,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
let statusText = self.presentationData.strings.Undo_DeletedTopic
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: statusText, text: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
guard let self else {
return false
}
@ -4156,7 +4156,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
return true
})
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: statusText, text: nil), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
guard let strongSelf = self else {
return false
}

View File

@ -15,6 +15,10 @@ import AvatarNode
import ChatListFilterSettingsHeaderItem
import PremiumUI
import InviteLinksUI
import QrCodeUI
import ContextUI
import AsyncDisplayKit
import UndoUI
private enum FilterSection: Int32, Hashable {
case include
@ -37,6 +41,7 @@ private final class ChatListFilterPresetControllerArguments {
let createLink: () -> Void
let openLink: (ExportedChatFolderLink) -> Void
let removeLink: (ExportedChatFolderLink) -> Void
let linkContextAction: (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void
init(
context: AccountContext,
@ -53,7 +58,8 @@ private final class ChatListFilterPresetControllerArguments {
expandSection: @escaping (FilterSection) -> Void,
createLink: @escaping () -> Void,
openLink: @escaping (ExportedChatFolderLink) -> Void,
removeLink: @escaping (ExportedChatFolderLink) -> Void
removeLink: @escaping (ExportedChatFolderLink) -> Void,
linkContextAction: @escaping (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void
) {
self.context = context
self.updateState = updateState
@ -70,6 +76,7 @@ private final class ChatListFilterPresetControllerArguments {
self.createLink = createLink
self.openLink = openLink
self.removeLink = removeLink
self.linkContextAction = linkContextAction
}
}
@ -537,7 +544,9 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry {
arguments.openLink(invite)
}, removeAction: { invite in
arguments.removeLink(invite)
}, contextAction: nil)
}, contextAction: { link, node, gesture in
arguments.linkContextAction(link, node, gesture)
})
case let .inviteLinkInfo(text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
}
@ -624,7 +633,8 @@ private func chatListFilterPresetControllerEntries(presentationData: Presentatio
if let currentPreset, let data = currentPreset.data, data.isShared {
} else {
entries.append(.excludePeersHeader(presentationData.strings.ChatListFolder_ExcludedSectionHeader))
entries.append(.addExcludePeer(title: presentationData.strings.ChatListFolder_AddChats))
//TODO:localize
entries.append(.addExcludePeer(title: "Add Chats to Exclude"))
var excludeCategoryIndex = 0
for category in ChatListFilterExcludeCategory.allCases {
@ -1085,6 +1095,8 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
var focusOnNameImpl: (() -> Void)?
var clearFocusImpl: (() -> Void)?
var applyImpl: ((Bool, @escaping () -> Void) -> Void)?
var getControllerImpl: (() -> ViewController?)?
var presentInGlobalOverlayImpl: ((ViewController) -> Void)?
let sharedLinks = Promise<[ExportedChatFolderLink]?>(nil)
if let currentPreset {
@ -1375,9 +1387,10 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
if let updatedLink {
if let index = links.firstIndex(where: { $0.link == link.link }) {
links.remove(at: index)
}
links[index] = updatedLink
} else {
links.insert(updatedLink, at: 0)
}
sharedLinks.set(.single(links))
} else {
if let index = links.firstIndex(where: { $0.link == link.link }) {
@ -1387,6 +1400,8 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
}
})
}
}, presentController: { c in
presentControllerImpl?(c, nil)
}))
})
}
@ -1404,6 +1419,59 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
actionsDisposable.add(context.engine.peers.deleteChatFolderLink(filterId: currentPreset.id, link: link).start())
})
}
},
linkContextAction: { invite, node, gesture in
guard let node = node as? ContextExtractedContentContainingNode, let controller = getControllerImpl?(), let invite = invite, let currentPreset else {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextCopy, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.default)
//dismissTooltipsImpl?()
UIPasteboard.general.string = invite.link
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor)
}, action: { _, f in
f(.dismissWithoutContent)
presentControllerImpl?(QrCodeScreen(context: context, updatedPresentationData: nil, subject: .chatFolder(slug: invite.slug)), nil)
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextRevoke, textColor: .destructive, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { _, f in
f(.dismissWithoutContent)
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
var links = links ?? []
if let index = links.firstIndex(where: { $0.link == invite.link }) {
links.remove(at: index)
}
sharedLinks.set(.single(links))
})
let _ = (context.engine.peers.editChatFolderLink(filterId: currentPreset.id, link: invite, title: nil, peerIds: nil, revoke: true)
|> deliverOnMainQueue).start(completed: {
let _ = (context.engine.peers.deleteChatFolderLink(filterId: currentPreset.id, link: invite)
|> deliverOnMainQueue).start(completed: {
})
})
})))
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .extracted(InviteLinkContextExtractedContentSource(controller: controller, sourceNode: node, keepInPlace: false, blurBackground: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
presentInGlobalOverlayImpl?(contextController)
}
)
@ -1577,6 +1645,14 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
})
})]), nil)
}
getControllerImpl = { [weak controller] in
return controller
}
presentInGlobalOverlayImpl = { [weak controller] c in
if let controller = controller {
controller.presentInGlobalOverlay(c)
}
}
attemptNavigationImpl = {
let state = stateValue.with { $0 }
if let currentPreset = currentPreset, case let .filter(currentId, currentTitle, currentEmoticon, currentData) = currentPreset {
@ -1652,7 +1728,9 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
|> deliverOnMainQueue).start(next: { existingLink in
if let existingLink {
completed()
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: existingLink, linkUpdated: linkUpdated))
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peerIds, currentInvitation: existingLink, linkUpdated: linkUpdated, presentController: { c in
presentController(c)
}))
return
}
@ -1672,7 +1750,9 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
})
if peers.allSatisfy({ !canShareLinkToPeer(peer: $0) }) {
completed()
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: nil, linkUpdated: linkUpdated))
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: nil, linkUpdated: linkUpdated, presentController: { c in
presentController(c)
}))
} else {
var enabledPeerIds: [EnginePeer.Id] = []
for peer in peers {
@ -1686,7 +1766,9 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
completed()
linkUpdated(link)
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: link, linkUpdated: linkUpdated))
pushController(folderInviteLinkListController(context: context, filterId: folderId, title: title, allPeerIds: peers.map(\.id), currentInvitation: link, linkUpdated: linkUpdated, presentController: { c in
presentController(c)
}))
}, error: { error in
completed()
//TODO:localize
@ -1714,3 +1796,17 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec
})
})
}
private final class InviteLinkContextReferenceContentSource: ContextReferenceContentSource {
private let controller: ViewController
private let sourceNode: ContextReferenceContentNode
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerReferenceViewInfo? {
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -316,7 +316,7 @@ private struct FolderInviteLinkListControllerState: Equatable {
var isSaving: Bool = false
}
public func folderInviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filterId: Int32, title filterTitle: String, allPeerIds: [PeerId], currentInvitation: ExportedChatFolderLink?, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void) -> ViewController {
public func folderInviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filterId: Int32, title filterTitle: String, allPeerIds: [PeerId], currentInvitation: ExportedChatFolderLink?, linkUpdated: @escaping (ExportedChatFolderLink?) -> Void, presentController parentPresentController: ((ViewController) -> Void)?) -> ViewController {
var pushControllerImpl: ((ViewController) -> Void)?
let _ = pushControllerImpl
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
@ -346,7 +346,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
var getControllerImpl: (() -> ViewController?)?
var displayTooltipImpl: ((UndoOverlayContent) -> Void)?
var displayTooltipImpl: ((UndoOverlayContent, Bool) -> Void)?
var didDisplayAddPeerNotice: Bool = false
@ -439,11 +439,6 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
}
}
})
/*promptController.dismissed = { byOutsideTap in
if byOutsideTap {
completionHandler(nil)
}
}*/
presentControllerImpl?(promptController, nil)
})))
@ -509,7 +504,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
dismissTooltipsImpl?()
//TODO:localize
displayTooltipImpl?(.info(title: nil, text: "People who already used the invite link will be able to join newly added chats.", timeout: 8))
displayTooltipImpl?(.info(title: nil, text: "People who already used the invite link will be able to join newly added chats.", timeout: 8), true)
}
} else {
//TODO:localize
@ -532,7 +527,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
}
}
dismissTooltipsImpl?()
displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil))
displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil), true)
}
}, toggleAllSelected: {
let _ = (context.engine.data.get(
@ -605,6 +600,9 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "An error occurred.", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, completed: {
linkUpdated(ExportedChatFolderLink(title: state.title ?? "", link: currentLink.link, peerIds: Array(state.selectedPeerIds), isRevoked: false))
//TODO:localize
displayTooltipImpl?(.info(title: nil, text: "Link updated", timeout: 3), false)
dismissImpl?()
}))
} else {
@ -613,7 +611,6 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
} else {
dismissImpl?()
}
dismissImpl?()
}
let _ = (allPeers
@ -768,18 +765,15 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
getControllerImpl = { [weak controller] in
return controller
}
displayTooltipImpl = { [weak controller] c in
if let controller = controller {
displayTooltipImpl = { [weak controller] c, inCurrentContext in
let presentationData = context.sharedContext.currentPresentationData.with({ $0 })
if let controller = controller, inCurrentContext {
controller.present(UndoOverlayController(presentationData: presentationData, content: c, elevatedLayout: false, action: { _ in return false }), in: .current)
} else if !inCurrentContext {
parentPresentController?(UndoOverlayController(presentationData: presentationData, content: c, elevatedLayout: false, action: { _ in return false }))
}
}
dismissTooltipsImpl = { [weak controller] in
controller?.window?.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()
}
})
controller?.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitAction()

View File

@ -956,26 +956,26 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
}
final class InviteLinkContextExtractedContentSource: ContextExtractedContentSource {
var keepInPlace: Bool
let ignoreContentTouches: Bool = true
let blurBackground: Bool
public final class InviteLinkContextExtractedContentSource: ContextExtractedContentSource {
public var keepInPlace: Bool
public let ignoreContentTouches: Bool = true
public let blurBackground: Bool
private let controller: ViewController
private let sourceNode: ContextExtractedContentContainingNode
init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool, blurBackground: Bool) {
public init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool, blurBackground: Bool) {
self.controller = controller
self.sourceNode = sourceNode
self.keepInPlace = keepInPlace
self.blurBackground = blurBackground
}
func takeView() -> ContextControllerTakeViewInfo? {
public func takeView() -> ContextControllerTakeViewInfo? {
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
}
func putBack() -> ContextControllerPutBackViewInfo? {
public func putBack() -> ContextControllerPutBackViewInfo? {
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
}
}

View File

@ -404,6 +404,7 @@ public struct ChatListViewReadState: Equatable {
final class MutableChatListView {
let groupId: PeerGroupId
let filterPredicate: ChatListFilterPredicate?
private let aroundIndex: ChatListIndex
private let summaryComponents: ChatListEntrySummaryComponents
fileprivate var groupEntries: [ChatListGroupReferenceEntry]
private var count: Int
@ -416,11 +417,16 @@ final class MutableChatListView {
private var additionalItems: [AdditionalChatListItem] = []
fileprivate var additionalItemEntries: [MutableChatListAdditionalItemEntry] = []
private var currentHiddenPeerIds = Set<PeerId>()
init(postbox: PostboxImpl, currentTransaction: Transaction, groupId: PeerGroupId, filterPredicate: ChatListFilterPredicate?, aroundIndex: ChatListIndex, count: Int, summaryComponents: ChatListEntrySummaryComponents) {
self.groupId = groupId
self.filterPredicate = filterPredicate
self.aroundIndex = aroundIndex
self.summaryComponents = summaryComponents
self.currentHiddenPeerIds = postbox.hiddenChatIds
var spaces: [ChatListViewSpace] = [
.group(groupId: self.groupId, pinned: .notPinned, predicate: filterPredicate)
]
@ -474,7 +480,7 @@ final class MutableChatListView {
if let entry = postbox.chatListTable.earlierEntryInfos(groupId: groupId, index: upperBound, messageHistoryTable: postbox.messageHistoryTable, peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: 1).first {
switch entry {
case let .message(index, messageIndex):
if let messageIndex = messageIndex {
if let messageIndex = messageIndex, !postbox.isChatHidden(peerId: messageIndex.id.peerId) {
foundIndices.append((index, messageIndex))
if index.pinningIndex == nil {
unpinnedCount += 1
@ -541,7 +547,7 @@ final class MutableChatListView {
func refreshDueToExternalTransaction(postbox: PostboxImpl, currentTransaction: Transaction) -> Bool {
var updated = false
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: .absoluteUpperBound, summaryComponents: self.summaryComponents, halfLimit: self.count)
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, halfLimit: self.count)
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
updated = true
@ -559,8 +565,19 @@ final class MutableChatListView {
func replay(postbox: PostboxImpl, currentTransaction: Transaction, operations: [PeerGroupId: [ChatListOperation]], updatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], updatedPeers: [PeerId: Peer], updatedPeerPresences: [PeerId: PeerPresence], transaction: PostboxTransaction, context: MutableChatListViewReplayContext) -> Bool {
var hasChanges = false
let hiddenChatIds = postbox.hiddenChatIds
var hasFilterChanges = false
if hiddenChatIds != self.currentHiddenPeerIds {
self.currentHiddenPeerIds = hiddenChatIds
hasFilterChanges = true
}
if transaction.updatedGlobalNotificationSettings && self.filterPredicate != nil {
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: .absoluteUpperBound, summaryComponents: self.summaryComponents, halfLimit: self.count)
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, halfLimit: self.count)
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
hasChanges = true
} else if hasFilterChanges {
self.state = ChatListViewState(postbox: postbox, currentTransaction: currentTransaction, spaces: self.spaces, anchorIndex: self.aroundIndex, summaryComponents: self.summaryComponents, halfLimit: self.count)
self.sampledState = self.state.sample(postbox: postbox, currentTransaction: currentTransaction)
hasChanges = true
} else {
@ -577,6 +594,9 @@ final class MutableChatListView {
invalidatedGroups = true
}
}
if hasFilterChanges {
invalidatedGroups = true
}
if invalidatedGroups {
self.reloadGroups(postbox: postbox)

View File

@ -71,6 +71,9 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: postbox.peerNotificationSettingsTable.getEffective(notificationsPeerId))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, threadId: nil, calculation: predicate.messageTagSummary)
if postbox.isChatHidden(peerId: peer.id) {
return false
}
if predicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) {
return true
} else {
@ -178,6 +181,21 @@ private final class ChatListViewSpaceState {
}
}
let predicate: ((ChatListIntermediateEntry) -> Bool)?
if let filterPredicate = filterPredicate {
predicate = mappedChatListFilterPredicate(postbox: postbox, currentTransaction: currentTransaction, groupId: groupId, predicate: filterPredicate)
} else if postbox.hasHiddenChatIds {
predicate = { entry in
if postbox.isChatHidden(peerId: entry.index.messageIndex.id.peerId) {
return false
} else {
return true
}
}
} else {
predicate = nil
}
if case .includePinnedAsUnpinned = pinned {
let unpinnedLowerBound: MutableChatListEntryIndex
let unpinnedUpperBound: MutableChatListEntryIndex
@ -186,7 +204,7 @@ private final class ChatListViewSpaceState {
let resolvedUnpinnedAnchorIndex = min(unpinnedUpperBound, max(self.anchorIndex, unpinnedLowerBound))
if lowerOrAtAnchorMessages.count < self.halfLimit || higherThanAnchorMessages.count < self.halfLimit {
let loadedMessages = postbox.chatListTable.entries(groupId: groupId, from: (ChatListIndex.pinnedLowerBound, true), to: (ChatListIndex.absoluteUpperBound, true), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit * 2, predicate: filterPredicate.flatMap { mappedChatListFilterPredicate(postbox: postbox, currentTransaction: currentTransaction, groupId: groupId, predicate: $0) }).map(mapEntry).sorted(by: { $0.entryIndex < $1.entryIndex })
let loadedMessages = postbox.chatListTable.entries(groupId: groupId, from: (ChatListIndex.pinnedLowerBound, true), to: (ChatListIndex.absoluteUpperBound, true), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit * 2, predicate: predicate).map(mapEntry).sorted(by: { $0.entryIndex < $1.entryIndex })
if lowerOrAtAnchorMessages.count < self.halfLimit {
var nextLowerIndex: MutableChatListEntryIndex
@ -225,7 +243,7 @@ private final class ChatListViewSpaceState {
} else {
nextLowerIndex = resolvedAnchorIndex.successor
}
let loadedLowerMessages = postbox.chatListTable.entries(groupId: groupId, from: (nextLowerIndex.index, nextLowerIndex.isMessage), to: (lowerBound.index, lowerBound.isMessage), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit - lowerOrAtAnchorMessages.count, predicate: filterPredicate.flatMap { mappedChatListFilterPredicate(postbox: postbox, currentTransaction: currentTransaction, groupId: groupId, predicate: $0) }).map(mapEntry)
let loadedLowerMessages = postbox.chatListTable.entries(groupId: groupId, from: (nextLowerIndex.index, nextLowerIndex.isMessage), to: (lowerBound.index, lowerBound.isMessage), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit - lowerOrAtAnchorMessages.count, predicate: predicate).map(mapEntry)
lowerOrAtAnchorMessages.append(contentsOf: loadedLowerMessages)
}
if higherThanAnchorMessages.count < self.halfLimit {
@ -235,7 +253,7 @@ private final class ChatListViewSpaceState {
} else {
nextHigherIndex = resolvedAnchorIndex
}
let loadedHigherMessages = postbox.chatListTable.entries(groupId: groupId, from: (nextHigherIndex.index, nextHigherIndex.isMessage), to: (upperBound.index, upperBound.isMessage), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit - higherThanAnchorMessages.count, predicate: filterPredicate.flatMap { mappedChatListFilterPredicate(postbox: postbox, currentTransaction: currentTransaction, groupId: groupId, predicate: $0) }).map(mapEntry)
let loadedHigherMessages = postbox.chatListTable.entries(groupId: groupId, from: (nextHigherIndex.index, nextHigherIndex.isMessage), to: (upperBound.index, upperBound.isMessage), peerChatInterfaceStateTable: postbox.peerChatInterfaceStateTable, count: self.halfLimit - higherThanAnchorMessages.count, predicate: predicate).map(mapEntry)
higherThanAnchorMessages.append(contentsOf: loadedHigherMessages)
}
}

View File

@ -1236,6 +1236,16 @@ public final class Transaction {
assert(!self.disposed)
return self.postbox!.messageHistoryThreadPinnedTable.get(peerId: peerId)
}
func addChatHidden(peerId: PeerId) -> Int {
assert(!self.disposed)
return self.postbox!.addChatHidden(peerId: peerId)
}
func removeChatHidden(peerId: PeerId, index: Int) {
assert(!self.disposed)
return self.postbox!.removeChatHidden(peerId: peerId, index: index)
}
}
public enum PostboxResult {
@ -1470,6 +1480,40 @@ final class PostboxImpl {
private var currentUpdatedPeerThreadCombinedStates = Set<PeerId>()
private var currentUpdatedPinnedThreads = Set<PeerId>()
private var currentHiddenChatIds: [PeerId: Bag<Void>] = [:]
private var currentUpdatedHiddenPeerIds: Bool = false
var hiddenChatIds: Set<PeerId> {
if self.currentHiddenChatIds.isEmpty {
return Set()
} else {
var result = Set<PeerId>()
for (peerId, bag) in self.currentHiddenChatIds {
if !bag.isEmpty {
result.insert(peerId)
}
}
return result
}
}
func isChatHidden(peerId: PeerId) -> Bool {
if let bag = self.currentHiddenChatIds[peerId], !bag.isEmpty {
return true
} else {
return false
}
}
var hasHiddenChatIds: Bool {
for (_, bag) in self.currentHiddenChatIds {
if !bag.isEmpty {
return true
}
}
return false
}
private var currentNeedsReindexUnreadCounters: Bool = false
private let statePipe: ValuePipe<PostboxCoding> = ValuePipe()
@ -2117,7 +2161,7 @@ final class PostboxImpl {
let updatedPeerTimeoutAttributes = self.peerTimeoutPropertiesTable.hasUpdates
let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadStates: self.currentUpdatedTotalUnreadStates, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds, updatedGlobalNotificationSettings: self.currentNeedsReindexUnreadCounters, updatedPeerTimeoutAttributes: updatedPeerTimeoutAttributes, updatedMessageThreadPeerIds: updatedMessageThreadPeerIds, updatedPeerThreadCombinedStates: self.currentUpdatedPeerThreadCombinedStates, updatedPeerThreadsSummaries: Set(alteredInitialPeerThreadsSummaries.keys), updatedPinnedThreads: self.currentUpdatedPinnedThreads)
let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentPeerHoleOperations: self.currentPeerHoleOperations, currentOperationsByPeerId: self.currentOperationsByPeerId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedPeerNotificationBehaviorTimestamps: self.currentUpdatedPeerNotificationBehaviorTimestamps, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadStates: self.currentUpdatedTotalUnreadStates, currentUpdatedTotalUnreadSummaries: self.currentUpdatedGroupTotalUnreadSummaries, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentUpdatedGroupSummarySynchronizeOperations: self.currentUpdatedGroupSummarySynchronizeOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId, updatedFailedMessagePeerIds: self.messageHistoryFailedTable.updatedPeerIds, updatedFailedMessageIds: self.messageHistoryFailedTable.updatedMessageIds, updatedGlobalNotificationSettings: self.currentNeedsReindexUnreadCounters, updatedPeerTimeoutAttributes: updatedPeerTimeoutAttributes, updatedMessageThreadPeerIds: updatedMessageThreadPeerIds, updatedPeerThreadCombinedStates: self.currentUpdatedPeerThreadCombinedStates, updatedPeerThreadsSummaries: Set(alteredInitialPeerThreadsSummaries.keys), updatedPinnedThreads: self.currentUpdatedPinnedThreads, updatedHiddenPeerIds: self.currentUpdatedHiddenPeerIds)
var updatedTransactionState: Int64?
var updatedMasterClientId: Int64?
if !transaction.isEmpty {
@ -2171,6 +2215,7 @@ final class PostboxImpl {
self.currentUpdatedPendingPeerNotificationSettings.removeAll()
self.currentGroupIdsWithUpdatedReadStats.removeAll()
self.currentUpdatedPinnedThreads.removeAll()
self.currentUpdatedHiddenPeerIds = false
self.currentNeedsReindexUnreadCounters = false
for table in self.tables {
@ -3623,6 +3668,36 @@ final class PostboxImpl {
return disposable
}
public func addHiddenChatFilterAndChatIds(peerIds: [PeerId]) -> Disposable {
let disposable = MetaDisposable()
let queue = self.queue
let _ = (self.transaction { transaction -> [PeerId: Int] in
var peerIndices: [PeerId: Int] = [:]
for peerId in peerIds {
peerIndices[peerId] = transaction.addChatHidden(peerId: peerId)
}
return peerIndices
}).start(next: { peerIndices in
disposable.set(ActionDisposable { [weak self] in
queue.async {
guard let `self` = self else {
return
}
let _ = (self.transaction { transaction -> Void in
for peerId in peerIds {
if let index = peerIndices[peerId] {
transaction.removeChatHidden(peerId: peerId, index: index)
}
}
}).start()
}
})
})
return disposable
}
fileprivate func scanMessages(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) {
var index = MessageIndex.lowerBound(peerId: peerId, namespace: namespace)
while true {
@ -3812,6 +3887,25 @@ final class PostboxImpl {
self.messageHistoryThreadPinnedTable.set(peerId: peerId, threadIds: threadIds)
}
fileprivate func addChatHidden(peerId: PeerId) -> Int {
let bag: Bag<Void>
if let current = self.currentHiddenChatIds[peerId] {
bag = current
} else {
bag = Bag()
self.currentHiddenChatIds[peerId] = bag
}
self.currentUpdatedHiddenPeerIds = true
return bag.add(Void())
}
fileprivate func removeChatHidden(peerId: PeerId, index: Int) {
if let current = self.currentHiddenChatIds[peerId] {
current.remove(index)
self.currentUpdatedHiddenPeerIds = true
}
}
fileprivate func reindexUnreadCounters(currentTransaction: Transaction) {
self.groupMessageStatsTable.removeAll()
let _ = CFAbsoluteTimeGetCurrent()
@ -4415,6 +4509,16 @@ public class Postbox {
return disposable
}
public func addHiddenChatIds(peerIds: [PeerId]) -> Disposable {
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.addHiddenChatFilterAndChatIds(peerIds: peerIds))
}
return disposable
}
public func isMasterClient() -> Signal<Bool, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()

View File

@ -48,6 +48,7 @@ final class PostboxTransaction {
let updatedPeerThreadCombinedStates: Set<PeerId>
let updatedPeerThreadsSummaries: Set<PeerId>
let updatedPinnedThreads: Set<PeerId>
let updatedHiddenPeerIds: Bool
var isEmpty: Bool {
if currentUpdatedState != nil {
@ -191,10 +192,13 @@ final class PostboxTransaction {
if !self.updatedPinnedThreads.isEmpty {
return false
}
if self.updatedHiddenPeerIds {
return false
}
return true
}
init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: Set<PeerId>, currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set<PeerId>, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set<PeerId>?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set<PeerId>, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set<NoticeEntryKey>, updatedCacheEntryKeys: Set<ItemCacheEntryId>, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set<PeerId>, updatedFailedMessageIds: Set<MessageId>, updatedGlobalNotificationSettings: Bool, updatedPeerTimeoutAttributes: Bool, updatedMessageThreadPeerIds: Set<PeerId>, updatedPeerThreadCombinedStates: Set<PeerId>, updatedPeerThreadsSummaries: Set<PeerId>, updatedPinnedThreads: Set<PeerId>) {
init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: Set<PeerId>, currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set<PeerId>, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set<PeerId>?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set<PeerId>, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set<NoticeEntryKey>, updatedCacheEntryKeys: Set<ItemCacheEntryId>, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set<PeerId>, updatedFailedMessageIds: Set<MessageId>, updatedGlobalNotificationSettings: Bool, updatedPeerTimeoutAttributes: Bool, updatedMessageThreadPeerIds: Set<PeerId>, updatedPeerThreadCombinedStates: Set<PeerId>, updatedPeerThreadsSummaries: Set<PeerId>, updatedPinnedThreads: Set<PeerId>, updatedHiddenPeerIds: Bool) {
self.currentUpdatedState = currentUpdatedState
self.currentPeerHoleOperations = currentPeerHoleOperations
self.currentOperationsByPeerId = currentOperationsByPeerId
@ -241,5 +245,6 @@ final class PostboxTransaction {
self.updatedPeerThreadCombinedStates = updatedPeerThreadCombinedStates
self.updatedPeerThreadsSummaries = updatedPeerThreadsSummaries
self.updatedPinnedThreads = updatedPinnedThreads
self.updatedHiddenPeerIds = updatedHiddenPeerIds
}
}

View File

@ -333,6 +333,12 @@ public final class AccountViewTracker {
public let chatListPreloadItems = Promise<Set<ChatHistoryPreloadItem>>([])
private let hiddenChatListFilterIdsValue = Atomic<[Int32: Bag<Void>]>(value: [:])
private let hiddenChatListFilterIdsPromise = ValuePromise<Set<Int32>>(Set())
public var hiddenChatListFilterIds: Signal<Set<Int32>, NoError> {
return self.hiddenChatListFilterIdsPromise.get()
}
var resetPeerHoleManagement: ((PeerId) -> Void)?
init(account: Account) {
@ -2162,4 +2168,52 @@ public final class AccountViewTracker {
return .never()
}
}
public func addHiddenChatListFilterIds(_ ids: [Int32]) -> Disposable {
var indices: [Int32: Int] = [:]
var updatedIds = Set<Int32>()
let _ = self.hiddenChatListFilterIdsValue.modify { value in
var value = value
for id in ids {
let bag: Bag<Void>
if let current = value[id] {
bag = current
} else {
bag = Bag()
value[id] = bag
}
indices[id] = bag.add(Void())
}
for (id, bag) in value {
if !bag.isEmpty {
updatedIds.insert(id)
}
}
return value
}
self.hiddenChatListFilterIdsPromise.set(updatedIds)
return ActionDisposable { [weak self] in
DispatchQueue.main.async {
guard let `self` = self else {
return
}
var updatedIds = Set<Int32>()
let _ = self.hiddenChatListFilterIdsValue.modify { value in
for id in ids {
if let bag = value[id], let index = indices[id] {
bag.remove(index)
}
}
for (id, bag) in value {
if !bag.isEmpty {
updatedIds.insert(id)
}
}
return value
}
self.hiddenChatListFilterIdsPromise.set(updatedIds)
}
}
}
}

View File

@ -107,7 +107,7 @@ extension UserLimitsConfiguration {
self.maxAboutLength = getValue("about_length_limit", orElse: defaultValue.maxAboutLength)
self.maxAnimatedEmojisInText = getGeneralValue("message_animated_emoji_max", orElse: defaultValue.maxAnimatedEmojisInText)
self.maxReactionsPerMessage = getValue("reactions_user_max", orElse: 1)
self.maxSharedFolderInviteLinks = getValue("community_invites_limit", orElse: 3)
self.maxSharedFolderJoin = getValue("communities_joined_limit", orElse: 2)
self.maxSharedFolderInviteLinks = getValue("chatlists_invites_limit", orElse: isPremium ? 100 : 3)
self.maxSharedFolderJoin = getValue("chatlists_joined_limit", orElse: isPremium ? 100 : 2)
}
}

View File

@ -1000,11 +1000,20 @@ func _internal_updateChatListFiltersInteractively(transaction: Transaction, _ f:
}
}
func _internal_updatedChatListFilters(postbox: Postbox) -> Signal<[ChatListFilter], NoError> {
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|> map { preferences -> [ChatListFilter] in
func _internal_updatedChatListFilters(postbox: Postbox, hiddenIds: Signal<Set<Int32>, NoError> = .single(Set())) -> Signal<[ChatListFilter], NoError> {
return combineLatest(
postbox.preferencesView(keys: [PreferencesKeys.chatListFilters]),
hiddenIds
)
|> map { preferences, hiddenIds -> [ChatListFilter] in
let filtersState = preferences.values[PreferencesKeys.chatListFilters]?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
return filtersState.filters
return filtersState.filters.filter { filter in
if hiddenIds.contains(filter.id) {
return false
} else {
return true
}
}
}
|> distinctUntilChanged
}

View File

@ -510,7 +510,7 @@ public extension TelegramEngine {
}
public func updatedChatListFilters() -> Signal<[ChatListFilter], NoError> {
return _internal_updatedChatListFilters(postbox: self.account.postbox)
return _internal_updatedChatListFilters(postbox: self.account.postbox, hiddenIds: self.account.viewTracker.hiddenChatListFilterIds)
}
public func updatedChatListFiltersInfo() -> Signal<(filters: [ChatListFilter], synchronized: Bool), NoError> {

View File

@ -437,7 +437,7 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
let text: String
if let linkContents = component.linkContents {
if case .remove = component.subject {
text = "Do you want to quit the chats you joined when\nadding the folder \(linkContents.title ?? "Folder")?"
text = "Do you also want to quit the chats included in this folder?"
} else if allChatsAdded {
text = "You have already added this\nfolder and its chats."
} else if linkContents.localFilterId == nil {
@ -763,7 +763,7 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
isEnabled: !self.selectedItems.isEmpty || component.linkContents?.localFilterId != nil,
displaysProgress: self.inProgress,
action: { [weak self] in
guard let self, let component = self.component, let controller = self.environment?.controller() else {
guard let self, let component = self.component, let linkContents = component.linkContents, let controller = self.environment?.controller() else {
return
}
@ -773,13 +773,78 @@ private final class ChatFolderLinkPreviewScreenComponent: Component {
component.completion?()
self.joinDisposable = (component.context.engine.peers.leaveChatFolder(folderId: folderId, removePeerIds: Array(self.selectedItems))
let disposable = DisposableSet()
disposable.add(component.context.account.postbox.addHiddenChatIds(peerIds: Array(self.selectedItems)))
disposable.add(component.context.account.viewTracker.addHiddenChatListFilterIds([folderId]))
let folderTitle = linkContents.title ?? ""
var additionalText: String?
if !self.selectedItems.isEmpty {
if self.selectedItems.count == 1 {
additionalText = "You also left **1** chat"
} else {
additionalText = "You also left **\(self.selectedItems.count)** chats"
}
}
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
var chatListController: ChatListController?
if let navigationController = controller.navigationController as? NavigationController {
for viewController in navigationController.viewControllers {
if let rootController = viewController as? TabBarController {
for c in rootController.controllers {
if let c = c as? ChatListController {
chatListController = c
break
}
}
} else if let c = viewController as? ChatListController {
chatListController = c
break
}
}
}
let context = component.context
let selectedItems = self.selectedItems
let undoOverlayController = UndoOverlayController(
presentationData: presentationData,
content: .removedChat(title: "Folder \(folderTitle) deleted", text: additionalText),
elevatedLayout: false,
action: { value in
if case .commit = value {
let _ = (context.engine.peers.leaveChatFolder(folderId: folderId, removePeerIds: Array(selectedItems))
|> deliverOnMainQueue).start(completed: {
Queue.mainQueue().after(1.0, {
disposable.dispose()
})
})
return true
} else if case .undo = value {
disposable.dispose()
return true
}
return false
}
)
if let chatListController, chatListController.view.window != nil {
chatListController.present(undoOverlayController, in: .current)
} else {
controller.present(undoOverlayController, in: .window(.root))
}
controller.dismiss()
/*self.joinDisposable = (component.context.engine.peers.leaveChatFolder(folderId: folderId, removePeerIds: Array(self.selectedItems))
|> deliverOnMainQueue).start(completed: { [weak self] in
guard let self, let controller = self.environment?.controller() else {
return
}
controller.dismiss()
})
})*/
} else if allChatsAdded {
controller.dismiss()
} else if let _ = component.linkContents {

View File

@ -11990,7 +11990,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
statusText = self.presentationData.strings.Undo_ChatCleared
}
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, action: { [weak self] value in
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: statusText, text: nil), elevatedLayout: false, action: { [weak self] value in
guard let strongSelf = self else {
return false
}
@ -15911,7 +15911,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.historyNode.ignoreMessagesInTimestampRange = range
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, action: { value in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: statusText, text: nil), elevatedLayout: false, action: { value in
guard let strongSelf = self else {
return false
}

View File

@ -7,7 +7,7 @@ import AccountContext
import ComponentFlow
public enum UndoOverlayContent {
case removedChat(text: String)
case removedChat(title: String, text: String?)
case archivedChat(peerId: Int64, title: String, text: String, undo: Bool)
case hidArchive(title: String, text: String, undo: Bool)
case revealedArchive(title: String, text: String, undo: Bool)

View File

@ -94,13 +94,22 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
var isUserInteractionEnabled = false
switch content {
case let .removedChat(text):
case let .removedChat(title, text):
self.avatarNode = nil
self.iconNode = nil
self.iconCheckNode = nil
self.animationNode = nil
self.animatedStickerNode = nil
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
if let text {
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.textNode.attributedText = attributedText
} else {
self.textNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
}
displayUndo = true
self.originalRemainingSeconds = 5
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)