Comments update

This commit is contained in:
Ali 2020-09-22 18:50:38 +04:00
parent d8ef15ee00
commit 95b12456dd
39 changed files with 1203 additions and 736 deletions

View File

@ -170,7 +170,7 @@ public enum ResolvedUrl {
case botStart(peerId: PeerId, payload: String)
case groupBotStart(peerId: PeerId, payload: String)
case channelMessage(peerId: PeerId, messageId: MessageId)
case replyThreadMessage(replyThreadMessageId: MessageId, isChannelPost: Bool, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, messageId: MessageId)
case replyThreadMessage(replyThreadMessage: ChatReplyThreadMessage, messageId: MessageId)
case stickerPack(name: String)
case instantView(TelegramMediaWebpage, String?)
case proxy(host: String, port: Int32, username: String?, password: String?, secret: Data?)
@ -253,7 +253,7 @@ public enum ChatSearchDomain: Equatable {
public enum ChatLocation: Equatable {
case peer(PeerId)
case replyThread(threadMessageId: MessageId, isChannelPost: Bool, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?)
case replyThread(ChatReplyThreadMessage)
}
public final class NavigateToChatControllerParams {

View File

@ -230,7 +230,7 @@ static int32_t fixedTimeDifferenceValue = 0;
#if DEBUG
_tempKeyExpiration = 1 * 60 * 60;
#else
_tempKeyExpiration = 1 * 60 * 60;
_tempKeyExpiration = 24 * 60 * 60;
#endif
_datacenterSeedAddressSetById = [[NSMutableDictionary alloc] init];

View File

@ -6,7 +6,7 @@ public enum ChatLocationInput {
case external(PeerId, Signal<MessageHistoryViewExternalInput, NoError>)
}
enum ResolvedChatLocationInput {
public enum ResolvedChatLocationInput {
case peer(PeerId)
case external(MessageHistoryViewExternalInput)
}

View File

@ -80,7 +80,7 @@ final class MessageHistoryThreadHoleIndexTable: Table {
let key = ValueBoxKey(length: 8 + 8 + 4 + 4)
key.setInt64(0, value: peerId.toInt64())
key.setInt64(8, value: threadId)
key.setInt32(8 + 4, value: namespace)
key.setInt32(8 + 8, value: namespace)
let tagValue: UInt32
switch space {
case .everywhere:

View File

@ -68,6 +68,11 @@ public final class Transaction {
return self.postbox?.messageHistoryHoleIndexTable.containing(id: id) ?? [:]
}
public func getHoles(peerId: PeerId, namespace: MessageId.Namespace) -> IndexSet {
assert(!self.disposed)
return self.postbox?.messageHistoryHoleIndexTable.closest(peerId: peerId, namespace: namespace, space: .everywhere, range: 1 ... (Int32.max - 1)) ?? IndexSet()
}
public func addThreadIndexHole(peerId: PeerId, threadId: Int64, namespace: MessageId.Namespace, space: MessageHistoryHoleSpace, range: ClosedRange<MessageId.Id>) {
assert(!self.disposed)
self.postbox?.addThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: range)
@ -985,6 +990,26 @@ public final class Transaction {
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
}
public func getMessagesHistoryViewState(input: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, namespaces: MessageIdNamespaces) -> MessageHistoryView {
precondition(!self.disposed)
guard let postbox = self.postbox else {
preconditionFailure()
}
precondition(postbox.queue.isCurrent())
var view: MessageHistoryView?
let subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> = Subscriber(next: { next in
view = next.0
}, error: { _ in }, completed: {})
let disposable = postbox.syncAroundMessageHistoryViewForPeerId(subscriber: subscriber, peerIds: input, count: count, clipHoles: clipHoles, anchor: anchor, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: nil, namespaces: namespaces, orderStatistics: MessageHistoryViewOrderStatistics(), additionalData: [])
disposable.dispose()
return view!
}
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
assert(!self.disposed)
self.postbox?.invalidateMessageHistoryTagsSummary(peerId: peerId, namespace: namespace, tagMask: tagMask)
@ -2530,7 +2555,7 @@ public final class Postbox {
}
}
private func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable {
fileprivate func syncAroundMessageHistoryViewForPeerId(subscriber: Subscriber<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError>, peerIds: MessageHistoryViewInput, count: Int, clipHoles: Bool, anchor: HistoryViewInputAnchor, fixedCombinedReadStates: MessageHistoryViewReadState?, topTaggedMessageIdNamespaces: Set<MessageId.Namespace>, tagMask: MessageTags?, namespaces: MessageIdNamespaces, orderStatistics: MessageHistoryViewOrderStatistics, additionalData: [AdditionalMessageHistoryViewData]) -> Disposable {
var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:]
var mainPeerIdForTopTaggedMessages: PeerId?
switch peerIds {

View File

@ -2291,21 +2291,21 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
final class MessageThreadStatsRecord {
var count: Int = 0
var peers: [PeerId] = []
var peers: [ReplyThreadUserMessage] = []
}
var messageThreadStatsDifferences: [MessageId: MessageThreadStatsRecord] = [:]
func addMessageThreadStatsDifference(threadMessageId: MessageId, add: Int, remove: Int, addedMessagePeer: PeerId?) {
func addMessageThreadStatsDifference(threadMessageId: MessageId, add: Int, remove: Int, addedMessagePeer: PeerId?, addedMessageId: MessageId?, isOutgoing: Bool) {
if let value = messageThreadStatsDifferences[threadMessageId] {
value.count += add - remove
if let addedMessagePeer = addedMessagePeer {
value.peers.append(addedMessagePeer)
if let addedMessagePeer = addedMessagePeer, let addedMessageId = addedMessageId {
value.peers.append(ReplyThreadUserMessage(id: addedMessagePeer, messageId: addedMessageId, isOutgoing: isOutgoing))
}
} else {
let value = MessageThreadStatsRecord()
messageThreadStatsDifferences[threadMessageId] = value
value.count = add - remove
if let addedMessagePeer = addedMessagePeer {
value.peers.append(addedMessagePeer)
if let addedMessagePeer = addedMessagePeer, let addedMessageId = addedMessageId {
value.peers.append(ReplyThreadUserMessage(id: addedMessagePeer, messageId: addedMessageId, isOutgoing: isOutgoing))
}
}
}
@ -2320,7 +2320,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
let messageThreadId = makeThreadIdMessageId(peerId: message.id.peerId, threadId: threadId)
if id.peerId.namespace == Namespaces.Peer.CloudChannel {
if !transaction.messageExists(id: id) {
addMessageThreadStatsDifference(threadMessageId: messageThreadId, add: 1, remove: 0, addedMessagePeer: message.authorId)
addMessageThreadStatsDifference(threadMessageId: messageThreadId, add: 1, remove: 0, addedMessagePeer: message.authorId, addedMessageId: id, isOutgoing: !message.flags.contains(.Incoming))
}
}
}
@ -2338,6 +2338,13 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
} else {
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: nil)]![authorId] = activityValue
}
if let threadId = message.threadId {
if updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: threadId)] == nil {
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: threadId)] = [authorId: activityValue]
} else {
updatedTypingActivities[PeerActivitySpace(peerId: chatPeerId, threadId: threadId)]![authorId] = activityValue
}
}
}
if case let .Id(id) = message.id {
@ -2435,7 +2442,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
}
case let .DeleteMessages(ids):
deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in
addMessageThreadStatsDifference(threadMessageId: id, add: add, remove: remove, addedMessagePeer: nil)
addMessageThreadStatsDifference(threadMessageId: id, add: add, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false)
})
case let .UpdateMinAvailableMessage(id):
if let message = transaction.getMessage(id) {

View File

@ -241,8 +241,15 @@ private final class FeaturedStickerPacksContext {
}
private struct ViewCountContextState {
struct ReplyInfo {
var commentsPeerId: PeerId?
var maxReadIncomingMessageId: MessageId?
var maxMessageId: MessageId?
}
var timestamp: Int32
var clientId: Int32
var result: ReplyInfo?
func isStillValidFor(_ other: ViewCountContextState) -> Bool {
if other.timestamp > self.timestamp + 30 {
@ -592,10 +599,32 @@ public final class AccountViewTracker {
}
}
public struct UpdatedMessageReplyInfo {
var timestamp: Int32
var commentsPeerId: PeerId
var maxReadIncomingMessageId: MessageId?
var maxMessageId: MessageId?
}
public func replyInfoForMessageId(_ id: MessageId) -> Signal<UpdatedMessageReplyInfo?, NoError> {
return Signal { [weak self] subscriber in
let state = self?.updatedViewCountMessageIdsAndTimestamps[id]
let result = state?.result
if let state = state, let result = result, let commentsPeerId = result.commentsPeerId {
subscriber.putNext(UpdatedMessageReplyInfo(timestamp: state.timestamp, commentsPeerId: commentsPeerId, maxReadIncomingMessageId: result.maxReadIncomingMessageId, maxMessageId: result.maxMessageId))
} else {
subscriber.putNext(nil)
}
subscriber.putCompletion()
return EmptyDisposable
}
|> runOn(self.queue)
}
public func updateViewCountForMessageIds(messageIds: Set<MessageId>, clientId: Int32) {
self.queue.async {
var addedMessageIds: [MessageId] = []
let updatedState = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: clientId)
let updatedState = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: clientId, result: nil)
for messageId in messageIds {
let messageTimestamp = self.updatedViewCountMessageIdsAndTimestamps[messageId]
if messageTimestamp == nil || !messageTimestamp!.isStillValidFor(updatedState) {
@ -609,22 +638,26 @@ public final class AccountViewTracker {
self.nextUpdatedViewCountDisposableId += 1
if let account = self.account {
let signal = (account.postbox.transaction { transaction -> Signal<Void, NoError> in
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
let signal: Signal<[MessageId: ViewCountContextState], NoError> = (account.postbox.transaction { transaction -> Signal<[MessageId: ViewCountContextState], NoError> in
guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
return .complete()
}
return account.network.request(Api.functions.messages.getMessagesViews(peer: inputPeer, id: messageIds.map { $0.id }, increment: .boolTrue))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.MessageViews?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Void, NoError> in
|> mapToSignal { result -> Signal<[MessageId: ViewCountContextState], NoError> in
guard case let .messageViews(viewCounts, chats, users)? = result else {
return .complete()
}
return account.postbox.transaction { transaction -> Void in
return account.postbox.transaction { transaction -> [MessageId: ViewCountContextState] in
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
var resultStates: [MessageId: ViewCountContextState] = [:]
for apiUser in users {
if let user = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) {
peers.append(user)
@ -673,6 +706,17 @@ public final class AccountViewTracker {
repliesReadMaxId = readMaxId
}
}
var maxReadIncomingMessageId: MessageId?
var maxMessageId: MessageId?
if let commentsChannelId = commentsChannelId {
if let repliesReadMaxId = repliesReadMaxId {
maxReadIncomingMessageId = MessageId(peerId: commentsChannelId, namespace: Namespaces.Message.Cloud, id: repliesReadMaxId)
}
if let repliesMaxId = repliesMaxId {
maxMessageId = MessageId(peerId: commentsChannelId, namespace: Namespaces.Message.Cloud, id: repliesMaxId)
}
}
resultStates[messageIds[i]] = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: clientId, result: ViewCountContextState.ReplyInfo(commentsPeerId: commentsChannelId, maxReadIncomingMessageId: maxReadIncomingMessageId, maxMessageId: maxMessageId))
loop: for j in 0 ..< attributes.count {
if let attribute = attributes[j] as? ViewCountMessageAttribute {
if let views = views {
@ -697,18 +741,25 @@ public final class AccountViewTracker {
}
}
}
return resultStates
}
}
} else {
return .complete()
}
} |> switchToLatest)
|> switchToLatest)
|> afterDisposed { [weak self] in
self?.queue.async {
self?.updatedViewCountDisposables.set(nil, forKey: disposableId)
}
}
self.updatedViewCountDisposables.set(signal.start(), forKey: disposableId)
|> deliverOn(self.queue)
self.updatedViewCountDisposables.set(signal.start(next: { [weak self] updatedStates in
guard let strongSelf = self else {
return
}
for (id, state) in updatedStates {
strongSelf.updatedViewCountMessageIdsAndTimestamps[id] = state
}
}), forKey: disposableId)
}
}
}

View File

@ -149,9 +149,45 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
let infoFlags = TelegramChannelGroupFlags()
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
}
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init))
} else {
return nil
}
}
}
func mergeChannel(lhs: TelegramChannel?, rhs: TelegramChannel) -> TelegramChannel {
guard let lhs = lhs else {
return rhs
}
if case .personal = rhs.accessHash {
return rhs
}
var channelFlags = lhs.flags
if rhs.flags.contains(.isVerified) {
channelFlags.insert(.isVerified)
} else {
let _ = channelFlags.remove(.isVerified)
}
var info = lhs.info
switch info {
case .broadcast:
break
case .group:
let infoFlags = TelegramChannelGroupFlags()
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
}
let accessHash: TelegramPeerAccessHash?
if let rhsAccessHashValue = lhs.accessHash, case .personal = rhsAccessHashValue {
accessHash = rhsAccessHashValue
} else {
accessHash = lhs.accessHash ?? rhs.accessHash
}
return TelegramChannel(id: lhs.id, accessHash: accessHash, title: rhs.title, username: rhs.username, photo: rhs.photo, creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: rhs.defaultBannedRights)
}

View File

@ -234,7 +234,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
if let threadId = updatedMessage.threadId {
let messageThreadId = makeThreadIdMessageId(peerId: updatedMessage.id.peerId, threadId: threadId)
if let authorId = updatedMessage.authorId {
updateMessageThreadStats(transaction: transaction, threadMessageId: messageThreadId, difference: 1, addedMessagePeers: [authorId])
updateMessageThreadStats(transaction: transaction, threadMessageId: messageThreadId, difference: 1, addedMessagePeers: [ReplyThreadUserMessage(id: authorId, messageId: updatedId, isOutgoing: true)])
}
}
}

View File

@ -102,7 +102,7 @@ private final class HistoryPreloadEntry: Comparable {
|> mapToSignal { download -> Signal<Never, NoError> in
switch hole.hole {
case let .peer(peerHole):
return fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .download(download), postbox: postbox, peerId: peerHole.peerId, namespace: peerHole.namespace, direction: hole.direction, space: .everywhere, threadId: nil, count: 60)
return fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .download(download), postbox: postbox, peerInput: .direct(peerId: peerHole.peerId, threadId: nil), namespace: peerHole.namespace, direction: hole.direction, space: .everywhere, count: 60)
|> ignoreValues
}
}

View File

@ -125,7 +125,39 @@ func withResolvedAssociatedMessages<T>(postbox: Postbox, source: FetchMessageHis
|> switchToLatest
}
func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerId: PeerId, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, threadId: MessageId?, count rawCount: Int) -> Signal<IndexSet, NoError> {
enum FetchMessageHistoryHoleThreadInput: CustomStringConvertible {
case direct(peerId: PeerId, threadId: Int64?)
case threadFromChannel(channelMessageId: MessageId)
var description: String {
switch self {
case let .direct(peerId, threadId):
return "direct(\(peerId), \(String(describing: threadId))"
case let .threadFromChannel(channelMessageId):
return "threadFromChannel(peerId: \(channelMessageId.peerId), postId: \(channelMessageId.id)"
}
}
var requestThreadId: MessageId? {
switch self {
case let .direct(peerId, threadId):
if let threadId = threadId {
return makeThreadIdMessageId(peerId: peerId, threadId: threadId)
} else {
return nil
}
case let .threadFromChannel(channelMessageId):
return channelMessageId
}
}
}
struct FetchMessageHistoryHoleResult: Equatable {
var removedIndices: IndexSet
var strictRemovedIndices: IndexSet
}
func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryHoleSource, postbox: Postbox, peerInput: FetchMessageHistoryHoleThreadInput, namespace: MessageId.Namespace, direction: MessageHistoryViewRelativeHoleDirection, space: MessageHistoryHoleSpace, count rawCount: Int) -> Signal<FetchMessageHistoryHoleResult, NoError> {
let count = min(100, rawCount)
return postbox.stateView()
@ -137,60 +169,29 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
}
}
|> take(1)
|> mapToSignal { _ -> Signal<IndexSet, NoError> in
return postbox.transaction { transaction -> (Peer?, Int32) in
var hash: Int32 = 0
switch space {
case .everywhere:
if let threadId = threadId {
let offsetId: Int32
let addOffset: Int32
let selectedLimit = count
let maxId: Int32
let minId: Int32
switch direction {
case let .range(start, end):
if start.id <= end.id {
offsetId = start.id <= 1 ? 1 : (start.id - 1)
addOffset = Int32(-selectedLimit)
maxId = end.id
minId = start.id - 1
} else {
offsetId = start.id == Int32.max ? start.id : (start.id + 1)
addOffset = 0
maxId = start.id == Int32.max ? start.id : (start.id + 1)
minId = end.id
|> mapToSignal { _ -> Signal<FetchMessageHistoryHoleResult, NoError> in
return postbox.transaction { transaction -> (Api.InputPeer?, Int32) in
switch peerInput {
case let .direct(peerId, _):
return (transaction.getPeer(peerId).flatMap(forceApiInputPeer), 0)
case let .threadFromChannel(channelMessageId):
return (transaction.getPeer(channelMessageId.peerId).flatMap(forceApiInputPeer), 0)
}
case let .aroundId(id):
offsetId = id.id
addOffset = Int32(-selectedLimit / 2)
maxId = Int32.max
minId = 1
}
|> mapToSignal { (inputPeer, hash) -> Signal<FetchMessageHistoryHoleResult, NoError> in
guard let inputPeer = inputPeer else {
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(), strictRemovedIndices: IndexSet()))
}
//request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
}
default:
break
}
return (transaction.getPeer(peerId), hash)
}
|> mapToSignal { (peer, hash) -> Signal<IndexSet, NoError> in
guard let peer = peer else {
return .single(IndexSet())
}
if let inputPeer = forceApiInputPeer(peer) {
print("fetchMessageHistoryHole for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
Logger.shared.log("fetchMessageHistoryHole", "fetch for \(peer.id) \(peer.debugDisplayTitle) \(direction) space \(space)")
print("fetchMessageHistoryHole for \(peerInput) direction \(direction) space \(space)")
Logger.shared.log("fetchMessageHistoryHole", "fetch for \(peerInput) direction \(direction) space \(space)")
let request: Signal<Api.messages.Messages, MTRpcError>
var implicitelyFillHole = false
let minMaxRange: ClosedRange<MessageId.Id>
switch space {
case .everywhere:
if let threadId = threadId {
if let requestThreadId = peerInput.requestThreadId {
let offsetId: Int32
let addOffset: Int32
let selectedLimit = count
@ -237,7 +238,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
minMaxRange = 1 ... (Int32.max - 1)
}
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: threadId.id, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
request = source.request(Api.functions.messages.getReplies(peer: inputPeer, msgId: requestThreadId.id, offsetId: offsetId, offsetDate: 0, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: hash))
} else {
let offsetId: Int32
let addOffset: Int32
@ -402,7 +403,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
return request
|> retryRequest
|> mapToSignal { result -> Signal<IndexSet, NoError> in
|> mapToSignal { result -> Signal<FetchMessageHistoryHoleResult, NoError> in
let messages: [Api.Message]
let chats: [Api.Chat]
let users: [Api.User]
@ -456,10 +457,11 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
}
}
return withResolvedAssociatedMessages(postbox: postbox, source: source, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages -> IndexSet in
return withResolvedAssociatedMessages(postbox: postbox, source: source, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages -> FetchMessageHistoryHoleResult in
let _ = transaction.addMessages(storeMessages, location: .Random)
let _ = transaction.addMessages(additionalMessages, location: .Random)
var filledRange: ClosedRange<MessageId.Id>
var strictFilledIndices: IndexSet
let ids = storeMessages.compactMap { message -> MessageId.Id? in
switch message.id {
case let .Id(id):
@ -479,12 +481,14 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
}
if ids.count == 0 || implicitelyFillHole {
filledRange = minMaxRange
strictFilledIndices = IndexSet()
} else {
let messageRange = ids.min()! ... ids.max()!
switch direction {
case let .aroundId(aroundId):
filledRange = min(aroundId.id, messageRange.lowerBound) ... max(aroundId.id, messageRange.upperBound)
if threadId != nil {
strictFilledIndices = IndexSet(integersIn: Int(min(aroundId.id, messageRange.lowerBound)) ... Int(max(aroundId.id, messageRange.upperBound)))
if peerInput.requestThreadId != nil {
if ids.count <= count / 2 - 1 {
filledRange = minMaxRange
}
@ -494,33 +498,49 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH
let minBound = start.id
let maxBound = messageRange.upperBound
filledRange = min(minBound, maxBound) ... max(minBound, maxBound)
var maxStrictIndex = max(minBound, maxBound)
maxStrictIndex = min(maxStrictIndex, messageRange.upperBound)
strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex))
} else {
let minBound = messageRange.lowerBound
let maxBound = start.id
filledRange = min(minBound, maxBound) ... max(minBound, maxBound)
var maxStrictIndex = max(minBound, maxBound)
maxStrictIndex = min(maxStrictIndex, messageRange.upperBound)
strictFilledIndices = IndexSet(integersIn: Int(min(minBound, maxBound)) ... Int(maxStrictIndex))
}
}
}
switch peerInput {
case let .direct(peerId, threadId):
if let threadId = threadId {
transaction.removeThreadIndexHole(peerId: peerId, threadId: makeMessageThreadId(threadId), namespace: namespace, space: .everywhere, range: filledRange)
for range in strictFilledIndices.rangeView {
transaction.removeThreadIndexHole(peerId: peerId, threadId: threadId, namespace: namespace, space: space, range: Int32(range.lowerBound) ... Int32(range.upperBound))
}
} else {
transaction.removeHole(peerId: peerId, namespace: namespace, space: space, range: filledRange)
}
case .threadFromChannel:
break
}
updatePeers(transaction: transaction, peers: peers + additionalPeers, update: { _, updated -> Peer in
return updated
})
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences)
print("fetchMessageHistoryHole for \(peer.id) \(peer.debugDisplayTitle) space \(space) threadId: \(String(describing: threadId)) done")
print("fetchMessageHistoryHole for \(peerInput) space \(space) done")
return IndexSet(integersIn: Int(filledRange.lowerBound) ... Int(filledRange.upperBound))
return FetchMessageHistoryHoleResult(
removedIndices: IndexSet(integersIn: Int(filledRange.lowerBound) ... Int(filledRange.upperBound)),
strictRemovedIndices: strictFilledIndices
)
})
}
} else {
return .complete()
}
}
}
}
@ -666,12 +686,13 @@ func fetchCallListHole(network: Network, postbox: Postbox, accountPeerId: PeerId
}
}
for user in users {
let telegramUser = TelegramUser(user: user)
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated

View File

@ -55,7 +55,7 @@ func managedMessageHistoryHoles(accountPeerId: PeerId, network: Network, postbox
for (entry, disposable) in added {
switch entry.hole {
case let .peer(hole):
disposable.set(fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .network(network), postbox: postbox, peerId: hole.peerId, namespace: hole.namespace, direction: entry.direction, space: entry.space, threadId: nil, count: entry.count).start())
disposable.set(fetchMessageHistoryHole(accountPeerId: accountPeerId, source: .network(network), postbox: postbox, peerInput: .direct(peerId: hole.peerId, threadId: nil), namespace: hole.namespace, direction: entry.direction, space: entry.space, count: entry.count).start())
}
}
})

View File

@ -43,35 +43,31 @@ private class ReplyThreadHistoryContextImpl {
private var readStateDisposable: Disposable?
private let readDisposable = MetaDisposable()
init(queue: Queue, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
init(queue: Queue, account: Account, data: ChatReplyThreadMessage) {
self.queue = queue
self.account = account
self.messageId = messageId
self.messageId = data.messageId
self.maxReadOutgoingMessageIdValue = maxReadOutgoingMessageId
self.maxReadOutgoingMessageIdValue = data.maxReadOutgoingMessageId
self.maxReadOutgoingMessageId.set(.single(self.maxReadOutgoingMessageIdValue))
self.initialStateDisposable = (account.postbox.transaction { transaction -> State in
var indices = transaction.getThreadIndexHoles(peerId: messageId.peerId, threadId: makeMessageThreadId(messageId), namespace: Namespaces.Message.Cloud)
switch maxMessage {
case .unknown:
indices.insert(integersIn: 1 ..< Int(Int32.max - 1))
case let .known(maxMessageId):
indices.insert(integersIn: 1 ..< Int(Int32.max - 1))
/*if let maxMessageId = maxMessageId {
let topMessage = transaction.getMessagesWithThreadId(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, threadId: makeMessageThreadId(messageId), from: MessageIndex.upperBound(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud), includeFrom: false, to: MessageIndex.lowerBound(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud), limit: 1).first
if let topMessage = topMessage {
if maxMessageId.id < maxMessageId.id {
indices.insert(integersIn: Int(topMessage.id.id + 1) ..< Int(Int32.max - 1))
var indices = transaction.getThreadIndexHoles(peerId: data.messageId.peerId, threadId: makeMessageThreadId(data.messageId), namespace: Namespaces.Message.Cloud)
indices.subtract(data.initialFilledHoles)
let isParticipant = transaction.getPeerChatListIndex(data.messageId.peerId) != nil
if isParticipant {
let historyHoles = transaction.getHoles(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud)
indices.formIntersection(historyHoles)
}
if let maxMessageId = data.maxMessage {
indices.remove(integersIn: Int(maxMessageId.id + 1) ..< Int(Int32.max))
} else {
indices.insert(integersIn: 1 ..< Int(Int32.max - 1))
indices.removeAll()
}
} else {
indices = IndexSet()
}*/
}
return State(messageId: messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
return State(messageId: data.messageId, holeIndices: [Namespaces.Message.Cloud: indices], maxReadIncomingMessageId: data.maxReadIncomingMessageId, maxReadOutgoingMessageId: data.maxReadOutgoingMessageId)
}
|> deliverOn(self.queue)).start(next: { [weak self] state in
guard let strongSelf = self else {
@ -108,8 +104,8 @@ private class ReplyThreadHistoryContextImpl {
guard let strongSelf = self else {
return
}
if let value = outgoing[messageId] {
strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: messageId.peerId, namespace: Namespaces.Message.Cloud, id: value)
if let value = outgoing[data.messageId] {
strongSelf.maxReadOutgoingMessageIdValue = MessageId(peerId: data.messageId.peerId, namespace: Namespaces.Message.Cloud, id: value)
}
})
}
@ -129,7 +125,7 @@ private class ReplyThreadHistoryContextImpl {
return
}
if var currentHoles = strongSelf.stateValue?.holeIndices[Namespaces.Message.Cloud] {
currentHoles.subtract(removedHoleIndices)
currentHoles.subtract(removedHoleIndices.removedIndices)
strongSelf.stateValue?.holeIndices[Namespaces.Message.Cloud] = currentHoles
}
}))
@ -139,11 +135,11 @@ private class ReplyThreadHistoryContextImpl {
}
}
private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal<IndexSet, NoError> {
private func fetchHole(entry: MessageHistoryHolesViewEntry) -> Signal<FetchMessageHistoryHoleResult, NoError> {
switch entry.hole {
case let .peer(hole):
let fetchCount = min(entry.count, 100)
return fetchMessageHistoryHole(accountPeerId: self.account.peerId, source: .network(self.account.network), postbox: self.account.postbox, peerId: hole.peerId, namespace: hole.namespace, direction: entry.direction, space: entry.space, threadId: hole.threadId.flatMap { makeThreadIdMessageId(peerId: self.messageId.peerId, threadId: $0) }, count: fetchCount)
return fetchMessageHistoryHole(accountPeerId: self.account.peerId, source: .network(self.account.network), postbox: self.account.postbox, peerInput: .direct(peerId: hole.peerId, threadId: hole.threadId), namespace: hole.namespace, direction: entry.direction, space: entry.space, count: fetchCount)
}
}
@ -257,10 +253,10 @@ public class ReplyThreadHistoryContext {
}
}
public init(account: Account, peerId: PeerId, threadMessageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
public init(account: Account, peerId: PeerId, data: ChatReplyThreadMessage) {
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return ReplyThreadHistoryContextImpl(queue: queue, account: account, messageId: threadMessageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
return ReplyThreadHistoryContextImpl(queue: queue, account: account, data: data)
})
}
@ -271,45 +267,59 @@ public class ReplyThreadHistoryContext {
}
}
public struct ChatReplyThreadMessage {
public enum MaxMessage: Equatable {
case unknown
case known(MessageId?)
}
public struct ChatReplyThreadMessage: Equatable {
public var messageId: MessageId
public var maxMessage: MaxMessage
public var isChannelPost: Bool
public var maxMessage: MessageId?
public var maxReadIncomingMessageId: MessageId?
public var maxReadOutgoingMessageId: MessageId?
public var initialFilledHoles: IndexSet
public init(messageId: MessageId, maxMessage: MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
fileprivate init(messageId: MessageId, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, initialFilledHoles: IndexSet) {
self.messageId = messageId
self.isChannelPost = isChannelPost
self.maxMessage = maxMessage
self.maxReadIncomingMessageId = maxReadIncomingMessageId
self.maxReadOutgoingMessageId = maxReadOutgoingMessageId
self.initialFilledHoles = initialFilledHoles
}
}
public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId) -> Signal<ChatReplyThreadMessage?, NoError> {
public enum FetchChannelReplyThreadMessageError {
case generic
}
public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageId) -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<ChatReplyThreadMessage?, NoError> in
|> castError(FetchChannelReplyThreadMessageError.self)
|> mapToSignal { inputPeer -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
guard let inputPeer = inputPeer else {
return .single(nil)
return .fail(.generic)
}
let discussionMessage: Signal<Api.messages.DiscussionMessage?, NoError> = account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id))
let replyInfo = Promise<AccountViewTracker.UpdatedMessageReplyInfo?>()
replyInfo.set(account.viewTracker.replyInfoForMessageId(messageId))
struct DiscussionMessage {
public var messageId: MessageId
public var isChannelPost: Bool
public var maxMessage: MessageId?
public var maxReadIncomingMessageId: MessageId?
public var maxReadOutgoingMessageId: MessageId?
}
let remoteDiscussionMessageSignal: Signal<DiscussionMessage?, NoError> = account.network.request(Api.functions.messages.getDiscussionMessage(peer: inputPeer, msgId: messageId.id))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.DiscussionMessage?, NoError> in
return .single(nil)
}
return discussionMessage
|> mapToSignal { discussionMessage -> Signal<ChatReplyThreadMessage?, NoError> in
|> mapToSignal { discussionMessage -> Signal<DiscussionMessage?, NoError> in
guard let discussionMessage = discussionMessage else {
return .single(nil)
}
return account.postbox.transaction { transaction -> ChatReplyThreadMessage? in
return account.postbox.transaction { transaction -> DiscussionMessage? in
switch discussionMessage {
case let .discussionMessage(_, messages, maxId, readInboxMaxId, readOutboxMaxId, chats, users):
let parsedMessages = messages.compactMap { message -> StoreMessage? in
@ -344,19 +354,20 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
let resolvedMaxMessage: ChatReplyThreadMessage.MaxMessage
let resolvedMaxMessage: MessageId?
if let maxId = maxId {
resolvedMaxMessage = .known(MessageId(
resolvedMaxMessage = MessageId(
peerId: parsedIndex.id.peerId,
namespace: Namespaces.Message.Cloud,
id: maxId
))
)
} else {
resolvedMaxMessage = .known(nil)
resolvedMaxMessage = nil
}
return ChatReplyThreadMessage(
return DiscussionMessage(
messageId: parsedIndex.id,
isChannelPost: true,
maxMessage: resolvedMaxMessage,
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
@ -368,5 +379,172 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
}
}
}
let discussionMessageSignal = (replyInfo.get()
|> mapToSignal { replyInfo -> Signal<DiscussionMessage?, NoError> in
guard let replyInfo = replyInfo else {
return .single(nil)
}
return account.postbox.transaction { transaction -> DiscussionMessage? in
let isParticipant = transaction.getPeerChatListIndex(replyInfo.commentsPeerId) != nil
guard isParticipant else {
return nil
}
var foundDiscussionMessageId: MessageId?
transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in
for attribute in attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
if attribute.messageId == messageId {
foundDiscussionMessageId = id
return true
}
}
}
if foundDiscussionMessageId != nil {
return false
}
return true
})
guard let discussionMessageId = foundDiscussionMessageId else {
return nil
}
return DiscussionMessage(
messageId: discussionMessageId,
isChannelPost: true,
maxMessage: replyInfo.maxMessageId,
maxReadIncomingMessageId: replyInfo.maxReadIncomingMessageId,
maxReadOutgoingMessageId: nil
)
}
})
|> mapToSignal { result -> Signal<DiscussionMessage?, NoError> in
if let result = result {
return .single(result)
} else {
return remoteDiscussionMessageSignal
}
}
let discussionMessage = Promise<DiscussionMessage?>()
discussionMessage.set(discussionMessageSignal)
let preloadedHistoryPosition: Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> = replyInfo.get()
|> castError(FetchChannelReplyThreadMessageError.self)
|> mapToSignal { replyInfo -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in
if let replyInfo = replyInfo {
return account.postbox.transaction { transaction -> (FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?) in
var threadInput: FetchMessageHistoryHoleThreadInput = .threadFromChannel(channelMessageId: messageId)
var threadMessageId: MessageId?
transaction.scanMessageAttributes(peerId: replyInfo.commentsPeerId, namespace: Namespaces.Message.Cloud, limit: 1000, { id, attributes in
for attribute in attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
if attribute.messageId == messageId {
threadMessageId = id
threadInput = .direct(peerId: id.peerId, threadId: makeMessageThreadId(id))
return false
}
}
}
return true
})
return (threadInput, replyInfo.commentsPeerId, threadMessageId, replyInfo.maxReadIncomingMessageId, replyInfo.maxMessageId)
}
|> castError(FetchChannelReplyThreadMessageError.self)
} else {
return discussionMessage.get()
|> castError(FetchChannelReplyThreadMessageError.self)
|> mapToSignal { discussionMessage -> Signal<(FetchMessageHistoryHoleThreadInput, PeerId, MessageId?, MessageId?, MessageId?), FetchChannelReplyThreadMessageError> in
guard let discussionMessage = discussionMessage else {
return .fail(.generic)
}
let topMessageId = discussionMessage.messageId
let commentsPeerId = topMessageId.peerId
return .single((.direct(peerId: commentsPeerId, threadId: makeMessageThreadId(topMessageId)), commentsPeerId, discussionMessage.messageId, discussionMessage.maxReadIncomingMessageId, discussionMessage.maxMessage))
}
}
}
let preloadedHistory = preloadedHistoryPosition
|> mapToSignal { peerInput, commentsPeerId, threadMessageId, aroundMessageId, maxMessageId -> Signal<FetchMessageHistoryHoleResult, FetchChannelReplyThreadMessageError> in
guard let maxMessageId = maxMessageId else {
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(integersIn: 1 ..< Int(Int32.max - 1)), strictRemovedIndices: IndexSet()))
}
return account.postbox.transaction { transaction -> Signal<FetchMessageHistoryHoleResult, FetchChannelReplyThreadMessageError> in
if let threadMessageId = threadMessageId {
var holes = transaction.getThreadIndexHoles(peerId: threadMessageId.peerId, threadId: makeMessageThreadId(threadMessageId), namespace: Namespaces.Message.Cloud)
holes.remove(integersIn: Int(maxMessageId.id + 1) ..< Int(Int32.max))
let isParticipant = transaction.getPeerChatListIndex(commentsPeerId) != nil
if isParticipant {
let historyHoles = transaction.getHoles(peerId: commentsPeerId, namespace: Namespaces.Message.Cloud)
holes.formIntersection(historyHoles)
}
let anchor: HistoryViewInputAnchor
if let aroundMessageId = aroundMessageId {
anchor = .message(aroundMessageId)
} else {
anchor = .upperBound
}
let testView = transaction.getMessagesHistoryViewState(
input: .external(MessageHistoryViewExternalInput(
peerId: commentsPeerId,
threadId: makeMessageThreadId(threadMessageId),
maxReadIncomingMessageId: nil,
maxReadOutgoingMessageId: nil,
holes: [
Namespaces.Message.Cloud: holes
]
)),
count: 30,
clipHoles: true,
anchor: anchor,
namespaces: .not(Namespaces.Message.allScheduled)
)
if !testView.isLoading {
return .single(FetchMessageHistoryHoleResult(removedIndices: IndexSet(), strictRemovedIndices: IndexSet()))
}
}
let direction: MessageHistoryViewRelativeHoleDirection
if let aroundMessageId = aroundMessageId {
direction = .aroundId(aroundMessageId)
} else {
direction = .range(start: MessageId(peerId: commentsPeerId, namespace: Namespaces.Message.Cloud, id: Int32.max - 1), end: MessageId(peerId: commentsPeerId, namespace: Namespaces.Message.Cloud, id: 1))
}
return fetchMessageHistoryHole(accountPeerId: account.peerId, source: .network(account.network), postbox: account.postbox, peerInput: peerInput, namespace: Namespaces.Message.Cloud, direction: direction, space: .everywhere, count: 30)
|> castError(FetchChannelReplyThreadMessageError.self)
}
|> castError(FetchChannelReplyThreadMessageError.self)
|> switchToLatest
}
return combineLatest(
discussionMessage.get()
|> castError(FetchChannelReplyThreadMessageError.self),
preloadedHistory
)
|> mapToSignal { discussionMessage, initialFilledHoles -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
guard let discussionMessage = discussionMessage else {
return .fail(.generic)
}
return account.postbox.transaction { transaction -> Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError> in
for range in initialFilledHoles.strictRemovedIndices.rangeView {
transaction.removeThreadIndexHole(peerId: discussionMessage.messageId.peerId, threadId: makeMessageThreadId(discussionMessage.messageId), namespace: Namespaces.Message.Cloud, space: .everywhere, range: Int32(range.lowerBound) ... Int32(range.upperBound))
}
return .single(ChatReplyThreadMessage(
messageId: discussionMessage.messageId,
isChannelPost: discussionMessage.isChannelPost,
maxMessage: discussionMessage.maxMessage,
maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId,
maxReadOutgoingMessageId: discussionMessage.maxReadOutgoingMessageId,
initialFilledHoles: initialFilledHoles.removedIndices
))
}
|> castError(FetchChannelReplyThreadMessageError.self)
|> switchToLatest
}
}
}

View File

@ -626,20 +626,27 @@ public func updatedRemotePeer(postbox: Postbox, network: Network, peer: PeerRefe
return .generic
}
|> mapToSignal { result -> Signal<Peer, UpdatedRemotePeerError> in
if let updatedPeer = result.first.flatMap(TelegramUser.init(user:)), updatedPeer.id == peer.id {
return postbox.transaction { transaction -> Peer in
updatePeers(transaction: transaction, peers: [updatedPeer], update: { _, updated in
guard let apiUser = result.first else {
return .fail(.generic)
}
return postbox.transaction { transaction -> Peer? in
guard let peer = TelegramUser.merge(transaction.getPeer(apiUser.peerId) as? TelegramUser, rhs: apiUser) else {
return nil
}
updatePeers(transaction: transaction, peers: [peer], update: { _, updated in
return updated
})
return updatedPeer
}
|> mapError { _ -> UpdatedRemotePeerError in
return .generic
return peer
}
|> castError(UpdatedRemotePeerError.self)
|> mapToSignal { peer -> Signal<Peer, UpdatedRemotePeerError> in
if let peer = peer {
return .single(peer)
} else {
return .fail(.generic)
}
}
}
} else if case let .group(id) = peer {
return network.request(Api.functions.messages.getChats(id: [id]))
|> mapError { _ -> UpdatedRemotePeerError in

View File

@ -8,7 +8,7 @@ func parsedTelegramProfilePhoto(_ photo: Api.UserProfilePhoto) -> [TelegramMedia
var representations: [TelegramMediaImageRepresentation] = []
switch photo {
case let .userProfilePhoto(flags, _, photoSmall, photoBig, dcId):
let hasVideo = (flags & (1 << 0)) != 0
let _ = (flags & (1 << 0)) != 0
let smallResource: TelegramMediaResource
let fullSizeResource: TelegramMediaResource
@ -138,4 +138,37 @@ extension TelegramUser {
return TelegramUser(user: rhs)
}
}
static func merge(lhs: TelegramUser?, rhs: TelegramUser) -> TelegramUser {
guard let lhs = lhs else {
return rhs
}
if case .personal = rhs.accessHash {
return rhs
} else {
var userFlags: UserInfoFlags = []
if rhs.flags.contains(.isVerified) {
userFlags.insert(.isVerified)
}
if rhs.flags.contains(.isSupport) {
userFlags.insert(.isSupport)
}
if rhs.flags.contains(.isScam) {
userFlags.insert(.isScam)
}
let botInfo: BotUserInfo? = rhs.botInfo
let restrictionInfo: PeerAccessRestrictionInfo? = rhs.restrictionInfo
let accessHash: TelegramPeerAccessHash?
if let rhsAccessHashValue = lhs.accessHash, case .personal = rhsAccessHashValue {
accessHash = rhsAccessHashValue
} else {
accessHash = lhs.accessHash ?? rhs.accessHash
}
return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: rhs.username, phone: lhs.phone, photo: rhs.photo.isEmpty ? lhs.photo : rhs.photo, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags)
}
}
}

View File

@ -17,12 +17,13 @@ public func updateAccountPeerName(account: Account, firstName: String, lastName:
|> mapToSignal { result -> Signal<Void, NoError> in
return account.postbox.transaction { transaction -> Void in
if let result = result {
let peer = TelegramUser(user: result)
if let peer = TelegramUser.merge(transaction.getPeer(result.peerId) as? TelegramUser, rhs: result) {
updatePeers(transaction: transaction, peers: [peer], update: { $1 })
}
}
}
}
}
public enum UpdateAboutError {
case generic
@ -43,6 +44,7 @@ public func updateAbout(account: Account, about: String?) -> Signal<Void, Update
return current
}
})
} |> mapError { _ -> UpdateAboutError in return .generic }
}
|> castError(UpdateAboutError.self)
}
}

View File

@ -173,13 +173,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
return postbox.transaction { transaction -> Bool in
switch result {
case let .userFull(userFull):
let telegramUser = TelegramUser(user: userFull.user)
if let telegramUser = TelegramUser.merge(transaction.getPeer(userFull.user.peerId) as? TelegramUser, rhs: userFull.user) {
updatePeers(transaction: transaction, peers: [telegramUser], update: { _, updated -> Peer in
return updated
})
}
transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: userFull.notifySettings)])
if let presence = TelegramUserPresence(apiUser: userFull.user) {
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: [telegramUser.id: presence])
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: [userFull.user.peerId: presence])
}
}
transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in
@ -267,12 +268,13 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
}
for user in users {
let telegramUser = TelegramUser(user: user)
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated
@ -416,18 +418,19 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
}
for user in users {
let telegramUser = TelegramUser(user: user)
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
}
if let participantResult = participantResult {
switch participantResult {
case let .channelParticipant(_, users):
for user in users {
let telegramUser = TelegramUser(user: user)
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
@ -435,6 +438,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
}
}
}
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
return updated

View File

@ -29,11 +29,17 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) {
}
}
func updateMessageThreadStats(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [PeerId]) {
struct ReplyThreadUserMessage {
var id: PeerId
var messageId: MessageId
var isOutgoing: Bool
}
func updateMessageThreadStats(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [ReplyThreadUserMessage]) {
updateMessageThreadStatsInternal(transaction: transaction, threadMessageId: threadMessageId, difference: difference, addedMessagePeers: addedMessagePeers, allowChannel: false)
}
private func updateMessageThreadStatsInternal(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [PeerId], allowChannel: Bool) {
private func updateMessageThreadStatsInternal(transaction: Transaction, threadMessageId: MessageId, difference: Int, addedMessagePeers: [ReplyThreadUserMessage], allowChannel: Bool) {
guard let channel = transaction.getPeer(threadMessageId.peerId) as? TelegramChannel else {
return
}
@ -77,7 +83,24 @@ private func updateMessageThreadStatsInternal(transaction: Transaction, threadMe
loop: for j in 0 ..< attributes.count {
if let attribute = attributes[j] as? ReplyThreadMessageAttribute {
let count = max(0, attribute.count + countDifference)
attributes[j] = ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: attribute.latestUsers, added: addedMessagePeers, isGroup: isGroup, isEmpty: count == 0), commentsPeerId: attribute.commentsPeerId, maxMessageId: attribute.maxMessageId, maxReadMessageId: attribute.maxReadMessageId)
var maxMessageId = attribute.maxMessageId
var maxReadMessageId = attribute.maxReadMessageId
if let maxAddedId = addedMessagePeers.map(\.messageId.id).max() {
if let currentMaxMessageId = maxMessageId {
maxMessageId = max(currentMaxMessageId, maxAddedId)
} else {
maxMessageId = maxAddedId
}
}
if let maxAddedReadId = addedMessagePeers.filter(\.isOutgoing).map(\.messageId.id).max() {
if let currentMaxMessageId = maxReadMessageId {
maxReadMessageId = max(currentMaxMessageId, maxAddedReadId)
} else {
maxReadMessageId = maxAddedReadId
}
}
attributes[j] = ReplyThreadMessageAttribute(count: count, latestUsers: mergeLatestUsers(current: attribute.latestUsers, added: addedMessagePeers.map(\.id), isGroup: isGroup, isEmpty: count == 0), commentsPeerId: attribute.commentsPeerId, maxMessageId: maxMessageId, maxReadMessageId: maxReadMessageId)
} else if let attribute = attributes[j] as? SourceReferenceMessageAttribute {
channelThreadMessageId = attribute.messageId
}

View File

@ -44,6 +44,14 @@ public func updatePeers(transaction: Transaction, peers: [Peer], update: (Peer?,
transaction.updatePeersInternal(peers, update: { previous, updated in
let peerId = updated.id
var updated = updated
if let previous = previous as? TelegramUser, let updatedUser = updated as? TelegramUser {
updated = TelegramUser.merge(lhs: previous, rhs: updatedUser)
} else if let previous = previous as? TelegramChannel, let updatedChannel = updated as? TelegramChannel {
updated = mergeChannel(lhs: previous, rhs: updatedChannel)
}
switch peerId.namespace {
case Namespaces.Peer.CloudUser:
break

View File

@ -303,9 +303,9 @@ public final class AccountContextImpl: AccountContext {
switch location {
case let .peer(peerId):
return .peer(peerId)
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
return .external(messageId.peerId, context.state)
case let .replyThread(data):
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
return .external(data.messageId.peerId, context.state)
}
}
@ -313,8 +313,8 @@ public final class AccountContextImpl: AccountContext {
switch location {
case .peer:
return .single(nil)
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
case let .replyThread(data):
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
return context.maxReadOutgoingMessageId
}
}
@ -323,19 +323,19 @@ public final class AccountContextImpl: AccountContext {
switch location {
case .peer:
let _ = applyMaxReadIndexInteractively(postbox: self.account.postbox, stateManager: self.account.stateManager, index: messageIndex).start()
case let .replyThread(messageId, _, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId):
let context = chatLocationContext(holder: contextHolder, account: self.account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
case let .replyThread(data):
let context = chatLocationContext(holder: contextHolder, account: self.account, data: data)
context.applyMaxReadIndex(messageIndex: messageIndex)
}
}
}
private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) -> ReplyThreadHistoryContext {
private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, account: Account, data: ChatReplyThreadMessage) -> ReplyThreadHistoryContext {
let holder = holder.modify { current in
if let current = current as? ChatLocationContextHolderImpl {
return current
} else {
return ChatLocationContextHolderImpl(account: account, messageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
return ChatLocationContextHolderImpl(account: account, data: data)
}
} as! ChatLocationContextHolderImpl
return holder.context
@ -344,8 +344,8 @@ private func chatLocationContext(holder: Atomic<ChatLocationContextHolder?>, acc
private final class ChatLocationContextHolderImpl: ChatLocationContextHolder {
let context: ReplyThreadHistoryContext
init(account: Account, messageId: MessageId, maxMessage: ChatReplyThreadMessage.MaxMessage, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?) {
self.context = ReplyThreadHistoryContext(account: account, peerId: messageId.peerId, threadMessageId: messageId, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId)
init(account: Account, data: ChatReplyThreadMessage) {
self.context = ReplyThreadHistoryContext(account: account, peerId: data.messageId.peerId, data: data)
}
}

View File

@ -69,8 +69,8 @@ extension ChatLocation {
switch self {
case let .peer(peerId):
return peerId
case let .replyThread(messageId, _, _, _, _):
return messageId.peerId
case let .replyThread(replyThreadMessage):
return replyThreadMessage.messageId.peerId
}
}
}
@ -371,16 +371,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
case let .peer(peerId):
locationBroadcastPanelSource = .peer(peerId)
self.chatLocationInfoData = .peer(Promise())
case let .replyThread(messageId, _, _, _, _):
case let .replyThread(replyThreadMessage):
locationBroadcastPanelSource = .none
let promise = Promise<Message?>()
let key = PostboxViewKey.messages([messageId])
let key = PostboxViewKey.messages([replyThreadMessage.messageId])
promise.set(context.account.postbox.combinedView(keys: [key])
|> map { views -> Message? in
guard let view = views.views[key] as? MessagesView else {
return nil
}
return view.messages[messageId]
return view.messages[replyThreadMessage.messageId]
})
self.chatLocationInfoData = .replyThread(promise)
}
@ -481,8 +481,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
var openChatLocation = strongSelf.chatLocation
if case let .replyThread(messageId, _, _, _, _) = openChatLocation {
if message.threadId != makeMessageThreadId(messageId) {
if case let .replyThread(replyThreadMessage) = openChatLocation {
if message.threadId != makeMessageThreadId(replyThreadMessage.messageId) {
openChatLocation = .peer(message.id.peerId)
}
}
@ -1601,7 +1601,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
if let strongSelf = self {
if case .replyThread(message.id, _, _, _, _) = strongSelf.chatLocation {
if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation, replyThreadMessage.messageId == message.id {
return .none
}
@ -1640,8 +1640,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch strongSelf.chatLocation {
case let .peer(peerId):
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
case let .replyThread(messageId, _, _, _, _):
let peerId = messageId.peerId
case let .replyThread(replyThreadMessage):
let peerId = replyThreadMessage.messageId.peerId
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
}
}, requestRedeliveryOfFailedMessages: { [weak self] id in
@ -2191,11 +2191,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
if let navigationController = strongSelf.navigationController as? NavigationController {
ChatControllerImpl.openMessageReplies(context: strongSelf.context, navigationController: navigationController, present: { c, a in
self?.present(c, in: .window(.root), with: a)
}, messageId: messageId, isChannelPost: isChannelPost, atMessage: nil)
}
strongSelf.openMessageReplies(messageId: messageId, isChannelPost: isChannelPost, atMessage: nil)
}, openReplyThreadOriginalMessage: { [weak self] message in
guard let strongSelf = self else {
return
@ -2211,9 +2207,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let attribute = attribute as? SourceReferenceMessageAttribute {
if let threadMessageId = threadMessageId {
if let navigationController = strongSelf.navigationController as? NavigationController {
ChatControllerImpl.openMessageReplies(context: strongSelf.context, navigationController: navigationController, present: { c, a in
self?.present(c, in: .window(.root), with: a)
}, messageId: threadMessageId, isChannelPost: true, atMessage: attribute.messageId)
strongSelf.openMessageReplies(messageId: threadMessageId, isChannelPost: true, atMessage: attribute.messageId)
}
} else {
strongSelf.navigateToMessage(from: nil, to: .id(attribute.messageId))
@ -2642,18 +2636,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}))
} else if case let .replyThread(messagePromise) = self.chatLocationInfoData {
let onlineMemberCount: Signal<Int32?, NoError> = .single(nil)
let hasScheduledMessages: Signal<Bool, NoError> = .single(false)
self.reportIrrelvantGeoNoticePromise.set(.single(nil))
let isReplyThread: Bool
let replyThreadType: ChatTitleContent.ReplyThreadType
switch chatLocation {
case .peer:
replyThreadType = .replies
case let .replyThread(_, isChannelPost, _, _, _):
isReplyThread = true
if isChannelPost {
case let .replyThread(replyThreadMessage):
if replyThreadMessage.isChannelPost {
replyThreadType = .comments
} else {
replyThreadType = .replies
@ -2997,8 +2988,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch self.chatLocation {
case let .peer(peerId):
activitySpace = PeerActivitySpace(peerId: peerId, threadId: nil)
case let .replyThread(threadMessageId, _, _, _, _):
activitySpace = PeerActivitySpace(peerId: threadMessageId.peerId, threadId: makeMessageThreadId(threadMessageId))
case let .replyThread(replyThreadMessage):
activitySpace = PeerActivitySpace(peerId: replyThreadMessage.messageId.peerId, threadId: makeMessageThreadId(replyThreadMessage.messageId))
}
self.inputActivityDisposable = (self.typingActivityPromise.get()
@ -3416,11 +3407,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} else if let _ = cachedData as? CachedSecretChatData {
}
if case let .replyThread(messageId, _, _, _, _) = strongSelf.chatLocation {
if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation {
if isTopReplyThreadMessageShown {
pinnedMessageId = nil
} else {
pinnedMessageId = messageId
pinnedMessageId = replyThreadMessage.messageId
}
}
@ -5105,7 +5096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let navigationController = strongSelf.effectiveNavigationController {
let subject: ChatControllerSubject? = sourceMessageId.flatMap(ChatControllerSubject.message)
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadResult.messageId, isChannelPost: false, maxMessage: replyThreadResult.maxMessage, maxReadIncomingMessageId: replyThreadResult.maxReadIncomingMessageId, maxReadOutgoingMessageId: replyThreadResult.maxReadOutgoingMessageId), subject: subject, keepStack: .always))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadResult), subject: subject, keepStack: .always))
}
}, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get()))
@ -5168,8 +5159,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let postbox = self.context.account.postbox
let previousPeerCache = Atomic<[PeerId: Peer]>(value: [:])
var activityThreadId: Int64?
if case let .replyThread(messageId, _, _, _, _) = self.chatLocation {
activityThreadId = makeMessageThreadId(messageId)
if case let .replyThread(replyThreadMessage) = self.chatLocation {
activityThreadId = makeMessageThreadId(replyThreadMessage.messageId)
}
self.peerInputActivitiesDisposable = (self.context.account.peerInputActivities(peerId: PeerActivitySpace(peerId: peerId, threadId: activityThreadId))
|> mapToSignal { activities -> Signal<[(Peer, PeerInputActivity)], NoError> in
@ -7566,8 +7557,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch self.chatLocation {
case .peer:
break
case let .replyThread(messageId, _, _, _, _):
defaultReplyMessageId = messageId
case let .replyThread(replyThreadMessage):
defaultReplyMessageId = replyThreadMessage.messageId
}
return messages.map { message in
@ -7609,13 +7600,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
private func sendMessages(_ messages: [EnqueueMessage], commit: Bool = false) {
let peerId: PeerId
switch self.chatLocation {
case let .peer(peerIdValue):
peerId = peerIdValue
case let .replyThread(messageId, _, _, _, _):
peerId = messageId.peerId
}
let peerId: PeerId = self.chatLocation.peerId
if commit || !self.presentationInterfaceState.isScheduledMessages {
self.commitPurposefulAction()
@ -8035,8 +8020,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
switch self.chatLocation {
case .peer:
break
case let .replyThread(messageId, _, _, _, _):
searchTopMsgId = messageId
case let .replyThread(replyThreadMessage):
searchTopMsgId = replyThreadMessage.messageId
}
switch search.domain {
case .everything:
@ -8251,9 +8236,56 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
static func openMessageReplies(context: AccountContext, navigationController: NavigationController, present: @escaping (ViewController, Any?) -> Void, messageId: MessageId, isChannelPost: Bool, atMessage atMessageId: MessageId?) {
private func openMessageReplies(messageId: MessageId, isChannelPost: Bool, atMessage atMessageId: MessageId?) {
guard let navigationController = self.navigationController as? NavigationController else {
return
}
let progressSignal: Signal<Never, NoError> = Signal { [weak self] _ in
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
return EmptyDisposable
}
let previousId = controllerInteraction.currentMessageWithLoadingReplyThread
controllerInteraction.currentMessageWithLoadingReplyThread = messageId
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(messageId)
if let previousId = previousId {
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(previousId)
}
return ActionDisposable {
Queue.mainQueue().async {
guard let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction else {
return
}
if controllerInteraction.currentMessageWithLoadingReplyThread == messageId {
controllerInteraction.currentMessageWithLoadingReplyThread = nil
strongSelf.chatDisplayNode.historyNode.requestMessageUpdate(messageId)
}
}
}
}
|> runOn(.mainQueue())
let progress = (progressSignal
|> delay(0.15, queue: .mainQueue())).start()
self.navigationActionDisposable.set((ChatControllerImpl.openMessageReplies(context: self.context, navigationController: navigationController, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a)
}, messageId: messageId, isChannelPost: isChannelPost, atMessage: atMessageId, displayModalProgress: false)
|> afterDisposed {
progress.dispose()
}).start())
}
static func openMessageReplies(context: AccountContext, navigationController: NavigationController, present: @escaping (ViewController, Any?) -> Void, messageId: MessageId, isChannelPost: Bool, atMessage atMessageId: MessageId?, displayModalProgress: Bool) -> Signal<Never, NoError> {
return Signal { subscriber in
let foundIndex = Promise<ReplyThreadInfo?>()
foundIndex.set(fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId)))
foundIndex.set(fetchAndPreloadReplyThreadInfo(context: context, subject: isChannelPost ? .channelPost(messageId) : .groupMessage(messageId))
|> map(Optional.init)
|> `catch` { _ -> Signal<ReplyThreadInfo?, NoError> in
return .single(nil)
})
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
@ -8261,15 +8293,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let statusController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
if displayModalProgress {
present(statusController, nil)
}
let disposable = (foundIndex.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak statusController] result in
if displayModalProgress {
statusController?.dismiss()
}
if let result = result {
let chatLocation: ChatLocation = .replyThread(threadMessageId: result.message.messageId, isChannelPost: result.isChannelPost, maxMessage: result.message.maxMessage, maxReadIncomingMessageId: result.message.maxReadIncomingMessageId, maxReadOutgoingMessageId: result.message.maxReadOutgoingMessageId)
let chatLocation: ChatLocation = .replyThread(result.message)
let subject: ChatControllerSubject?
if let atMessageId = atMessageId {
@ -8279,13 +8316,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: chatLocation, chatLocationContextHolder: result.contextHolder, subject: subject, activateInput: result.isEmpty, keepStack: .always))
subscriber.putCompletion()
}
})
cancelImpl = { [weak statusController] in
disposable.dispose()
statusController?.dismiss()
subscriber.putCompletion()
}
return ActionDisposable {
cancelImpl?()
}
}
|> runOn(.mainQueue())
}
public func navigateToMessage(messageLocation: NavigateToMessageLocation, animated: Bool, forceInCurrentChat: Bool = false, dropStack: Bool = false, completion: (() -> Void)? = nil, customPresentProgress: ((ViewController, Any?) -> Void)? = nil) {

View File

@ -130,6 +130,7 @@ public final class ChatControllerInteraction {
var stickerSettings: ChatInterfaceStickerSettings
var searchTextHighightState: (String, [MessageIndex])?
var seenOneTimeAnimatedMedia = Set<MessageId>()
var currentMessageWithLoadingReplyThread: MessageId?
init(
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,

View File

@ -136,10 +136,10 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
}
var addedThreadHead = false
if case let .replyThread(messageId, isChannelPost, _, _, _) = location, view.earlierId == nil, !view.holeEarlier, !view.isLoading {
if case let .replyThread(replyThreadMessage) = location, view.earlierId == nil, !view.holeEarlier, !view.isLoading {
loop: for entry in view.additionalData {
switch entry {
case let .message(id, messages) where id == messageId:
case let .message(id, messages) where id == replyThreadMessage.messageId:
if !messages.isEmpty {
let selection: ChatHistoryMessageSelection = .none
@ -172,7 +172,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
let replyCount = view.entries.isEmpty ? 0 : 1
entries.insert(.ReplyCountEntry(messages[0].index, isChannelPost, replyCount, presentationData), at: 1)
entries.insert(.ReplyCountEntry(messages[0].index, replyThreadMessage.isChannelPost, replyCount, presentationData), at: 1)
}
break loop
default:

View File

@ -627,15 +627,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if !isAuxiliaryChat {
additionalData.append(.totalUnreadState)
}
if case let .replyThread(messageId, _, _, _, _) = chatLocation {
additionalData.append(.cachedPeerData(messageId.peerId))
additionalData.append(.peerNotificationSettings(messageId.peerId))
if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: messageId.peerId)))
additionalData.append(.peer(messageId.peerId))
if case let .replyThread(replyThreadMessage) = chatLocation {
additionalData.append(.cachedPeerData(replyThreadMessage.messageId.peerId))
additionalData.append(.peerNotificationSettings(replyThreadMessage.messageId.peerId))
if replyThreadMessage.messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
additionalData.append(.cacheEntry(cachedChannelAdminRanksEntryId(peerId: replyThreadMessage.messageId.peerId)))
additionalData.append(.peer(replyThreadMessage.messageId.peerId))
}
additionalData.append(.message(messageId))
additionalData.append(.message(replyThreadMessage.messageId))
}
let currentViewVersion = Atomic<Int?>(value: nil)
@ -1173,7 +1173,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if hasUnconsumedMention && !hasUnconsumedContent {
messageIdsWithUnseenPersonalMention.append(message.id)
}
if case .replyThread(message.id, _, _, _, _) = self.chatLocation {
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId == message.id {
isTopReplyThreadMessageShownValue = true
}
case let .MessageGroupEntry(_, messages, _):
@ -1203,7 +1203,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if hasUnconsumedMention && !hasUnconsumedContent {
messageIdsWithUnseenPersonalMention.append(message.id)
}
if case .replyThread(message.id, _, _, _, _) = self.chatLocation {
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId == message.id {
isTopReplyThreadMessageShownValue = true
}
}

View File

@ -302,8 +302,8 @@ enum ReplyThreadSubject {
case groupMessage(MessageId)
}
func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject) -> Signal<ReplyThreadInfo?, NoError> {
let message: Signal<ChatReplyThreadMessage?, NoError>
func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThreadSubject) -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> {
let message: Signal<ChatReplyThreadMessage, FetchChannelReplyThreadMessageError>
switch subject {
case let .channelPost(messageId):
message = fetchChannelReplyThreadMessage(account: context.account, messageId: messageId)
@ -312,19 +312,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
}
return message
|> mapToSignal { message -> Signal<ReplyThreadInfo?, NoError> in
guard let message = message else {
return .single(nil)
}
let isChannelPost: Bool
switch subject {
case .channelPost:
isChannelPost = true
case .groupMessage:
isChannelPost = false
}
|> mapToSignal { replyThreadMessage -> Signal<ReplyThreadInfo, FetchChannelReplyThreadMessageError> in
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let preloadSignal = preloadedChatHistoryViewForLocation(
@ -333,13 +321,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
id: 0
),
context: context,
chatLocation: .replyThread(
threadMessageId: message.messageId,
isChannelPost: isChannelPost,
maxMessage: message.maxMessage,
maxReadIncomingMessageId: message.maxReadIncomingMessageId,
maxReadOutgoingMessageId: message.maxReadOutgoingMessageId
),
chatLocation: .replyThread(replyThreadMessage),
chatLocationContextHolder: chatLocationContextHolder,
fixedCombinedReadStates: nil,
tagMask: nil,
@ -362,13 +344,14 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
}
}
|> take(1)
|> map { isEmpty -> ReplyThreadInfo? in
|> map { isEmpty -> ReplyThreadInfo in
return ReplyThreadInfo(
message: message,
isChannelPost: isChannelPost,
message: replyThreadMessage,
isChannelPost: replyThreadMessage.isChannelPost,
isEmpty: isEmpty,
contextHolder: chatLocationContextHolder
)
}
|> castError(FetchChannelReplyThreadMessageError.self)
}
}

View File

@ -468,8 +468,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
}
var isReplyThreadHead = false
if case let .replyThread(messageId, _, _, _, _) = chatPresentationInterfaceState.chatLocation {
isReplyThreadHead = messages[0].id == messageId
if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation {
isReplyThreadHead = messages[0].id == replyThreadMessage.messageId
}
if !isReplyThreadHead, data.canReply {
@ -614,8 +614,12 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replies"), color: theme.actionSheet.primaryTextColor)
}, action: { c, _ in
let foundIndex = Promise<ChatReplyThreadMessage?>()
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel, case .broadcast = channel.info {
foundIndex.set(fetchChannelReplyThreadMessage(account: context.account, messageId: messages[0].id))
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
foundIndex.set(fetchChannelReplyThreadMessage(account: context.account, messageId: messages[0].id)
|> map(Optional.init)
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
return .single(nil)
})
}
c.dismiss(completion: {
if let channel = messages[0].peers[messages[0].id.peerId] as? TelegramChannel {
@ -745,8 +749,8 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
var threadMessageId: MessageId?
if case let .replyThread(replyThread, _, _, _, _) = chatPresentationInterfaceState.chatLocation {
threadMessageId = replyThread
if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation {
threadMessageId = replyThreadMessage.messageId
}
let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id, isThread: threadMessageId != nil)
|> map { result -> String? in

View File

@ -561,15 +561,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} else if incoming {
hasAvatar = true
}
case let .replyThread(messageId, isChannelPost, _, _, _):
if messageId.peerId != item.context.account.peerId {
if messageId.peerId.isGroupOrChannel && item.message.author != nil {
case let .replyThread(replyThreadMessage):
if replyThreadMessage.messageId.peerId != item.context.account.peerId {
if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil {
var isBroadcastChannel = false
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
isBroadcastChannel = true
}
if isChannelPost, messageId == item.message.id {
if replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
isBroadcastChannel = true
}
@ -746,7 +746,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
} else {
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude))
}
@ -1359,6 +1359,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
return
}
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
return
}
if let selectionState = item.controllerInteraction.selectionState {
var selected = false
var incoming = true

View File

@ -837,13 +837,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
var hasAvatar = false
var allowFullWidth = false
let chatLocationPeerId: PeerId
switch item.chatLocation {
case let .peer(peerId):
chatLocationPeerId = peerId
case let .replyThread(messageId, _, _, _, _):
chatLocationPeerId = messageId.peerId
}
let chatLocationPeerId: PeerId = item.chatLocation.peerId
do {
let peerId = chatLocationPeerId
@ -885,7 +879,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
allowFullWidth = true
}
if case let .replyThread(messageId, isChannelPost, _, _, _) = item.chatLocation, isChannelPost, messageId == firstMessage.id {
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == firstMessage.id {
isBroadcastChannel = true
}
@ -1048,7 +1042,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
inlineBotNameString = attribute.title
}
} else if let attribute = attribute as? ReplyMessageAttribute {
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == attribute.messageId {
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == attribute.messageId {
} else {
replyMessage = firstMessage.associatedMessages[attribute.messageId]
}
@ -2953,6 +2947,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
default:
break
}
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
canHaveSelection = false
}
if let selectionState = item.controllerInteraction.selectionState, canHaveSelection {
var selected = false

View File

@ -7,6 +7,7 @@ import SwiftSignalKit
import TelegramCore
import SyncCore
import TelegramPresentationData
import RadialStatusNode
final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
private let separatorNode: ASDisplayNode
@ -17,6 +18,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
private let buttonNode: HighlightTrackingButtonNode
private let avatarsNode: MergedAvatarsNode
private let unreadIconNode: ASImageNode
private var statusNode: RadialStatusNode?
required init() {
self.separatorNode = ASDisplayNode()
@ -257,6 +259,32 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
strongSelf.iconNode.isHidden = !replyPeers.isEmpty
let hasActivity = item.controllerInteraction.currentMessageWithLoadingReplyThread == item.message.id
if hasActivity {
strongSelf.arrowNode.isHidden = true
let statusNode: RadialStatusNode
if let current = strongSelf.statusNode {
statusNode = current
} else {
statusNode = RadialStatusNode(backgroundNodeColor: .clear)
strongSelf.statusNode = statusNode
strongSelf.buttonNode.addSubnode(statusNode)
}
let statusSize = CGSize(width: 20.0, height: 20.0)
statusNode.frame = CGRect(origin: CGPoint(x: boundingWidth - statusSize.width - 11.0, y: 8.0 + topOffset), size: statusSize)
statusNode.transitionToState(.progress(color: messageTheme.accentTextColor, lineWidth: 1.5, value: nil, cancelEnabled: false), animated: false, synchronous: false, completion: {})
} else {
strongSelf.arrowNode.isHidden = false
if let statusNode = strongSelf.statusNode {
strongSelf.statusNode = nil
statusNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak statusNode] _ in
statusNode?.removeFromSupernode()
})
strongSelf.arrowNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
let avatarsFrame = CGRect(origin: CGPoint(x: 13.0, y: 3.0 + topOffset), size: CGSize(width: imageSize * 3.0, height: imageSize))
strongSelf.avatarsNode.frame = avatarsFrame
strongSelf.avatarsNode.updateLayout(size: avatarsFrame.size)

View File

@ -182,8 +182,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
switch item.chatLocation {
case let .peer(peerId):
messagePeerId = peerId
case let .replyThread(messageId, _, _, _, _):
messagePeerId = messageId.peerId
case let .replyThread(replyThreadMessage):
messagePeerId = replyThreadMessage.messageId.peerId
}
do {
@ -194,7 +194,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
isBroadcastChannel = true
}
if case let .replyThread(messageId, isChannelPost, _, _, _) = item.chatLocation, isChannelPost, messageId == item.message.id {
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
isBroadcastChannel = true
}
@ -341,7 +341,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
}
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
} else {
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
}
@ -854,6 +854,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
return
}
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
return
}
if let selectionState = item.controllerInteraction.selectionState {
var selected = false
var incoming = true

View File

@ -276,13 +276,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
var effectiveAuthor: Peer?
let displayAuthorInfo: Bool
let messagePeerId: PeerId
switch chatLocation {
case let .peer(peerId):
messagePeerId = peerId
case let .replyThread(messageId, _, _, _, _):
messagePeerId = messageId.peerId
}
let messagePeerId: PeerId = chatLocation.peerId
do {
let peerId = messagePeerId
@ -333,7 +327,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
isBroadcastChannel = true
}
} else if case let .replyThread(messageId, isChannelPost, _, _, _) = chatLocation, isChannelPost, messageId == message.id {
} else if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == message.id {
isBroadcastChannel = true
}
if !hasActionMedia && !isBroadcastChannel {

View File

@ -249,15 +249,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
} else if incoming {
hasAvatar = true
}
case let .replyThread(messageId, isChannelPost, _, _, _):
if messageId.peerId != item.context.account.peerId {
if messageId.peerId.isGroupOrChannel && item.message.author != nil {
case let .replyThread(replyThreadMessage):
if replyThreadMessage.messageId.peerId != item.context.account.peerId {
if replyThreadMessage.messageId.peerId.isGroupOrChannel && item.message.author != nil {
var isBroadcastChannel = false
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
isBroadcastChannel = true
}
if isChannelPost, messageId == item.message.id {
if replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
isBroadcastChannel = true
}
@ -415,7 +415,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
}
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
if case let .replyThread(replyThreadMessageId, _, _, _, _) = item.chatLocation, replyThreadMessageId == replyAttribute.messageId {
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == replyAttribute.messageId {
} else {
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
}
@ -920,6 +920,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return
}
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
return
}
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
var isEmoji = false
if let item = self.item, item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {

View File

@ -817,9 +817,9 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
if let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(messageId)))
}
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = strongSelf.getNavigationController() {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(threadMessageId: replyThreadMessageId, isChannelPost: isChannelPost, maxMessage: maxMessage, maxReadIncomingMessageId: maxReadIncomingMessageId, maxReadOutgoingMessageId: maxReadOutgoingMessageId), subject: .message(messageId)))
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .replyThread(replyThreadMessage), subject: .message(messageId)))
}
case let .stickerPack(name):
let packReference: StickerPackReference = .name(name)

View File

@ -792,8 +792,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
}
} else if let channel = peer as? TelegramChannel, case .group = channel.info, channel.hasPermission(.canBeAnonymous) {
placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder
} else if case let .replyThread(_, isChannelPost, _, _, _) = interfaceState.chatLocation {
if isChannelPost {
} else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation {
if replyThreadMessage.isChannelPost {
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment
} else {
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderReply

View File

@ -125,8 +125,8 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if message.id.peerId == peerId {
return true
}
case let .replyThread(messageId, _, _, _, _):
if message.id.peerId == messageId.peerId {
case let .replyThread(replyThreadMessage):
if message.id.peerId == replyThreadMessage.messageId.peerId {
return true
}
}

View File

@ -90,11 +90,11 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
navigationController?.pushViewController(controller)
case let .channelMessage(peerId, messageId):
openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId), peekData: nil))
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = navigationController {
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { c, a in
present(c, a)
}, messageId: replyThreadMessageId, isChannelPost: isChannelPost, atMessage: messageId)
}, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true)
}
case let .stickerPack(name):
dismissInput()

View File

@ -59,11 +59,11 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
if let navigationController = controller.navigationController as? NavigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId), subject: .message(messageId)))
}
case let .replyThreadMessage(replyThreadMessageId, isChannelPost, maxMessage, maxReadIncomingMessageId, maxReadOutgoingMessageId, messageId):
case let .replyThreadMessage(replyThreadMessage, messageId):
if let navigationController = controller.navigationController as? NavigationController {
ChatControllerImpl.openMessageReplies(context: context, navigationController: navigationController, present: { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}, messageId: replyThreadMessageId, isChannelPost: isChannelPost, atMessage: messageId)
}, messageId: replyThreadMessage.messageId, isChannelPost: replyThreadMessage.isChannelPost, atMessage: messageId, displayModalProgress: true)
}
case let .stickerPack(name):
let packReference: StickerPackReference = .name(name)

View File

@ -322,11 +322,15 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
case let .replyThread(id, replyId):
let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId)
|> map(Optional.init)
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
return .single(nil)
}
|> map { result -> ResolvedUrl? in
guard let result = result else {
return .channelMessage(peerId: peer.id, messageId: replyThreadMessageId)
}
return .replyThreadMessage(replyThreadMessageId: result.messageId, isChannelPost: true, maxMessage: result.maxMessage, maxReadIncomingMessageId: result.maxReadIncomingMessageId, maxReadOutgoingMessageId: result.maxReadIncomingMessageId, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
return .replyThreadMessage(replyThreadMessage: result, messageId: MessageId(peerId: result.messageId.peerId, namespace: Namespaces.Message.Cloud, id: replyId))
}
}
} else {
@ -368,11 +372,15 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
if let threadId = threadId {
let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId)
return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId)
|> map(Optional.init)
|> `catch` { _ -> Signal<ChatReplyThreadMessage?, NoError> in
return .single(nil)
}
|> map { result -> ResolvedUrl? in
guard let result = result else {
return .channelMessage(peerId: foundPeer.id, messageId: replyThreadMessageId)
}
return .replyThreadMessage(replyThreadMessageId: result.messageId, isChannelPost: true, maxMessage: result.maxMessage, maxReadIncomingMessageId: result.maxReadIncomingMessageId, maxReadOutgoingMessageId: result.maxReadIncomingMessageId, messageId: messageId)
return .replyThreadMessage(replyThreadMessage: result, messageId: messageId)
}
} else {
return .single(.peer(foundPeer.id, .chat(textInputState: nil, subject: .message(messageId), peekData: nil)))

@ -1 +1 @@
Subproject commit e4d49e73cd8206518e7b3dd75d54af0f0ef5b810
Subproject commit 11255bcfff3180210a012f368e2d2bcd169b6877