diff --git a/Telegram/SiriIntents/IntentMessages.swift b/Telegram/SiriIntents/IntentMessages.swift index 37360288e3..3369b10bde 100644 --- a/Telegram/SiriIntents/IntentMessages.swift +++ b/Telegram/SiriIntents/IntentMessages.swift @@ -53,7 +53,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> { } if !isMuted && hasUnread { - signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: index.messageIndex.id.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: .combinedLocation) + signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: index.messageIndex.id.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: .combinedLocation) |> take(1) |> map { view -> [INMessage] in var messages: [INMessage] = [] diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 9b4817a56d..d3673bf717 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -678,7 +678,7 @@ public class GalleryController: ViewController, StandalonePresentableController, } else { inputTag = .tag(tags) } - return context.account.postbox.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(message.index), ignoreMessagesInTimestampRange: nil, count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: inputTag, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) + return context.account.postbox.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(message.index), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: inputTag, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view, peerIsCopyProtected) return .single(mapped) @@ -1409,7 +1409,7 @@ public class GalleryController: ViewController, StandalonePresentableController, } else { namespaces = .not(Namespaces.Message.allNonRegular) } - let signal = strongSelf.context.account.postbox.aroundMessageHistoryViewForLocation(strongSelf.context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(reloadAroundIndex), ignoreMessagesInTimestampRange: nil, count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: tag, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) + let signal = strongSelf.context.account.postbox.aroundMessageHistoryViewForLocation(strongSelf.context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), anchor: .index(reloadAroundIndex), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 50, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: tag, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: [.combinedLocation]) |> mapToSignal { (view, _, _) -> Signal in let mapped = GalleryMessageHistoryView.view(view, peerIsCopyProtected) return .single(mapped) diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index b4fde57f49..ae901e7d20 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -1640,6 +1640,7 @@ struct ChatListViewState { includeFrom: true, to: .absoluteLowerBound().withPeerId(associatedMessageId.peerId).withNamespace(associatedMessageId.namespace), ignoreMessagesInTimestampRange: nil, + ignoreMessageIds: Set(), limit: 2 ) for innerMessage in innerMessages { diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index 4cd515d838..507d313574 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -3227,7 +3227,7 @@ final class MessageHistoryTable: Table { return (messagesByMediaId, mediaRefs, count == 0 ? nil : lastIndex) } - func fetch(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags?, customTag: MemoryBuffer?, threadId: Int64?, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange?, limit: Int) -> [IntermediateMessage] { + func fetch(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags?, customTag: MemoryBuffer?, threadId: Int64?, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, limit: Int) -> [IntermediateMessage] { precondition(fromIndex.id.peerId == toIndex.id.peerId) precondition(fromIndex.id.namespace == toIndex.id.namespace) @@ -3246,6 +3246,9 @@ final class MessageHistoryTable: Table { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } if fromIndex < toIndex { if index < fromIndex || index > toIndex { continue @@ -3274,6 +3277,9 @@ final class MessageHistoryTable: Table { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } if fromIndex < toIndex { if index < fromIndex || index > toIndex { continue @@ -3304,6 +3310,9 @@ final class MessageHistoryTable: Table { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } if fromIndex < toIndex { if index < fromIndex || index > toIndex { continue @@ -3343,6 +3352,9 @@ final class MessageHistoryTable: Table { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } if let tag = tag { if self.tagsTable.entryExists(tag: tag, index: index) { indices.append(index) @@ -3385,6 +3397,9 @@ final class MessageHistoryTable: Table { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } if fromIndex < toIndex { if index < fromIndex || index > toIndex { continue @@ -3400,7 +3415,7 @@ final class MessageHistoryTable: Table { assertionFailure() } } - } else if ignoreMessagesInTimestampRange != nil { + } else if ignoreMessagesInTimestampRange != nil || !ignoreMessageIds.isEmpty { var indices: [MessageIndex] = [] var startIndex = fromIndex var localIncludeFrom = includeFrom @@ -3435,6 +3450,9 @@ final class MessageHistoryTable: Table { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } indices.append(index) if indices.count >= limit { break diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index 9d09f85842..2bd829b5c0 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -305,6 +305,7 @@ public enum HistoryViewInputAnchor: Equatable { final class MutableMessageHistoryView: MutablePostboxView { private(set) var peerIds: MessageHistoryViewInput private let ignoreMessagesInTimestampRange: ClosedRange? + private let ignoreMessageIds: Set let tag: HistoryViewInputTag? private let appendMessagesFromTheSameGroup: Bool let namespaces: MessageIdNamespaces @@ -337,6 +338,7 @@ final class MutableMessageHistoryView: MutablePostboxView { trackHoles: Bool, peerIds: MessageHistoryViewInput, ignoreMessagesInTimestampRange: ClosedRange?, + ignoreMessageIds: Set, anchor inputAnchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState?, transientReadStates: MessageHistoryViewReadState?, @@ -354,6 +356,7 @@ final class MutableMessageHistoryView: MutablePostboxView { self.trackHoles = trackHoles self.peerIds = peerIds self.ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange + self.ignoreMessageIds = ignoreMessageIds self.combinedReadStates = combinedReadStates self.transientReadStates = transientReadStates self.tag = tag @@ -382,12 +385,12 @@ final class MutableMessageHistoryView: MutablePostboxView { } } - self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: count + 1, locations: peerIds) + self.state = HistoryViewState(postbox: postbox, inputAnchor: inputAnchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, halfLimit: count + 1, locations: peerIds) if case let .loading(loadingState) = self.state { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, halfLimit: count + 1, locations: peerIds, postbox: postbox, holes: holes)) self.sampledState = self.state.sample(postbox: postbox, clipHoles: self.clipHoles) case .loadHole: break @@ -401,12 +404,12 @@ final class MutableMessageHistoryView: MutablePostboxView { } private func reset(postbox: PostboxImpl) { - self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds) + self.state = HistoryViewState(postbox: postbox, inputAnchor: self.anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, halfLimit: self.fillCount + 1, locations: self.peerIds) if case let .loading(loadingState) = self.state { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) case .loadHole: break } @@ -415,7 +418,7 @@ final class MutableMessageHistoryView: MutablePostboxView { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) case .loadHole: break } @@ -771,7 +774,7 @@ final class MutableMessageHistoryView: MutablePostboxView { let sampledState = loadingState.checkAndSample(postbox: postbox) switch sampledState { case let .ready(anchor, holes): - self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) + self.state = .loaded(HistoryViewLoadedState(anchor: anchor, tag: self.tag, appendMessagesFromTheSameGroup: self.appendMessagesFromTheSameGroup, namespaces: self.namespaces, statistics: self.orderStatistics, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, halfLimit: self.fillCount + 1, locations: self.peerIds, postbox: postbox, holes: holes)) case .loadHole: break } diff --git a/submodules/Postbox/Sources/MessageHistoryViewState.swift b/submodules/Postbox/Sources/MessageHistoryViewState.swift index 04cb937f96..c2812171f9 100644 --- a/submodules/Postbox/Sources/MessageHistoryViewState.swift +++ b/submodules/Postbox/Sources/MessageHistoryViewState.swift @@ -40,7 +40,7 @@ public enum MessageHistoryInput: Equatable, Hashable { } private extension MessageHistoryInput { - func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange?, limit: Int) -> [IntermediateMessage] { + func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, limit: Int) -> [IntermediateMessage] { switch self { case let .automatic(threadId, tagInfo): var tag: MessageTags? @@ -65,7 +65,7 @@ private extension MessageHistoryInput { } } - var items = postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, customTag: customTag, threadId: threadId, from: fromIndex, includeFrom: includeFrom || shouldAddFromSameGroup, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) + var items = postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, customTag: customTag, threadId: threadId, from: fromIndex, includeFrom: includeFrom || shouldAddFromSameGroup, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, limit: limit) if shouldAddFromSameGroup { enum Direction { @@ -152,7 +152,7 @@ private extension MessageHistoryInput { case let .external(input, tag): switch input.content { case let .thread(peerId, id, _): - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, customTag: nil, threadId: id, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, limit: limit) + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: tag, customTag: nil, threadId: id, from: fromIndex, includeFrom: includeFrom, to: toIndex, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, limit: limit) case let .messages(allIndices, _, _): if allIndices.isEmpty { return [] @@ -213,6 +213,9 @@ private extension MessageHistoryInput { continue } } + if !ignoreMessageIds.isEmpty && ignoreMessageIds.contains(index.id) { + continue + } indices.append(index) } if indices.count >= limit { @@ -1301,18 +1304,20 @@ final class HistoryViewLoadedState { let input: MessageHistoryInput let statistics: MessageHistoryViewOrderStatistics let ignoreMessagesInTimestampRange: ClosedRange? + let ignoreMessageIds: Set let halfLimit: Int let seedConfiguration: SeedConfiguration var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries] var holes: HistoryViewHoles var spacesWithRemovals = Set() - init(anchor: HistoryViewAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange?, halfLimit: Int, locations: MessageHistoryViewInput, postbox: PostboxImpl, holes: HistoryViewHoles) { + init(anchor: HistoryViewAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, halfLimit: Int, locations: MessageHistoryViewInput, postbox: PostboxImpl, holes: HistoryViewHoles) { precondition(halfLimit >= 3) self.anchor = anchor self.namespaces = namespaces self.statistics = statistics self.ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange + self.ignoreMessageIds = ignoreMessageIds self.halfLimit = halfLimit self.seedConfiguration = postbox.seedConfiguration self.orderedEntriesBySpace = [:] @@ -1419,7 +1424,7 @@ final class HistoryViewLoadedState { } else { nextLowerIndex = (anchorIndex, true) } - lowerOrAtAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, limit: self.halfLimit - lowerOrAtAnchorMessages.count).map(mapEntry)) + lowerOrAtAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextLowerIndex.index, includeFrom: nextLowerIndex.includeFrom, to: lowerBound, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, limit: self.halfLimit - lowerOrAtAnchorMessages.count).map(mapEntry)) } if higherThanAnchorMessages.count < self.halfLimit { let nextHigherIndex: MessageIndex @@ -1428,7 +1433,7 @@ final class HistoryViewLoadedState { } else { nextHigherIndex = anchorIndex } - higherThanAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextHigherIndex, includeFrom: false, to: upperBound, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, limit: self.halfLimit - higherThanAnchorMessages.count).map(mapEntry)) + higherThanAnchorMessages.append(contentsOf: self.input.fetch(postbox: postbox, peerId: space.peerId, namespace: space.namespace, from: nextHigherIndex, includeFrom: false, to: upperBound, ignoreMessagesInTimestampRange: self.ignoreMessagesInTimestampRange, ignoreMessageIds: self.ignoreMessageIds, limit: self.halfLimit - higherThanAnchorMessages.count).map(mapEntry)) } lowerOrAtAnchorMessages.reverse() @@ -1703,6 +1708,9 @@ final class HistoryViewLoadedState { return false } } + if !self.ignoreMessageIds.isEmpty && self.ignoreMessageIds.contains(entry.index.id) { + return false + } let space = PeerIdAndNamespace(peerId: entry.index.id.peerId, namespace: entry.index.id.namespace) @@ -2120,90 +2128,90 @@ enum HistoryViewState { case loaded(HistoryViewLoadedState) case loading(HistoryViewLoadingState) - init(postbox: PostboxImpl, inputAnchor: HistoryViewInputAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange?, halfLimit: Int, locations: MessageHistoryViewInput) { + init(postbox: PostboxImpl, inputAnchor: HistoryViewInputAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, halfLimit: Int, locations: MessageHistoryViewInput) { switch inputAnchor { - case let .index(index): - self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) - case .lowerBound: - self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) - case .upperBound: - self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) - case .unread: - let anchorPeerId: PeerId - switch locations { - case let .single(peerId, threadId): - anchorPeerId = peerId - if threadId != nil { - self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) - return - } - case let .associated(peerId, _): - anchorPeerId = peerId - case .external: - self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) - return + case let .index(index): + self = .loaded(HistoryViewLoadedState(anchor: .index(index), tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + case .lowerBound: + self = .loaded(HistoryViewLoadedState(anchor: .lowerBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + case .upperBound: + self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + case .unread: + let anchorPeerId: PeerId + switch locations { + case let .single(peerId, threadId): + anchorPeerId = peerId + if threadId != nil { + self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + return } - if postbox.chatListIndexTable.get(peerId: anchorPeerId).includedIndex(peerId: anchorPeerId) != nil, let combinedState = postbox.readStateTable.getCombinedState(anchorPeerId) { - var messageId: MessageId? - var anchor: HistoryViewAnchor? - loop: for (namespace, state) in combinedState.states { - switch state { - case let .idBased(maxIncomingReadId, _, _, count, _): - if count == 0 { - anchor = .upperBound - break loop - } else { - messageId = MessageId(peerId: anchorPeerId, namespace: namespace, id: maxIncomingReadId) - break loop - } - case let .indexBased(maxIncomingReadIndex, _, count, _): - if count == 0 { - anchor = .upperBound - break loop - } else { - anchor = .index(maxIncomingReadIndex) - break loop - } - } - } - if let messageId = messageId { - let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, threadId: nil, namespaces: namespaces, messageId: messageId, halfLimit: halfLimit) - let sampledState = loadingState.checkAndSample(postbox: postbox) - switch sampledState { - case let .ready(anchor, holes): - self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) - case .loadHole: - self = .loading(loadingState) - } + case let .associated(peerId, _): + anchorPeerId = peerId + case .external: + self = .loaded(HistoryViewLoadedState(anchor: .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + return + } + if postbox.chatListIndexTable.get(peerId: anchorPeerId).includedIndex(peerId: anchorPeerId) != nil, let combinedState = postbox.readStateTable.getCombinedState(anchorPeerId) { + var messageId: MessageId? + var anchor: HistoryViewAnchor? + loop: for (namespace, state) in combinedState.states { + switch state { + case let .idBased(maxIncomingReadId, _, _, count, _): + if count == 0 { + anchor = .upperBound + break loop } else { - self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) + messageId = MessageId(peerId: anchorPeerId, namespace: namespace, id: maxIncomingReadId) + break loop } - } else { - preconditionFailure() - } - case let .message(messageId): - var threadId: Int64? - switch locations { - case let .single(_, threadIdValue): - threadId = threadIdValue - case let .external(input): - switch input.content { - case let .thread(_, id, _): - threadId = id - case .messages: - break + case let .indexBased(maxIncomingReadIndex, _, count, _): + if count == 0 { + anchor = .upperBound + break loop + } else { + anchor = .index(maxIncomingReadIndex) + break loop } - default: - break } - let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, threadId: threadId, namespaces: namespaces, messageId: messageId, halfLimit: halfLimit) - let sampledState = loadingState.checkAndSample(postbox: postbox) - switch sampledState { + } + if let messageId = messageId { + let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, threadId: nil, namespaces: namespaces, messageId: messageId, halfLimit: halfLimit) + let sampledState = loadingState.checkAndSample(postbox: postbox) + switch sampledState { case let .ready(anchor, holes): - self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) + self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) case .loadHole: self = .loading(loadingState) + } + } else { + self = .loaded(HistoryViewLoadedState(anchor: anchor ?? .upperBound, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: HistoryViewHoles(holesBySpace: fetchHoles(postbox: postbox, locations: locations, tag: tag, namespaces: namespaces)))) } + } else { + preconditionFailure() + } + case let .message(messageId): + var threadId: Int64? + switch locations { + case let .single(_, threadIdValue): + threadId = threadIdValue + case let .external(input): + switch input.content { + case let .thread(_, id, _): + threadId = id + case .messages: + break + } + default: + break + } + let loadingState = HistoryViewLoadingState(postbox: postbox, locations: locations, tag: tag, threadId: threadId, namespaces: namespaces, messageId: messageId, halfLimit: halfLimit) + let sampledState = loadingState.checkAndSample(postbox: postbox) + switch sampledState { + case let .ready(anchor, holes): + self = .loaded(HistoryViewLoadedState(anchor: anchor, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, statistics: statistics, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, halfLimit: halfLimit, locations: locations, postbox: postbox, holes: holes)) + case .loadHole: + self = .loading(loadingState) + } } } diff --git a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift index 66c7377eec..182b8fa7ff 100644 --- a/submodules/Postbox/Sources/MessageOfInterestHolesView.swift +++ b/submodules/Postbox/Sources/MessageOfInterestHolesView.swift @@ -60,7 +60,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { } } self.anchor = anchor - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: []) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: []) let _ = self.updateFromView() } @@ -134,7 +134,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { case let .peer(id, threadId): peerIds = postbox.peerIdsForLocation(.peer(peerId: id, threadId: threadId), ignoreRelatedChats: false) } - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: false, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: []) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: false, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: []) return self.updateFromView() } else if self.wrappedView.replay(postbox: postbox, transaction: transaction) { var reloadView = false @@ -167,7 +167,7 @@ final class MutableMessageOfInterestHolesView: MutablePostboxView { case let .peer(id, threadId): peerIds = postbox.peerIdsForLocation(.peer(peerId: id, threadId: threadId), ignoreRelatedChats: false) } - self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: false, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: []) + self.wrappedView = MutableMessageHistoryView(postbox: postbox, orderStatistics: [], clipHoles: true, trackHoles: false, peerIds: peerIds, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), anchor: self.anchor, combinedReadStates: nil, transientReadStates: nil, tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .all, count: self.count, topTaggedMessages: [:], additionalDatas: []) } return self.updateFromView() diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 4df8d50bcf..9391945d66 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -1077,7 +1077,7 @@ public final class Transaction { guard let postbox = self.postbox else { return [] } - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: nil, threadId: threadId, from: from, includeFrom: includeFrom, to: to, ignoreMessagesInTimestampRange: nil, limit: limit).map(postbox.renderIntermediateMessage(_:)) + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: nil, threadId: threadId, from: from, includeFrom: includeFrom, to: to, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), limit: limit).map(postbox.renderIntermediateMessage(_:)) } public func getMessagesWithCustomTag(peerId: PeerId, namespace: MessageId.Namespace, threadId: Int64?, customTag: MemoryBuffer, from: MessageIndex, includeFrom: Bool, to: MessageIndex, limit: Int) -> [Message] { @@ -1085,7 +1085,7 @@ public final class Transaction { guard let postbox = self.postbox else { return [] } - return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: customTag, threadId: threadId, from: from, includeFrom: includeFrom, to: to, ignoreMessagesInTimestampRange: nil, limit: limit).map(postbox.renderIntermediateMessage(_:)) + return postbox.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: customTag, threadId: threadId, from: from, includeFrom: includeFrom, to: to, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), limit: limit).map(postbox.renderIntermediateMessage(_:)) } public func scanMessages(peerId: PeerId, namespace: MessageId.Namespace, tag: MessageTags, _ f: (Message) -> Bool) { @@ -1107,7 +1107,7 @@ public final class Transaction { self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f) } - public func getMessagesHistoryViewState(input: MessageHistoryViewInput, ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView { + public func getMessagesHistoryViewState(input: MessageHistoryViewInput, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView { precondition(!self.disposed) guard let postbox = self.postbox else { preconditionFailure() @@ -1121,7 +1121,7 @@ public final class Transaction { view = next.0 }, error: { _ in }, completed: {}) - let disposable = postbox.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: input, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: MessageHistoryViewOrderStatistics(), additionalData: [], useRootInterfaceStateForThread: false) + let disposable = postbox.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: input, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: MessageHistoryViewOrderStatistics(), additionalData: [], useRootInterfaceStateForThread: false) disposable.dispose() return view! @@ -2269,7 +2269,7 @@ final class PostboxImpl { if let states = initialCombinedStates?.states { for (namespace, state) in states { if namespace != messageIndex.id.namespace && state.count != 0 { - if let item = self.messageHistoryTable.fetch(peerId: messageIndex.id.peerId, namespace: namespace, tag: nil, customTag: nil, threadId: nil, from: MessageIndex(id: MessageId(peerId: messageIndex.id.peerId, namespace: namespace, id: 1), timestamp: messageIndex.timestamp), includeFrom: true, to: MessageIndex.lowerBound(peerId: messageIndex.id.peerId, namespace: namespace), ignoreMessagesInTimestampRange: nil, limit: 1).first { + if let item = self.messageHistoryTable.fetch(peerId: messageIndex.id.peerId, namespace: namespace, tag: nil, customTag: nil, threadId: nil, from: MessageIndex(id: MessageId(peerId: messageIndex.id.peerId, namespace: namespace, id: 1), timestamp: messageIndex.timestamp), includeFrom: true, to: MessageIndex.lowerBound(peerId: messageIndex.id.peerId, namespace: namespace), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), limit: 1).first { resultIds.append(contentsOf: self.messageHistoryTable.applyInteractiveMaxReadIndex(postbox: self, messageIndex: item.index, operationsByPeerId: &self.currentOperationsByPeerId, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations)) } } @@ -3123,7 +3123,7 @@ final class PostboxImpl { return peerIds } - public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, customUnreadMessageId: MessageId?, additionalData: [AdditionalMessageHistoryViewData], useRootInterfaceStateForThread: Bool) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageOfInterestHistoryViewForChatLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, customUnreadMessageId: MessageId?, additionalData: [AdditionalMessageHistoryViewData], useRootInterfaceStateForThread: Bool) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) |> mapToSignal { chatLocationData in let (chatLocation, isHoleFill) = chatLocationData @@ -3181,7 +3181,7 @@ final class PostboxImpl { anchor = .upperBound } } - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) }) return signal @@ -3195,13 +3195,13 @@ final class PostboxImpl { } } - public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) |> mapToSignal { chatLocationData in let (chatLocation, isHoleFill) = chatLocationData let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: ignoreRelatedChats) - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, anchor: .message(messageId), fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) } return signal @@ -3215,14 +3215,14 @@ final class PostboxImpl { } } - public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, ignoreMessageIds: Set, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { return self.resolvedChatLocationInput(chatLocation: chatLocation) |> mapToSignal { chatLocationData -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> in let (chatLocation, isHoleFill) = chatLocationData let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = self.transactionSignal { subscriber, transaction in let peerIds = self.peerIdsForLocation(chatLocation, ignoreRelatedChats: ignoreRelatedChats) - return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + return self.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) } return signal @@ -3241,6 +3241,7 @@ final class PostboxImpl { subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, ignoreMessagesInTimestampRange: ClosedRange?, + ignoreMessageIds: Set, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, @@ -3350,7 +3351,7 @@ final class PostboxImpl { readStates = transientReadStates } - let mutableView = MutableMessageHistoryView(postbox: self, orderStatistics: orderStatistics, clipHoles: clipHoles, trackHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, anchor: anchor, combinedReadStates: readStates, transientReadStates: transientReadStates, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, count: count, topTaggedMessages: topTaggedMessages, additionalDatas: additionalDataEntries) + let mutableView = MutableMessageHistoryView(postbox: self, orderStatistics: orderStatistics, clipHoles: clipHoles, trackHoles: true, peerIds: peerIds, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, anchor: anchor, combinedReadStates: readStates, transientReadStates: transientReadStates, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: namespaces, count: count, topTaggedMessages: topTaggedMessages, additionalDatas: additionalDataEntries) let initialUpdateType: ViewUpdateType = .Initial @@ -4091,7 +4092,7 @@ final class PostboxImpl { var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace) var remainingLimit = limit while remainingLimit > 0 { - let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: nil, threadId: nil, from: index, includeFrom: false, to: lowerBound, ignoreMessagesInTimestampRange: nil, limit: 10) + let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: nil, threadId: nil, from: index, includeFrom: false, to: lowerBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), limit: 10) remainingLimit -= 10 for message in messages { if !f(self.renderIntermediateMessage(message)) { @@ -4110,7 +4111,7 @@ final class PostboxImpl { var remainingLimit = limit var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace) while remainingLimit > 0 { - let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: nil, threadId: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), ignoreMessagesInTimestampRange: nil, limit: 32) + let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, customTag: nil, threadId: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), limit: 32) for message in messages { let attributes = MessageHistoryTable.renderMessageAttributes(message) if !f(message.id, attributes) { @@ -4422,6 +4423,7 @@ public class Postbox { public func aroundMessageOfInterestHistoryViewForChatLocation( _ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, + ignoreMessageIds: Set, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set, @@ -4440,6 +4442,7 @@ public class Postbox { disposable.set(impl.aroundMessageOfInterestHistoryViewForChatLocation( chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, + ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, topTaggedMessageIdNamespaces: topTaggedMessageIdNamespaces, @@ -4460,6 +4463,7 @@ public class Postbox { public func aroundIdMessageHistoryViewForLocation( _ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, + ignoreMessageIds: Set, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, @@ -4479,6 +4483,7 @@ public class Postbox { disposable.set(impl.aroundIdMessageHistoryViewForLocation( chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, + ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, @@ -4501,6 +4506,7 @@ public class Postbox { _ chatLocation: ChatLocationInput, anchor: HistoryViewInputAnchor, ignoreMessagesInTimestampRange: ClosedRange?, + ignoreMessageIds: Set, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, @@ -4520,6 +4526,7 @@ public class Postbox { disposable.set(impl.aroundMessageHistoryViewForLocation( chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, + ignoreMessageIds: ignoreMessageIds, anchor: anchor, count: count, clipHoles: clipHoles, diff --git a/submodules/Postbox/Sources/Views.swift b/submodules/Postbox/Sources/Views.swift index 48dd9e5633..3570dbbf69 100644 --- a/submodules/Postbox/Sources/Views.swift +++ b/submodules/Postbox/Sources/Views.swift @@ -8,6 +8,7 @@ public enum PostboxViewKey: Hashable { public var trackHoles: Bool public var orderStatistics: MessageHistoryViewOrderStatistics public var ignoreMessagesInTimestampRange: ClosedRange? + public var ignoreMessageIds: Set public var anchor: HistoryViewInputAnchor public var combinedReadStates: MessageHistoryViewReadState? public var transientReadStates: MessageHistoryViewReadState? @@ -23,6 +24,7 @@ public enum PostboxViewKey: Hashable { trackHoles: Bool, orderStatistics: MessageHistoryViewOrderStatistics = [], ignoreMessagesInTimestampRange: ClosedRange? = nil, + ignoreMessageIds: Set = Set(), anchor: HistoryViewInputAnchor, combinedReadStates: MessageHistoryViewReadState? = nil, transientReadStates: MessageHistoryViewReadState? = nil, @@ -37,6 +39,7 @@ public enum PostboxViewKey: Hashable { self.trackHoles = trackHoles self.orderStatistics = orderStatistics self.ignoreMessagesInTimestampRange = ignoreMessagesInTimestampRange + self.ignoreMessageIds = ignoreMessageIds self.anchor = anchor self.combinedReadStates = combinedReadStates self.transientReadStates = transientReadStates @@ -647,6 +650,7 @@ func postboxViewForKey(postbox: PostboxImpl, key: PostboxViewKey) -> MutablePost trackHoles: historyView.trackHoles, peerIds: .single(peerId: historyView.peerId, threadId: historyView.threadId), ignoreMessagesInTimestampRange: historyView.ignoreMessagesInTimestampRange, + ignoreMessageIds: historyView.ignoreMessageIds, anchor: historyView.anchor, combinedReadStates: historyView.combinedReadStates, transientReadStates: historyView.transientReadStates, diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 222303780a..bcd1c8f501 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1135,7 +1135,7 @@ private func statsEntries( data: ChannelStats, peer: EnginePeer?, messages: [Message]?, - stories: PeerStoryListContext.State?, + stories: StoryListContext.State?, interactions: [ChannelStatsPostInteractions.PostId: ChannelStatsPostInteractions]? ) -> [StatsEntry] { var entries: [StatsEntry] = [] @@ -1217,8 +1217,8 @@ private func statsEntries( } if let stories { for story in stories.items { - if let _ = interactions[.story(peerId: peer.id, id: story.id)] { - posts.append(.story(peer, story)) + if let _ = interactions[.story(peerId: peer.id, id: story.storyItem.id)] { + posts.append(.story(peer, story.storyItem)) } } } @@ -1463,8 +1463,8 @@ private func channelStatsControllerEntries( peer: EnginePeer?, data: ChannelStats?, messages: [Message]?, - stories: PeerStoryListContext.State?, - interactions: [ChannelStatsPostInteractions.PostId: ChannelStatsPostInteractions]?, + stories: StoryListContext.State?, + interactions: [ChannelStatsPostInteractions.PostId: ChannelStatsPostInteractions]?, boostData: ChannelBoostStatus?, boostersState: ChannelBoostersContext.State?, giftsState: ChannelBoostersContext.State?, @@ -1543,7 +1543,7 @@ public func channelStatsController(context: AccountContext, updatedPresentationD let withdrawalDisposable = MetaDisposable() actionsDisposable.add(withdrawalDisposable) - let storiesPromise = Promise() + let storiesPromise = Promise() let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId) let dataSignal: Signal = statsContext.state diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index c81a2d32b9..cee7c18c4f 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -286,6 +286,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } dict[1903173033] = { return Api.ForumTopic.parse_forumTopic($0) } dict[37687451] = { return Api.ForumTopic.parse_forumTopicDeleted($0) } + dict[-394605632] = { return Api.FoundStory.parse_foundStory($0) } dict[-1107729093] = { return Api.Game.parse_game($0) } dict[-1297942941] = { return Api.GeoPoint.parse_geoPoint($0) } dict[286776671] = { return Api.GeoPoint.parse_geoPointEmpty($0) } @@ -1344,6 +1345,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) } dict[1862033025] = { return Api.stories.AllStories.parse_allStories($0) } dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) } + dict[-488736969] = { return Api.stories.FoundStories.parse_foundStories($0) } dict[-890861720] = { return Api.stories.PeerStories.parse_peerStories($0) } dict[1673780490] = { return Api.stories.Stories.parse_stories($0) } dict[-1436583780] = { return Api.stories.StoryReactionsList.parse_storyReactionsList($0) } @@ -1380,7 +1382,7 @@ public extension Api { return parser(reader) } else { - telegramApiLog("Type constructor \(String(signature, radix: 16, uppercase: false)) not found") + telegramApiLog("Type constructor \(String(UInt32(bitPattern: signature), radix: 16, uppercase: false)) not found") return nil } } @@ -1618,6 +1620,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.ForumTopic: _1.serialize(buffer, boxed) + case let _1 as Api.FoundStory: + _1.serialize(buffer, boxed) case let _1 as Api.Game: _1.serialize(buffer, boxed) case let _1 as Api.GeoPoint: @@ -2380,6 +2384,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.stories.AllStories: _1.serialize(buffer, boxed) + case let _1 as Api.stories.FoundStories: + _1.serialize(buffer, boxed) case let _1 as Api.stories.PeerStories: _1.serialize(buffer, boxed) case let _1 as Api.stories.Stories: diff --git a/submodules/TelegramApi/Sources/Api34.swift b/submodules/TelegramApi/Sources/Api34.swift index 8952189c1c..b95acb0d27 100644 --- a/submodules/TelegramApi/Sources/Api34.swift +++ b/submodules/TelegramApi/Sources/Api34.swift @@ -1212,6 +1212,80 @@ public extension Api.stories { } } +public extension Api.stories { + enum FoundStories: TypeConstructorDescription { + case foundStories(flags: Int32, count: Int32, stories: [Api.FoundStory], nextOffset: String?, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .foundStories(let flags, let count, let stories, let nextOffset, let chats, let users): + if boxed { + buffer.appendInt32(-488736969) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .foundStories(let flags, let count, let stories, let nextOffset, let chats, let users): + return ("foundStories", [("flags", flags as Any), ("count", count as Any), ("stories", stories as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_foundStories(_ reader: BufferReader) -> FoundStories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.FoundStory]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.FoundStory.self) + } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.stories.FoundStories.foundStories(flags: _1!, count: _2!, stories: _3!, nextOffset: _4, chats: _5!, users: _6!) + } + else { + return nil + } + } + + } +} public extension Api.stories { enum PeerStories: TypeConstructorDescription { case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) @@ -1562,175 +1636,3 @@ public extension Api.stories { } } -public extension Api.updates { - indirect enum ChannelDifference: TypeConstructorDescription { - case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) - case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?) - case channelDifferenceTooLong(flags: Int32, timeout: Int32?, dialog: Api.Dialog, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): - if boxed { - buffer.appendInt32(543450958) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(newMessages.count)) - for item in newMessages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(otherUpdates.count)) - for item in otherUpdates { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .channelDifferenceEmpty(let flags, let pts, let timeout): - if boxed { - buffer.appendInt32(1041346555) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(pts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} - break - case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): - if boxed { - buffer.appendInt32(-1531132162) - } - serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} - dialog.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): - return ("channelDifference", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any), ("newMessages", newMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any)]) - case .channelDifferenceEmpty(let flags, let pts, let timeout): - return ("channelDifferenceEmpty", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any)]) - case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): - return ("channelDifferenceTooLong", [("flags", flags as Any), ("timeout", timeout as Any), ("dialog", dialog as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_channelDifference(_ reader: BufferReader) -> ChannelDifference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } - var _4: [Api.Message]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _5: [Api.Update]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) - } - var _6: [Api.Chat]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _7: [Api.User]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!) - } - else { - return nil - } - } - public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3) - } - else { - return nil - } - } - public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } - var _3: Api.Dialog? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.Dialog - } - var _4: [Api.Message]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api35.swift b/submodules/TelegramApi/Sources/Api35.swift index f6d0dc14fb..0cfe25da84 100644 --- a/submodules/TelegramApi/Sources/Api35.swift +++ b/submodules/TelegramApi/Sources/Api35.swift @@ -1,3 +1,175 @@ +public extension Api.updates { + indirect enum ChannelDifference: TypeConstructorDescription { + case channelDifference(flags: Int32, pts: Int32, timeout: Int32?, newMessages: [Api.Message], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User]) + case channelDifferenceEmpty(flags: Int32, pts: Int32, timeout: Int32?) + case channelDifferenceTooLong(flags: Int32, timeout: Int32?, dialog: Api.Dialog, messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): + if boxed { + buffer.appendInt32(543450958) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(newMessages.count)) + for item in newMessages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(otherUpdates.count)) + for item in otherUpdates { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .channelDifferenceEmpty(let flags, let pts, let timeout): + if boxed { + buffer.appendInt32(1041346555) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(pts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + break + case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): + if boxed { + buffer.appendInt32(-1531132162) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(timeout!, buffer: buffer, boxed: false)} + dialog.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .channelDifference(let flags, let pts, let timeout, let newMessages, let otherUpdates, let chats, let users): + return ("channelDifference", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any), ("newMessages", newMessages as Any), ("otherUpdates", otherUpdates as Any), ("chats", chats as Any), ("users", users as Any)]) + case .channelDifferenceEmpty(let flags, let pts, let timeout): + return ("channelDifferenceEmpty", [("flags", flags as Any), ("pts", pts as Any), ("timeout", timeout as Any)]) + case .channelDifferenceTooLong(let flags, let timeout, let dialog, let messages, let chats, let users): + return ("channelDifferenceTooLong", [("flags", flags as Any), ("timeout", timeout as Any), ("dialog", dialog as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_channelDifference(_ reader: BufferReader) -> ChannelDifference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + var _4: [Api.Message]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _5: [Api.Update]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Update.self) + } + var _6: [Api.Chat]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _7: [Api.User]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!) + } + else { + return nil + } + } + public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + if _c1 && _c2 && _c3 { + return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3) + } + else { + return nil + } + } + public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } + var _3: Api.Dialog? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.Dialog + } + var _4: [Api.Message]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!) + } + else { + return nil + } + } + + } +} public extension Api.updates { enum Difference: TypeConstructorDescription { case difference(newMessages: [Api.Message], newEncryptedMessages: [Api.EncryptedMessage], otherUpdates: [Api.Update], chats: [Api.Chat], users: [Api.User], state: Api.updates.State) diff --git a/submodules/TelegramApi/Sources/Api36.swift b/submodules/TelegramApi/Sources/Api36.swift index 283bdcd1dc..f5fddc6624 100644 --- a/submodules/TelegramApi/Sources/Api36.swift +++ b/submodules/TelegramApi/Sources/Api36.swift @@ -8795,13 +8795,12 @@ public extension Api.functions.payments { } } public extension Api.functions.payments { - static func refundStarsCharge(userId: Api.InputUser, msgId: Int32, chargeId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func refundStarsCharge(userId: Api.InputUser, chargeId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-258950164) + buffer.appendInt32(632196938) userId.serialize(buffer, true) - serializeInt32(msgId, buffer: buffer, boxed: false) serializeString(chargeId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.refundStarsCharge", parameters: [("userId", String(describing: userId)), ("msgId", String(describing: msgId)), ("chargeId", String(describing: chargeId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + return (FunctionDescription(name: "payments.refundStarsCharge", parameters: [("userId", String(describing: userId)), ("chargeId", String(describing: chargeId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { @@ -10393,6 +10392,23 @@ public extension Api.functions.stories { }) } } +public extension Api.functions.stories { + static func searchPosts(hashtag: String, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-688008311) + serializeString(hashtag, buffer: buffer, boxed: false) + serializeString(offset, buffer: buffer, boxed: false) + serializeInt32(limit, buffer: buffer, boxed: false) + return (FunctionDescription(name: "stories.searchPosts", parameters: [("hashtag", String(describing: hashtag)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stories.FoundStories? in + let reader = BufferReader(buffer) + var result: Api.stories.FoundStories? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.stories.FoundStories + } + return result + }) + } +} public extension Api.functions.stories { static func sendReaction(flags: Int32, peer: Api.InputPeer, storyId: Int32, reaction: Api.Reaction) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api7.swift b/submodules/TelegramApi/Sources/Api7.swift index af25d1b035..dacc9ebe84 100644 --- a/submodules/TelegramApi/Sources/Api7.swift +++ b/submodules/TelegramApi/Sources/Api7.swift @@ -472,6 +472,50 @@ public extension Api { } } +public extension Api { + indirect enum FoundStory: TypeConstructorDescription { + case foundStory(peer: Api.Peer, story: Api.StoryItem) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .foundStory(let peer, let story): + if boxed { + buffer.appendInt32(-394605632) + } + peer.serialize(buffer, true) + story.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .foundStory(let peer, let story): + return ("foundStory", [("peer", peer as Any), ("story", story as Any)]) + } + } + + public static func parse_foundStory(_ reader: BufferReader) -> FoundStory? { + var _1: Api.Peer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Peer + } + var _2: Api.StoryItem? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StoryItem + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.FoundStory.foundStory(peer: _1!, story: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum Game: TypeConstructorDescription { case game(flags: Int32, id: Int64, accessHash: Int64, shortName: String, title: String, description: String, photo: Api.Photo, document: Api.Document?) @@ -1268,69 +1312,3 @@ public extension Api { } } -public extension Api { - indirect enum InputBotApp: TypeConstructorDescription { - case inputBotAppID(id: Int64, accessHash: Int64) - case inputBotAppShortName(botId: Api.InputUser, shortName: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputBotAppID(let id, let accessHash): - if boxed { - buffer.appendInt32(-1457472134) - } - serializeInt64(id, buffer: buffer, boxed: false) - serializeInt64(accessHash, buffer: buffer, boxed: false) - break - case .inputBotAppShortName(let botId, let shortName): - if boxed { - buffer.appendInt32(-1869872121) - } - botId.serialize(buffer, true) - serializeString(shortName, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputBotAppID(let id, let accessHash): - return ("inputBotAppID", [("id", id as Any), ("accessHash", accessHash as Any)]) - case .inputBotAppShortName(let botId, let shortName): - return ("inputBotAppShortName", [("botId", botId as Any), ("shortName", shortName as Any)]) - } - } - - public static func parse_inputBotAppID(_ reader: BufferReader) -> InputBotApp? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputBotApp.inputBotAppID(id: _1!, accessHash: _2!) - } - else { - return nil - } - } - public static func parse_inputBotAppShortName(_ reader: BufferReader) -> InputBotApp? { - var _1: Api.InputUser? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.InputUser - } - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputBotApp.inputBotAppShortName(botId: _1!, shortName: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api8.swift b/submodules/TelegramApi/Sources/Api8.swift index 429d2a4e3a..e936ab4fbc 100644 --- a/submodules/TelegramApi/Sources/Api8.swift +++ b/submodules/TelegramApi/Sources/Api8.swift @@ -1,3 +1,69 @@ +public extension Api { + indirect enum InputBotApp: TypeConstructorDescription { + case inputBotAppID(id: Int64, accessHash: Int64) + case inputBotAppShortName(botId: Api.InputUser, shortName: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputBotAppID(let id, let accessHash): + if boxed { + buffer.appendInt32(-1457472134) + } + serializeInt64(id, buffer: buffer, boxed: false) + serializeInt64(accessHash, buffer: buffer, boxed: false) + break + case .inputBotAppShortName(let botId, let shortName): + if boxed { + buffer.appendInt32(-1869872121) + } + botId.serialize(buffer, true) + serializeString(shortName, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputBotAppID(let id, let accessHash): + return ("inputBotAppID", [("id", id as Any), ("accessHash", accessHash as Any)]) + case .inputBotAppShortName(let botId, let shortName): + return ("inputBotAppShortName", [("botId", botId as Any), ("shortName", shortName as Any)]) + } + } + + public static func parse_inputBotAppID(_ reader: BufferReader) -> InputBotApp? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int64? + _2 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InputBotApp.inputBotAppID(id: _1!, accessHash: _2!) + } + else { + return nil + } + } + public static func parse_inputBotAppShortName(_ reader: BufferReader) -> InputBotApp? { + var _1: Api.InputUser? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.InputUser + } + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.InputBotApp.inputBotAppShortName(botId: _1!, shortName: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum InputBotInlineMessage: TypeConstructorDescription { case inputBotInlineMessageGame(flags: Int32, replyMarkup: Api.ReplyMarkup?) @@ -1236,59 +1302,3 @@ public extension Api { } } -public extension Api { - enum InputCollectible: TypeConstructorDescription { - case inputCollectiblePhone(phone: String) - case inputCollectibleUsername(username: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputCollectiblePhone(let phone): - if boxed { - buffer.appendInt32(-1562241884) - } - serializeString(phone, buffer: buffer, boxed: false) - break - case .inputCollectibleUsername(let username): - if boxed { - buffer.appendInt32(-476815191) - } - serializeString(username, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputCollectiblePhone(let phone): - return ("inputCollectiblePhone", [("phone", phone as Any)]) - case .inputCollectibleUsername(let username): - return ("inputCollectibleUsername", [("username", username as Any)]) - } - } - - public static func parse_inputCollectiblePhone(_ reader: BufferReader) -> InputCollectible? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.InputCollectible.inputCollectiblePhone(phone: _1!) - } - else { - return nil - } - } - public static func parse_inputCollectibleUsername(_ reader: BufferReader) -> InputCollectible? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.InputCollectible.inputCollectibleUsername(username: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api9.swift b/submodules/TelegramApi/Sources/Api9.swift index 3395b6c52d..4634979e31 100644 --- a/submodules/TelegramApi/Sources/Api9.swift +++ b/submodules/TelegramApi/Sources/Api9.swift @@ -1,3 +1,59 @@ +public extension Api { + enum InputCollectible: TypeConstructorDescription { + case inputCollectiblePhone(phone: String) + case inputCollectibleUsername(username: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputCollectiblePhone(let phone): + if boxed { + buffer.appendInt32(-1562241884) + } + serializeString(phone, buffer: buffer, boxed: false) + break + case .inputCollectibleUsername(let username): + if boxed { + buffer.appendInt32(-476815191) + } + serializeString(username, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputCollectiblePhone(let phone): + return ("inputCollectiblePhone", [("phone", phone as Any)]) + case .inputCollectibleUsername(let username): + return ("inputCollectibleUsername", [("username", username as Any)]) + } + } + + public static func parse_inputCollectiblePhone(_ reader: BufferReader) -> InputCollectible? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.InputCollectible.inputCollectiblePhone(phone: _1!) + } + else { + return nil + } + } + public static func parse_inputCollectibleUsername(_ reader: BufferReader) -> InputCollectible? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.InputCollectible.inputCollectibleUsername(username: _1!) + } + else { + return nil + } + } + + } +} public extension Api { enum InputContact: TypeConstructorDescription { case inputPhoneContact(clientId: Int64, phone: String, firstName: String, lastName: String) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index 1e9d4f8d64..432ed9bf48 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1962,7 +1962,7 @@ public final class AccountViewTracker { public func scheduledMessagesViewForLocation(_ chatLocation: ChatLocationInput, additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Namespaces.Message.allScheduled), orderStatistics: [], additionalData: additionalData) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Namespaces.Message.allScheduled), orderStatistics: [], additionalData: additionalData) return withState(signal, { [weak self] () -> Int32 in if let strongSelf = self { return OSAtomicIncrement32(&strongSelf.nextViewId) @@ -1995,7 +1995,7 @@ public final class AccountViewTracker { return .never() } let chatLocation: ChatLocationInput = .peer(peerId: account.peerId, threadId: Int64(quickReplyId)) - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Namespaces.Message.allQuickReply), orderStatistics: [], additionalData: additionalData) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Namespaces.Message.allQuickReply), orderStatistics: [], additionalData: additionalData) return withState(signal, { [weak self] () -> Int32 in if let strongSelf = self { return OSAtomicIncrement32(&strongSelf.nextViewId) @@ -2025,7 +2025,7 @@ public final class AccountViewTracker { return .never() } let chatLocation: ChatLocationInput = .peer(peerId: account.peerId, threadId: nil) - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just([Namespaces.Message.QuickReplyLocal]), orderStatistics: [], additionalData: []) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 200, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just([Namespaces.Message.QuickReplyLocal]), orderStatistics: [], additionalData: []) |> map { view, update, initialData in var entries: [MessageHistoryEntry] = [] for entry in view.entries { @@ -2049,7 +2049,7 @@ public final class AccountViewTracker { return signal } - public func aroundMessageOfInterestHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, count: Int, tag: HistoryViewInputTag? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageOfInterestHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, ignoreMessageIds: Set = Set(), count: Int, tag: HistoryViewInputTag? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> if let peerId = chatLocation.peerId, let threadId = chatLocation.threadId, tag == nil { @@ -2074,6 +2074,7 @@ public final class AccountViewTracker { chatLocation, anchor: anchor, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, + ignoreMessageIds: ignoreMessageIds, count: count, fixedCombinedReadStates: .peer([peerId: CombinedPeerReadState(states: [ (Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: Int32.max - 1, maxOutgoingReadId: Int32.max - 1, maxKnownId: Int32.max - 1, count: 0, markedUnread: false)) @@ -2102,6 +2103,7 @@ public final class AccountViewTracker { chatLocation, anchor: anchor, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, + ignoreMessageIds: ignoreMessageIds, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], @@ -2115,10 +2117,10 @@ public final class AccountViewTracker { } } - return account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, customUnreadMessageId: nil, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) + return account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, customUnreadMessageId: nil, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) } } else { - signal = account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, customUnreadMessageId: nil, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) + signal = account.postbox.aroundMessageOfInterestHistoryViewForChatLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, customUnreadMessageId: nil, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) } return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, fixedCombinedReadStates: nil, addHoleIfNeeded: true) } else { @@ -2126,16 +2128,16 @@ public final class AccountViewTracker { } } - public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, count: Int, ignoreRelatedChats: Bool, messageId: MessageId, tag: HistoryViewInputTag? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundIdMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, ignoreMessageIds: Set = Set(), count: Int, ignoreRelatedChats: Bool, messageId: MessageId, tag: HistoryViewInputTag? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { - let signal = account.postbox.aroundIdMessageHistoryViewForLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) + let signal = account.postbox.aroundIdMessageHistoryViewForLocation(chatLocation, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, fixedCombinedReadStates: nil, addHoleIfNeeded: false) } else { return .never() } } - public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, index: MessageHistoryAnchorIndex, anchorIndex: MessageHistoryAnchorIndex, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, tag: HistoryViewInputTag? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { + public func aroundMessageHistoryViewForLocation(_ chatLocation: ChatLocationInput, ignoreMessagesInTimestampRange: ClosedRange? = nil, ignoreMessageIds: Set = Set(), index: MessageHistoryAnchorIndex, anchorIndex: MessageHistoryAnchorIndex, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, tag: HistoryViewInputTag? = nil, appendMessagesFromTheSameGroup: Bool = false, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = [], useRootInterfaceStateForThread: Bool = false) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> { if let account = self.account { let inputAnchor: HistoryViewInputAnchor switch index { @@ -2146,7 +2148,7 @@ public final class AccountViewTracker { case let .message(index): inputAnchor = .index(index) } - let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: inputAnchor, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) + let signal = account.postbox.aroundMessageHistoryViewForLocation(chatLocation, anchor: inputAnchor, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, clipHoles: clipHoles, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(chatLocation: chatLocation, additionalData: additionalData), useRootInterfaceStateForThread: useRootInterfaceStateForThread) return wrappedMessageHistorySignal(chatLocation: chatLocation, signal: signal, fixedCombinedReadStates: fixedCombinedReadStates, addHoleIfNeeded: false) } else { return .never() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift index 2f5b83d23f..989ff1ddd8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/QuickReplyMessages.swift @@ -212,7 +212,7 @@ func _internal_shortcutMessageList(account: Account, onlyRemote: Bool) -> Signal if onlyRemote { pendingShortcuts = .single([:]) } else { - pendingShortcuts = account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 100, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Set([Namespaces.Message.QuickReplyLocal])), orderStatistics: []) + pendingShortcuts = account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 100, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .just(Set([Namespaces.Message.QuickReplyLocal])), orderStatistics: []) |> map { view , _, _ -> [String: EngineMessage] in var topMessages: [String: EngineMessage] = [:] for entry in view.entries { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift index 4c57ab1f13..2d02461232 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReplyThreadHistory.swift @@ -840,6 +840,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa maxReadOutgoingMessageId: nil )), ignoreMessagesInTimestampRange: nil, + ignoreMessageIds: Set(), count: 40, clipHoles: true, anchor: inputAnchor, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift index f6f258c154..9aa4fff9e3 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/SparseMessageList.swift @@ -192,7 +192,7 @@ public final class SparseMessageList { let location: ChatLocationInput = .peer(peerId: self.peerId, threadId: self.threadId) - self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(location, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: .tag(self.messageTag), appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) + self.topItemsDisposable.set((self.account.postbox.aroundMessageHistoryViewForLocation(location, anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: count, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: .tag(self.messageTag), appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) |> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in guard let strongSelf = self else { return diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 7346f5fa52..11891ef7b2 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -533,7 +533,69 @@ private final class CachedPeerStoryListHead: Codable { } } -public final class PeerStoryListContext { +public struct StoryListContextState: Equatable { + public final class Item: Equatable { + public let storyItem: EngineStoryItem + public let peer: EnginePeer? + + public init(storyItem: EngineStoryItem, peer: EnginePeer?) { + self.storyItem = storyItem + self.peer = peer + } + + public static func ==(lhs: Item, rhs: Item) -> Bool { + if lhs === rhs { + return true + } + if lhs.storyItem != rhs.storyItem { + return false + } + if lhs.peer != rhs.peer { + return false + } + return true + } + } + + public var peerReference: PeerReference? + public var items: [Item] + public var pinnedIds: Set + public var totalCount: Int + public var loadMoreToken: AnyHashable? + public var isCached: Bool + public var hasCache: Bool + public var allEntityFiles: [MediaId: TelegramMediaFile] + + public init( + peerReference: PeerReference?, + items: [Item], + pinnedIds: Set, + totalCount: Int, + loadMoreToken: AnyHashable?, + isCached: Bool, + hasCache: Bool, + allEntityFiles: [MediaId: TelegramMediaFile] + ) { + self.peerReference = peerReference + self.items = items + self.pinnedIds = pinnedIds + self.totalCount = totalCount + self.loadMoreToken = loadMoreToken + self.isCached = isCached + self.hasCache = hasCache + self.allEntityFiles = allEntityFiles + } +} + +public protocol StoryListContext: AnyObject { + typealias State = StoryListContextState + + var state: Signal { get } + + func loadMore(completion: (() -> Void)?) +} + +public final class PeerStoryListContext: StoryListContext { private final class Impl { private let queue: Queue private let account: Account @@ -555,7 +617,7 @@ public final class PeerStoryListContext { private var updatesDisposable: Disposable? - private var completionCallbacksByToken: [Int: [() -> Void]] = [:] + private var completionCallbacksByToken: [AnyHashable: [() -> Void]] = [:] init(queue: Queue, account: Account, peerId: EnginePeer.Id, isArchived: Bool) { self.queue = queue @@ -563,9 +625,9 @@ public final class PeerStoryListContext { self.peerId = peerId self.isArchived = isArchived - self.stateValue = State(peerReference: nil, items: [], pinnedIds: Set(), totalCount: 0, loadMoreToken: 0, isCached: true, hasCache: false, allEntityFiles: [:]) + self.stateValue = State(peerReference: nil, items: [], pinnedIds: Set(), totalCount: 0, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: false, allEntityFiles: [:]) - let _ = (account.postbox.transaction { transaction -> (PeerReference?, [EngineStoryItem], [Int32], Int, [MediaId: TelegramMediaFile], Bool) in + let _ = (account.postbox.transaction { transaction -> (PeerReference?, [State.Item], [Int32], Int, [MediaId: TelegramMediaFile], Bool) in let key = ValueBoxKey(length: 8 + 1) key.setInt64(0, value: peerId.toInt64()) key.setInt8(8, value: isArchived ? 1 : 0) @@ -573,7 +635,7 @@ public final class PeerStoryListContext { guard let cached = cached else { return (nil, [], [], 0, [:], false) } - var items: [EngineStoryItem] = [] + var items: [State.Item] = [] var allEntityFiles: [MediaId: TelegramMediaFile] = [:] for storedItem in cached.items { if case let .item(item) = storedItem, let media = item.media { @@ -613,7 +675,10 @@ public final class PeerStoryListContext { forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }, author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) } ) - items.append(mappedItem) + items.append(State.Item( + storyItem: mappedItem, + peer: nil + )) for entity in mappedItem.entities { if case let .CustomEmoji(_, fileId) = entity.type { @@ -649,10 +714,10 @@ public final class PeerStoryListContext { return } - var updatedState = State(peerReference: peerReference, items: items, pinnedIds: Set(pinnedIds), totalCount: totalCount, loadMoreToken: 0, isCached: true, hasCache: hasCache, allEntityFiles: allEntityFiles) + var updatedState = State(peerReference: peerReference, items: items, pinnedIds: Set(pinnedIds), totalCount: totalCount, loadMoreToken: AnyHashable(0 as Int), isCached: true, hasCache: hasCache, allEntityFiles: allEntityFiles) updatedState.items.sort(by: { lhs, rhs in - let lhsPinned = updatedState.pinnedIds.contains(lhs.id) - let rhsPinned = updatedState.pinnedIds.contains(rhs.id) + let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id) + let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id) if lhsPinned != rhsPinned { if lhsPinned { return true @@ -660,7 +725,7 @@ public final class PeerStoryListContext { return false } } - return lhs.timestamp > rhs.timestamp + return lhs.storyItem.timestamp > rhs.storyItem.timestamp }) self.stateValue = updatedState @@ -673,7 +738,7 @@ public final class PeerStoryListContext { } func loadMore(completion: (() -> Void)?) { - guard let loadMoreToken = self.stateValue.loadMoreToken else { + guard let loadMoreTokenValue = self.stateValue.loadMoreToken, let loadMoreToken = loadMoreTokenValue.base as? Int else { return } @@ -699,7 +764,7 @@ public final class PeerStoryListContext { self.requestDisposable = (self.account.postbox.transaction { transaction -> Api.InputPeer? in return transaction.getPeer(peerId).flatMap(apiInputPeer) } - |> mapToSignal { inputPeer -> Signal<([EngineStoryItem], Int, PeerReference?, Bool), NoError> in + |> mapToSignal { inputPeer -> Signal<([State.Item], Int, PeerReference?, Bool), NoError> in guard let inputPeer = inputPeer else { return .single(([], 0, nil, false)) } @@ -717,13 +782,13 @@ public final class PeerStoryListContext { |> `catch` { _ -> Signal in return .single(nil) } - |> mapToSignal { result -> Signal<([EngineStoryItem], Int, PeerReference?, Bool), NoError> in + |> mapToSignal { result -> Signal<([State.Item], Int, PeerReference?, Bool), NoError> in guard let result = result else { return .single(([], 0, nil, false)) } - return account.postbox.transaction { transaction -> ([EngineStoryItem], Int, PeerReference?, Bool) in - var storyItems: [EngineStoryItem] = [] + return account.postbox.transaction { transaction -> ([State.Item], Int, PeerReference?, Bool) in + var storyItems: [State.Item] = [] var totalCount: Int = 0 var hasMore: Bool = false @@ -775,7 +840,10 @@ public final class PeerStoryListContext { forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }, author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) } ) - storyItems.append(mappedItem) + storyItems.append(State.Item( + storyItem: mappedItem, + peer: nil + )) } } } @@ -784,7 +852,7 @@ public final class PeerStoryListContext { let key = ValueBoxKey(length: 8 + 1) key.setInt64(0, value: peerId.toInt64()) key.setInt8(8, value: isArchived ? 1 : 0) - if let entry = CodableEntry(CachedPeerStoryListHead(items: storyItems.prefix(100).map { .item($0.asStoryItem()) }, pinnedIds: Array(pinnedIds), totalCount: count)) { + if let entry = CodableEntry(CachedPeerStoryListHead(items: storyItems.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: Array(pinnedIds), totalCount: count)) { transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry) } } @@ -795,7 +863,7 @@ public final class PeerStoryListContext { } } |> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, peerReference, hasMore in - guard let `self` = self else { + guard let self else { return } @@ -808,12 +876,12 @@ public final class PeerStoryListContext { } updatedState.hasCache = true - var existingIds = Set(updatedState.items.map { $0.id }) + var existingIds = Set(updatedState.items.map { $0.storyItem.id }) for item in storyItems { - if existingIds.contains(item.id) { + if existingIds.contains(item.storyItem.id) { continue } - existingIds.insert(item.id) + existingIds.insert(item.storyItem.id) updatedState.items.append(item) } @@ -823,7 +891,7 @@ public final class PeerStoryListContext { } if hasMore { - updatedState.loadMoreToken = (storyItems.last?.id).flatMap(Int.init) + updatedState.loadMoreToken = (storyItems.last?.storyItem.id).flatMap(Int.init).flatMap({ AnyHashable($0) }) } else { updatedState.loadMoreToken = nil } @@ -834,7 +902,7 @@ public final class PeerStoryListContext { } self.stateValue = updatedState - if let callbacks = self.completionCallbacksByToken.removeValue(forKey: loadMoreToken) { + if let callbacks = self.completionCallbacksByToken.removeValue(forKey: AnyHashable(loadMoreToken)) { for f in callbacks { f() } @@ -882,17 +950,19 @@ public final class PeerStoryListContext { return peers } |> deliverOn(self.queue)).start(next: { [weak self] peers in - guard let `self` = self else { + guard let self else { return } var finalUpdatedState: State? + finalUpdatedState = nil + let _ = finalUpdatedState for update in updates { switch update { case let .deleted(peerId, id): if self.peerId == peerId { - if let index = (finalUpdatedState ?? self.stateValue).items.firstIndex(where: { $0.id == id }) { + if let index = (finalUpdatedState ?? self.stateValue).items.firstIndex(where: { $0.storyItem.id == id }) { var updatedState = finalUpdatedState ?? self.stateValue updatedState.items.remove(at: index) updatedState.totalCount = max(0, updatedState.totalCount - 1) @@ -901,13 +971,13 @@ public final class PeerStoryListContext { } case let .added(peerId, item): if self.peerId == peerId { - if let index = (finalUpdatedState ?? self.stateValue).items.firstIndex(where: { $0.id == item.id }) { + if let index = (finalUpdatedState ?? self.stateValue).items.firstIndex(where: { $0.storyItem.id == item.id }) { if !self.isArchived { if case let .item(item) = item { if item.isPinned { if let media = item.media { var updatedState = finalUpdatedState ?? self.stateValue - updatedState.items[index] = EngineStoryItem( + updatedState.items[index] = State.Item(storyItem: EngineStoryItem( id: item.id, timestamp: item.timestamp, expirationTimestamp: item.expirationTimestamp, @@ -942,7 +1012,7 @@ public final class PeerStoryListContext { myReaction: item.myReaction, forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }, author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) } - ) + ), peer: nil) finalUpdatedState = updatedState } } else { @@ -956,7 +1026,7 @@ public final class PeerStoryListContext { if case let .item(item) = item { if let media = item.media { var updatedState = finalUpdatedState ?? self.stateValue - updatedState.items[index] = EngineStoryItem( + updatedState.items[index] = State.Item(storyItem: EngineStoryItem( id: item.id, timestamp: item.timestamp, expirationTimestamp: item.expirationTimestamp, @@ -991,7 +1061,7 @@ public final class PeerStoryListContext { myReaction: item.myReaction, forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }, author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) } - ) + ), peer: nil) finalUpdatedState = updatedState } else { var updatedState = finalUpdatedState ?? self.stateValue @@ -1007,7 +1077,7 @@ public final class PeerStoryListContext { if item.isPinned { if let media = item.media { var updatedState = finalUpdatedState ?? self.stateValue - updatedState.items.append(EngineStoryItem( + updatedState.items.append(State.Item(storyItem: EngineStoryItem( id: item.id, timestamp: item.timestamp, expirationTimestamp: item.expirationTimestamp, @@ -1042,10 +1112,10 @@ public final class PeerStoryListContext { myReaction: item.myReaction, forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }, author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) } - )) + ), peer: nil)) updatedState.items.sort(by: { lhs, rhs in - let lhsPinned = updatedState.pinnedIds.contains(lhs.id) - let rhsPinned = updatedState.pinnedIds.contains(rhs.id) + let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id) + let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id) if lhsPinned != rhsPinned { if lhsPinned { return true @@ -1053,7 +1123,7 @@ public final class PeerStoryListContext { return false } } - return lhs.timestamp > rhs.timestamp + return lhs.storyItem.timestamp > rhs.storyItem.timestamp }) finalUpdatedState = updatedState } @@ -1063,7 +1133,7 @@ public final class PeerStoryListContext { if case let .item(item) = item { if let media = item.media { var updatedState = finalUpdatedState ?? self.stateValue - updatedState.items.append(EngineStoryItem( + updatedState.items.append(State.Item(storyItem: EngineStoryItem( id: item.id, timestamp: item.timestamp, expirationTimestamp: item.expirationTimestamp, @@ -1098,10 +1168,10 @@ public final class PeerStoryListContext { myReaction: item.myReaction, forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, peers: peers) }, author: item.authorId.flatMap { peers[$0].flatMap(EnginePeer.init) } - )) + ), peer: nil)) updatedState.items.sort(by: { lhs, rhs in - let lhsPinned = updatedState.pinnedIds.contains(lhs.id) - let rhsPinned = updatedState.pinnedIds.contains(rhs.id) + let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id) + let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id) if lhsPinned != rhsPinned { if lhsPinned { return true @@ -1109,7 +1179,7 @@ public final class PeerStoryListContext { return false } } - return lhs.timestamp > rhs.timestamp + return lhs.storyItem.timestamp > rhs.storyItem.timestamp }) finalUpdatedState = updatedState } @@ -1126,8 +1196,8 @@ public final class PeerStoryListContext { var updatedState = finalUpdatedState ?? self.stateValue updatedState.pinnedIds = Set(ids) updatedState.items.sort(by: { lhs, rhs in - let lhsPinned = updatedState.pinnedIds.contains(lhs.id) - let rhsPinned = updatedState.pinnedIds.contains(rhs.id) + let lhsPinned = updatedState.pinnedIds.contains(lhs.storyItem.id) + let rhsPinned = updatedState.pinnedIds.contains(rhs.storyItem.id) if lhsPinned != rhsPinned { if lhsPinned { return true @@ -1135,7 +1205,7 @@ public final class PeerStoryListContext { return false } } - return lhs.timestamp > rhs.timestamp + return lhs.storyItem.timestamp > rhs.storyItem.timestamp }) finalUpdatedState = updatedState } @@ -1153,7 +1223,7 @@ public final class PeerStoryListContext { let key = ValueBoxKey(length: 8 + 1) key.setInt64(0, value: peerId.toInt64()) key.setInt8(8, value: isArchived ? 1 : 0) - if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.asStoryItem()) }, pinnedIds: Array(pinnedIds), totalCount: Int32(totalCount))) { + if let entry = CodableEntry(CachedPeerStoryListHead(items: items.prefix(100).map { .item($0.storyItem.asStoryItem()) }, pinnedIds: Array(pinnedIds), totalCount: Int32(totalCount))) { transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerStoryListHeads, key: key), entry: entry) } }).start() @@ -1165,37 +1235,6 @@ public final class PeerStoryListContext { } } - public struct State: Equatable { - public var peerReference: PeerReference? - public var items: [EngineStoryItem] - public var pinnedIds: Set - public var totalCount: Int - public var loadMoreToken: Int? - public var isCached: Bool - public var hasCache: Bool - public var allEntityFiles: [MediaId: TelegramMediaFile] - - public init( - peerReference: PeerReference?, - items: [EngineStoryItem], - pinnedIds: Set, - totalCount: Int, - loadMoreToken: Int?, - isCached: Bool, - hasCache: Bool, - allEntityFiles: [MediaId: TelegramMediaFile] - ) { - self.peerReference = peerReference - self.items = items - self.pinnedIds = pinnedIds - self.totalCount = totalCount - self.loadMoreToken = loadMoreToken - self.isCached = isCached - self.hasCache = hasCache - self.allEntityFiles = allEntityFiles - } - } - public var state: Signal { return impl.signalWith { impl, subscriber in return impl.state.start(next: subscriber.putNext) @@ -1220,6 +1259,217 @@ public final class PeerStoryListContext { } } +public final class SearchStoryListContext: StoryListContext { + private final class Impl { + private let queue: Queue + private let account: Account + private let query: String + + private let statePromise = Promise() + private var stateValue: State { + didSet { + self.statePromise.set(.single(self.stateValue)) + } + } + var state: Signal { + return self.statePromise.get() + } + + private var isLoadingMore: Bool = false + private var requestDisposable: Disposable? + + private var updatesDisposable: Disposable? + + private var completionCallbacksByToken: [AnyHashable: [() -> Void]] = [:] + + init(queue: Queue, account: Account, query: String) { + self.queue = queue + self.account = account + self.query = query + + self.stateValue = State(peerReference: nil, items: [], pinnedIds: Set(), totalCount: 0, loadMoreToken: AnyHashable(""), isCached: false, hasCache: false, allEntityFiles: [:]) + self.statePromise.set(.single(self.stateValue)) + + self.loadMore(completion: nil) + } + + deinit { + self.requestDisposable?.dispose() + } + + func loadMore(completion: (() -> Void)?) { + guard let loadMoreTokenValue = self.stateValue.loadMoreToken, let loadMoreToken = loadMoreTokenValue.base as? String else { + return + } + + if let completion = completion { + if self.completionCallbacksByToken[loadMoreToken] == nil { + self.completionCallbacksByToken[loadMoreToken] = [] + } + self.completionCallbacksByToken[loadMoreToken]?.append(completion) + } + + if self.isLoadingMore { + return + } + + self.isLoadingMore = true + + let limit = 100 + + let account = self.account + let accountPeerId = account.peerId + + let searchHashtag: String + if self.query.hasPrefix("#") { + searchHashtag = String(self.query[self.query.index(after: self.query.startIndex)...]) + } else { + searchHashtag = self.query + } + + self.requestDisposable = (account.network.request(Api.functions.stories.searchPosts(hashtag: searchHashtag, offset: "", limit: Int32(limit))) + |> map { result -> Api.stories.FoundStories? in + return result + } + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<([State.Item], Int, String?), NoError> in + guard let result else { + return .single(([], 0, nil)) + } + + return account.postbox.transaction { transaction -> ([State.Item], Int, String?) in + var storyItems: [State.Item] = [] + var totalCount: Int = 0 + var nextOffsetValue: String? + + switch result { + case let .foundStories(_, count, stories, nextOffset, chats, users): + updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: AccumulatedPeers(transaction: transaction, chats: chats, users: users)) + + totalCount = Int(count) + nextOffsetValue = nextOffset + + for story in stories { + switch story { + case let .foundStory(peer, story): + if let storedItem = Stories.StoredItem(apiStoryItem: story, peerId: peer.peerId, transaction: transaction) { + if case let .item(item) = storedItem, let media = item.media { + let mappedItem = EngineStoryItem( + id: item.id, + timestamp: item.timestamp, + expirationTimestamp: item.expirationTimestamp, + media: EngineMedia(media), + alternativeMedia: item.alternativeMedia.flatMap(EngineMedia.init), + mediaAreas: item.mediaAreas, + text: item.text, + entities: item.entities, + views: item.views.flatMap { views in + return EngineStoryItem.Views( + seenCount: views.seenCount, + reactedCount: views.reactedCount, + forwardCount: views.forwardCount, + seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in + return transaction.getPeer(id).flatMap(EnginePeer.init) + }, + reactions: views.reactions, + hasList: views.hasList + ) + }, + privacy: item.privacy.flatMap(EngineStoryPrivacy.init), + isPinned: item.isPinned, + isExpired: item.isExpired, + isPublic: item.isPublic, + isPending: false, + isCloseFriends: item.isCloseFriends, + isContacts: item.isContacts, + isSelectedContacts: item.isSelectedContacts, + isForwardingDisabled: item.isForwardingDisabled, + isEdited: item.isEdited, + isMy: item.isMy, + myReaction: item.myReaction, + forwardInfo: item.forwardInfo.flatMap { EngineStoryItem.ForwardInfo($0, transaction: transaction) }, + author: item.authorId.flatMap { transaction.getPeer($0).flatMap(EnginePeer.init) } + ) + storyItems.append(State.Item( + storyItem: mappedItem, + peer: transaction.getPeer(peer.peerId).flatMap(EnginePeer.init) + )) + } + } + } + } + } + + return (storyItems, totalCount, nextOffsetValue) + } + } + |> deliverOn(self.queue)).start(next: { [weak self] storyItems, totalCount, nextOffset in + guard let `self` = self else { + return + } + + self.isLoadingMore = false + + var updatedState = self.stateValue + updatedState.hasCache = true + + var existingIds = Set(updatedState.items.map { $0.storyItem.id }) + for item in storyItems { + if existingIds.contains(item.storyItem.id) { + continue + } + existingIds.insert(item.storyItem.id) + + updatedState.items.append(item) + } + + if let nextOffset { + updatedState.loadMoreToken = AnyHashable(nextOffset) + } else { + updatedState.loadMoreToken = nil + } + if updatedState.loadMoreToken != nil { + updatedState.totalCount = max(totalCount, updatedState.items.count) + } else { + updatedState.totalCount = updatedState.items.count + } + self.stateValue = updatedState + + if let callbacks = self.completionCallbacksByToken.removeValue(forKey: loadMoreToken) { + for f in callbacks { + f() + } + } + }) + } + } + + public var state: Signal { + return impl.signalWith { impl, subscriber in + return impl.state.start(next: subscriber.putNext) + } + } + + private let queue: Queue + private let impl: QueueLocalObject + + public init(account: Account, query: String) { + let queue = Queue.mainQueue() + self.queue = queue + self.impl = QueueLocalObject(queue: queue, generate: { + return Impl(queue: queue, account: account, query: query) + }) + } + + public func loadMore(completion: (() -> Void)? = nil) { + self.impl.with { impl in + impl.loadMore(completion : completion) + } + } +} + public final class PeerExpiringStoryListContext { private final class Impl { private let queue: Queue diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 6f1e12f55b..7ed04d6676 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -1475,7 +1475,7 @@ public extension TelegramEngine { return .single(false) } - return self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: id, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 44, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) + return self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: id, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 44, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) |> map { view -> Bool in for entry in view.0.entries { if entry.message.flags.contains(.Incoming) { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift index d9e9c17fe1..db1861d0fc 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoData.swift @@ -334,8 +334,8 @@ final class PeerInfoScreenData { let groupsInCommon: GroupsInCommonContext? let linkedDiscussionPeer: Peer? let members: PeerInfoMembersData? - let storyListContext: PeerStoryListContext? - let storyArchiveListContext: PeerStoryListContext? + let storyListContext: StoryListContext? + let storyArchiveListContext: StoryListContext? let encryptionKeyFingerprint: SecretChatKeyFingerprint? let globalSettings: TelegramGlobalSettings? let invitations: PeerExportedInvitationsState? @@ -375,8 +375,8 @@ final class PeerInfoScreenData { groupsInCommon: GroupsInCommonContext?, linkedDiscussionPeer: Peer?, members: PeerInfoMembersData?, - storyListContext: PeerStoryListContext?, - storyArchiveListContext: PeerStoryListContext?, + storyListContext: StoryListContext?, + storyArchiveListContext: StoryListContext?, encryptionKeyFingerprint: SecretChatKeyFingerprint?, globalSettings: TelegramGlobalSettings?, invitations: PeerExportedInvitationsState?, @@ -635,7 +635,7 @@ private func peerInfoPersonalChannel(context: AccountContext, peerId: EnginePeer ) return combineLatest( - context.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: channelPeer.id, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 10, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []), + context.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: channelPeer.id, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 10, clipHoles: false, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []), context.engine.data.subscribe( TelegramEngine.EngineData.Item.Peer.StoryStats(id: channelPeer.id) ), @@ -1071,7 +1071,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen |> distinctUntilChanged let hasStoryArchive: Signal - var storyArchiveListContext: PeerStoryListContext? + var storyArchiveListContext: StoryListContext? if isMyProfile { let storyArchiveListContextValue = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: true) storyArchiveListContext = storyArchiveListContextValue @@ -1556,7 +1556,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen let requestsContextPromise = Promise(nil) let requestsStatePromise = Promise(nil) - let storyListContext: PeerStoryListContext? + let storyListContext: StoryListContext? let hasStories: Signal if peerId.namespace == Namespaces.Peer.CloudChannel { storyListContext = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index 3a5051bada..ecd262ff0a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -435,7 +435,7 @@ private final class DurationLayer: SimpleLayer { avatarLayer.contents = other.avatarLayer?.contents } - func update(directMediaImageCache: DirectMediaImageCache, author: EnginePeer, synchronous: SparseItemGrid.Synchronous) { + func update(directMediaImageCache: DirectMediaImageCache, author: EnginePeer, constrainedWidth: CGFloat, synchronous: SparseItemGrid.Synchronous) { let avatarLayer: SimpleLayer if let current = self.avatarLayer { avatarLayer = current @@ -451,7 +451,7 @@ private final class DurationLayer: SimpleLayer { if self.authorPeerId != author.id { let string = NSAttributedString(string: author.debugDisplayTitle, font: durationFont, textColor: .white) - let bounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + let bounds = string.boundingRect(with: CGSize(width: constrainedWidth - 20.0, height: 20.0), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) let textSize = CGSize(width: ceil(bounds.width), height: ceil(bounds.height)) let sideInset: CGFloat = 6.0 let verticalInset: CGFloat = 2.0 @@ -463,7 +463,7 @@ private final class DurationLayer: SimpleLayer { context.setShadow(offset: CGSize(width: 0.0, height: 0.0), blur: 2.5, color: UIColor(rgb: 0x000000, alpha: 0.22).cgColor) UIGraphicsPushContext(context) - string.draw(in: bounds.offsetBy(dx: sideInset, dy: verticalInset)) + string.draw(with: bounds.offsetBy(dx: sideInset, dy: verticalInset), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) UIGraphicsPopContext() }) self.contents = image?.cgImage @@ -552,7 +552,7 @@ private final class ItemLayer: CALayer, SparseItemGridLayer { self.item = item } - func updateDuration(viewCount: Int32?, duration: Int32?, topRightIcon: ItemTopRightIcon?, author: EnginePeer?, isMin: Bool, minFactor: CGFloat, directMediaImageCache: DirectMediaImageCache, synchronous: SparseItemGrid.Synchronous) { + func updateDuration(size: CGSize, viewCount: Int32?, duration: Int32?, topRightIcon: ItemTopRightIcon?, author: EnginePeer?, isMin: Bool, minFactor: CGFloat, directMediaImageCache: DirectMediaImageCache, synchronous: SparseItemGrid.Synchronous) { self.minFactor = minFactor if let viewCount { @@ -607,11 +607,11 @@ private final class ItemLayer: CALayer, SparseItemGridLayer { if let author { if let authorLayer = self.authorLayer { - authorLayer.update(directMediaImageCache: directMediaImageCache, author: author, synchronous: synchronous) + authorLayer.update(directMediaImageCache: directMediaImageCache, author: author, constrainedWidth: size.width, synchronous: synchronous) } else { let authorLayer = DurationLayer() authorLayer.contentsGravity = .bottomLeft - authorLayer.update(directMediaImageCache: directMediaImageCache, author: author, synchronous: synchronous) + authorLayer.update(directMediaImageCache: directMediaImageCache, author: author, constrainedWidth: size.width, synchronous: synchronous) self.addSublayer(authorLayer) authorLayer.frame = CGRect(origin: CGPoint(x: 17.0, y: 3.0), size: CGSize()) authorLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0) @@ -1292,7 +1292,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding { isMin = layer.bounds.width < 80.0 } - layer.updateDuration(viewCount: viewCount, duration: duration, topRightIcon: topRightIcon, author: item.authorPeer, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0), directMediaImageCache: self.directMediaImageCache, synchronous: synchronous) + layer.updateDuration(size: layer.bounds.size, viewCount: viewCount, duration: duration, topRightIcon: topRightIcon, author: item.authorPeer, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0), directMediaImageCache: self.directMediaImageCache, synchronous: synchronous) } func unbindLayer(layer: SparseItemGridLayer) { @@ -1468,7 +1468,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr private var animationTimer: SwiftSignalKit.Timer? public private(set) var calendarSource: SparseMessageCalendar? - private var listSource: PeerStoryListContext + private var listSource: StoryListContext public var openCurrentDate: (() -> Void)? public var paneDidScroll: (() -> Void)? @@ -1484,14 +1484,14 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr private weak var pendingOpenListContext: PeerStoryListContentContextImpl? - private var preloadArchiveListContext: PeerStoryListContext? + private var preloadArchiveListContext: StoryListContext? private var emptyStateView: ComponentView? private weak var contextControllerToDismissOnSelection: ContextControllerProtocol? private weak var tempContextContentItemNode: TempExtractedItemNode? - public init(context: AccountContext, peerId: PeerId?, searchQuery: String? = nil, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, isProfileEmbedded: Bool, canManageStories: Bool, navigationController: @escaping () -> NavigationController?, listContext: PeerStoryListContext?) { + public init(context: AccountContext, peerId: PeerId?, searchQuery: String? = nil, contentType: ContentType, captureProtected: Bool, isSaved: Bool, isArchive: Bool, isProfileEmbedded: Bool, canManageStories: Bool, navigationController: @escaping () -> NavigationController?, listContext: StoryListContext?) { self.context = context self.peerId = peerId self.searchQuery = searchQuery @@ -1518,7 +1518,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr displayPrivacy: isProfileEmbedded ) - self.listSource = listContext ?? PeerStoryListContext(account: context.account, peerId: peerId ?? context.account.peerId, isArchived: self.isArchive) + if let listContext { + self.listSource = listContext + } else if let searchQuery { + self.listSource = SearchStoryListContext(account: context.account, query: searchQuery) + } else { + self.listSource = PeerStoryListContext(account: context.account, peerId: peerId ?? context.account.peerId, isArchived: self.isArchive) + } self.calendarSource = nil super.init() @@ -2237,37 +2243,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr var firstTime = true let queue = Queue() - let authorPeer: Signal - if self.searchQuery != nil { - authorPeer = self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId) - ) - } else { - authorPeer = .single(nil) - } - - var state = self.listSource.state - if self.peerId == nil && self.listDisposable == nil { - state = .single(PeerStoryListContext.State( - peerReference: nil, - items: [], - pinnedIds: Set(), - totalCount: 0, - loadMoreToken: 0, - isCached: false, - hasCache: false, - allEntityFiles: [:] - )) |> then(state |> delay(2.0, queue: .mainQueue())) - } + let state = self.listSource.state self.listDisposable?.dispose() self.listDisposable = nil - self.listDisposable = (combineLatest( - state, - authorPeer - ) - |> deliverOn(queue)).startStrict(next: { [weak self] state, authorPeer in + self.listDisposable = (state + |> deliverOn(queue)).startStrict(next: { [weak self] state in guard let self else { return } @@ -2291,20 +2273,28 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr var mappedItems: [SparseItemGrid.Item] = [] var mappedHoles: [SparseItemGrid.HoleAnchor] = [] var totalCount: Int = 0 - if let peerReference = state.peerReference { - for item in state.items { - mappedItems.append(VisualMediaItem( - index: mappedItems.count, - peer: peerReference, - story: item, - authorPeer: authorPeer, - isPinned: state.pinnedIds.contains(item.id), - localMonthTimestamp: Month(localTimestamp: item.timestamp + timezoneOffset).packedValue - )) + for item in state.items { + var peerReference: PeerReference? + if let value = state.peerReference { + peerReference = value + } else if let peer = item.peer { + peerReference = PeerReference(peer._asPeer()) } - if mappedItems.count < state.totalCount, let lastItem = state.items.last, let loadMoreToken = state.loadMoreToken { - mappedHoles.append(VisualMediaHoleAnchor(index: mappedItems.count, storyId: Int32(loadMoreToken), localMonthTimestamp: Month(localTimestamp: lastItem.timestamp + timezoneOffset).packedValue)) + guard let peerReference else { + continue } + + mappedItems.append(VisualMediaItem( + index: mappedItems.count, + peer: peerReference, + story: item.storyItem, + authorPeer: item.peer, + isPinned: state.pinnedIds.contains(item.storyItem.id), + localMonthTimestamp: Month(localTimestamp: item.storyItem.timestamp + timezoneOffset).packedValue + )) + } + if mappedItems.count < state.totalCount, let lastItem = state.items.last, let _ = state.loadMoreToken { + mappedHoles.append(VisualMediaHoleAnchor(index: mappedItems.count, storyId: Int32.max, localMonthTimestamp: Month(localTimestamp: lastItem.storyItem.timestamp + timezoneOffset).packedValue)) } totalCount = state.totalCount totalCount = max(mappedItems.count, totalCount) @@ -2833,7 +2823,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr transition.updateFrame(node: self.contextGestureContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) - if let items = self.items, items.items.isEmpty, items.count == 0 { + if self.searchQuery == nil, let items = self.items, items.items.isEmpty, items.count == 0 { let emptyStateView: ComponentView var emptyStateTransition = Transition(transition) if let current = self.emptyStateView { diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index e902ce5358..55e2d785db 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -678,58 +678,67 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, reportPeerIrrelevantGeoLocation: { }, displaySlowmodeTooltip: { _, _ in }, displaySendMessageOptions: { [weak self] node, gesture in - guard let strongSelf = self, let textInputPanelNode = strongSelf.textInputPanelNode else { - return - } - textInputPanelNode.loadTextInputNodeIfNeeded() - guard let textInputNode = textInputPanelNode.textInputNode else { + guard let strongSelf = self else { return } - var hasEntityKeyboard = false - if case .media = strongSelf.presentationInterfaceState.inputMode { - hasEntityKeyboard = true - } - - let controller = makeChatSendMessageActionSheetController( - context: strongSelf.context, - peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, - params: .sendMessage(SendMessageActionSheetControllerParams.SendMessage( - isScheduledMessages: false, - mediaPreview: nil, - mediaCaptionIsAbove: nil, - attachment: false, - canSendWhenOnline: false, - forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] - )), - hasEntityKeyboard: hasEntityKeyboard, - gesture: gesture, - sourceSendButton: node, - textInputView: textInputNode.textView, - emojiViewProvider: textInputPanelNode.emojiViewProvider, - completion: { - }, - sendMessage: { [weak textInputPanelNode] mode, messageEffect in - switch mode { - case .generic: - textInputPanelNode?.sendMessage(.generic, messageEffect) - case .silently: - textInputPanelNode?.sendMessage(.silent, messageEffect) - case .whenOnline: - textInputPanelNode?.sendMessage(.whenOnline, messageEffect) - } - }, - schedule: { [weak textInputPanelNode] messageEffect in - textInputPanelNode?.sendMessage(.schedule, messageEffect) - }, - openPremiumPaywall: { [weak controller] c in - guard let controller else { - return - } - controller.push(c) + let _ = (ChatSendMessageContextScreen.initialData(context: strongSelf.context, currentMessageEffectId: nil) + |> deliverOnMainQueue).start(next: { initialData in + guard let strongSelf = self, let textInputPanelNode = strongSelf.textInputPanelNode else { + return } - ) - strongSelf.presentInGlobalOverlay(controller, nil) + textInputPanelNode.loadTextInputNodeIfNeeded() + guard let textInputNode = textInputPanelNode.textInputNode else { + return + } + + var hasEntityKeyboard = false + if case .media = strongSelf.presentationInterfaceState.inputMode { + hasEntityKeyboard = true + } + + let controller = makeChatSendMessageActionSheetController( + initialData: initialData, + context: strongSelf.context, + peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, + params: .sendMessage(SendMessageActionSheetControllerParams.SendMessage( + isScheduledMessages: false, + mediaPreview: nil, + mediaCaptionIsAbove: nil, + messageEffect: nil, + attachment: false, + canSendWhenOnline: false, + forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] + )), + hasEntityKeyboard: hasEntityKeyboard, + gesture: gesture, + sourceSendButton: node, + textInputView: textInputNode.textView, + emojiViewProvider: textInputPanelNode.emojiViewProvider, + completion: { + }, + sendMessage: { [weak textInputPanelNode] mode, messageEffect in + switch mode { + case .generic: + textInputPanelNode?.sendMessage(.generic, messageEffect) + case .silently: + textInputPanelNode?.sendMessage(.silent, messageEffect) + case .whenOnline: + textInputPanelNode?.sendMessage(.whenOnline, messageEffect) + } + }, + schedule: { [weak textInputPanelNode] messageEffect in + textInputPanelNode?.sendMessage(.schedule, messageEffect) + }, + openPremiumPaywall: { [weak controller] c in + guard let controller else { + return + } + controller.push(c) + } + ) + strongSelf.presentInGlobalOverlay(controller, nil) + }) }, openScheduledMessages: { }, openPeersNearby: { }, displaySearchResultsTooltip: { _, _ in diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift index c8e338546a..2c660af749 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/OpenStories.swift @@ -14,7 +14,7 @@ public extension StoryContainerScreen { |> take(1) |> mapToSignal { state -> Signal in if let slice = state.slice { - return waitUntilStoryMediaPreloaded(context: context, peerId: slice.peer.id, storyItem: slice.item.storyItem) + return waitUntilStoryMediaPreloaded(context: context, peerId: slice.effectivePeer.id, storyItem: slice.item.storyItem) |> timeout(4.0, queue: .mainQueue(), alternate: .complete()) |> map { _ -> Void in } @@ -185,7 +185,7 @@ public extension StoryContainerScreen { } #endif - return waitUntilStoryMediaPreloaded(context: context, peerId: slice.peer.id, storyItem: slice.item.storyItem) + return waitUntilStoryMediaPreloaded(context: context, peerId: slice.effectivePeer.id, storyItem: slice.item.storyItem) |> timeout(4.0, queue: .mainQueue(), alternate: .complete()) |> map { _ -> StoryContentContextState in } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index dc8f056aab..53e8d0412e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -444,7 +444,8 @@ public final class StoryContentContextImpl: StoryContentContext { dayCounters: nil, peerId: peer.id, storyItem: item, - entityFiles: extractItemEntityFiles(item: item, allEntityFiles: allEntityFiles) + entityFiles: extractItemEntityFiles(item: item, allEntityFiles: allEntityFiles), + itemPeer: nil ) } @@ -457,7 +458,8 @@ public final class StoryContentContextImpl: StoryContentContext { dayCounters: nil, peerId: peer.id, storyItem: mappedItem, - entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) + entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles), + itemPeer: nil ), totalCount: totalCount, previousItemId: previousItemId, @@ -1344,7 +1346,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { dayCounters: nil, peerId: peer.id, storyItem: mappedItem, - entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) + entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles), + itemPeer: nil ) let stateValue = StoryContentContextState( slice: StoryContentContextState.FocusedSlice( @@ -1421,7 +1424,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { private var requestedStoryKeys = Set() private var requestStoryDisposables = DisposableSet() - private var listState: PeerStoryListContext.State? + private var listState: StoryListContext.State? private var focusedId: Int32? private var focusedIdUpdated = Promise(Void()) @@ -1429,7 +1432,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { private var preloadStoryResourceDisposables: [EngineMedia.Id: Disposable] = [:] private var pollStoryMetadataDisposables = DisposableSet() - public init(context: AccountContext, peerId: EnginePeer.Id, listContext: PeerStoryListContext, initialId: Int32?) { + public init(context: AccountContext, peerId: EnginePeer.Id, listContext: StoryListContext, initialId: Int32?) { self.context = context context.engine.account.viewTracker.refreshCanSendMessagesForPeerIds(peerIds: [peerId]) @@ -1494,9 +1497,9 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { let focusedIndex: Int? if let current = self.focusedId { - if let index = state.items.firstIndex(where: { $0.id == current }) { + if let index = state.items.firstIndex(where: { $0.storyItem.id == current }) { focusedIndex = index - } else if let index = state.items.firstIndex(where: { $0.id <= current }) { + } else if let index = state.items.firstIndex(where: { $0.storyItem.id <= current }) { focusedIndex = index } else if !state.items.isEmpty { focusedIndex = 0 @@ -1504,9 +1507,9 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { focusedIndex = nil } } else if let initialId = initialId { - if let index = state.items.firstIndex(where: { $0.id == initialId }) { + if let index = state.items.firstIndex(where: { $0.storyItem.id == initialId }) { focusedIndex = index - } else if let index = state.items.firstIndex(where: { $0.id <= initialId }) { + } else if let index = state.items.firstIndex(where: { $0.storyItem.id <= initialId }) { focusedIndex = index } else { focusedIndex = nil @@ -1536,7 +1539,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { let stateValue: StoryContentContextState if let focusedIndex = focusedIndex { let item = state.items[focusedIndex] - self.focusedId = item.id + self.focusedId = item.storyItem.id var allItems: [StoryContentItem] = [] @@ -1549,11 +1552,12 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { position: i, dayCounters: nil, peerId: peer.id, - storyItem: stateItem, - entityFiles: extractItemEntityFiles(item: stateItem, allEntityFiles: state.allEntityFiles) + storyItem: stateItem.storyItem, + entityFiles: extractItemEntityFiles(item: stateItem.storyItem, allEntityFiles: state.allEntityFiles), + itemPeer: stateItem.peer )) - let day = DayIndex(timestamp: stateItem.timestamp) + let day = DayIndex(timestamp: stateItem.storyItem.timestamp) let dayCount: Int if let current = dayCounts[day] { dayCount = current + 1 @@ -1562,11 +1566,11 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { dayCount = 1 dayCounts[day] = dayCount } - itemDayIndices[stateItem.id] = (dayCount - 1, day) + itemDayIndices[stateItem.storyItem.id] = (dayCount - 1, day) } var dayCounters: StoryContentItem.DayCounters? - if let (offset, day) = itemDayIndices[item.id], let dayCount = dayCounts[day] { + if let (offset, day) = itemDayIndices[item.storyItem.id], let dayCount = dayCounts[day] { dayCounters = StoryContentItem.DayCounters( position: offset, totalCount: dayCount @@ -1581,12 +1585,13 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { position: focusedIndex, dayCounters: dayCounters, peerId: peer.id, - storyItem: item, - entityFiles: extractItemEntityFiles(item: item, allEntityFiles: state.allEntityFiles) + storyItem: item.storyItem, + entityFiles: extractItemEntityFiles(item: item.storyItem, allEntityFiles: state.allEntityFiles), + itemPeer: item.peer ), totalCount: state.totalCount, - previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].id, - nextItemId: (focusedIndex == state.items.count - 1) ? nil : state.items[focusedIndex + 1].id, + previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].storyItem.id, + nextItemId: (focusedIndex == state.items.count - 1) ? nil : state.items[focusedIndex + 1].storyItem.id, allItems: allItems, forwardInfoStories: [:] ), @@ -1612,7 +1617,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { var pollItems: [StoryKey] = [] if let focusedIndex, let slice = stateValue.slice { - var possibleItems: [(EnginePeer, EngineStoryItem)] = [] + var possibleItems: [(EnginePeer, StoryListContext.State.Item)] = [] if peer.id == self.context.account.peerId { pollItems.append(StoryKey(peerId: peer.id, id: slice.item.storyItem.id)) } @@ -1623,7 +1628,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { } if slice.peer.id == self.context.account.peerId { - pollItems.append(StoryKey(peerId: slice.peer.id, id: state.items[i].id)) + pollItems.append(StoryKey(peerId: slice.peer.id, id: state.items[i].storyItem.id)) } } @@ -1631,9 +1636,9 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { for i in 0 ..< min(possibleItems.count, 3) { let peer = possibleItems[i].0 let item = possibleItems[i].1 - if let peerReference = PeerReference(peer._asPeer()), let mediaId = item.media.id { + if let peerReference = PeerReference(peer._asPeer()), let mediaId = item.storyItem.media.id { var reactions: [MessageReaction.Reaction] = [] - for mediaArea in item.mediaAreas { + for mediaArea in item.storyItem.mediaAreas { if case let .reaction(_, reaction, _) = mediaArea { if !reactions.contains(reaction) { reactions.append(reaction) @@ -1642,15 +1647,15 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { } var selectedMedia: EngineMedia - if let alternativeMedia = item.alternativeMedia, (!preferHighQualityStories && !item.isMy) { + if let alternativeMedia = item.storyItem.alternativeMedia, (!preferHighQualityStories && !item.storyItem.isMy) { selectedMedia = alternativeMedia } else { - selectedMedia = item.media + selectedMedia = item.storyItem.media } resultResources[mediaId] = StoryPreloadInfo( peer: peerReference, - storyId: item.id, + storyId: item.storyItem.id, media: selectedMedia, reactions: reactions, priority: .top(position: nextPriority) @@ -1721,13 +1726,13 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { case .previous: indexDifference = -1 case let .id(id): - if let listState = self.listState, let focusedId = self.focusedId, let index = listState.items.firstIndex(where: { $0.id == focusedId }), let nextIndex = listState.items.firstIndex(where: { $0.id == id }) { + if let listState = self.listState, let focusedId = self.focusedId, let index = listState.items.firstIndex(where: { $0.storyItem.id == focusedId }), let nextIndex = listState.items.firstIndex(where: { $0.storyItem.id == id }) { indexDifference = nextIndex - index } } if let indexDifference, let listState = self.listState, let focusedId = self.focusedId { - if let index = listState.items.firstIndex(where: { $0.id == focusedId }) { + if let index = listState.items.firstIndex(where: { $0.storyItem.id == focusedId }) { var nextIndex = index + indexDifference if nextIndex < 0 { nextIndex = 0 @@ -1736,7 +1741,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { nextIndex = listState.items.count - 1 } if nextIndex != index { - self.focusedId = listState.items[nextIndex].id + self.focusedId = listState.items[nextIndex].storyItem.id self.focusedIdUpdated.set(.single(Void())) } } @@ -2516,7 +2521,8 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { dayCounters: nil, peerId: peer.id, storyItem: item, - entityFiles: extractItemEntityFiles(item: item, allEntityFiles: allEntityFiles) + entityFiles: extractItemEntityFiles(item: item, allEntityFiles: allEntityFiles), + itemPeer: nil ) } @@ -2529,7 +2535,8 @@ public final class RepostStoriesContentContextImpl: StoryContentContext { dayCounters: nil, peerId: peer.id, storyItem: mappedItem, - entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles) + entityFiles: extractItemEntityFiles(item: mappedItem, allEntityFiles: allEntityFiles), + itemPeer: nil ), totalCount: totalCount, previousItemId: previousItemId, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index 3b4f6bbf43..d353b2c3f8 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -104,19 +104,22 @@ public final class StoryContentItem: Equatable { public let peerId: EnginePeer.Id? public let storyItem: EngineStoryItem public let entityFiles: [EngineMedia.Id: TelegramMediaFile] + public let itemPeer: EnginePeer? public init( position: Int?, dayCounters: DayCounters?, peerId: EnginePeer.Id?, storyItem: EngineStoryItem, - entityFiles: [EngineMedia.Id: TelegramMediaFile] + entityFiles: [EngineMedia.Id: TelegramMediaFile], + itemPeer: EnginePeer? ) { self.position = position self.dayCounters = dayCounters self.peerId = peerId self.storyItem = storyItem self.entityFiles = entityFiles + self.itemPeer = itemPeer } public static func ==(lhs: StoryContentItem, rhs: StoryContentItem) -> Bool { @@ -135,6 +138,9 @@ public final class StoryContentItem: Equatable { if lhs.entityFiles != rhs.entityFiles { return false } + if lhs.itemPeer != rhs.itemPeer { + return false + } return true } } @@ -209,6 +215,10 @@ public final class StoryContentContextState { public let allItems: [StoryContentItem] public let forwardInfoStories: [StoryId: Promise] + var effectivePeer: EnginePeer { + return self.item.itemPeer ?? self.peer + } + public init( peer: EnginePeer, additionalPeerData: AdditionalPeerData, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 5ebfa32336..17ef0ad48a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1231,9 +1231,9 @@ public final class StoryItemSetContainerComponent: Component { self.component?.controller()?.dismiss() } else if translation.y < -200.0 || (translation.y < -100.0 && velocity.y < -100.0) { var displayViewLists = false - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { displayViewLists = true - } else if case let .channel(channel) = component.slice.peer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats { + } else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats { displayViewLists = true } @@ -1563,7 +1563,7 @@ public final class StoryItemSetContainerComponent: Component { component: AnyComponent(StoryItemContentComponent( context: component.context, strings: component.strings, - peer: component.slice.peer, + peer: component.slice.effectivePeer, item: item.storyItem, availableReactions: component.availableReactions, entityFiles: item.entityFiles, @@ -1661,7 +1661,7 @@ public final class StoryItemSetContainerComponent: Component { var isChannel = false var canShare = true var displayFooter = false - if case let .channel(channel) = component.slice.peer { + if case let .channel(channel) = component.slice.effectivePeer { isChannel = true if channel.addressName == nil { canShare = false @@ -1678,7 +1678,7 @@ public final class StoryItemSetContainerComponent: Component { displayFooter = true } } - } else if component.slice.peer.id == component.context.account.peerId { + } else if component.slice.effectivePeer.id == component.context.account.peerId { displayFooter = true } else if component.slice.item.storyItem.isPending { displayFooter = true @@ -1723,7 +1723,7 @@ public final class StoryItemSetContainerComponent: Component { context: component.context, theme: component.theme, strings: component.strings, - peer: component.slice.peer, + peer: component.slice.effectivePeer, storyItem: item.storyItem, myReaction: item.storyItem.myReaction.flatMap { value -> StoryFooterPanelComponent.MyReaction? in var centerAnimation: TelegramMediaFile? @@ -1931,9 +1931,9 @@ public final class StoryItemSetContainerComponent: Component { } var displayViewLists = false - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { displayViewLists = true - } else if case let .channel(channel) = component.slice.peer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats { + } else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats { displayViewLists = true } @@ -1946,12 +1946,12 @@ public final class StoryItemSetContainerComponent: Component { return true } else { var canReply = false - if case .user = component.slice.peer { + if case .user = component.slice.effectivePeer { canReply = true - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { canReply = false - } else if component.slice.peer.isService { + } else if component.slice.effectivePeer.isService { canReply = false } else if case .unsupported = component.slice.item.storyItem.media { canReply = false @@ -1974,12 +1974,12 @@ public final class StoryItemSetContainerComponent: Component { } var canReply = false - if case .user = component.slice.peer { + if case .user = component.slice.effectivePeer { canReply = true - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { canReply = false - } else if component.slice.peer.isService { + } else if component.slice.effectivePeer.isService { canReply = false } else if case .unsupported = component.slice.item.storyItem.media { canReply = false @@ -2515,7 +2515,7 @@ public final class StoryItemSetContainerComponent: Component { if likeButtonView.alpha == 0.0 { return } - if component.slice.peer.isService { + if component.slice.effectivePeer.isService { return } else if case .unsupported = component.slice.item.storyItem.media { return @@ -2558,7 +2558,7 @@ public final class StoryItemSetContainerComponent: Component { let previousInput = inputPanelView.getSendMessageInput() switch previousInput { case let .text(value): - component.storyItemSharedState.replyDrafts[StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id)] = value + component.storyItemSharedState.replyDrafts[StoryId(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id)] = value } } } @@ -2608,10 +2608,10 @@ public final class StoryItemSetContainerComponent: Component { resetInputContents = .text(NSAttributedString()) self.saveDraft() - if let draft = component.storyItemSharedState.replyDrafts[StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id)] { + if let draft = component.storyItemSharedState.replyDrafts[StoryId(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id)] { resetInputContents = .text(draft) } - component.storyItemSharedState.replyDrafts.removeValue(forKey: StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id)) + component.storyItemSharedState.replyDrafts.removeValue(forKey: StoryId(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id)) if let tooltipScreen = self.sendMessageContext.tooltipScreen { if let tooltipScreen = tooltipScreen as? UndoOverlayController, let tag = tooltipScreen.tag as? String, tag == "no_auto_dismiss" { @@ -2761,7 +2761,7 @@ public final class StoryItemSetContainerComponent: Component { var isGroup = false var showMessageInputPanel = true var isGroupCommentRestricted = false - if case let .channel(channel) = component.slice.peer { + if case let .channel(channel) = component.slice.effectivePeer { switch channel.info { case .broadcast: isChannel = true @@ -2777,7 +2777,7 @@ public final class StoryItemSetContainerComponent: Component { isGroup = true } } else { - showMessageInputPanel = component.slice.peer.id != component.context.account.peerId + showMessageInputPanel = component.slice.effectivePeer.id != component.context.account.peerId } var isUnsupported = false @@ -2788,10 +2788,10 @@ public final class StoryItemSetContainerComponent: Component { self?.presentBoostToUnrestrict() }) } else if component.slice.additionalPeerData.isPremiumRequiredForMessaging { - disabledPlaceholder = .premiumRequired(title: component.strings.Story_MessagingRestrictedPlaceholder(component.slice.peer.compactDisplayTitle).string, subtitle: component.strings.Story_MessagingRestrictedPlaceholderAction, action: { [weak self] in + disabledPlaceholder = .premiumRequired(title: component.strings.Story_MessagingRestrictedPlaceholder(component.slice.effectivePeer.compactDisplayTitle).string, subtitle: component.strings.Story_MessagingRestrictedPlaceholderAction, action: { [weak self] in self?.presentPremiumRequiredForMessaging() }) - } else if component.slice.peer.isService { + } else if component.slice.effectivePeer.isService { disabledPlaceholder = .text(component.strings.Story_FooterReplyUnavailable) } else if case .unsupported = component.slice.item.storyItem.media { isUnsupported = true @@ -2860,10 +2860,10 @@ public final class StoryItemSetContainerComponent: Component { if showMessageInputPanel { var haveLikeOptions = false - if case .user = component.slice.peer { + if case .user = component.slice.effectivePeer { haveLikeOptions = true - if component.slice.peer.isService { + if component.slice.effectivePeer.isService { haveLikeOptions = false } } @@ -2945,7 +2945,7 @@ public final class StoryItemSetContainerComponent: Component { self.sendMessageContext.videoRecorderValue?.dismissVideo() self.sendMessageContext.discardMediaRecordingPreview(view: self) }, - attachmentAction: component.slice.peer.isService ? nil : { [weak self] in + attachmentAction: component.slice.effectivePeer.isService ? nil : { [weak self] in guard let self else { return } @@ -2975,7 +2975,7 @@ public final class StoryItemSetContainerComponent: Component { return MessageInputPanelComponent.MyReaction(reaction: value, file: centerAnimation, animationFileId: animationFileId) }, - likeAction: component.slice.peer.isService ? nil : { [weak self] in + likeAction: component.slice.effectivePeer.isService ? nil : { [weak self] in guard let self else { return } @@ -3018,7 +3018,7 @@ public final class StoryItemSetContainerComponent: Component { } let rect = view.convert(view.bounds, to: nil) let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - let text = presentationData.strings.Conversation_VoiceMessagesRestricted(component.slice.peer.compactDisplayTitle).string + let text = presentationData.strings.Conversation_VoiceMessagesRestricted(component.slice.effectivePeer.compactDisplayTitle).string let controller = TooltipController(content: .text(text), baseFontSize: presentationData.listsFontSize.baseDisplaySize, isBlurred: true, padding: 2.0) controller.dismissed = { [weak self] _ in if let self { @@ -3176,9 +3176,9 @@ public final class StoryItemSetContainerComponent: Component { var validViewListIds: [Int32] = [] var displayViewLists = false - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { displayViewLists = true - } else if case let .channel(channel) = component.slice.peer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats { + } else if case let .channel(channel) = component.slice.effectivePeer, channel.flags.contains(.isCreator) || component.slice.additionalPeerData.canViewStats { displayViewLists = true } @@ -3210,15 +3210,15 @@ public final class StoryItemSetContainerComponent: Component { } for (id, views) in preloadViewListIds { - if component.sharedViewListsContext.viewLists[StoryId(peerId: component.slice.peer.id, id: id)] == nil { + if component.sharedViewListsContext.viewLists[StoryId(peerId: component.slice.effectivePeer.id, id: id)] == nil { let defaultSortMode: EngineStoryViewListContext.SortMode - if component.slice.peer.id.isGroupOrChannel { + if component.slice.effectivePeer.id.isGroupOrChannel { defaultSortMode = .repostsFirst } else { defaultSortMode = .reactionsFirst } - let viewList = component.context.engine.messages.storyViewList(peerId: component.slice.peer.id, id: id, views: views, listMode: .everyone, sortMode: defaultSortMode) - component.sharedViewListsContext.viewLists[StoryId(peerId: component.slice.peer.id, id: id)] = viewList + let viewList = component.context.engine.messages.storyViewList(peerId: component.slice.effectivePeer.id, id: id, views: views, listMode: .everyone, sortMode: defaultSortMode) + component.sharedViewListsContext.viewLists[StoryId(peerId: component.slice.effectivePeer.id, id: id)] = viewList } } @@ -3304,7 +3304,7 @@ public final class StoryItemSetContainerComponent: Component { safeInsets.bottom = max(safeInsets.bottom, component.inputHeight) var hasPremium = false - if case let .user(user) = component.slice.peer { + if case let .user(user) = component.slice.effectivePeer { hasPremium = user.isPremium } @@ -3320,7 +3320,7 @@ public final class StoryItemSetContainerComponent: Component { theme: component.theme, strings: component.strings, sharedListsContext: component.sharedViewListsContext, - peerId: component.slice.peer.id, + peerId: component.slice.effectivePeer.id, safeInsets: safeInsets, storyItem: item.storyItem, hasPremium: hasPremium, @@ -3438,7 +3438,7 @@ public final class StoryItemSetContainerComponent: Component { action: { _ in return false } ), nil) }))) - } else { + } else if isContact { itemList.append(.action(ContextMenuActionItem(text: component.strings.Story_ContextHideStoriesFrom(peer.compactDisplayTitle).string, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Stories"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in @@ -3903,14 +3903,14 @@ public final class StoryItemSetContainerComponent: Component { } let storyPrivacyIcon: StoryPrivacyIconComponent.Privacy? - if case .user = component.slice.peer { + if case .user = component.slice.effectivePeer { if component.slice.item.storyItem.isCloseFriends { storyPrivacyIcon = .closeFriends } else if component.slice.item.storyItem.isContacts { storyPrivacyIcon = .contacts } else if component.slice.item.storyItem.isSelectedContacts { storyPrivacyIcon = .selectedContacts - } else if component.slice.peer.id == component.context.account.peerId { + } else if component.slice.effectivePeer.id == component.context.account.peerId { storyPrivacyIcon = .everyone } else { storyPrivacyIcon = nil @@ -3935,7 +3935,7 @@ public final class StoryItemSetContainerComponent: Component { content: AnyComponent( StoryPrivacyIconComponent( privacy: storyPrivacyIcon, - isEditable: component.slice.peer.id == component.context.account.peerId + isEditable: component.slice.effectivePeer.id == component.context.account.peerId ) ), effectAlignment: .center, @@ -3943,7 +3943,7 @@ public final class StoryItemSetContainerComponent: Component { guard let self, let component = self.component else { return } - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { self.openItemPrivacySettings() return } @@ -3953,11 +3953,11 @@ public final class StoryItemSetContainerComponent: Component { let tooltipText: String switch storyPrivacyIcon { case .closeFriends: - tooltipText = component.strings.Story_TooltipPrivacyCloseFriends2(component.slice.peer.compactDisplayTitle).string + tooltipText = component.strings.Story_TooltipPrivacyCloseFriends2(component.slice.effectivePeer.compactDisplayTitle).string case .contacts: - tooltipText = component.strings.Story_TooltipPrivacyContacts(component.slice.peer.compactDisplayTitle).string + tooltipText = component.strings.Story_TooltipPrivacyContacts(component.slice.effectivePeer.compactDisplayTitle).string case .selectedContacts: - tooltipText = component.strings.Story_TooltipPrivacySelectedContacts(component.slice.peer.compactDisplayTitle).string + tooltipText = component.strings.Story_TooltipPrivacySelectedContacts(component.slice.effectivePeer.compactDisplayTitle).string case .everyone: tooltipText = "" } @@ -4011,7 +4011,7 @@ public final class StoryItemSetContainerComponent: Component { var currentLeftInfoItem: InfoItem? if focusedItem != nil { - let leftInfoComponent = AnyComponent(StoryAvatarInfoComponent(context: component.context, peer: component.slice.peer)) + let leftInfoComponent = AnyComponent(StoryAvatarInfoComponent(context: component.context, peer: component.slice.effectivePeer)) if let leftInfoItem = self.leftInfoItem, leftInfoItem.component == leftInfoComponent { currentLeftInfoItem = leftInfoItem } else { @@ -4042,7 +4042,7 @@ public final class StoryItemSetContainerComponent: Component { let centerInfoComponent = AnyComponent(StoryAuthorInfoComponent( context: component.context, strings: component.strings, - peer: component.slice.peer, + peer: component.slice.effectivePeer, forwardInfo: component.slice.item.storyItem.forwardInfo, author: component.slice.item.storyItem.author, timestamp: component.slice.item.storyItem.timestamp, @@ -4085,10 +4085,10 @@ public final class StoryItemSetContainerComponent: Component { self.navigateToPeer(peer: author, chat: false) } } else { - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { self.navigateToMyStories() } else { - self.navigateToPeer(peer: component.slice.peer, chat: false) + self.navigateToPeer(peer: component.slice.effectivePeer, chat: false) } } })), @@ -4120,10 +4120,10 @@ public final class StoryItemSetContainerComponent: Component { guard let self, let component = self.component else { return } - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { self.navigateToMyStories() } else { - self.navigateToPeer(peer: component.slice.peer, chat: false) + self.navigateToPeer(peer: component.slice.effectivePeer, chat: false) } })), environment: {}, @@ -4157,7 +4157,7 @@ public final class StoryItemSetContainerComponent: Component { if let inputPanelSize { let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize) inputPanelFrameValue = inputPanelFrame - var inputPanelAlpha: CGFloat = (component.slice.peer.id == component.context.account.peerId || component.hideUI || self.isEditingStory || component.slice.item.storyItem.isPending) ? 0.0 : 1.0 + var inputPanelAlpha: CGFloat = (component.slice.effectivePeer.id == component.context.account.peerId || component.hideUI || self.isEditingStory || component.slice.item.storyItem.isPending) ? 0.0 : 1.0 if case .regular = component.metrics.widthClass { inputPanelAlpha *= component.visibilityFraction } @@ -4169,7 +4169,7 @@ public final class StoryItemSetContainerComponent: Component { } var inputPanelOffset: CGFloat = 0.0 - if component.slice.peer.id != component.context.account.peerId && !self.inputPanelExternalState.isEditing { + if component.slice.effectivePeer.id != component.context.account.peerId && !self.inputPanelExternalState.isEditing { let bandingOffset = scrollingRubberBandingOffset(offset: verticalPanFraction * availableSize.height, bandingStart: 0.0, range: 10.0) inputPanelOffset = -max(0.0, min(10.0, bandingOffset)) } @@ -4203,8 +4203,8 @@ public final class StoryItemSetContainerComponent: Component { } var enableEntities = true - if case .user = component.slice.peer { - if !component.slice.peer.isService && !component.slice.peer.isPremium { + if case .user = component.slice.effectivePeer { + if !component.slice.effectivePeer.isService && !component.slice.effectivePeer.isPremium { enableEntities = false } } @@ -4220,7 +4220,7 @@ public final class StoryItemSetContainerComponent: Component { strings: component.strings, theme: component.theme, text: component.slice.item.storyItem.text, - author: component.slice.peer, + author: component.slice.effectivePeer, forwardInfo: component.slice.item.storyItem.forwardInfo, forwardInfoStory: forwardInfoStory, entities: enableEntities ? component.slice.item.storyItem.entities : [], @@ -4231,7 +4231,7 @@ public final class StoryItemSetContainerComponent: Component { } switch action { case let .url(url, concealed): - let _ = openUserGeneratedUrl(context: component.context, peerId: component.slice.peer.id, url: url, concealed: concealed, skipUrlAuth: false, skipConcealedAlert: false, forceDark: true, present: { [weak self] c in + let _ = openUserGeneratedUrl(context: component.context, peerId: component.slice.effectivePeer.id, url: url, concealed: concealed, skipUrlAuth: false, skipConcealedAlert: false, forceDark: true, present: { [weak self] c in guard let self, let component = self.component, let controller = component.controller() else { return } @@ -4265,7 +4265,7 @@ public final class StoryItemSetContainerComponent: Component { return } self.sendMessageContext.presentTextEntityActions(view: self, action: action, openUrl: { [weak self] url, concealed in - let _ = openUserGeneratedUrl(context: component.context, peerId: component.slice.peer.id, url: url, concealed: concealed, skipUrlAuth: false, skipConcealedAlert: false, present: { [weak self] c in + let _ = openUserGeneratedUrl(context: component.context, peerId: component.slice.effectivePeer.id, url: url, concealed: concealed, skipUrlAuth: false, skipConcealedAlert: false, present: { [weak self] c in guard let self, let component = self.component, let controller = component.controller() else { return } @@ -4325,7 +4325,7 @@ public final class StoryItemSetContainerComponent: Component { } if let story { let context = component.context - let peerId = component.slice.peer.id + let peerId = component.slice.effectivePeer.id let currentResult: ResolvedUrl = .story(peerId: peerId, id: component.slice.item.storyItem.id) self.sendMessageContext.openResolved(view: self, result: .story(peerId: peer.id, id: story.id), completion: { [weak self] in @@ -4535,7 +4535,7 @@ public final class StoryItemSetContainerComponent: Component { if self.displayLikeReactions { if component.slice.item.storyItem.myReaction == updateReaction.reaction { - let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id, reaction: nil).startStandalone() + let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id, reaction: nil).startStandalone() self.displayLikeReactions = false self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) } else { @@ -4546,7 +4546,7 @@ public final class StoryItemSetContainerComponent: Component { self.state?.updated(transition: Transition(animation: .curve(duration: 0.25, curve: .easeInOut))) self.waitingForReactionAnimateOutToLike = updateReaction.reaction - let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id, reaction: updateReaction.reaction).startStandalone() + let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id, reaction: updateReaction.reaction).startStandalone() } } else { let _ = (component.context.engine.stickers.availableReactions() @@ -4630,7 +4630,7 @@ public final class StoryItemSetContainerComponent: Component { mediaReference: nil, threadId: nil, replyToMessageId: nil, - replyToStoryId: StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id), + replyToStoryId: StoryId(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id), localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [] @@ -4639,7 +4639,7 @@ public final class StoryItemSetContainerComponent: Component { let context = component.context let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) let presentController = component.presentController - let peer = component.slice.peer + let peer = component.slice.effectivePeer let _ = (enqueueMessages(account: context.account, peerId: peer.id, messages: [message]) |> deliverOnMainQueue).startStandalone(next: { [weak self] messageIds in @@ -4938,7 +4938,7 @@ public final class StoryItemSetContainerComponent: Component { } component.externalState.derivedMediaSize = contentFrame.size - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { component.externalState.derivedBottomInset = availableSize.height - itemsContainerFrame.maxY } else if let inputPanelFrameValue { component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrameValue.minY, contentFrame.maxY) @@ -5291,7 +5291,7 @@ public final class StoryItemSetContainerComponent: Component { } let context = component.context - let storyContent = RepostStoriesContentContextImpl(context: context, originalPeerId: component.slice.peer.id, originalStory: component.slice.item.storyItem, focusedStoryId: StoryId(peerId: peer.id, id: id), viewListContext: viewListContext, readGlobally: false) + let storyContent = RepostStoriesContentContextImpl(context: context, originalPeerId: component.slice.effectivePeer.id, originalStory: component.slice.item.storyItem, focusedStoryId: StoryId(peerId: peer.id, id: id), viewListContext: viewListContext, readGlobally: false) let _ = (storyContent.state |> take(1) |> deliverOnMainQueue).startStandalone(next: { [weak controller, weak viewListView, weak sourceView] _ in @@ -5374,7 +5374,7 @@ public final class StoryItemSetContainerComponent: Component { guard let controller = MediaEditorScreen.makeEditStoryController( context: component.context, - peer: component.slice.peer, + peer: component.slice.effectivePeer, storyItem: component.slice.item.storyItem, videoPlaybackPosition: videoPlaybackPosition, repost: repost, @@ -5497,7 +5497,7 @@ public final class StoryItemSetContainerComponent: Component { HapticFeedback().impact() let _ = combineLatest(queue: Queue.mainQueue(), - component.context.engine.peers.getChannelBoostStatus(peerId: component.slice.peer.id), + component.context.engine.peers.getChannelBoostStatus(peerId: component.slice.effectivePeer.id), component.context.engine.peers.getMyBoostStatus() ).startStandalone(next: { [weak self] boostStatus, myBoostStatus in guard let self, let component = self.component, let boostStatus, let myBoostStatus else { @@ -5505,7 +5505,7 @@ public final class StoryItemSetContainerComponent: Component { } let boostController = PremiumBoostLevelsScreen( context: component.context, - peerId: component.slice.peer.id, + peerId: component.slice.effectivePeer.id, mode: .user(mode: .unrestrict(Int(boostsToUnrestrict))), status: boostStatus, myBoostStatus: myBoostStatus, @@ -5586,7 +5586,7 @@ public final class StoryItemSetContainerComponent: Component { } private func requestSave() { - guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else { + guard let component = self.component, let peerReference = PeerReference(component.slice.effectivePeer._asPeer()) else { return } @@ -5625,9 +5625,9 @@ public final class StoryItemSetContainerComponent: Component { guard let component = self.component else { return } - if component.slice.peer.id == component.context.account.peerId { + if component.slice.effectivePeer.id == component.context.account.peerId { self.performMyMoreAction(sourceView: sourceView, gesture: gesture) - } else if case let .channel(channel) = component.slice.peer { + } else if case let .channel(channel) = component.slice.effectivePeer { var canPerformStoryActions = false if channel.hasPermission(.editStories) { @@ -5681,7 +5681,7 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id, reaction: component.slice.item.storyItem.myReaction == nil ? .builtin("❤") : nil).startStandalone() + let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id, reaction: component.slice.item.storyItem.myReaction == nil ? .builtin("❤") : nil).startStandalone() if component.slice.item.storyItem.myReaction != nil { return @@ -5808,7 +5808,7 @@ public final class StoryItemSetContainerComponent: Component { } } - if !emojiFileIds.isEmpty || hasLinkedStickers, let peerReference = PeerReference(component.slice.peer._asPeer()) { + if !emojiFileIds.isEmpty || hasLinkedStickers, let peerReference = PeerReference(component.slice.effectivePeer._asPeer()) { let context = component.context tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil) @@ -6081,7 +6081,7 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.peer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone() + let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.effectivePeer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone() let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) if component.slice.item.storyItem.isPinned { @@ -6117,7 +6117,7 @@ public final class StoryItemSetContainerComponent: Component { self.requestSave() }))) - if case let .user(accountUser) = component.slice.peer { + if case let .user(accountUser) = component.slice.effectivePeer { items.append(.action(ContextMenuActionItem(text: component.strings.Story_ContextStealthMode, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: accountUser.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6134,7 +6134,7 @@ public final class StoryItemSetContainerComponent: Component { }))) } - if component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { + if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6144,7 +6144,7 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id) + let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id) |> deliverOnMainQueue).startStandalone(next: { [weak self] link in guard let self, let component = self.component else { return @@ -6197,7 +6197,7 @@ public final class StoryItemSetContainerComponent: Component { guard let component = self.component, let controller = component.controller() else { return } - guard case let .channel(channel) = component.slice.peer else { + guard case let .channel(channel) = component.slice.effectivePeer else { return } @@ -6270,10 +6270,10 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.peer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone() + let _ = component.context.engine.messages.updateStoriesArePinned(peerId: component.slice.effectivePeer.id, ids: [component.slice.item.storyItem.id: component.slice.item.storyItem], isPinned: !component.slice.item.storyItem.isPinned).startStandalone() var isGroup = false - if case let .channel(channel) = component.slice.peer, case .group = channel.info { + if case let .channel(channel) = component.slice.effectivePeer, case .group = channel.info { isGroup = true } @@ -6313,7 +6313,7 @@ public final class StoryItemSetContainerComponent: Component { let statsController = component.context.sharedContext.makeStoryStatsController( context: component.context, updatedPresentationData: (presentationData, .single(presentationData)), - peerId: component.slice.peer.id, + peerId: component.slice.effectivePeer.id, storyId: component.slice.item.storyItem.id, storyItem: component.slice.item.storyItem, fromStory: true @@ -6334,7 +6334,7 @@ public final class StoryItemSetContainerComponent: Component { self.requestSave() }))) - if component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { + if component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) && (component.slice.item.storyItem.expirationTimestamp > Int32(Date().timeIntervalSince1970) || component.slice.item.storyItem.isPinned) { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6344,7 +6344,7 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id) + let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id) |> deliverOnMainQueue).startStandalone(next: { [weak self] link in guard let self, let component = self.component else { return @@ -6376,7 +6376,7 @@ public final class StoryItemSetContainerComponent: Component { } var isHidden = false - if case let .channel(channel) = component.slice.peer, let storiesHidden = channel.storiesHidden { + if case let .channel(channel) = component.slice.effectivePeer, let storiesHidden = channel.storiesHidden { isHidden = storiesHidden } items.append(.action(ContextMenuActionItem(text: isHidden ? component.strings.StoryFeed_ContextUnarchive : component.strings.StoryFeed_ContextArchive, icon: { theme in @@ -6388,20 +6388,20 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.peer.id, isHidden: !isHidden) + let _ = component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.effectivePeer.id, isHidden: !isHidden) - let text = !isHidden ? component.strings.StoryFeed_TooltipArchive(component.slice.peer.compactDisplayTitle).string : component.strings.StoryFeed_TooltipUnarchive(component.slice.peer.compactDisplayTitle).string + let text = !isHidden ? component.strings.StoryFeed_TooltipArchive(component.slice.effectivePeer.compactDisplayTitle).string : component.strings.StoryFeed_TooltipUnarchive(component.slice.effectivePeer.compactDisplayTitle).string let tooltipScreen = TooltipScreen( context: component.context, account: component.context.account, sharedContext: component.context.sharedContext, text: .markdown(text: text), style: .customBlur(UIColor(rgb: 0x1c1c1c), 0.0), - icon: .peer(peer: component.slice.peer, isStory: true), + icon: .peer(peer: component.slice.effectivePeer, isStory: true), action: TooltipScreen.Action( title: component.strings.Undo_Undo, action: { - component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.peer.id, isHidden: isHidden) + component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.effectivePeer.id, isHidden: isHidden) } ), location: .bottom, @@ -6503,10 +6503,10 @@ public final class StoryItemSetContainerComponent: Component { let _ = (combineLatest( queue: Queue.mainQueue(), component.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: component.slice.peer.id), + TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: component.slice.effectivePeer.id), TelegramEngine.EngineData.Item.NotificationSettings.Global(), TelegramEngine.EngineData.Item.Contacts.Top(), - TelegramEngine.EngineData.Item.Peer.IsContact(id: component.slice.peer.id), + TelegramEngine.EngineData.Item.Peer.IsContact(id: component.slice.effectivePeer.id), TelegramEngine.EngineData.Item.Peer.Peer(id: component.context.account.peerId) ), translationSettings, @@ -6558,9 +6558,9 @@ public final class StoryItemSetContainerComponent: Component { items.append(.separator) } - let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.peer._asPeer(), peerSettings: settings._asNotificationSettings(), topSearchPeers: topSearchPeers) + let isMuted = resolvedAreStoriesMuted(globalSettings: globalSettings._asGlobalNotificationSettings(), peer: component.slice.effectivePeer._asPeer(), peerSettings: settings._asNotificationSettings(), topSearchPeers: topSearchPeers) - if !component.slice.peer.isService && isContact { + if !component.slice.effectivePeer.isService && isContact { items.append(.action(ContextMenuActionItem(text: isMuted ? component.strings.StoryFeed_ContextNotifyOn : component.strings.StoryFeed_ContextNotifyOff, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: component.slice.additionalPeerData.isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6570,7 +6570,7 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = component.context.engine.peers.togglePeerStoriesMuted(peerId: component.slice.peer.id).startStandalone() + let _ = component.context.engine.peers.togglePeerStoriesMuted(peerId: component.slice.effectivePeer.id).startStandalone() let iconColor = UIColor.white let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) @@ -6583,7 +6583,7 @@ public final class StoryItemSetContainerComponent: Component { "Bottom.Group 1.Fill 1": iconColor, "EXAMPLE.Group 1.Fill 1": iconColor, "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: component.strings.StoryFeed_TooltipNotifyOn(component.slice.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil), + ], title: nil, text: component.strings.StoryFeed_TooltipNotifyOn(component.slice.effectivePeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, blurred: true, @@ -6598,7 +6598,7 @@ public final class StoryItemSetContainerComponent: Component { "Bottom.Group 1.Fill 1": iconColor, "EXAMPLE.Group 1.Fill 1": iconColor, "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: component.strings.StoryFeed_TooltipNotifyOff(component.slice.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil), + ], title: nil, text: component.strings.StoryFeed_TooltipNotifyOff(component.slice.effectivePeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, blurred: true, @@ -6608,7 +6608,7 @@ public final class StoryItemSetContainerComponent: Component { }))) } - if !component.slice.peer.isService && component.slice.item.storyItem.isPublic && (component.slice.peer.addressName != nil || !component.slice.peer._asPeer().usernames.isEmpty) { + if !component.slice.effectivePeer.isService && component.slice.item.storyItem.isPublic && (component.slice.effectivePeer.addressName != nil || !component.slice.effectivePeer._asPeer().usernames.isEmpty) { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_CopyLink, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6618,7 +6618,7 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id) + let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id) |> deliverOnMainQueue).startStandalone(next: { [weak self] link in guard let self, let component = self.component else { return @@ -6688,9 +6688,9 @@ public final class StoryItemSetContainerComponent: Component { } var isHidden = false - if case let .user(user) = component.slice.peer, let storiesHidden = user.storiesHidden { + if case let .user(user) = component.slice.effectivePeer, let storiesHidden = user.storiesHidden { isHidden = storiesHidden - } else if case let .channel(channel) = component.slice.peer, let storiesHidden = channel.storiesHidden { + } else if case let .channel(channel) = component.slice.effectivePeer, let storiesHidden = channel.storiesHidden { isHidden = storiesHidden } @@ -6698,9 +6698,9 @@ public final class StoryItemSetContainerComponent: Component { if isHidden { canArchive = true } else { - if case .user = component.slice.peer, !component.slice.peer.isService { + if case .user = component.slice.effectivePeer, !component.slice.effectivePeer.isService { canArchive = true - } else if case .channel = component.slice.peer { + } else if case .channel = component.slice.effectivePeer { canArchive = true } } @@ -6715,20 +6715,20 @@ public final class StoryItemSetContainerComponent: Component { return } - let _ = component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.peer.id, isHidden: !isHidden) + let _ = component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.effectivePeer.id, isHidden: !isHidden) - let text = !isHidden ? component.strings.StoryFeed_TooltipArchive(component.slice.peer.compactDisplayTitle).string : component.strings.StoryFeed_TooltipUnarchive(component.slice.peer.compactDisplayTitle).string + let text = !isHidden ? component.strings.StoryFeed_TooltipArchive(component.slice.effectivePeer.compactDisplayTitle).string : component.strings.StoryFeed_TooltipUnarchive(component.slice.effectivePeer.compactDisplayTitle).string let tooltipScreen = TooltipScreen( context: component.context, account: component.context.account, sharedContext: component.context.sharedContext, text: .markdown(text: text), style: .customBlur(UIColor(rgb: 0x1c1c1c), 0.0), - icon: .peer(peer: component.slice.peer, isStory: true), + icon: .peer(peer: component.slice.effectivePeer, isStory: true), action: TooltipScreen.Action( title: component.strings.Undo_Undo, action: { - component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.peer.id, isHidden: isHidden) + component.context.engine.peers.updatePeerStoriesHidden(id: component.slice.effectivePeer.id, isHidden: isHidden) } ), location: .bottom, @@ -6767,7 +6767,7 @@ public final class StoryItemSetContainerComponent: Component { }))) } - if case .user = component.slice.peer { + if case .user = component.slice.effectivePeer { items.append(.action(ContextMenuActionItem(text: component.strings.Story_ContextStealthMode, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: accountUser.isPremium ? "Chat/Context Menu/Eye" : "Chat/Context Menu/EyeLocked"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, a in @@ -6797,7 +6797,7 @@ public final class StoryItemSetContainerComponent: Component { let statsController = component.context.sharedContext.makeStoryStatsController( context: component.context, updatedPresentationData: (presentationData, .single(presentationData)), - peerId: component.slice.peer.id, + peerId: component.slice.effectivePeer.id, storyId: component.slice.item.storyItem.id, storyItem: component.slice.item.storyItem, fromStory: true @@ -6822,7 +6822,7 @@ public final class StoryItemSetContainerComponent: Component { } } - if !component.slice.peer.isService { + if !component.slice.effectivePeer.isService { items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_Report, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, a in @@ -6836,7 +6836,7 @@ public final class StoryItemSetContainerComponent: Component { parent: controller, contextController: c, backAction: { _ in }, - subject: .story(component.slice.peer.id, component.slice.item.storyItem.id), + subject: .story(component.slice.effectivePeer.id, component.slice.item.storyItem.id), options: options, passthrough: true, forceTheme: defaultDarkPresentationTheme, @@ -6851,7 +6851,7 @@ public final class StoryItemSetContainerComponent: Component { guard let self, let component = self.component, let controller = component.controller(), let reason else { return } - let _ = component.context.engine.peers.reportPeerStory(peerId: component.slice.peer.id, storyId: component.slice.item.storyItem.id, reason: reason, message: "").startStandalone() + let _ = component.context.engine.peers.reportPeerStory(peerId: component.slice.effectivePeer.id, storyId: component.slice.item.storyItem.id, reason: reason, message: "").startStandalone() controller.present( UndoOverlayController( presentationData: presentationData, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 8abf1537a3..9e054777a8 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -571,7 +571,7 @@ final class StoryItemSetContainerSendMessage { guard let inputPanelView = view.inputPanel.view as? MessageInputPanelComponent.View else { return } - let peer = component.slice.peer + let peer = component.slice.effectivePeer let controller = component.controller() as? StoryContainerScreen @@ -638,7 +638,7 @@ final class StoryItemSetContainerSendMessage { return } let focusedStoryId = StoryId(peerId: peerId, id: focusedItem.storyItem.id) - let peer = component.slice.peer + let peer = component.slice.effectivePeer let controller = component.controller() as? StoryContainerScreen @@ -692,7 +692,7 @@ final class StoryItemSetContainerSendMessage { return } let focusedStoryId = StoryId(peerId: peerId, id: focusedItem.storyItem.id) - let peer = component.slice.peer + let peer = component.slice.effectivePeer let controller = component.controller() as? StoryContainerScreen @@ -740,7 +740,7 @@ final class StoryItemSetContainerSendMessage { guard let component = view.component else { return } - let peer = component.slice.peer + let peer = component.slice.effectivePeer let _ = (legacyEnqueueGifMessage(account: component.context.account, data: data) |> deliverOnMainQueue).start(next: { [weak self, weak view] message in if let self, let view { self.sendMessages(view: view, peer: peer, messages: [message]) @@ -752,7 +752,7 @@ final class StoryItemSetContainerSendMessage { guard let component = view.component else { return } - let peer = component.slice.peer + let peer = component.slice.effectivePeer let size = image.size.aspectFitted(CGSize(width: 512.0, height: 512.0)) @@ -1013,7 +1013,7 @@ final class StoryItemSetContainerSendMessage { component.presentController(actionSheet, nil) } else { var preferredAction: ShareControllerPreferredAction? - if focusedItem.storyItem.isPublic && !component.slice.peer.isService { + if focusedItem.storyItem.isPublic && !component.slice.effectivePeer.isService { preferredAction = .custom(action: ShareControllerAction(title: component.strings.Story_Context_CopyLink, action: { let _ = ((component.context.engine.messages.exportStoryLink(peerId: peerId, id: focusedItem.storyItem.id)) |> deliverOnMainQueue).start(next: { link in @@ -1170,7 +1170,7 @@ final class StoryItemSetContainerSendMessage { guard let self, let view else { return } - let peer = component.slice.peer + let peer = component.slice.effectivePeer let _ = self @@ -1260,7 +1260,7 @@ final class StoryItemSetContainerSendMessage { return } - let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id) + let _ = (component.context.engine.messages.exportStoryLink(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id) |> deliverOnMainQueue).start(next: { [weak view] link in guard let view, let component = view.component else { return @@ -2214,7 +2214,7 @@ final class StoryItemSetContainerSendMessage { inputText = text } - let peer = component.slice.peer + let peer = component.slice.effectivePeer let theme = defaultDarkPresentationTheme let updatedPresentationData: (initial: PresentationData, signal: Signal) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }) let controller = mediaPasteboardScreen( @@ -2283,7 +2283,7 @@ final class StoryItemSetContainerSendMessage { return } let context = component.context - let peer = component.slice.peer + let peer = component.slice.effectivePeer let storyId = component.slice.item.storyItem.id let theme = component.theme @@ -2669,7 +2669,7 @@ final class StoryItemSetContainerSendMessage { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme) let updatedPresentationData: (PresentationData, Signal) = (presentationData, .single(presentationData)) - let peerId = component.slice.peer.id + let peerId = component.slice.effectivePeer.id component.context.sharedContext.openResolvedUrl( result, context: component.context, @@ -2851,7 +2851,7 @@ final class StoryItemSetContainerSendMessage { return } - let peerId = component.slice.peer.id + let peerId = component.slice.effectivePeer.id var resolveSignal: Signal if let peerName = peerName { @@ -2918,13 +2918,13 @@ final class StoryItemSetContainerSendMessage { return } if !hashtag.isEmpty { - /*if !"".isEmpty { + if peerName == nil { let searchController = component.context.sharedContext.makeStorySearchController(context: component.context, query: hashtag) navigationController.pushViewController(searchController) - } else {*/ + } else { let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true) navigationController.pushViewController(searchController) - //} + } } })) } @@ -3485,8 +3485,8 @@ final class StoryItemSetContainerSendMessage { guard let view, let component = view.component else { return } - if component.slice.peer.id != component.context.account.peerId { - let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id, reaction: reaction).start() + if component.slice.effectivePeer.id != component.context.account.peerId { + let _ = component.context.engine.messages.setStoryReaction(peerId: component.slice.effectivePeer.id, id: component.slice.item.storyItem.id, reaction: reaction).start() } let targetFrame = reactionView.convert(reactionView.bounds, to: view) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 892682cd50..d68c4f8bf0 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -1779,7 +1779,7 @@ extension ChatControllerImpl { }) } } else { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil) }) }, completion: { t in + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }) }, completion: { t in completion(t, {}) }) } @@ -2497,7 +2497,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } }) } }, nil) @@ -2521,7 +2521,7 @@ extension ChatControllerImpl { } self.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } }) if !self.presentationInterfaceState.isPremium { @@ -2537,7 +2537,7 @@ extension ChatControllerImpl { return } self.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } }) }, nil) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift index 9217def435..143746e193 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift @@ -209,7 +209,7 @@ extension ChatControllerImpl { self.chatDisplayNode.collapseInput() self.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedMediaDraftState(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedMediaDraftState(nil) } }) } }, usedCorrelationId ? correlationId : nil) @@ -336,7 +336,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, usedCorrelationId ? correlationId : nil) @@ -506,7 +506,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedMediaDraftState(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedMediaDraftState(nil).withUpdatedSendMessageEffect(nil) } }) strongSelf.updateDownButtonVisibility() diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerPaste.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerPaste.swift index 937c739c15..88a9c0d8aa 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerPaste.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerPaste.swift @@ -49,7 +49,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -67,7 +67,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -97,7 +97,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -115,7 +115,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index 179f566e64..e755d19825 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -471,7 +471,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch } var replySubject = replySubject replySubject.quote = nil - selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withoutSelectionState() }).updatedSearch(nil) }) + selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withoutSelectionState() }).updatedSearch(nil) }) }))) } @@ -646,7 +646,7 @@ func moveReplyMessageToAnotherChat(selfController: ChatControllerImpl, replySubj guard let selfController else { return } - selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withoutSelectionState() }) }) + selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withoutSelectionState() }) }) let navigationController: NavigationController? if let parentController = selfController.parentController { diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift index 733dc9cbe1..a28d7590b5 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageDisplaySendMessageOptions.swift @@ -64,14 +64,22 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no editMessages = .single([]) } + var currentMessageEffect: ChatSendMessageActionSheetControllerSendParameters.Effect? + if selfController.presentationInterfaceState.interfaceState.editMessage == nil { + if let sendMessageEffect = selfController.presentationInterfaceState.interfaceState.sendMessageEffect { + currentMessageEffect = ChatSendMessageActionSheetControllerSendParameters.Effect(id: sendMessageEffect) + } + } + let _ = (combineLatest( selfController.context.account.viewTracker.peerView(peerId) |> take(1), effectItems, availableMessageEffects, hasPremium, - editMessages + editMessages, + ChatSendMessageContextScreen.initialData(context: selfController.context, currentMessageEffectId: currentMessageEffect?.id) ) - |> deliverOnMainQueue).startStandalone(next: { [weak selfController] peerView, effectItems, availableMessageEffects, hasPremium, editMessages in + |> deliverOnMainQueue).startStandalone(next: { [weak selfController] peerView, effectItems, availableMessageEffects, hasPremium, editMessages, initialData in guard let selfController, let peer = peerViewMainPeer(peerView) else { return } @@ -112,6 +120,7 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no } let controller = makeChatSendMessageActionSheetController( + initialData: initialData, context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, peerId: selfController.presentationInterfaceState.chatLocation.peerId, @@ -200,6 +209,7 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no } let controller = makeChatSendMessageActionSheetController( + initialData: initialData, context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, peerId: selfController.presentationInterfaceState.chatLocation.peerId, @@ -207,6 +217,16 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no isScheduledMessages: false, mediaPreview: mediaPreview, mediaCaptionIsAbove: nil, + messageEffect: (currentMessageEffect, { [weak selfController] updatedEffect in + guard let selfController else { + return + } + selfController.updateChatPresentationInterfaceState(transition: .immediate, interactive: true, { presentationInterfaceState in + return presentationInterfaceState.updatedInterfaceState { interfaceState in + return interfaceState.withUpdatedSendMessageEffect(updatedEffect?.id) + } + }) + }), attachment: false, canSendWhenOnline: sendWhenOnlineAvailable, forwardMessageIds: selfController.presentationInterfaceState.interfaceState.forwardMessageIds ?? [] @@ -238,7 +258,7 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no return } selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: selfController.presentationInterfaceState.subject != .scheduledMessages, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) } }) selfController.openScheduledMessages() } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6b82bad4f4..7fc2521879 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1792,7 +1792,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -1850,7 +1850,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var current = current current = current.updatedInterfaceState { interfaceState in var interfaceState = interfaceState - interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil) + interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) if clearInput { interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString())) } @@ -1982,7 +1982,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) }.updatedInputMode { current in + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }.updatedInputMode { current in if case let .media(mode, maybeExpanded, focused) = current, maybeExpanded != nil { return .media(mode: mode, expanded: nil, focused: focused) } @@ -2426,7 +2426,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))).withUpdatedComposeDisableUrlPreviews([]) } }) } }, nil) @@ -3390,7 +3390,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) { [weak self] in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: strongSelf.presentationInterfaceState.subject != .scheduledMessages, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) } }) if strongSelf.presentationInterfaceState.subject != .scheduledMessages && time != scheduleWhenOnlineTimestamp { @@ -3572,7 +3572,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G completion(nil) } } else { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil) }) }, completion: completion) + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) }) }, completion: completion) } } }, displayImportedMessageTooltip: { [weak self] _ in @@ -7236,7 +7236,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G chatLocation = .peer(id: peerId) } - return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, context: context, chatLocation: chatLocation, chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tag: .tag(MessageTags.pinned), appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) + return (chatHistoryViewForLocation(ChatHistoryLocationInput(content: location, id: 0), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), context: context, chatLocation: chatLocation, chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tag: .tag(MessageTags.pinned), appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -8103,7 +8103,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage) if case .peer = self.chatLocation, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.flags.contains(.isForum) { - interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState()).withUpdatedReplyMessageSubject(nil) + interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState()).withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in return interfaceState @@ -8881,7 +8881,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -9135,6 +9135,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G attributes.append(SendAsMessageAttribute(peerId: sendAsPeerId)) } } + if let sendMessageEffect = self.presentationInterfaceState.interfaceState.sendMessageEffect { + if attributes.first(where: { $0 is EffectMessageAttribute }) == nil { + attributes.append(EffectMessageAttribute(id: sendMessageEffect)) + } + } return attributes } } @@ -9304,7 +9309,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } completionImpl?() @@ -9348,7 +9353,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if resetTextInputState { state = state.updatedInterfaceState { interfaceState in var interfaceState = interfaceState - interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil) + interfaceState = interfaceState.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) interfaceState = interfaceState.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) interfaceState = interfaceState.withUpdatedComposeDisableUrlPreviews([]) return interfaceState diff --git a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift index 642feb20c2..e1e8b439f0 100644 --- a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift +++ b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift @@ -319,88 +319,31 @@ extension ChatControllerImpl { )) }) })) + } + + func beginDeleteMessagesWithUndo(messageIds: Set, type: InteractiveMessagesDeletionType) { + self.chatDisplayNode.historyNode.ignoreMessageIds = Set(messageIds) - /*do { - self.navigationActionDisposable.set((self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id) - |> deliverOnMainQueue).startStrict(next: { - if let strongSelf = self { - if "".isEmpty { - - return - } - - let canBan = participant?.canBeBannedBy(peerId: accountPeerId) ?? true - - let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) - var items: [ActionSheetItem] = [] - - var actions = Set([0]) - - let toggleCheck: (Int, Int) -> Void = { [weak actionSheet] category, itemIndex in - if actions.contains(category) { - actions.remove(category) - } else { - actions.insert(category) - } - actionSheet?.updateItem(groupIndex: 0, itemIndex: itemIndex, { item in - if let item = item as? ActionSheetCheckboxItem { - return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action) - } - return item - }) - } - - var itemIndex = 0 - var categories: [Int] = [0] - if canBan { - categories.append(1) - } - categories.append(contentsOf: [2, 3]) - - for categoryId in categories as [Int] { - var title = "" - if categoryId == 0 { - title = strongSelf.presentationData.strings.Conversation_Moderate_Delete - } else if categoryId == 1 { - title = strongSelf.presentationData.strings.Conversation_Moderate_Ban - } else if categoryId == 2 { - title = strongSelf.presentationData.strings.Conversation_Moderate_Report - } else if categoryId == 3 { - title = strongSelf.presentationData.strings.Conversation_Moderate_DeleteAllMessages(EnginePeer(author).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string - } - let index = itemIndex - items.append(ActionSheetCheckboxItem(title: title, label: "", value: actions.contains(categoryId), action: { value in - toggleCheck(categoryId, index) - })) - itemIndex += 1 - } - - items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Done, action: { [weak self, weak actionSheet] in - actionSheet?.dismissAnimated() - if let strongSelf = self { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - if actions.contains(3) { - let _ = strongSelf.context.engine.messages.deleteAllMessagesWithAuthor(peerId: peerId, authorId: author.id, namespace: Namespaces.Message.Cloud).startStandalone() - let _ = strongSelf.context.engine.messages.clearAuthorHistory(peerId: peerId, memberId: author.id).startStandalone() - } else if actions.contains(0) { - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() - } - if actions.contains(1) { - let _ = strongSelf.context.engine.peers.removePeerMember(peerId: peerId, memberId: author.id).startStandalone() - } - } - })) - - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - strongSelf.chatDisplayNode.dismissInput() - strongSelf.present(actionSheet, in: .window(.root)) - } - })) - }*/ + //TODO:localize + let undoTitle: String + if messageIds.count == 1 { + undoTitle = "Message Deleted" + } else { + undoTitle = "\(messageIds.count) Messages Deleted" + } + self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(title: undoTitle, text: nil), elevatedLayout: false, position: .top, action: { [weak self] value in + guard let self else { + return false + } + if value == .commit { + let _ = self.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: type).startStandalone() + return true + } else if value == .undo { + self.chatDisplayNode.historyNode.ignoreMessageIds = Set() + return true + } + return false + }), in: .current) } func presentDeleteMessageOptions(messageIds: Set, options: ChatAvailableMessageActionOptions, contextController: ContextControllerProtocol?, completion: @escaping (ContextMenuActionResult) -> Void) { @@ -429,7 +372,7 @@ extension ChatControllerImpl { actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() + strongSelf.beginDeleteMessagesWithUndo(messageIds: messageIds, type: .forEveryone) } })) } @@ -466,7 +409,8 @@ extension ChatControllerImpl { } let commit = { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() + + strongSelf.beginDeleteMessagesWithUndo(messageIds: messageIds, type: .forEveryone) } if let giveaway { let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) @@ -501,7 +445,8 @@ extension ChatControllerImpl { actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() + + strongSelf.beginDeleteMessagesWithUndo(messageIds: messageIds, type: .forEveryone) } })) } @@ -534,7 +479,7 @@ extension ChatControllerImpl { guard let strongSelf = self else { return } - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone() + strongSelf.beginDeleteMessagesWithUndo(messageIds: messageIds, type: unsendPersonalMessages ? .forEveryone : .forLocalPeer) } if "".isEmpty { @@ -553,8 +498,8 @@ extension ChatControllerImpl { actionSheet?.dismissAnimated() if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState { $0.withoutSelectionState() } }) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: unsendPersonalMessages ? .forEveryone : .forLocalPeer).startStandalone() + strongSelf.beginDeleteMessagesWithUndo(messageIds: messageIds, type: unsendPersonalMessages ? .forEveryone : .forLocalPeer) } })) } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index fac4e5e20b..88dc0f482a 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2705,6 +2705,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { return chatHistoryViewForLocation( input, ignoreMessagesInTimestampRange: nil, + ignoreMessageIds: Set(), context: context, chatLocation: chatLocation, chatLocationContextHolder: Atomic(value: nil), @@ -4123,7 +4124,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { strongSelf.ignoreUpdateHeight = true textInputPanelNode.text = "" - strongSelf.requestUpdateChatInterfaceState(.immediate, true, { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeDisableUrlPreviews([]) }) + strongSelf.requestUpdateChatInterfaceState(.immediate, true, { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeDisableUrlPreviews([]) }) strongSelf.ignoreUpdateHeight = false } }, usedCorrelationId) diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index a056f61a5c..4d69df2e70 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -384,7 +384,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -448,7 +448,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -520,7 +520,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -551,7 +551,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -616,7 +616,7 @@ extension ChatControllerImpl { controller.completion = { [weak self] in if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() } @@ -1116,7 +1116,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -1491,7 +1491,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -1540,7 +1540,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -1601,7 +1601,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) @@ -1621,7 +1621,7 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.collapseInput() strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil) } + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil) } }) } }, nil) diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift index d1445389c0..3fa030cf7b 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageShareMenu.swift @@ -31,7 +31,7 @@ func chatShareToSavedMessagesAdditionalView(_ chatController: ChatControllerImpl return } - let _ = (chatController.context.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: chatController.context.account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, count: 45, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) + let _ = (chatController.context.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: chatController.context.account.peerId, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 45, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) |> map { view, _, _ -> [EngineMessage.Id] in let messageIds = correlationIds.compactMap { correlationId in return chatController.context.engine.messages.synchronouslyLookupCorrelationId(correlationId: correlationId) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 16cdfd2f62..da9dab3e5f 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -83,6 +83,7 @@ struct ChatHistoryView { let id: Int32 let locationInput: ChatHistoryLocationInput? let ignoreMessagesInTimestampRange: ClosedRange? + let ignoreMessageIds: Set } enum ChatHistoryViewTransitionReason { @@ -536,6 +537,15 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } } + private let ignoreMessageIdsPromise = ValuePromise>(Set()) + var ignoreMessageIds: Set = Set() { + didSet { + if self.ignoreMessageIds != oldValue { + self.ignoreMessageIdsPromise.set(self.ignoreMessageIds) + } + } + } + private let chatHasBotsPromise = ValuePromise(false) var chatHasBots: Bool = false { didSet { @@ -1263,7 +1273,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let currentViewVersion = self.currentViewVersion - let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?), NoError> + let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?, Set), NoError> var isFirstTime = true var updateAllOnEachVersion = false if case let .custom(messages, at, quote, _) = self.source { @@ -1286,12 +1296,13 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto scrollPosition = nil } - return (ChatHistoryViewUpdate.HistoryView(view: MessageHistoryView(tag: nil, namespaces: .all, entries: messages.reversed().map { MessageHistoryEntry(message: $0, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)) }, holeEarlier: hasMore, holeLater: false, isLoading: false), type: .Generic(type: version > 0 ? ViewUpdateType.Generic : ViewUpdateType.Initial), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: nil, buttonKeyboardMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil), id: 0), version, nil, nil) + return (ChatHistoryViewUpdate.HistoryView(view: MessageHistoryView(tag: nil, namespaces: .all, entries: messages.reversed().map { MessageHistoryEntry(message: $0, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)) }, holeEarlier: hasMore, holeLater: false, isLoading: false), type: .Generic(type: version > 0 ? ViewUpdateType.Generic : ViewUpdateType.Initial), scrollPosition: scrollPosition, flashIndicators: false, originalScrollPosition: nil, initialData: ChatHistoryCombinedInitialData(initialData: nil, buttonKeyboardMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil), id: 0), version, nil, nil, Set()) } } else if case let .customView(historyView) = self.source { historyViewUpdate = combineLatest(queue: .mainQueue(), self.chatHistoryLocationPromise.get(), - self.ignoreMessagesInTimestampRangePromise.get() + self.ignoreMessagesInTimestampRangePromise.get(), + self.ignoreMessageIdsPromise.get() ) |> distinctUntilChanged(isEqual: { lhs, rhs in if lhs.0 != rhs.0 { @@ -1300,9 +1311,12 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto if lhs.1 != rhs.1 { return false } + if lhs.2 != rhs.2 { + return false + } return true }) - |> mapToSignal { location, _ -> Signal<((MessageHistoryView, ViewUpdateType), ChatHistoryLocationInput?), NoError> in + |> mapToSignal { location, _, _ -> Signal<((MessageHistoryView, ViewUpdateType), ChatHistoryLocationInput?), NoError> in return historyView |> map { historyView in return (historyView, location) @@ -1347,13 +1361,15 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto ), version, location, - nil + nil, + Set() ) } } else { historyViewUpdate = combineLatest(queue: .mainQueue(), self.chatHistoryLocationPromise.get(), - self.ignoreMessagesInTimestampRangePromise.get() + self.ignoreMessagesInTimestampRangePromise.get(), + self.ignoreMessageIdsPromise.get() ) |> distinctUntilChanged(isEqual: { lhs, rhs in if lhs.0 != rhs.0 { @@ -1362,10 +1378,13 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto if lhs.1 != rhs.1 { return false } + if lhs.2 != rhs.2 { + return false + } return true }) - |> mapToSignal { location, ignoreMessagesInTimestampRange in - return chatHistoryViewForLocation(location, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduledMessages, fixedCombinedReadStates: fixedCombinedReadStates.with { $0 }, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, additionalData: additionalData, orderStatistics: [], useRootInterfaceStateForThread: useRootInterfaceStateForThread) + |> mapToSignal { location, ignoreMessagesInTimestampRange, ignoreMessageIds in + return chatHistoryViewForLocation(location, ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduledMessages, fixedCombinedReadStates: fixedCombinedReadStates.with { $0 }, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, additionalData: additionalData, orderStatistics: [], useRootInterfaceStateForThread: useRootInterfaceStateForThread) |> beforeNext { viewUpdate in switch viewUpdate { case let .HistoryView(view, _, _, _, _, _, _): @@ -1374,7 +1393,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto break } } - |> map { view -> (ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?) in + |> map { view -> (ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange?, Set) in let version = currentViewVersion.modify({ value in if let value = value { return value + 1 @@ -1382,7 +1401,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto return 0 } })! - return (view, version, location, ignoreMessagesInTimestampRange) + return (view, version, location, ignoreMessagesInTimestampRange, ignoreMessageIds) } } } @@ -1675,7 +1694,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto if resetScrolling, let previousViewValue = previousView.with({ $0 })?.0 { let filteredEntries: [ChatHistoryEntry] = [] - let processedView = ChatHistoryView(originalView: MessageHistoryView(tag: nil, namespaces: .all, entries: [], holeEarlier: false, holeLater: false, isLoading: true), filteredEntries: filteredEntries, associatedData: previousViewValue.associatedData, lastHeaderId: 0, id: previousViewValue.id, locationInput: previousViewValue.locationInput, ignoreMessagesInTimestampRange: nil) + let processedView = ChatHistoryView(originalView: MessageHistoryView(tag: nil, namespaces: .all, entries: [], holeEarlier: false, holeLater: false, isLoading: true), filteredEntries: filteredEntries, associatedData: previousViewValue.associatedData, lastHeaderId: 0, id: previousViewValue.id, locationInput: previousViewValue.locationInput, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set()) let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages, allAdMessages.version)) let previous = previousValueAndVersion?.0 let previousSelectedMessages = previousValueAndVersion?.2 @@ -1847,7 +1866,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto dynamicAdMessages: allAdMessages.opportunistic ) let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0 - let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2, ignoreMessagesInTimestampRange: update.3) + let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id, locationInput: update.2, ignoreMessagesInTimestampRange: update.3, ignoreMessageIds: update.4) let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages, allAdMessages.version)) let previous = previousValueAndVersion?.0 let previousSelectedMessages = previousValueAndVersion?.2 @@ -1877,6 +1896,9 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } else if let previous = previous, previous.id == processedView.id, previous.originalView.entries == processedView.originalView.entries { reason = ChatHistoryViewTransitionReason.InteractiveChanges updatedScrollPosition = nil + } else if let previous = previous, previous.id == processedView.id, previous.ignoreMessageIds != processedView.ignoreMessageIds { + reason = ChatHistoryViewTransitionReason.InteractiveChanges + updatedScrollPosition = nil } else { switch type { case let .Initial(fadeIn): @@ -3423,6 +3445,14 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } } } + for id in self.ignoreMessageIds { + inner: for (stableId, listId) in maybeRemovedInteractivelyMessageIds { + if listId == id { + expiredMessageStableIds.insert(stableId) + break inner + } + } + } } self.currentDeleteAnimationCorrelationIds.formUnion(expiredMessageStableIds) diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index b1a88c7e93..5f78baacda 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -18,7 +18,7 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, c tag = .tag(.pinned) } - return (chatHistoryViewForLocation(location, ignoreMessagesInTimestampRange: nil, context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduled, fixedCombinedReadStates: fixedCombinedReadStates, tag: tag, appendMessagesFromTheSameGroup: false, additionalData: additionalData, orderStatistics: orderStatistics) + return (chatHistoryViewForLocation(location, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, scheduled: isScheduled, fixedCombinedReadStates: fixedCombinedReadStates, tag: tag, appendMessagesFromTheSameGroup: false, additionalData: additionalData, orderStatistics: orderStatistics) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -36,7 +36,21 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, c |> restartIfError } -func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMessagesInTimestampRange: ClosedRange?, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic, scheduled: Bool, fixedCombinedReadStates: MessageHistoryViewReadState?, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = [], useRootInterfaceStateForThread: Bool = false) -> Signal { +func chatHistoryViewForLocation( + _ location: ChatHistoryLocationInput, + ignoreMessagesInTimestampRange: ClosedRange?, + ignoreMessageIds: Set, + context: AccountContext, + chatLocation: ChatLocation, + chatLocationContextHolder: Atomic, + scheduled: Bool, + fixedCombinedReadStates: MessageHistoryViewReadState?, + tag: HistoryViewInputTag?, + appendMessagesFromTheSameGroup: Bool, + additionalData: [AdditionalMessageHistoryViewData], + orderStatistics: MessageHistoryViewOrderStatistics = [], + useRootInterfaceStateForThread: Bool = false +) -> Signal { let account = context.account if scheduled { var first = true @@ -93,9 +107,9 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess } if requestAroundId { - signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: .upperBound, anchorIndex: .upperBound, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: preFixedReadState, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, index: .upperBound, anchorIndex: .upperBound, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: preFixedReadState, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, useRootInterfaceStateForThread: useRootInterfaceStateForThread) } else { - signal = account.viewTracker.aroundMessageOfInterestHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + signal = account.viewTracker.aroundMessageOfInterestHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) } let isPossibleIntroLoaded: Signal @@ -220,9 +234,9 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess let signal: Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> switch searchLocationSubject.location { case let .index(index): - signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: .message(index), anchorIndex: .message(index), count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: nil, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + signal = account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, index: .message(index), anchorIndex: .message(index), count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: nil, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) case let .id(id): - signal = account.viewTracker.aroundIdMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: id, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + signal = account.viewTracker.aroundIdMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, count: count, ignoreRelatedChats: ignoreRelatedChats, messageId: id, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) } return signal |> map { view, updateType, initialData -> ChatHistoryViewUpdate in @@ -271,7 +285,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess } case let .Navigation(index, anchorIndex, count, _): var first = true - return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: index, anchorIndex: anchorIndex, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in + return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, index: index, anchorIndex: anchorIndex, count: count, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation) let genericType: ViewUpdateType @@ -287,7 +301,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMess let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > subject.index ? .Down : .Up let chatScrollPosition = ChatHistoryViewScrollPosition.index(subject: subject, position: scrollPosition, directionHint: directionHint, animated: animated, highlight: highlight, displayLink: false) var first = true - return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, index: subject.index, anchorIndex: anchorIndex, count: 128, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) + return account.viewTracker.aroundMessageHistoryViewForLocation(context.chatLocationInput(for: chatLocation, contextHolder: chatLocationContextHolder), ignoreMessagesInTimestampRange: ignoreMessagesInTimestampRange, ignoreMessageIds: ignoreMessageIds, index: subject.index, anchorIndex: anchorIndex, count: 128, ignoreRelatedChats: ignoreRelatedChats, fixedCombinedReadStates: fixedCombinedReadStates, tag: tag, appendMessagesFromTheSameGroup: appendMessagesFromTheSameGroup, orderStatistics: orderStatistics, additionalData: additionalData, useRootInterfaceStateForThread: useRootInterfaceStateForThread) |> map { view, updateType, initialData -> ChatHistoryViewUpdate in let (cachedData, cachedDataMessages, readStateData) = extractAdditionalData(view: view, chatLocation: chatLocation) diff --git a/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift b/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift index 09c63ace83..da675ea763 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputActionButtonsNode.swift @@ -12,8 +12,121 @@ import ChatControllerInteraction import AccountContext import ChatTextInputMediaRecordingButton import ChatSendButtonRadialStatusNode +import ChatSendMessageActionUI +import ComponentFlow -final class ChatTextInputActionButtonsNode: ASDisplayNode { +private final class EffectBadgeView: UIView { + private let context: AccountContext + private var currentEffectId: Int64? + + private let backgroundView: UIImageView + + private var theme: PresentationTheme? + + private var effect: AvailableMessageEffects.MessageEffect? + private var effectIcon: ComponentView? + + private let effectDisposable = MetaDisposable() + + init(context: AccountContext) { + self.context = context + self.backgroundView = UIImageView() + + super.init(frame: CGRect()) + + self.isUserInteractionEnabled = false + + self.addSubview(self.backgroundView) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + deinit { + self.effectDisposable.dispose() + } + + func update(size: CGSize, theme: PresentationTheme, effectId: Int64) { + if self.theme !== theme { + self.theme = theme + self.backgroundView.image = generateFilledCircleImage(diameter: size.width, color: theme.list.plainBackgroundColor, strokeColor: nil, strokeWidth: nil, backgroundColor: nil) + self.backgroundView.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: size)).cgPath + self.backgroundView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor + self.backgroundView.layer.shadowOpacity = 0.14 + self.backgroundView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0) + self.backgroundView.layer.shadowRadius = 1.0 + } + + self.backgroundView.frame = CGRect(origin: CGPoint(), size: size) + + if self.currentEffectId != effectId { + self.currentEffectId = effectId + + let messageEffect = self.context.engine.stickers.availableMessageEffects() + |> take(1) + |> map { availableMessageEffects -> AvailableMessageEffects.MessageEffect? in + guard let availableMessageEffects else { + return nil + } + for messageEffect in availableMessageEffects.messageEffects { + if messageEffect.id == effectId || messageEffect.effectSticker.fileId.id == effectId { + return messageEffect + } + } + return nil + } + + self.effectDisposable.set((messageEffect |> deliverOnMainQueue).start(next: { [weak self] effect in + guard let self, let effect else { + return + } + self.effect = effect + self.updateIcon() + })) + } + } + + private func updateIcon() { + guard let effect else { + return + } + + let effectIcon: ComponentView + if let current = self.effectIcon { + effectIcon = current + } else { + effectIcon = ComponentView() + self.effectIcon = effectIcon + } + let effectIconContent: ChatSendMessageScreenEffectIcon.Content + if let staticIcon = effect.staticIcon { + effectIconContent = .file(staticIcon) + } else { + effectIconContent = .text(effect.emoticon) + } + let effectIconSize = effectIcon.update( + transition: .immediate, + component: AnyComponent(ChatSendMessageScreenEffectIcon( + context: self.context, + content: effectIconContent + )), + environment: {}, + containerSize: CGSize(width: 8.0, height: 8.0) + ) + + let size = CGSize(width: 16.0, height: 16.0) + if let effectIconView = effectIcon.view { + if effectIconView.superview == nil { + self.addSubview(effectIconView) + } + effectIconView.frame = CGRect(origin: CGPoint(x: floor((size.width - effectIconSize.width) * 0.5), y: floor((size.height - effectIconSize.height) * 0.5)), size: effectIconSize) + } + } +} + +final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessageActionSheetControllerSourceSendButtonNode { + private let context: AccountContext private let presentationContext: ChatPresentationContext? private let strings: PresentationStrings @@ -26,6 +139,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { var sendButtonHasApplyIcon = false var animatingSendButton = false let expandMediaInputButton: HighlightableButtonNode + private var effectBadgeView: EffectBadgeView? var sendButtonLongPressed: ((ASDisplayNode, ContextGesture) -> Void)? @@ -42,6 +156,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { private var validLayout: CGSize? init(context: AccountContext, presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) { + self.context = context self.presentationContext = presentationContext let theme = presentationInterfaceState.theme let strings = presentationInterfaceState.strings @@ -145,7 +260,7 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { } } - func updateLayout(size: CGSize, isMediaInputExpanded: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) { + func updateLayout(size: CGSize, isMediaInputExpanded: Bool, currentMessageEffectId: Int64?, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) { self.validLayout = size transition.updateFrame(layer: self.micButton.layer, frame: CGRect(origin: CGPoint(), size: size)) self.micButton.layoutItems() @@ -154,7 +269,8 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { transition.updateFrame(node: self.sendContainerNode, frame: CGRect(origin: CGPoint(), size: size)) let backgroundSize = CGSize(width: 33.0, height: 33.0) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize)) + let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - backgroundSize.width) / 2.0), y: floorToScreenPixels((size.height - backgroundSize.height) / 2.0)), size: backgroundSize) + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) self.backgroundNode.cornerRadius = backgroundSize.width / 2.0 transition.updateFrame(node: self.backdropNode, frame: CGRect(origin: CGPoint(x: -2.0, y: -2.0), size: CGSize(width: size.width + 12.0, height: size.height + 2.0))) @@ -162,42 +278,31 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { self.backdropNode.update(rect: rect, within: containerSize) } -// var isScheduledMessages = false -// if case .scheduledMessages = interfaceState.subject { -// isScheduledMessages = true -// } -// -// if let slowmodeState = interfaceState.slowmodeState, !isScheduledMessages && interfaceState.editMessageState == nil { -// let sendButtonRadialStatusNode: ChatSendButtonRadialStatusNode -// if let current = self.sendButtonRadialStatusNode { -// sendButtonRadialStatusNode = current -// } else { -// sendButtonRadialStatusNode = ChatSendButtonRadialStatusNode(color: interfaceState.theme.chat.inputPanel.panelControlAccentColor) -// sendButtonRadialStatusNode.alpha = self.sendContainerNode.alpha -// self.sendButtonRadialStatusNode = sendButtonRadialStatusNode -// self.addSubnode(sendButtonRadialStatusNode) -// } -// -// transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 0.7575, y: 0.7575)) -// -// let defaultSendButtonSize: CGFloat = 25.0 -// let defaultOriginX = floorToScreenPixels((self.sendButton.bounds.width - defaultSendButtonSize) / 2.0) -// let defaultOriginY = floorToScreenPixels((self.sendButton.bounds.height - defaultSendButtonSize) / 2.0) -// -// let radialStatusFrame = CGRect(origin: CGPoint(x: defaultOriginX - 4.0, y: defaultOriginY - 4.0), size: CGSize(width: 33.0, height: 33.0)) -// sendButtonRadialStatusNode.frame = radialStatusFrame -// sendButtonRadialStatusNode.slowmodeState = slowmodeState -// } else { -// if let sendButtonRadialStatusNode = self.sendButtonRadialStatusNode { -// self.sendButtonRadialStatusNode = nil -// sendButtonRadialStatusNode.removeFromSupernode() -// } -// transition.updateSublayerTransformScale(layer: self.sendContainerNode.layer, scale: CGPoint(x: 1.0, y: 1.0)) -// } - transition.updateFrame(node: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size)) let expanded = isMediaInputExpanded transition.updateSublayerTransformScale(node: self.expandMediaInputButton, scale: CGPoint(x: 1.0, y: expanded ? 1.0 : -1.0)) + + if let currentMessageEffectId { + let effectBadgeView: EffectBadgeView + if let current = self.effectBadgeView { + effectBadgeView = current + } else { + effectBadgeView = EffectBadgeView(context: self.context) + self.effectBadgeView = effectBadgeView + self.sendContainerNode.view.addSubview(effectBadgeView) + + effectBadgeView.alpha = 0.0 + transition.updateAlpha(layer: effectBadgeView.layer, alpha: 1.0) + } + let badgeSize = CGSize(width: 16.0, height: 16.0) + effectBadgeView.frame = CGRect(origin: CGPoint(x: backgroundFrame.minX + backgroundSize.width + 3.0 - badgeSize.width, y: backgroundFrame.minY + backgroundSize.height + 3.0 - badgeSize.height), size: badgeSize) + effectBadgeView.update(size: badgeSize, theme: interfaceState.theme, effectId: currentMessageEffectId) + } else if let effectBadgeView = self.effectBadgeView { + self.effectBadgeView = nil + transition.updateAlpha(layer: effectBadgeView.layer, alpha: 0.0, completion: { [weak effectBadgeView] _ in + effectBadgeView?.removeFromSuperview() + }) + } } func updateAccessibility() { @@ -216,4 +321,17 @@ final class ChatTextInputActionButtonsNode: ASDisplayNode { self.accessibilityHint = nil } } + + func makeCustomContents() -> UIView? { + if self.sendButtonHasApplyIcon { + let result = UIView() + result.frame = self.bounds + if let copyView = self.sendContainerNode.view.snapshotView(afterScreenUpdates: false) { + copyView.frame = self.sendContainerNode.frame + result.addSubview(copyView) + } + return result + } + return nil + } } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 7d3da90cdd..c368cb1c7a 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -2483,7 +2483,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } if let presentationInterfaceState = self.presentationInterfaceState { - self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), isMediaInputExpanded: isMediaInputExpanded, transition: transition, interfaceState: presentationInterfaceState) + self.actionButtons.updateLayout(size: CGSize(width: 44.0, height: minimalHeight), isMediaInputExpanded: isMediaInputExpanded, currentMessageEffectId: presentationInterfaceState.interfaceState.sendMessageEffect, transition: transition, interfaceState: presentationInterfaceState) } let slowModeButtonFrame = CGRect(origin: CGPoint(x: hideOffset.x + width - rightInset - 5.0 - slowModeButtonSize.width + composeButtonsOffset, y: hideOffset.y + panelHeight - minimalHeight + 6.0), size: slowModeButtonSize) diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index 6830e72ed3..a09f5b61de 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -566,7 +566,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { return .single(nil) } - return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(message.index), ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: .tag(tagMask), appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) + return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(message.index), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: .tag(tagMask), appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in if let (message, aroundMessages, _) = navigatedMessageFromView(view.0, anchorIndex: message.index, position: .exact, reversed: reversed) { return .single((message, aroundMessages)) @@ -678,7 +678,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { guard let inputIndex = inputIndex else { return .single(nil) } - return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(inputIndex), ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: .tag(tagMask), appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) + return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: .index(inputIndex), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: .tag(tagMask), appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition switch navigation { @@ -708,7 +708,7 @@ final class PeerMessagesMediaPlaylist: SharedMediaPlaylist { } else { viewIndex = .lowerBound } - return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: viewIndex, ignoreMessagesInTimestampRange: nil, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: .tag(tagMask), appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) + return self.context.account.postbox.aroundMessageHistoryViewForLocation(self.context.chatLocationInput(for: chatLocation, contextHolder: self.chatLocationContextHolder ?? Atomic(value: nil)), anchor: viewIndex, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: [], tag: .tag(tagMask), appendMessagesFromTheSameGroup: false, namespaces: namespaces, orderStatistics: []) |> mapToSignal { view -> Signal<(Message, [Message])?, NoError> in let position: NavigatedMessageFromViewPosition switch navigation {