- Story search UI

- Delete message confirmation
This commit is contained in:
Isaac 2024-06-04 19:18:41 +04:00
parent 6ccc9ed693
commit d7bb7d55b4
46 changed files with 1534 additions and 951 deletions

View file

@ -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] = []

View file

@ -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<GalleryMessageHistoryView?, NoError> 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<GalleryMessageHistoryView?, NoError> in
let mapped = GalleryMessageHistoryView.view(view, peerIsCopyProtected)
return .single(mapped)

View file

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

View file

@ -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<Int32>?, 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<Int32>?, ignoreMessageIds: Set<MessageId>, 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

View file

@ -305,6 +305,7 @@ public enum HistoryViewInputAnchor: Equatable {
final class MutableMessageHistoryView: MutablePostboxView {
private(set) var peerIds: MessageHistoryViewInput
private let ignoreMessagesInTimestampRange: ClosedRange<Int32>?
private let ignoreMessageIds: Set<MessageId>
let tag: HistoryViewInputTag?
private let appendMessagesFromTheSameGroup: Bool
let namespaces: MessageIdNamespaces
@ -337,6 +338,7 @@ final class MutableMessageHistoryView: MutablePostboxView {
trackHoles: Bool,
peerIds: MessageHistoryViewInput,
ignoreMessagesInTimestampRange: ClosedRange<Int32>?,
ignoreMessageIds: Set<MessageId>,
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
}

View file

@ -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<Int32>?, limit: Int) -> [IntermediateMessage] {
func fetch(postbox: PostboxImpl, peerId: PeerId, namespace: MessageId.Namespace, from fromIndex: MessageIndex, includeFrom: Bool, to toIndex: MessageIndex, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, ignoreMessageIds: Set<MessageId>, 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<Int32>?
let ignoreMessageIds: Set<MessageId>
let halfLimit: Int
let seedConfiguration: SeedConfiguration
var orderedEntriesBySpace: [PeerIdAndNamespace: OrderedHistoryViewEntries]
var holes: HistoryViewHoles
var spacesWithRemovals = Set<PeerIdAndNamespace>()
init(anchor: HistoryViewAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, halfLimit: Int, locations: MessageHistoryViewInput, postbox: PostboxImpl, holes: HistoryViewHoles) {
init(anchor: HistoryViewAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, ignoreMessageIds: Set<MessageId>, 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<Int32>?, halfLimit: Int, locations: MessageHistoryViewInput) {
init(postbox: PostboxImpl, inputAnchor: HistoryViewInputAnchor, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, namespaces: MessageIdNamespaces, statistics: MessageHistoryViewOrderStatistics, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, ignoreMessageIds: Set<MessageId>, 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)
}
}
}

View file

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

View file

@ -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<Int32>?, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView {
public func getMessagesHistoryViewState(input: MessageHistoryViewInput, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, ignoreMessageIds: Set<MessageId>, 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<Int32>?, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, 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<Int32>?, ignoreMessageIds: Set<MessageId>, count: Int, clipHoles: Bool = true, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, 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<Int32>?, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, 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<Int32>?, ignoreMessageIds: Set<MessageId>, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, messageId: MessageId, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, 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<Int32>?, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, 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<Int32>?, ignoreMessageIds: Set<MessageId>, anchor: HistoryViewInputAnchor, count: Int, clipHoles: Bool = true, ignoreRelatedChats: Bool = false, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, 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<Int32>?,
ignoreMessageIds: Set<MessageId>,
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<Int32>?,
ignoreMessageIds: Set<MessageId>,
count: Int,
clipHoles: Bool = true,
topTaggedMessageIdNamespaces: Set<MessageId.Namespace>,
@ -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<Int32>?,
ignoreMessageIds: Set<MessageId>,
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<Int32>?,
ignoreMessageIds: Set<MessageId>,
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,

View file

@ -8,6 +8,7 @@ public enum PostboxViewKey: Hashable {
public var trackHoles: Bool
public var orderStatistics: MessageHistoryViewOrderStatistics
public var ignoreMessagesInTimestampRange: ClosedRange<Int32>?
public var ignoreMessageIds: Set<MessageId>
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<Int32>? = nil,
ignoreMessageIds: Set<MessageId> = 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,

View file

@ -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<PeerStoryListContext.State?>()
let storiesPromise = Promise<StoryListContext.State?>()
let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId)
let dataSignal: Signal<ChannelStats?, NoError> = statsContext.state

View file

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

View file

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

View file

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

View file

@ -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<Api.Updates>) {
static func refundStarsCharge(userId: Api.InputUser, chargeId: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
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<Api.stories.FoundStories>) {
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<Api.Updates>) {
let buffer = Buffer()

View file

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

View file

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

View file

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

View file

@ -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<Int32>? = 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<Int32>? = nil, ignoreMessageIds: Set<MessageId> = 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<Int32>? = 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<Int32>? = nil, ignoreMessageIds: Set<MessageId> = 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<Int32>? = 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<Int32>? = nil, ignoreMessageIds: Set<MessageId> = 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()

View file

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

View file

@ -840,6 +840,7 @@ func _internal_fetchChannelReplyThreadMessage(account: Account, messageId: Messa
maxReadOutgoingMessageId: nil
)),
ignoreMessagesInTimestampRange: nil,
ignoreMessageIds: Set(),
count: 40,
clipHoles: true,
anchor: inputAnchor,

View file

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

View file

@ -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<Int32>
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<Int32>,
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<State, NoError> { 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<Api.stories.Stories?, NoError> 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<Int32>
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<Int32>,
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<State, NoError> {
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<State>()
private var stateValue: State {
didSet {
self.statePromise.set(.single(self.stateValue))
}
}
var state: Signal<State, NoError> {
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<Api.stories.FoundStories?, NoError> 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<State, NoError> {
return impl.signalWith { impl, subscriber in
return impl.state.start(next: subscriber.putNext)
}
}
private let queue: Queue
private let impl: QueueLocalObject<Impl>
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

View file

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

View file

@ -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<Bool?, NoError>
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<PeerInvitationImportersContext?>(nil)
let requestsStatePromise = Promise<PeerInvitationImportersState?>(nil)
let storyListContext: PeerStoryListContext?
let storyListContext: StoryListContext?
let hasStories: Signal<Bool?, NoError>
if peerId.namespace == Namespaces.Peer.CloudChannel {
storyListContext = PeerStoryListContext(account: context.account, peerId: peerId, isArchived: false)

View file

@ -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<Empty>?
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<EnginePeer?, NoError>
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<Empty>
var emptyStateTransition = Transition(transition)
if let current = self.emptyStateView {

View file

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

View file

@ -14,7 +14,7 @@ public extension StoryContainerScreen {
|> take(1)
|> mapToSignal { state -> Signal<Void, NoError> 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
}

View file

@ -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<StoryKey>()
private var requestStoryDisposables = DisposableSet()
private var listState: PeerStoryListContext.State?
private var listState: StoryListContext.State?
private var focusedId: Int32?
private var focusedIdUpdated = Promise<Void>(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,

View file

@ -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<EngineStoryItem?>]
var effectivePeer: EnginePeer {
return self.item.itemPeer ?? self.peer
}
public init(
peer: EnginePeer,
additionalPeerData: AdditionalPeerData,

View file

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

View file

@ -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<PresentationData, NoError>) = (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, NoError>) = (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<Peer?, NoError>
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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<ChatLocationContextHolder?>(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<ChatLocationContextHolder?>(value: nil), scheduled: false, fixedCombinedReadStates: nil, tag: .tag(MessageTags.pinned), appendMessagesFromTheSameGroup: false, additionalData: [], orderStatistics: .combinedLocation)
|> castError(Bool.self)
|> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> 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

View file

@ -319,88 +319,31 @@ extension ChatControllerImpl {
))
})
}))
}
func beginDeleteMessagesWithUndo(messageIds: Set<MessageId>, 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<Int>([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<MessageId>, 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)
}
}))
}

View file

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

View file

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

View file

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

View file

@ -83,6 +83,7 @@ struct ChatHistoryView {
let id: Int32
let locationInput: ChatHistoryLocationInput?
let ignoreMessagesInTimestampRange: ClosedRange<Int32>?
let ignoreMessageIds: Set<MessageId>
}
enum ChatHistoryViewTransitionReason {
@ -536,6 +537,15 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
}
}
private let ignoreMessageIdsPromise = ValuePromise<Set<EngineMessage.Id>>(Set())
var ignoreMessageIds: Set<EngineMessage.Id> = Set() {
didSet {
if self.ignoreMessageIds != oldValue {
self.ignoreMessageIdsPromise.set(self.ignoreMessageIds)
}
}
}
private let chatHasBotsPromise = ValuePromise<Bool>(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<Int32>?), NoError>
let historyViewUpdate: Signal<(ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange<Int32>?, Set<MessageId>), 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<Int32>?) in
|> map { view -> (ChatHistoryViewUpdate, Int, ChatHistoryLocationInput?, ClosedRange<Int32>?, Set<MessageId>) 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)

View file

@ -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<ChatHistoryViewUpdate, Bool> in
switch update {
@ -36,7 +36,21 @@ func preloadedChatHistoryViewForLocation(_ location: ChatHistoryLocationInput, c
|> restartIfError
}
func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, ignoreMessagesInTimestampRange: ClosedRange<Int32>?, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, scheduled: Bool, fixedCombinedReadStates: MessageHistoryViewReadState?, tag: HistoryViewInputTag?, appendMessagesFromTheSameGroup: Bool, additionalData: [AdditionalMessageHistoryViewData], orderStatistics: MessageHistoryViewOrderStatistics = [], useRootInterfaceStateForThread: Bool = false) -> Signal<ChatHistoryViewUpdate, NoError> {
func chatHistoryViewForLocation(
_ location: ChatHistoryLocationInput,
ignoreMessagesInTimestampRange: ClosedRange<Int32>?,
ignoreMessageIds: Set<EngineMessage.Id>,
context: AccountContext,
chatLocation: ChatLocation,
chatLocationContextHolder: Atomic<ChatLocationContextHolder?>,
scheduled: Bool,
fixedCombinedReadStates: MessageHistoryViewReadState?,
tag: HistoryViewInputTag?,
appendMessagesFromTheSameGroup: Bool,
additionalData: [AdditionalMessageHistoryViewData],
orderStatistics: MessageHistoryViewOrderStatistics = [],
useRootInterfaceStateForThread: Bool = false
) -> Signal<ChatHistoryViewUpdate, NoError> {
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<Bool, NoError>
@ -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)

View file

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

View file

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

View file

@ -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<ChatLocationContextHolder?>(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<ChatLocationContextHolder?>(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<ChatLocationContextHolder?>(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<ChatLocationContextHolder?>(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<ChatLocationContextHolder?>(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<ChatLocationContextHolder?>(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 {