From c007ff3d2e2bdb5385b7e8b3b6204e673949ad35 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Thu, 10 Jan 2019 18:12:44 +0300 Subject: [PATCH] API updates --- TelegramCore/Api0.swift | 2 +- TelegramCore/Api1.swift | 84 ++--- TelegramCore/Api3.swift | 42 +-- TelegramCore/ApiGroupOrChannel.swift | 4 +- TelegramCore/FetchedMediaResource.swift | 2 +- TelegramCore/InvitationLinks.swift | 58 +-- TelegramCore/MultipartUpload.swift | 6 +- .../PendingMessageUploadedContent.swift | 6 +- TelegramCore/SearchMessages.swift | 345 ++++++++++++------ 9 files changed, 337 insertions(+), 212 deletions(-) diff --git a/TelegramCore/Api0.swift b/TelegramCore/Api0.swift index 91d76f82f7..54d3ad819b 100644 --- a/TelegramCore/Api0.swift +++ b/TelegramCore/Api0.swift @@ -222,8 +222,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) } dict[-1557620115] = { return Api.ChannelParticipant.parse_channelParticipantSelf($0) } dict[-471670279] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) } - dict[1674301556] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) } dict[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) } + dict[1571450403] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) } dict[471043349] = { return Api.contacts.Blocked.parse_blocked($0) } dict[-1878523231] = { return Api.contacts.Blocked.parse_blockedSlice($0) } dict[-55902537] = { return Api.InputDialogPeer.parse_inputDialogPeer($0) } diff --git a/TelegramCore/Api1.swift b/TelegramCore/Api1.swift index 11a78d948b..7960252822 100644 --- a/TelegramCore/Api1.swift +++ b/TelegramCore/Api1.swift @@ -5489,8 +5489,8 @@ extension Api { case channelParticipant(userId: Int32, date: Int32) case channelParticipantSelf(userId: Int32, inviterId: Int32, date: Int32) case channelParticipantCreator(userId: Int32) - case channelParticipantAdmin(flags: Int32, userId: Int32, inviterId: Int32, promotedBy: Int32, date: Int32, adminRights: Api.ChatAdminRights) case channelParticipantBanned(flags: Int32, userId: Int32, kickedBy: Int32, date: Int32, bannedRights: Api.ChatBannedRights) + case channelParticipantAdmin(flags: Int32, userId: Int32, inviterId: Int32?, promotedBy: Int32, date: Int32, adminRights: Api.ChatAdminRights) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -5515,17 +5515,6 @@ extension Api { } serializeInt32(userId, buffer: buffer, boxed: false) break - case .channelParticipantAdmin(let flags, let userId, let inviterId, let promotedBy, let date, let adminRights): - if boxed { - buffer.appendInt32(1674301556) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(userId, buffer: buffer, boxed: false) - serializeInt32(inviterId, buffer: buffer, boxed: false) - serializeInt32(promotedBy, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - adminRights.serialize(buffer, true) - break case .channelParticipantBanned(let flags, let userId, let kickedBy, let date, let bannedRights): if boxed { buffer.appendInt32(470789295) @@ -5536,6 +5525,17 @@ extension Api { serializeInt32(date, buffer: buffer, boxed: false) bannedRights.serialize(buffer, true) break + case .channelParticipantAdmin(let flags, let userId, let inviterId, let promotedBy, let date, let adminRights): + if boxed { + buffer.appendInt32(1571450403) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(userId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(inviterId!, buffer: buffer, boxed: false)} + serializeInt32(promotedBy, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + adminRights.serialize(buffer, true) + break } } @@ -5547,10 +5547,10 @@ extension Api { return ("channelParticipantSelf", [("userId", userId), ("inviterId", inviterId), ("date", date)]) case .channelParticipantCreator(let userId): return ("channelParticipantCreator", [("userId", userId)]) - case .channelParticipantAdmin(let flags, let userId, let inviterId, let promotedBy, let date, let adminRights): - return ("channelParticipantAdmin", [("flags", flags), ("userId", userId), ("inviterId", inviterId), ("promotedBy", promotedBy), ("date", date), ("adminRights", adminRights)]) case .channelParticipantBanned(let flags, let userId, let kickedBy, let date, let bannedRights): return ("channelParticipantBanned", [("flags", flags), ("userId", userId), ("kickedBy", kickedBy), ("date", date), ("bannedRights", bannedRights)]) + case .channelParticipantAdmin(let flags, let userId, let inviterId, let promotedBy, let date, let adminRights): + return ("channelParticipantAdmin", [("flags", flags), ("userId", userId), ("inviterId", inviterId), ("promotedBy", promotedBy), ("date", date), ("adminRights", adminRights)]) } } @@ -5596,34 +5596,6 @@ extension Api { return nil } } - static func parse_channelParticipantAdmin(_ reader: BufferReader) -> ChannelParticipant? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Api.ChatAdminRights? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.ChannelParticipant.channelParticipantAdmin(flags: _1!, userId: _2!, inviterId: _3!, promotedBy: _4!, date: _5!, adminRights: _6!) - } - else { - return nil - } - } static func parse_channelParticipantBanned(_ reader: BufferReader) -> ChannelParticipant? { var _1: Int32? _1 = reader.readInt32() @@ -5649,6 +5621,34 @@ extension Api { return nil } } + static func parse_channelParticipantAdmin(_ reader: BufferReader) -> ChannelParticipant? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Api.ChatAdminRights? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.ChannelParticipant.channelParticipantAdmin(flags: _1!, userId: _2!, inviterId: _3, promotedBy: _4!, date: _5!, adminRights: _6!) + } + else { + return nil + } + } } enum InputDialogPeer: TypeConstructorDescription { diff --git a/TelegramCore/Api3.swift b/TelegramCore/Api3.swift index 62a524679b..f32feceb35 100644 --- a/TelegramCore/Api3.swift +++ b/TelegramCore/Api3.swift @@ -1547,20 +1547,6 @@ extension Api { }) } - static func exportChatInvite(chatId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(2106086025) - serializeInt32(chatId, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("chatId", chatId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in - let reader = BufferReader(buffer) - var result: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - return result - }) - } - static func checkChatInvite(hash: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(1051570619) @@ -2796,6 +2782,20 @@ extension Api { return result }) } + + static func exportChatInvite(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(234312524) + peer.serialize(buffer, true) + return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in + let reader = BufferReader(buffer) + var result: Api.ExportedChatInvite? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite + } + return result + }) + } } struct channels { static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -3053,20 +3053,6 @@ extension Api { }) } - static func exportInvite(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-950663035) - channel.serialize(buffer, true) - return (FunctionDescription(name: "channels.exportInvite", parameters: [("channel", channel)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in - let reader = BufferReader(buffer) - var result: Api.ExportedChatInvite? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite - } - return result - }) - } - static func deleteChannel(channel: Api.InputChannel) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(-1072619549) diff --git a/TelegramCore/ApiGroupOrChannel.swift b/TelegramCore/ApiGroupOrChannel.swift index 2d4036f9b8..138d9790fc 100644 --- a/TelegramCore/ApiGroupOrChannel.swift +++ b/TelegramCore/ApiGroupOrChannel.swift @@ -36,8 +36,8 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { var role: TelegramGroupRole = .member if (flags & (1 << 0)) != 0 { role = .creator - } else if adminRights != nil { - //role = .admin + } else if let adminRights = adminRights { + role = .admin(TelegramChatAdminRights(apiAdminRights: adminRights)) } if (flags & (1 << 5)) != 0 { groupFlags.insert(.deactivated) diff --git a/TelegramCore/FetchedMediaResource.swift b/TelegramCore/FetchedMediaResource.swift index 27e171c6ff..37d2a20158 100644 --- a/TelegramCore/FetchedMediaResource.swift +++ b/TelegramCore/FetchedMediaResource.swift @@ -504,7 +504,7 @@ final class TelegramCloudMediaResourceFetchInfo: MediaResourceFetchInfo { } } -public func fetchedMediaResource(postbox: Postbox, reference: MediaResourceReference, range: (Range, MediaBoxFetchPriority)? = nil, statsCategory: MediaResourceStatsCategory = .generic, reportResultStatus: Bool = false, preferBackgroundReferenceRevalidation: Bool = false, continueInBackground: Bool = false) -> Signal { +public func fetchedMediaResource(postbox: Postbox, reference: MediaResourceReference, range: (Range, MediaBoxFetchPriority)? = nil, statsCategory: MediaResourceStatsCategory = .generic, reportResultStatus: Bool = false, preferBackgroundReferenceRevalidation: Bool = false, continueInBackground: Bool = false) -> Signal { if let (range, priority) = range { return postbox.mediaBox.fetchedResourceData(reference.resource, in: range, priority: priority, parameters: MediaResourceFetchParameters(tag: TelegramMediaResourceFetchTag(statsCategory: statsCategory), info: TelegramCloudMediaResourceFetchInfo(reference: reference, preferBackgroundReferenceRevalidation: preferBackgroundReferenceRevalidation, continueInBackground: continueInBackground))) |> map { _ in .local } diff --git a/TelegramCore/InvitationLinks.swift b/TelegramCore/InvitationLinks.swift index e7f1ece3a0..c4aa009c08 100644 --- a/TelegramCore/InvitationLinks.swift +++ b/TelegramCore/InvitationLinks.swift @@ -11,45 +11,45 @@ import Foundation public func ensuredExistingPeerExportedInvitation(account: Account, peerId: PeerId, revokeExisted: Bool = false) -> Signal { return account.postbox.transaction { transaction -> Signal in - if let peer = transaction.getPeer(peerId) { - if let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) { + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { + if let _ = peer as? TelegramChannel { if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, cachedData.exportedInvitation != nil && !revokeExisted { return .complete() } else { - return account.network.request(Api.functions.channels.exportInvite(channel: inputChannel)) - |> retryRequest - |> mapToSignal { result -> Signal in - return account.postbox.transaction { transaction -> Void in - if let invitation = ExportedInvitation(apiExportedInvite: result) { - transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in - if let current = current as? CachedChannelData { - return current.withUpdatedExportedInvitation(invitation) - } else { - return CachedChannelData().withUpdatedExportedInvitation(invitation) - } - }) - } + return account.network.request(Api.functions.messages.exportChatInvite(peer: inputPeer)) + |> retryRequest + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> Void in + if let invitation = ExportedInvitation(apiExportedInvite: result) { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedChannelData { + return current.withUpdatedExportedInvitation(invitation) + } else { + return CachedChannelData().withUpdatedExportedInvitation(invitation) + } + }) } } + } } - } else if let group = peer as? TelegramGroup { + } else if let _ = peer as? TelegramGroup { if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData, cachedData.exportedInvitation != nil && !revokeExisted { return .complete() } else { - return account.network.request(Api.functions.messages.exportChatInvite(chatId: group.id.id)) - |> retryRequest - |> mapToSignal { result -> Signal in - return account.postbox.transaction { transaction -> Void in - if let invitation = ExportedInvitation(apiExportedInvite: result) { - transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in - if let current = current as? CachedGroupData { - return current.withUpdatedExportedInvitation(invitation) - } else { - return current - } - }) - } + return account.network.request(Api.functions.messages.exportChatInvite(peer: inputPeer)) + |> retryRequest + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> Void in + if let invitation = ExportedInvitation(apiExportedInvite: result) { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedGroupData { + return current.withUpdatedExportedInvitation(invitation) + } else { + return current + } + }) } + } } } } else { diff --git a/TelegramCore/MultipartUpload.swift b/TelegramCore/MultipartUpload.swift index 0d6322de09..49ce18e0a5 100644 --- a/TelegramCore/MultipartUpload.swift +++ b/TelegramCore/MultipartUpload.swift @@ -391,7 +391,7 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload let dataSignal: Signal let headerSize: Int32 - let fetchedResource: Signal + let fetchedResource: Signal switch source { case let .resource(resource): dataSignal = postbox.mediaBox.resourceData(resource.resource, option: .incremental(waitUntilFetchStatus: true)) |> map { MultipartUploadData.resourceData($0) } @@ -454,7 +454,9 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload manager.start() - let fetchedResourceDisposable = fetchedResource.start() + let fetchedResourceDisposable = fetchedResource.start(error: { _ in + subscriber.putError(.generic) + }) return ActionDisposable { manager.cancel() diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 056e2c7c5a..a346a861ed 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -189,7 +189,9 @@ private func maybePredownloadedImageResource(postbox: Postbox, peerId: PeerId, r } } }) - let fetched = postbox.mediaBox.fetchedResource(resource, parameters: nil).start() + let fetched = postbox.mediaBox.fetchedResource(resource, parameters: nil).start(error: { _ in + subscriber.putError(.generic) + }) return ActionDisposable { data.dispose() @@ -609,7 +611,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili var thumbnailFile: Api.InputFile? if case let .file(file) = thumbnail { - //thumbnailFile = file + thumbnailFile = file } if let _ = thumbnailFile { diff --git a/TelegramCore/SearchMessages.swift b/TelegramCore/SearchMessages.swift index ad5f8c9fc9..9bdb642338 100644 --- a/TelegramCore/SearchMessages.swift +++ b/TelegramCore/SearchMessages.swift @@ -15,18 +15,173 @@ public enum SearchMessagesLocation: Equatable { case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?) } -public func searchMessages(account: Account, location: SearchMessagesLocation, query: String, lowerBound: MessageIndex? = nil, limit: Int32 = 100) -> Signal<([Message], [PeerId : CombinedPeerReadState], Int32), NoError> { - let remoteSearchResult: Signal +private struct SearchMessagesPeerState: Equatable { + let messages: [Message] + let readStates: [PeerId: CombinedPeerReadState] + let totalCount: Int32 + let completed: Bool + + static func ==(lhs: SearchMessagesPeerState, rhs: SearchMessagesPeerState) -> Bool { + if lhs.totalCount != rhs.totalCount { + return false + } + if lhs.completed != rhs.completed { + return false + } + if lhs.messages.count != rhs.messages.count { + return false + } + for i in 0 ..< lhs.messages.count { + if lhs.messages[i].id != rhs.messages[i].id { + return false + } + } + return true + } +} + +public struct SearchMessagesResult { + public let messages: [Message] + public let readStates: [PeerId: CombinedPeerReadState] + public let totalCount: Int32 + public let completed: Bool +} + +public struct SearchMessagesState: Equatable { + fileprivate let main: SearchMessagesPeerState + fileprivate let additional: SearchMessagesPeerState? +} + +private func mergedState(transaction: Transaction, state: SearchMessagesPeerState?, result: Api.messages.Messages?) -> SearchMessagesPeerState? { + guard let result = result else { + return state + } + let messages: [Api.Message] + let chats: [Api.Chat] + let users: [Api.User] + let totalCount: Int32 + switch result { + case let .channelMessages(_, _, count, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + totalCount = count + case let .messages(apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + totalCount = Int32(messages.count) + case let .messagesSlice(_, count, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + totalCount = count + case .messagesNotModified: + messages = [] + chats = [] + users = [] + totalCount = 0 + } + + var peers: [PeerId: Peer] = [:] + + for user in users { + if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { + peers[user.id] = user + } + } + + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers[groupOrChannel.id] = groupOrChannel + } + } + + var peerIdsSet: Set = Set() + var readStates: [PeerId: CombinedPeerReadState] = [:] + + var renderedMessages: [Message] = [] + for message in messages { + if let message = StoreMessage(apiMessage: message), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { + renderedMessages.append(renderedMessage) + peerIdsSet.insert(message.id.peerId) + } + } + + for peerId in peerIdsSet { + if let readState = transaction.getCombinedPeerReadState(peerId) { + readStates[peerId] = readState + } + } + + renderedMessages.sort(by: { lhs, rhs in + return MessageIndex(lhs) > MessageIndex(rhs) + }) + + let completed = renderedMessages.isEmpty + if let previous = state { + var currentIds = Set() + var mergedMessages: [Message] = [] + for message in previous.messages { + if currentIds.contains(message.id) { + continue + } + currentIds.insert(message.id) + mergedMessages.append(message) + } + for message in renderedMessages { + if currentIds.contains(message.id) { + continue + } + currentIds.insert(message.id) + mergedMessages.append(message) + } + mergedMessages.sort(by: { lhs, rhs in + return MessageIndex(lhs) > MessageIndex(rhs) + }) + return SearchMessagesPeerState(messages: mergedMessages, readStates: readStates, totalCount: completed ? Int32(mergedMessages.count) : totalCount, completed: completed) + } else { + return SearchMessagesPeerState(messages: renderedMessages, readStates: readStates, totalCount: completed ? Int32(renderedMessages.count) : totalCount, completed: completed) + } +} + +private func mergedResult(_ state: SearchMessagesState) -> SearchMessagesResult { + var messages: [Message] = state.main.messages + if let additional = state.additional { + if state.main.completed { + messages.append(contentsOf: additional.messages) + } else if let lastMessage = state.main.messages.last { + let earliestIndex = MessageIndex(lastMessage) + messages.append(contentsOf: additional.messages.filter({ MessageIndex($0) > earliestIndex })) + } + } + messages.sort(by: { lhs, rhs in + return MessageIndex(lhs) > MessageIndex(rhs) + }) + + var readStates: [PeerId: CombinedPeerReadState] = [:] + for message in messages { + let readState = state.main.readStates[message.id.peerId] ?? state.additional?.readStates[message.id.peerId] + if let readState = readState { + readStates[message.id.peerId] = readState + } + } + + return SearchMessagesResult(messages: messages, readStates: readStates, totalCount: state.main.totalCount + (state.additional?.totalCount ?? 0), completed: state.main.completed && (state.additional?.completed ?? true)) +} + +public func searchMessages(account: Account, location: SearchMessagesLocation, query: String, state: SearchMessagesState?, limit: Int32 = 100) -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> { + let remoteSearchResult: Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> switch location { case let .peer(peerId, fromId, tags): if peerId.namespace == Namespaces.Peer.SecretChat { - return account.postbox.transaction { transaction -> ([Message], [PeerId : CombinedPeerReadState], Int32) in - var readStates: [PeerId : CombinedPeerReadState] = [:] + return account.postbox.transaction { transaction -> (SearchMessagesResult, SearchMessagesState) in + var readStates: [PeerId: CombinedPeerReadState] = [:] if let readState = transaction.getCombinedPeerReadState(peerId) { readStates[peerId] = readState } let result = transaction.searchMessages(peerId: peerId, query: query, tags: tags) - return (result, readStates, Int32(result.count)) + return (SearchMessagesResult(messages: result, readStates: readStates, totalCount: Int32(result.count), completed: true), SearchMessagesState(main: SearchMessagesPeerState(messages: [], readStates: [:], totalCount: 0, completed: true), additional: nil)) } } @@ -45,134 +200,114 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q } else { filter = .inputMessagesFilterEmpty } - - remoteSearchResult = account.postbox.transaction { transaction -> (peer: Peer?, from: Peer?) in - if let fromId = fromId { - return (peer: transaction.getPeer(peerId), from: transaction.getPeer(fromId)) + remoteSearchResult = account.postbox.transaction { transaction -> (peer: Peer, additionalPeer: Peer?, from: Peer?)? in + guard let peer = transaction.getPeer(peerId) else { + return nil } - return (peer: transaction.getPeer(peerId), from: nil) + var additionalPeer: Peer? + if let _ = peer as? TelegramChannel, let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, let migrationReference = cachedData.migrationReference { + additionalPeer = transaction.getPeer(migrationReference.maxMessageId.peerId) + } + if let fromId = fromId { + return (peer: peer, additionalPeer: additionalPeer, from: transaction.getPeer(fromId)) + } + return (peer: peer, additionalPeer: additionalPeer, from: nil) } - |> mapToSignal { values -> Signal in - if let peer = values.peer, let inputPeer = apiInputPeer(peer) { - var fromInputUser: Api.InputUser? = nil - var flags: Int32 = 0 - if let from = values.from { - fromInputUser = apiInputUser(from) - if let _ = fromInputUser { - flags |= (1 << 0) - } + |> mapToSignal { values -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in + guard let values = values else { + return .single((nil, nil)) + } + let peer = values.peer + guard let inputPeer = apiInputPeer(peer) else { + return .single((nil, nil)) + } + var fromInputUser: Api.InputUser? = nil + var flags: Int32 = 0 + if let from = values.from { + fromInputUser = apiInputUser(from) + if let _ = fromInputUser { + flags |= (1 << 0) } - return account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, filter: filter, minDate: 0, maxDate: Int32.max - 1, offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) + } + let peerMessages: Signal + if let completed = state?.main.completed, completed { + peerMessages = .single(nil) + } else { + let lowerBound = state?.main.messages.last.flatMap(MessageIndex.init) + peerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, filter: filter, minDate: 0, maxDate: Int32.max - 1, offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } - } else { - return .never() } + let additionalPeerMessages: Signal + if let inputPeer = values.additionalPeer.flatMap(apiInputPeer) { + let mainCompleted = state?.main.completed ?? false + let hasAdditional = state?.additional != nil + if let completed = state?.additional?.completed, completed { + additionalPeerMessages = .single(nil) + } else if mainCompleted || !hasAdditional { + let lowerBound = state?.additional?.messages.last.flatMap(MessageIndex.init) + additionalPeerMessages = account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, filter: filter, minDate: 0, maxDate: Int32.max - 1, offsetId: lowerBound?.id.id ?? 0, addOffset: 0, limit: limit, maxId: Int32.max - 1, minId: 0, hash: 0)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + } else { + additionalPeerMessages = .single(nil) + } + } else { + additionalPeerMessages = .single(nil) + } + return combineLatest(peerMessages, additionalPeerMessages) } case let .group(groupId): /*feed*/ - remoteSearchResult = .single(nil) + remoteSearchResult = .single((nil, nil)) /*remoteSearchResult = account.network.request(Api.functions.channels.searchFeed(feedId: groupId.rawValue, q: query, offsetDate: 0, offsetPeer: Api.InputPeer.inputPeerEmpty, offsetId: 0, limit: 64), automaticFloodWait: false) |> mapError { _ in } |> map(Optional.init)*/ case .general: - remoteSearchResult = account.postbox.transaction { transaction -> Api.InputPeer in + remoteSearchResult = account.postbox.transaction { transaction -> (MessageIndex?, Api.InputPeer) in + var lowerBound: MessageIndex? + if let state = state, let message = state.main.messages.last { + lowerBound = MessageIndex(message) + } if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) { - return inputPeer + return (lowerBound, inputPeer) } else { - return .inputPeerEmpty + return (lowerBound, .inputPeerEmpty) } } - |> mapToSignal { inputPeer in + |> mapToSignal { (lowerBound, inputPeer) in account.network.request(Api.functions.messages.searchGlobal(q: query, offsetDate: lowerBound?.timestamp ?? 0, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit), automaticFloodWait: false) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) + |> map { result -> (Api.messages.Messages?, Api.messages.Messages?) in + return (result, nil) + } + |> `catch` { _ -> Signal<(Api.messages.Messages?, Api.messages.Messages?), NoError> in + return .single((nil, nil)) } } } - let processedSearchResult = remoteSearchResult - |> mapToSignal { result -> Signal<([Message], [PeerId: CombinedPeerReadState], Int32), NoError> in - guard let result = result else { - return .single(([], [:], 0)) - } - - //assert(false) - let messages: [Api.Message] - let chats: [Api.Chat] - let users: [Api.User] - let totalCount: Int32 - switch result { - case let .channelMessages(_, _, count, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - totalCount = count - case let .messages(apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - totalCount = Int32(messages.count) - case let .messagesSlice(_, count, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - totalCount = count - case .messagesNotModified: - messages = [] - chats = [] - users = [] - totalCount = 0 - } - - return account.postbox.transaction { transaction -> ([Message], [PeerId : CombinedPeerReadState], Int32) in - var peers: [PeerId: Peer] = [:] - - for user in users { - if let user = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) { - peers[user.id] = user - } - } - - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers[groupOrChannel.id] = groupOrChannel - } - } - - var peerIdsSet: Set = Set() - var readStates:[PeerId : CombinedPeerReadState] = [:] - - var renderedMessages: [Message] = [] - for message in messages { - if let message = StoreMessage(apiMessage: message), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) { - renderedMessages.append(renderedMessage) - peerIdsSet.insert(message.id.peerId) - } - } - - for peerId in peerIdsSet { - if let readState = transaction.getCombinedPeerReadState(peerId) { - readStates[peerId] = readState - } - } - - if case .general = location { + return remoteSearchResult + |> mapToSignal { result, additionalResult -> Signal<(SearchMessagesResult, SearchMessagesState), NoError> in + return account.postbox.transaction { transaction -> (SearchMessagesResult, SearchMessagesState) in + var additional: SearchMessagesPeerState? = mergedState(transaction: transaction, state: state?.additional, result: additionalResult) + if state?.additional == nil, case .general = location { let secretMessages = transaction.searchMessages(peerId: nil, query: query, tags: nil) - renderedMessages.append(contentsOf: secretMessages) + var readStates: [PeerId: CombinedPeerReadState] = [:] + for message in secretMessages { + if let readState = transaction.getCombinedPeerReadState(message.id.peerId) { + readStates[message.id.peerId] = readState + } + } + additional = SearchMessagesPeerState(messages: secretMessages, readStates: readStates, totalCount: Int32(secretMessages.count), completed: true) } - renderedMessages.sort(by: { lhs, rhs in - return MessageIndex(lhs) > MessageIndex(rhs) - }) - - return (renderedMessages, readStates, totalCount) + let updatedState = SearchMessagesState(main: mergedState(transaction: transaction, state: state?.main, result: result) ?? SearchMessagesPeerState(messages: [], readStates: [:], totalCount: 0, completed: true), additional: additional) + return (mergedResult(updatedState), updatedState) } } - - return processedSearchResult } public func downloadMessage(postbox: Postbox, network: Network, messageId: MessageId) -> Signal {