import Postbox import SwiftSignalKit import TelegramApi public final class MessageReadStats { public let reactionCount: Int public let peers: [EnginePeer] public let readTimestamps: [EnginePeer.Id: Int32] public init(reactionCount: Int, peers: [EnginePeer], readTimestamps: [EnginePeer.Id: Int32]) { self.reactionCount = reactionCount self.peers = peers self.readTimestamps = readTimestamps } } func _internal_messageReadStats(account: Account, id: MessageId) -> Signal { return account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(id.peerId) } |> mapToSignal { peer -> Signal in guard let peer, let inputPeer = apiInputPeer(peer) else { return .single(nil) } if id.namespace != Namespaces.Message.Cloud { return .single(nil) } if id.peerId.namespace == Namespaces.Peer.CloudUser { return account.network.request(Api.functions.messages.getOutboxReadDate(peer: inputPeer, msgId: id.id)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> map { result -> MessageReadStats? in guard let result else { return MessageReadStats(reactionCount: 0, peers: [], readTimestamps: [:]) } switch result { case let .outboxReadDate(date): return MessageReadStats(reactionCount: 0, peers: [EnginePeer(peer)], readTimestamps: [peer.id: date]) } } } else { let readPeers: Signal<[(Int64, Int32)]?, NoError> = account.network.request(Api.functions.messages.getMessageReadParticipants(peer: inputPeer, msgId: id.id)) |> map { result -> [(Int64, Int32)]? in var items: [(Int64, Int32)] = [] for item in result { switch item { case let .readParticipantDate(userId, date): items.append((userId, date)) } } return items } |> `catch` { _ -> Signal<[(Int64, Int32)]?, NoError> in return .single(nil) } let reactionCount: Signal = account.network.request(Api.functions.messages.getMessageReactionsList(flags: 0, peer: inputPeer, id: id.id, reaction: nil, offset: nil, limit: 1)) |> map { result -> Int in switch result { case let .messageReactionsList(_, count, _, _, _, _): return Int(count) } } |> `catch` { _ -> Signal in return .single(0) } return combineLatest(readPeers, reactionCount) |> mapToSignal { result, reactionCount -> Signal in return account.postbox.transaction { transaction -> (peerIds: [PeerId], readTimestamps: [PeerId: Int32], missingPeerIds: [PeerId]) in var peerIds: [PeerId] = [] var readTimestamps: [PeerId: Int32] = [:] var missingPeerIds: [PeerId] = [] let authorId = transaction.getMessage(id)?.author?.id if let result = result { for (id, timestamp) in result { let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)) readTimestamps[peerId] = timestamp if peerId == account.peerId { continue } if peerId == authorId { continue } peerIds.append(peerId) if transaction.getPeer(peerId) == nil { missingPeerIds.append(peerId) } } } return (peerIds: peerIds, readTimestamps: readTimestamps, missingPeerIds: missingPeerIds) } |> mapToSignal { peerIds, readTimestamps, missingPeerIds -> Signal in if missingPeerIds.isEmpty || id.peerId.namespace != Namespaces.Peer.CloudChannel { return account.postbox.transaction { transaction -> MessageReadStats? in return MessageReadStats(reactionCount: reactionCount, peers: peerIds.compactMap { peerId -> EnginePeer? in return transaction.getPeer(peerId).flatMap(EnginePeer.init) }, readTimestamps: readTimestamps) } } else { return _internal_channelMembers(postbox: account.postbox, network: account.network, accountPeerId: account.peerId, peerId: id.peerId, category: .recent(.all), offset: 0, limit: 50, hash: 0) |> mapToSignal { _ -> Signal in return account.postbox.transaction { transaction -> MessageReadStats? in return MessageReadStats(reactionCount: reactionCount, peers: peerIds.compactMap { peerId -> EnginePeer? in return transaction.getPeer(peerId).flatMap(EnginePeer.init) }, readTimestamps: readTimestamps) } } } } } } } }