mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Folder improvements
This commit is contained in:
@@ -1796,8 +1796,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
}
|
||||
} else {
|
||||
let groupId = self.groupId
|
||||
let filterPredicate = (self.chatListDisplayNode.containerNode.currentItemNode.chatListFilter?.data).flatMap(chatListFilterPredicate)
|
||||
signal = self.context.account.postbox.transaction { transaction -> Void in
|
||||
let filterPredicate = (self.chatListDisplayNode.containerNode.currentItemNode.chatListFilter?.data).flatMap(chatListFilterPredicate)
|
||||
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: context.account.viewTracker, groupId: groupId, filterPredicate: filterPredicate)
|
||||
if let filterPredicate = filterPredicate {
|
||||
for additionalGroupId in filterPredicate.includeAdditionalPeerGroupIds {
|
||||
|
||||
@@ -341,7 +341,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
if strongSelf.emptyShimmerEffectNode == nil {
|
||||
let emptyShimmerEffectNode = ChatListShimmerNode()
|
||||
strongSelf.emptyShimmerEffectNode = emptyShimmerEffectNode
|
||||
strongSelf.addSubnode(emptyShimmerEffectNode)
|
||||
strongSelf.insertSubnode(emptyShimmerEffectNode, belowSubnode: strongSelf.listNode)
|
||||
if let (size, insets, _) = strongSelf.validLayout, let offset = strongSelf.floatingHeaderOffset {
|
||||
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset, transition: .immediate)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ final class ChatListEmptyNode: ASDisplayNode {
|
||||
let string = NSMutableAttributedString(string: self.isFilter ? strings.ChatList_EmptyChatListFilterTitle : strings.ChatList_EmptyChatList, font: Font.medium(17.0), textColor: theme.list.itemPrimaryTextColor)
|
||||
let descriptionString: NSAttributedString
|
||||
if self.isFilter {
|
||||
descriptionString = NSAttributedString(string: strings.ChatList_EmptyChatListFilterText, font: Font.medium(14.0), textColor: theme.list.itemSecondaryTextColor)
|
||||
descriptionString = NSAttributedString(string: strings.ChatList_EmptyChatListFilterText, font: Font.regular(14.0), textColor: theme.list.itemSecondaryTextColor)
|
||||
} else {
|
||||
descriptionString = NSAttributedString()
|
||||
}
|
||||
|
||||
@@ -283,6 +283,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
||||
if view.laterIndex == nil && savedMessagesPeer == nil {
|
||||
pinnedIndexOffset += UInt16(view.additionalItemEntries.count)
|
||||
}
|
||||
var filterAfterHole = false
|
||||
loop: for entry in view.entries {
|
||||
switch entry {
|
||||
case let .MessageEntry(index, message, combinedReadState, isRemovedFromTotalUnreadCount, embeddedState, peer, peerPresence, summaryInfo, hasFailed, isContact):
|
||||
@@ -301,8 +302,9 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
||||
result.append(.PeerEntry(index: offsetPinnedIndex(index, offset: pinnedIndexOffset), presentationData: state.presentationData, message: updatedMessage, readState: updatedCombinedReadState, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: embeddedState, peer: peer, presence: peerPresence, summaryInfo: summaryInfo, editing: state.editing, hasActiveRevealControls: index.messageIndex.id.peerId == state.peerIdWithRevealedOptions, selected: state.selectedPeerIds.contains(index.messageIndex.id.peerId), inputActivities: state.peerInputActivities?.activities[index.messageIndex.id.peerId], isAd: false, hasFailedMessages: hasFailed, isContact: isContact))
|
||||
case let .HoleEntry(hole):
|
||||
if hole.index.timestamp == Int32.max - 1 {
|
||||
return ([.HeaderEntry], true)
|
||||
//return ([.HeaderEntry], true)
|
||||
}
|
||||
filterAfterHole = true
|
||||
result.append(.HoleEntry(hole, theme: state.presentationData.theme))
|
||||
}
|
||||
}
|
||||
@@ -352,10 +354,34 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
||||
}
|
||||
}
|
||||
|
||||
var isLoading: Bool = false
|
||||
|
||||
if filterAfterHole {
|
||||
var seenHole = false
|
||||
for i in (0 ..< result.count).reversed() {
|
||||
if seenHole {
|
||||
result.remove(at: i)
|
||||
} else {
|
||||
switch result[i] {
|
||||
case .HeaderEntry:
|
||||
break
|
||||
case .ArchiveIntro, .AdditionalCategory, .GroupReferenceEntry:
|
||||
break
|
||||
case .PeerEntry:
|
||||
break
|
||||
case .HoleEntry:
|
||||
isLoading = true
|
||||
seenHole = true
|
||||
result.remove(at: i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.count >= 1, case .HoleEntry = result[result.count - 1] {
|
||||
return ([.HeaderEntry], true)
|
||||
} else if result.count == 1, case .HoleEntry = result[0] {
|
||||
return ([.HeaderEntry], true)
|
||||
}
|
||||
return (result, false)
|
||||
return (result, isLoading)
|
||||
}
|
||||
|
||||
@@ -262,7 +262,9 @@ final class ChatListTable: Table {
|
||||
|
||||
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: postbox, peerId: peer.id, calculation: filterPredicate.messageTagSummary)
|
||||
|
||||
if filterPredicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) {
|
||||
if filterPredicate.pinnedPeerIds.contains(peer.id) {
|
||||
passFilter = true
|
||||
} else if filterPredicate.includes(peer: peer, groupId: groupId, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) {
|
||||
passFilter = true
|
||||
} else {
|
||||
passFilter = false
|
||||
|
||||
@@ -1488,7 +1488,7 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable
|
||||
|
||||
var updatedState = state
|
||||
switch result {
|
||||
case let .peerDialogs(dialogs, messages, chats, users, state):
|
||||
case let .peerDialogs(dialogs, messages, chats, users, _):
|
||||
updatedState.mergeChats(chats)
|
||||
updatedState.mergeUsers(users)
|
||||
|
||||
@@ -1496,7 +1496,7 @@ private func resolveMissingPeerChatInfos(network: Network, state: AccountMutable
|
||||
|
||||
for dialog in dialogs {
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, draft, folderId):
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId):
|
||||
let peerId = peer.peerId
|
||||
|
||||
updatedState.setNeedsHoleFromPreviousState(peerId: peerId, namespace: Namespaces.Message.Cloud)
|
||||
|
||||
@@ -2,9 +2,12 @@ import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import SyncCore
|
||||
import TelegramApi
|
||||
|
||||
private final class ManagedChatListHolesState {
|
||||
private var currentHole: (ChatListHolesEntry, Disposable)?
|
||||
private var currentPinnedIds: (Set<PeerId>, Disposable)?
|
||||
private var processedPinnedIds: Set<PeerId>?
|
||||
|
||||
func clearDisposables() -> [Disposable] {
|
||||
if let (_, disposable) = self.currentHole {
|
||||
@@ -15,9 +18,17 @@ private final class ManagedChatListHolesState {
|
||||
}
|
||||
}
|
||||
|
||||
func update(entries: [ChatListHolesEntry]) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable]) {
|
||||
func update(entries: [ChatListHolesEntry], pinnedIds: Set<PeerId>) -> (removed: [Disposable], added: [ChatListHolesEntry: MetaDisposable], addedPinnedIds: (Set<PeerId>, MetaDisposable)?) {
|
||||
var removed: [Disposable] = []
|
||||
var added: [ChatListHolesEntry: MetaDisposable] = [:]
|
||||
var addedPinnedIds: (Set<PeerId>, MetaDisposable)?
|
||||
|
||||
if self.processedPinnedIds == nil && !pinnedIds.isEmpty {
|
||||
self.processedPinnedIds = pinnedIds
|
||||
let disposable = MetaDisposable()
|
||||
self.currentPinnedIds = (pinnedIds, disposable)
|
||||
addedPinnedIds = (pinnedIds, disposable)
|
||||
}
|
||||
|
||||
if let (entry, disposable) = self.currentHole {
|
||||
if !entries.contains(entry) {
|
||||
@@ -32,7 +43,7 @@ private final class ManagedChatListHolesState {
|
||||
added[entry] = disposable
|
||||
}
|
||||
|
||||
return (removed, added)
|
||||
return (removed, added, addedPinnedIds)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +61,13 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
|
||||
return lhs.hole.index > rhs.hole.index
|
||||
})
|
||||
|
||||
var pinnedIds = Set<PeerId>()
|
||||
|
||||
if let preferencesView = combinedView.views[filtersKey] as? PreferencesView, let filtersState = preferencesView.values[PreferencesKeys.chatListFilters] as? ChatListFiltersState, !filtersState.filters.isEmpty {
|
||||
for filter in filtersState.filters {
|
||||
pinnedIds.formUnion(filter.data.includePeers.pinnedPeers)
|
||||
}
|
||||
|
||||
if let topRootHole = combinedView.views[topRootHoleKey] as? AllChatListHolesView, let hole = topRootHole.latestHole {
|
||||
let entry = ChatListHolesEntry(groupId: .root, hole: hole)
|
||||
if !entries.contains(entry) {
|
||||
@@ -67,8 +84,8 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
|
||||
}
|
||||
}
|
||||
|
||||
let (removed, added) = state.with { state in
|
||||
return state.update(entries: entries)
|
||||
let (removed, added, addedPinnedIds) = state.with { state in
|
||||
return state.update(entries: entries, pinnedIds: pinnedIds)
|
||||
}
|
||||
|
||||
for disposable in removed {
|
||||
@@ -78,6 +95,22 @@ 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 (ids, disposable) = addedPinnedIds {
|
||||
let signal = postbox.transaction { transaction -> [Api.InputPeer] in
|
||||
var peers: [Api.InputPeer] = []
|
||||
for id in ids {
|
||||
if let inputPeer = transaction.getPeer(id).flatMap(apiInputPeer) {
|
||||
peers.append(inputPeer)
|
||||
}
|
||||
}
|
||||
return peers
|
||||
}
|
||||
|> mapToSignal { inputPeers -> Signal<Never, NoError> in
|
||||
return loadAndStorePeerChatInfos(accountPeerId: accountPeerId, postbox: postbox, network: network, peers: inputPeers)
|
||||
}
|
||||
disposable.set(signal.start())
|
||||
}
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
@@ -90,3 +123,144 @@ func managedChatListHoles(network: Network, postbox: Postbox, accountPeerId: Pee
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox, network: Network, peers: [Api.InputPeer]) -> Signal<Never, NoError> {
|
||||
let signal = network.request(Api.functions.messages.getPeerDialogs(peers: peers.map(Api.InputDialogPeer.inputDialogPeer(peer:))))
|
||||
|> map(Optional.init)
|
||||
|
||||
return signal
|
||||
|> `catch` { _ -> Signal<Api.messages.PeerDialogs?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||
guard let result = result else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
return postbox.transaction { transaction -> Void in
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||
var channelStates: [PeerId: ChannelState] = [:]
|
||||
|
||||
switch result {
|
||||
case let .peerDialogs(dialogs, messages, chats, users, _):
|
||||
for chat in chats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
|
||||
var topMessageIds = Set<MessageId>()
|
||||
|
||||
for dialog in dialogs {
|
||||
switch dialog {
|
||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, notifySettings, pts, _, folderId):
|
||||
let peerId = peer.peerId
|
||||
|
||||
if topMessage != 0 {
|
||||
topMessageIds.insert(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: topMessage))
|
||||
}
|
||||
|
||||
var isExcludedFromChatList = false
|
||||
for chat in chats {
|
||||
if chat.peerId == peerId {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
if let group = groupOrChannel as? TelegramGroup {
|
||||
if group.flags.contains(.deactivated) {
|
||||
isExcludedFromChatList = true
|
||||
} else {
|
||||
switch group.membership {
|
||||
case .Member:
|
||||
break
|
||||
default:
|
||||
isExcludedFromChatList = true
|
||||
}
|
||||
}
|
||||
} else if let channel = groupOrChannel as? TelegramChannel {
|
||||
switch channel.participationStatus {
|
||||
case .member:
|
||||
break
|
||||
default:
|
||||
isExcludedFromChatList = true
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isExcludedFromChatList {
|
||||
let groupId = PeerGroupId(rawValue: folderId ?? 0)
|
||||
let currentInclusion = transaction.getPeerChatListInclusion(peerId)
|
||||
var currentPinningIndex: UInt16?
|
||||
var currentMinTimestamp: Int32?
|
||||
switch currentInclusion {
|
||||
case let .ifHasMessagesOrOneOf(currentGroupId, pinningIndex, minTimestamp):
|
||||
if currentGroupId == groupId {
|
||||
currentPinningIndex = pinningIndex
|
||||
}
|
||||
currentMinTimestamp = minTimestamp
|
||||
default:
|
||||
break
|
||||
}
|
||||
transaction.updatePeerChatListInclusion(peerId, inclusion: .ifHasMessagesOrOneOf(groupId: groupId, pinningIndex: currentPinningIndex, minTimestamp: currentMinTimestamp))
|
||||
}
|
||||
|
||||
notificationSettings[peer.peerId] = TelegramPeerNotificationSettings(apiSettings: notifySettings)
|
||||
|
||||
transaction.resetIncomingReadStates([peerId: [Namespaces.Message.Cloud: .idBased(maxIncomingReadId: readInboxMaxId, maxOutgoingReadId: readOutboxMaxId, maxKnownId: topMessage, count: unreadCount, markedUnread: false)]])
|
||||
|
||||
transaction.replaceMessageTagSummary(peerId: peerId, tagMask: .unseenPersonalMessage, namespace: Namespaces.Message.Cloud, count: unreadMentionsCount, maxId: topMessage)
|
||||
|
||||
if let pts = pts {
|
||||
let channelState = ChannelState(pts: pts, invalidatedPts: pts)
|
||||
transaction.setPeerChatState(peerId, state: channelState)
|
||||
channelStates[peer.peerId] = channelState
|
||||
}
|
||||
case .dialogFolder:
|
||||
assertionFailure()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var storeMessages: [StoreMessage] = []
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message) {
|
||||
var updatedStoreMessage = storeMessage
|
||||
if case let .Id(id) = storeMessage.id {
|
||||
if let channelState = channelStates[id.peerId] {
|
||||
var updatedAttributes = storeMessage.attributes
|
||||
updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts))
|
||||
updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes)
|
||||
}
|
||||
}
|
||||
storeMessages.append(updatedStoreMessage)
|
||||
}
|
||||
}
|
||||
|
||||
for message in storeMessages {
|
||||
if case let .Id(id) = message.id {
|
||||
let _ = transaction.addMessages([message], location: topMessageIds.contains(id) ? .UpperHistoryBlock : .Random)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
|
||||
|
||||
transaction.updateCurrentPeerNotificationSettings(notificationSettings)
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user