diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index fb3cc499eb..742b57c67c 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -151,7 +151,6 @@ D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; }; D033FEB71E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; }; D03B0CB91D62233400955575 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CB81D62233400955575 /* Either.swift */; }; - D03B0CBB1D62233C00955575 /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBA1D62233C00955575 /* MergeLists.swift */; }; D03B0CBD1D62234300955575 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBC1D62234300955575 /* Regex.swift */; }; D03B0CBF1D62234A00955575 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; }; D03B0CC11D62235000955575 /* StringFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CC01D62235000955575 /* StringFormat.swift */; }; @@ -454,7 +453,6 @@ D0B8440D1DAB91CD005F29E1 /* ImageRepresentationsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */; }; D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */; }; D0B8440F1DAB91CD005F29E1 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CB81D62233400955575 /* Either.swift */; }; - D0B844101DAB91CD005F29E1 /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBA1D62233C00955575 /* MergeLists.swift */; }; D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBC1D62234300955575 /* Regex.swift */; }; D0B844121DAB91CD005F29E1 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; }; D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CC01D62235000955575 /* StringFormat.swift */; }; @@ -556,6 +554,8 @@ D0E35A151DE4C6A200BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */; }; D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; }; D0E652201E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; }; + D0E817492010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */; }; + D0E8174A2010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */; }; D0F02CE51E9926C40065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; }; D0F02CE61E9926C50065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; }; D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */; }; @@ -694,7 +694,6 @@ D033FEB21E61F3C000644997 /* ReportPeer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportPeer.swift; sourceTree = ""; }; D033FEB51E61F3F900644997 /* BlockedPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeers.swift; sourceTree = ""; }; D03B0CB81D62233400955575 /* Either.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = ""; }; - D03B0CBA1D62233C00955575 /* MergeLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergeLists.swift; sourceTree = ""; }; D03B0CBC1D62234300955575 /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = ""; }; D03B0CBE1D62234A00955575 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; D03B0CC01D62235000955575 /* StringFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringFormat.swift; sourceTree = ""; }; @@ -930,6 +929,7 @@ D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageWithChatContextResult.swift; sourceTree = ""; }; D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingChatContextResultMessageAttribute.swift; sourceTree = ""; }; D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateAccountPeerName.swift; sourceTree = ""; }; + D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelAdminEventLogContext.swift; sourceTree = ""; }; D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConfigurationUpdates.swift; sourceTree = ""; }; D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeChatInputStateOperation.swift; sourceTree = ""; }; D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeChatInputStateOperations.swift; sourceTree = ""; }; @@ -1090,7 +1090,6 @@ D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */, D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */, D03B0CB81D62233400955575 /* Either.swift */, - D03B0CBA1D62233C00955575 /* MergeLists.swift */, D03B0CBC1D62234300955575 /* Regex.swift */, D03B0CBE1D62234A00955575 /* Log.swift */, D03B0CC01D62235000955575 /* StringFormat.swift */, @@ -1575,6 +1574,7 @@ C23BC3861E9BE3CA00D79F92 /* ImportContact.swift */, C205FEA71EB3B75900455808 /* ExportMessageLink.swift */, C230BEB51EE9A3760029586C /* ChannelAdminEventLogs.swift */, + D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */, D0A472B51F4CBE8B00E0EEDA /* LoadedPeer.swift */, D0DA1D311F7043D50034E892 /* ManagedPendingPeerNotificationSettings.swift */, D02395D51F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift */, @@ -1950,6 +1950,7 @@ D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */, D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */, D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */, + D0E817492010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */, D05452071E7B5093006EEF19 /* LoadedStickerPack.swift in Sources */, D01C7F041EFC1C49008305F1 /* DeviceContact.swift in Sources */, D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */, @@ -2040,7 +2041,6 @@ D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */, D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */, D0C48F391E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */, - D03B0CBB1D62233C00955575 /* MergeLists.swift in Sources */, C239BE971E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift in Sources */, D03B0CC11D62235000955575 /* StringFormat.swift in Sources */, D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */, @@ -2289,6 +2289,7 @@ D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */, D0DFD5E01FCDBCFD0039B3B1 /* CachedSentMediaReferences.swift in Sources */, D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */, + D0E8174A2010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */, D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */, D0AD02E41FFFA14800C1DCFF /* PeerLiveLocationsContext.swift in Sources */, D0613FCB1E60440600202CDB /* InvitationLinks.swift in Sources */, @@ -2324,7 +2325,6 @@ D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */, D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */, D0FA8BB41E201B02001E855B /* ProcessSecretChatIncomingEncryptedOperations.swift in Sources */, - D0B844101DAB91CD005F29E1 /* MergeLists.swift in Sources */, D0F3A8A31E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */, D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */, D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */, diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index fee2cf2f08..fc01e1f9d2 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -802,13 +802,10 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState, if !entities.isEmpty { attributes.append(TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities))) } - var messageText = text + let messageText = text var medias: [Media] = [] - let (mediaText, mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId) - if let mediaText = mediaText { - messageText = mediaText - } + let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId) if let mediaValue = mediaValue { medias.append(mediaValue) } @@ -829,7 +826,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState, updatedState.readInbox(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: maxId)) case let .updateReadHistoryOutbox(peer, maxId, _, _): updatedState.readOutbox(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: maxId)) - case let .updateReadFeed(feedId, maxPosition): + case let .updateReadFeed(_, feedId, maxPosition, unreadCount, unreadMutedCount): switch maxPosition { case let .feedPosition(date, peer, id): let index = MessageIndex(id: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: id), timestamp: date) @@ -1157,6 +1154,8 @@ private func resolveAssociatedMessages(account: Account, state: AccountMutableSt return (messages, chats, users) case let .channelMessages(_, _, _, messages, chats, users): return (messages, chats, users) + case .messagesNotModified: + return ([], [], []) } } |> `catch` { _ in return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], [])) @@ -1318,7 +1317,14 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta } } return account.network.request(Api.functions.messages.getPeerDialogs(peers: inputPeers)) - |> retryRequest + |> map(Optional.init) + |> `catch` { error -> Signal in + if error.errorDescription == "CHANNEL_PRIVATE" && inputPeers.count == 1 { + return .single(nil) + } else { + return .single(nil) + } + } |> map { result -> AccountMutableState in var updatedState = state @@ -1331,74 +1337,76 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta var channelStates: [PeerId: ChannelState] = [:] var notificationSettings: [PeerId: PeerNotificationSettings] = [:] - switch result { - case let .peerDialogs(dialogs, messages, chats, users, _): - dialogsChats.append(contentsOf: chats) - dialogsUsers.append(contentsOf: users) - - loop: for dialog in dialogs { - let apiPeer: Api.Peer - let apiReadInboxMaxId: Int32 - let apiReadOutboxMaxId: Int32 - let apiTopMessage: Int32 - let apiUnreadCount: Int32 - let apiUnreadMentionsCount: Int32 - var apiChannelPts: Int32? - let apiNotificationSettings: Api.PeerNotifySettings - switch dialog { - case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): - apiPeer = peer - apiTopMessage = topMessage - apiReadInboxMaxId = readInboxMaxId - apiReadOutboxMaxId = readOutboxMaxId - apiUnreadCount = unreadCount - apiUnreadMentionsCount = unreadMentionsCount - apiNotificationSettings = peerNotificationSettings - apiChannelPts = pts - case .dialogFeed: - assertionFailure() - continue loop - } + if let result = result { + switch result { + case let .peerDialogs(dialogs, messages, chats, users, _): + dialogsChats.append(contentsOf: chats) + dialogsUsers.append(contentsOf: users) - let peerId: PeerId - switch apiPeer { - case let .peerUser(userId): - peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - case let .peerChat(chatId): - peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) - case let .peerChannel(channelId): - peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - } - - if readStates[peerId] == nil { - readStates[peerId] = [:] - } - readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount) - - if apiTopMessage != 0 { - mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)) - } - - if let apiChannelPts = apiChannelPts { - channelStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts) - } - - notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) - } - - for message in messages { - if let storeMessage = StoreMessage(apiMessage: message) { - var updatedStoreMessage = storeMessage - if case let .Id(id) = storeMessage.id { - if let channelState = channelStates[id.peerId] { - var updatedAttributes = storeMessage.attributes - updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts)) - updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes) - } + loop: for dialog in dialogs { + let apiPeer: Api.Peer + let apiReadInboxMaxId: Int32 + let apiReadOutboxMaxId: Int32 + let apiTopMessage: Int32 + let apiUnreadCount: Int32 + let apiUnreadMentionsCount: Int32 + var apiChannelPts: Int32? + let apiNotificationSettings: Api.PeerNotifySettings + switch dialog { + case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): + apiPeer = peer + apiTopMessage = topMessage + apiReadInboxMaxId = readInboxMaxId + apiReadOutboxMaxId = readOutboxMaxId + apiUnreadCount = unreadCount + apiUnreadMentionsCount = unreadMentionsCount + apiNotificationSettings = peerNotificationSettings + apiChannelPts = pts + case .dialogFeed: + assertionFailure() + continue loop } - storeMessages.append(updatedStoreMessage) + + let peerId: PeerId + switch apiPeer { + case let .peerUser(userId): + peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + case let .peerChat(chatId): + peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId) + case let .peerChannel(channelId): + peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) + } + + if readStates[peerId] == nil { + readStates[peerId] = [:] + } + readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount) + + if apiTopMessage != 0 { + mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage)) + } + + if let apiChannelPts = apiChannelPts { + channelStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts) + } + + notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings) } - } + + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message) { + var updatedStoreMessage = storeMessage + if case let .Id(id) = storeMessage.id { + if let channelState = channelStates[id.peerId] { + var updatedAttributes = storeMessage.attributes + updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts)) + updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes) + } + } + storeMessages.append(updatedStoreMessage) + } + } + } } updatedState.mergeChats(dialogsChats) @@ -1774,10 +1782,16 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif modifier.deleteMessagesInRange(peerId: id.peerId, namespace: id.namespace, minId: 1, maxId: id.id) case let .EditMessage(id, message): modifier.updateMessage(id, update: { previousMessage in + var updatedFlags = message.flags var updatedLocalTags = message.localTags if previousMessage.localTags.contains(.OutgoingLiveLocation) { updatedLocalTags.insert(.OutgoingLiveLocation) } + if message.flags.contains(.Incoming) { + updatedFlags.insert(.Incoming) + } else { + updatedFlags.remove(.Incoming) + } return .update(message.withUpdatedLocalTags(updatedLocalTags)) }) case let .UpdateMedia(id, media): diff --git a/TelegramCore/AccountStateManager.swift b/TelegramCore/AccountStateManager.swift index ba5bd48d39..f845b52384 100644 --- a/TelegramCore/AccountStateManager.swift +++ b/TelegramCore/AccountStateManager.swift @@ -75,8 +75,8 @@ public final class AccountStateManager { return self.isUpdatingValue.get() } - private let notificationMessagesPipe = ValuePipe<[Message]>() - public var notificationMessages: Signal<[Message], NoError> { + private let notificationMessagesPipe = ValuePipe<[(Message, PeerGroupId?)]>() + public var notificationMessages: Signal<[(Message, PeerGroupId?)], NoError> { return self.notificationMessagesPipe.signal() } @@ -460,12 +460,12 @@ public final class AccountStateManager { } } - let signal = self.account.postbox.modify { modifier -> [Message] in - var messages: [Message] = [] + let signal = self.account.postbox.modify { modifier -> [(Message, PeerGroupId?)] in + var messages: [(Message, PeerGroupId?)] = [] for id in events.addedIncomingMessageIds { let (message, notify, _, _) = messageForNotification(modifier: modifier, id: id, alwaysReturnMessage: false) if let message = message, notify { - messages.append(message) + messages.append((message, modifier.getPeerGroupId(message.id.peerId))) } } return messages @@ -473,7 +473,7 @@ public final class AccountStateManager { let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in if let strongSelf = self { - for message in messages { + for (message, _) in messages { Logger.shared.log("State" , "notify: \(String(describing: messageMainPeer(message)?.displayTitle)): \(message.id)") } diff --git a/TelegramCore/AccountViewTracker.swift b/TelegramCore/AccountViewTracker.swift index 5f0a7d15a2..8d39ebcf73 100644 --- a/TelegramCore/AccountViewTracker.swift +++ b/TelegramCore/AccountViewTracker.swift @@ -77,6 +77,10 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal Void in diff --git a/TelegramCore/ApiGroupOrChannel.swift b/TelegramCore/ApiGroupOrChannel.swift index 5f4c0abcd0..2bdb6db61a 100644 --- a/TelegramCore/ApiGroupOrChannel.swift +++ b/TelegramCore/ApiGroupOrChannel.swift @@ -108,7 +108,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { case .chat, .chatEmpty, .chatForbidden, .channelForbidden: return parseTelegramGroupOrChannel(chat: rhs) case let .channel(flags, _, accessHash, title, username, photo, date, version, restrictionReason, adminRights, bannedRights, _, feedId): - if let _ = accessHash { + if accessHash != nil && (flags & (1 << 12)) == 0 { return parseTelegramGroupOrChannel(chat: rhs) } else if let lhs = lhs as? TelegramChannel { var channelFlags = lhs.flags @@ -128,7 +128,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { } 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, peerGroupId: feedId.flatMap { PeerGroupId(rawValue: $0) }) + 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, peerGroupId: lhs.peerGroupId) } else { return nil } diff --git a/TelegramCore/ApplyUpdateMessage.swift b/TelegramCore/ApplyUpdateMessage.swift index c07be545fd..52d98752cd 100644 --- a/TelegramCore/ApplyUpdateMessage.swift +++ b/TelegramCore/ApplyUpdateMessage.swift @@ -50,6 +50,8 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes } } + let channelPts = result.channelPts + var sentStickers: [TelegramMediaFile] = [] var sentGifs: [TelegramMediaFile] = [] @@ -62,14 +64,14 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes } let media: [Media] - let attributes: [MessageAttribute] + var attributes: [MessageAttribute] let text: String if let apiMessage = apiMessage, let updatedMessage = StoreMessage(apiMessage: apiMessage) { media = updatedMessage.media attributes = updatedMessage.attributes text = updatedMessage.text } else if case let .updateShortSentMessage(_, _, _, _, _, apiMedia, entities) = result { - let (_, mediaValue, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, currentMessage.id.peerId) + let (mediaValue, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, currentMessage.id.peerId) if let mediaValue = mediaValue { media = [mediaValue] } else { @@ -95,6 +97,16 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes text = currentMessage.text } + if let channelPts = channelPts { + for i in 0 ..< attributes.count { + if let _ = attributes[i] as? ChannelMessageStateVersionAttribute { + attributes.remove(at: i) + break + } + } + attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) + } + var storeForwardInfo: StoreMessageForwardInfo? if let forwardInfo = currentMessage.forwardInfo { storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) diff --git a/TelegramCore/ChannelAdminEventLogContext.swift b/TelegramCore/ChannelAdminEventLogContext.swift new file mode 100644 index 0000000000..f2422db064 --- /dev/null +++ b/TelegramCore/ChannelAdminEventLogContext.swift @@ -0,0 +1,124 @@ +#if os(macOS) + import PostboxMac + import SwiftSignalKitMac +#else + import Postbox + import SwiftSignalKit +#endif + +public struct ChannelAdminEventLogEntry: Comparable { + public let event: AdminLogEvent + public let peers: [PeerId: Peer] + + public static func ==(lhs: ChannelAdminEventLogEntry, rhs: ChannelAdminEventLogEntry) -> Bool { + return lhs.event == rhs.event + } + + public static func <(lhs: ChannelAdminEventLogEntry, rhs: ChannelAdminEventLogEntry) -> Bool { + return lhs.event < rhs.event + } +} + +public enum ChannelAdminEventLogUpdateType { + case initial + case generic + case load +} + +public final class ChannelAdminEventLogContext { + private let queue: Queue = Queue.mainQueue() + + private let postbox: Postbox + private let network: Network + private let peerId: PeerId + + private var entries: [ChannelAdminEventLogEntry] = [] + private var hasEarlier: Bool = true + private var loadingMoreEarlier: Bool = false + + private var subscribers = Bag<([ChannelAdminEventLogEntry], Bool, ChannelAdminEventLogUpdateType) -> Void>() + + private let loadMoreDisposable = MetaDisposable() + + public init(postbox: Postbox, network: Network, peerId: PeerId) { + self.postbox = postbox + self.network = network + self.peerId = peerId + } + + deinit { + self.loadMoreDisposable.dispose() + } + + public func get() -> Signal<([ChannelAdminEventLogEntry], Bool, ChannelAdminEventLogUpdateType), NoError> { + let queue = self.queue + return Signal { [weak self] subscriber in + if let strongSelf = self { + subscriber.putNext((strongSelf.entries, strongSelf.hasEarlier, .initial)) + + let index = strongSelf.subscribers.add({ entries, hasEarlier, type in + subscriber.putNext((strongSelf.entries, strongSelf.hasEarlier, type)) + }) + + return ActionDisposable { + queue.async { + if let strongSelf = self { + strongSelf.subscribers.remove(index) + } + } + } + } else { + return EmptyDisposable + } + } |> runOn(queue) + } + + public func loadMoreEntries() { + assert(self.queue.isCurrent()) + + if self.loadingMoreEarlier { + return + } + + let maxId: AdminLogEventId + if let last = self.entries.last { + maxId = last.event.id + } else { + maxId = AdminLogEventId.max + } + + self.loadingMoreEarlier = true + self.loadMoreDisposable.set((channelAdminLogEvents(postbox: self.postbox, network: self.network, peerId: self.peerId, maxId: maxId, minId: AdminLogEventId.min, limit: 10, query: nil, filter: nil, admins: nil) + |> deliverOn(self.queue)).start(next: { [weak self] result in + if let strongSelf = self { + var events = result.events.sorted() + if let first = strongSelf.entries.first { + var clipIndex = events.count + for i in (0 ..< events.count).reversed() { + if events[i] >= first.event { + clipIndex = i - 1 + } + } + if clipIndex < events.count { + events.removeSubrange(clipIndex ..< events.count) + } + } + + strongSelf.hasEarlier = !events.isEmpty + + var entries: [ChannelAdminEventLogEntry] = events.map { event in + return ChannelAdminEventLogEntry(event: event, peers: result.peers) + + } + entries.append(contentsOf: strongSelf.entries) + strongSelf.entries = entries + + strongSelf.loadingMoreEarlier = false + + for subscriber in strongSelf.subscribers.copyItems() { + subscriber(strongSelf.entries, strongSelf.hasEarlier, .load) + } + } + })) + } +} diff --git a/TelegramCore/ChannelAdminEventLogs.swift b/TelegramCore/ChannelAdminEventLogs.swift index 8a5944f6cb..26c6662163 100644 --- a/TelegramCore/ChannelAdminEventLogs.swift +++ b/TelegramCore/ChannelAdminEventLogs.swift @@ -8,17 +8,29 @@ public typealias AdminLogEventId = Int64 -public struct AdminLogEvent { +public struct AdminLogEvent: Comparable { public let id: AdminLogEventId - public let peerId:PeerId - public let date:Int32 + public let peerId: PeerId + public let date: Int32 public let action: AdminLogEventAction + + public static func ==(lhs: AdminLogEvent, rhs: AdminLogEvent) -> Bool { + return lhs.id == rhs.id + } + + public static func <(lhs: AdminLogEvent, rhs: AdminLogEvent) -> Bool { + if lhs.date != rhs.date { + return lhs.date < rhs.date + } else { + return lhs.id < rhs.id + } + } } public struct AdminLogEventsResult { public let peerId: PeerId - public let peers:[PeerId: Peer] - public let events:[AdminLogEvent] + public let peers: [PeerId: Peer] + public let events: [AdminLogEvent] } public enum AdminLogEventAction { @@ -29,7 +41,7 @@ public enum AdminLogEventAction { case toggleInvites(Bool) case toggleSignatures(Bool) case updatePinned(Message?) - case editMessage(prev: Message, new:Message) + case editMessage(prev: Message, new: Message) case deleteMessage(Message) case participantJoin case participantLeave @@ -69,129 +81,129 @@ public struct AdminLogEventsFlags : OptionSet { public static let editMessages = AdminLogEventsFlags(rawValue: 1 << 12) public static let deleteMessages = AdminLogEventsFlags(rawValue: 1 << 13) - public static var all:[AdminLogEventsFlags] { + public static var all: [AdminLogEventsFlags] { return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .pinnedMessages, .editMessages, .deleteMessages] } - public static var flags:AdminLogEventsFlags { + public static var flags: AdminLogEventsFlags { return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .pinnedMessages, .editMessages, .deleteMessages] } } private func boolFromApiValue(_ value: Api.Bool) -> Bool { switch value { - case .boolFalse: - return false - case .boolTrue: - return true + case .boolFalse: + return false + case .boolTrue: + return true } } -public func channelAdminLogEvents(_ account:Account, peerId:PeerId, maxId:AdminLogEventId, minId:AdminLogEventId, limit:Int32 = 100, query:String? = nil, filter:AdminLogEventsFlags? = nil, admins:[PeerId]? = nil) -> Signal { - - return account.postbox.modify { modifier -> (Peer?, [Peer]?) in - return (modifier.getPeer(peerId), admins?.flatMap {modifier.getPeer($0)}) - } |> mapError {return .generic} |> mapToSignal { (peer, admins) -> Signal in +public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, maxId: AdminLogEventId, minId: AdminLogEventId, limit: Int32 = 100, query: String? = nil, filter: AdminLogEventsFlags? = nil, admins: [PeerId]? = nil) -> Signal { + return postbox.modify { modifier -> (Peer?, [Peer]?) in + return (modifier.getPeer(peerId), admins?.flatMap { modifier.getPeer($0) }) + } + |> mapError { return .generic } + |> mapToSignal { (peer, admins) -> Signal in + if let peer = peer, let inputChannel = apiInputChannel(peer) { + let inputAdmins = admins?.flatMap {apiInputUser($0)} - if let peer = peer, let inputChannel = apiInputChannel(peer) { - let inputAdmins = admins?.flatMap {apiInputUser($0)} - - var flags:Int32 = 0 - var eventsFilter:Api.ChannelAdminLogEventsFilter? = nil - if let filter = filter { - flags += Int32(1 << 0) - eventsFilter = Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: Int32(filter.rawValue)) - } - if let _ = inputAdmins { - flags += Int32(1 << 1) - } - return account.network.request(Api.functions.channels.getAdminLog(flags: flags, channel: inputChannel, q: query ?? "", eventsFilter: eventsFilter, admins: inputAdmins, maxId: maxId, minId: minId, limit: limit)) |> map { result in - - switch result { - case let .adminLogResults(apiEvents, apiChats, apiUsers): - let peers = (apiChats.flatMap {parseTelegramGroupOrChannel(chat: $0)} + apiUsers.flatMap { TelegramUser(user: $0) } + Array(arrayLiteral: peer)).reduce([:], { current, peer -> [PeerId : Peer] in - var current = current - current[peer.id] = peer - return current - }) - - var events: [AdminLogEvent] = [] - - for event in apiEvents { - switch event { - case let .channelAdminLogEvent(id, date, userId, apiAction): - var action: AdminLogEventAction? - switch apiAction { - case let .channelAdminLogEventActionChangeTitle(prev, new): - action = .changeTitle(prev: prev, new: new) - case let .channelAdminLogEventActionChangeAbout(prev, new): - action = .changeAbout(prev: prev, new: new) - case let .channelAdminLogEventActionChangeUsername(prev, new): - action = .changeUsername(prev: prev, new: new) - case let .channelAdminLogEventActionChangePhoto(prev, new): - action = .changePhoto(prev: imageRepresentationsForApiChatPhoto(prev), new: imageRepresentationsForApiChatPhoto(new)) - case let .channelAdminLogEventActionToggleInvites(new): - action = .toggleInvites(boolFromApiValue(new)) - case let .channelAdminLogEventActionToggleSignatures(new): - action = .toggleSignatures(boolFromApiValue(new)) - case let .channelAdminLogEventActionUpdatePinned(new): - switch new { - case .messageEmpty: - action = .updatePinned(nil) - default: - if let message = StoreMessage(apiMessage: new), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .updatePinned(rendered) - } - } - - case let .channelAdminLogEventActionEditMessage(prev, new): - if let prev = StoreMessage(apiMessage: prev), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new), let newRendered = locallyRenderedMessage(message: new, peers: peers) { - action = .editMessage(prev: prevRendered, new: newRendered) - } - case let .channelAdminLogEventActionDeleteMessage(message): - if let message = StoreMessage(apiMessage: message), let rendered = locallyRenderedMessage(message: message, peers: peers) { - action = .deleteMessage(rendered) - } - case .channelAdminLogEventActionParticipantJoin: - action = .participantJoin - case .channelAdminLogEventActionParticipantLeave: - action = .participantLeave - case let .channelAdminLogEventActionParticipantInvite(participant): - let participant = ChannelParticipant(apiParticipant: participant) - - if let peer = peers[participant.peerId] { - action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer)) - } - case let .channelAdminLogEventActionParticipantToggleBan(prev, new): - let prevParticipant = ChannelParticipant(apiParticipant: prev) - let newParticipant = ChannelParticipant(apiParticipant: new) - - if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { - action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) - } - case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new): - let prevParticipant = ChannelParticipant(apiParticipant: prev) - let newParticipant = ChannelParticipant(apiParticipant: new) - - if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { - action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) - } - case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset): - action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset)) - case let .channelAdminLogEventActionTogglePreHistoryHidden(value): - action = .togglePreHistoryHidden(value == .boolTrue) - } - let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) - if let action = action { - events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action)) - } - } - } - return AdminLogEventsResult(peerId: peerId, peers: peers, events: events) - } - - } |> mapError {_ in return .generic} + var flags: Int32 = 0 + var eventsFilter: Api.ChannelAdminLogEventsFilter? = nil + if let filter = filter { + flags += Int32(1 << 0) + eventsFilter = Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: Int32(filter.rawValue)) } - - return .complete() + if let _ = inputAdmins { + flags += Int32(1 << 1) + } + return network.request(Api.functions.channels.getAdminLog(flags: flags, channel: inputChannel, q: query ?? "", eventsFilter: eventsFilter, admins: inputAdmins, maxId: maxId, minId: minId, limit: limit)) |> map { result in + + switch result { + case let .adminLogResults(apiEvents, apiChats, apiUsers): + let peers = (apiChats.flatMap {parseTelegramGroupOrChannel(chat: $0)} + apiUsers.flatMap { TelegramUser(user: $0) } + Array(arrayLiteral: peer)).reduce([:], { current, peer -> [PeerId : Peer] in + var current = current + current[peer.id] = peer + return current + }) + + var events: [AdminLogEvent] = [] + + for event in apiEvents { + switch event { + case let .channelAdminLogEvent(id, date, userId, apiAction): + var action: AdminLogEventAction? + switch apiAction { + case let .channelAdminLogEventActionChangeTitle(prev, new): + action = .changeTitle(prev: prev, new: new) + case let .channelAdminLogEventActionChangeAbout(prev, new): + action = .changeAbout(prev: prev, new: new) + case let .channelAdminLogEventActionChangeUsername(prev, new): + action = .changeUsername(prev: prev, new: new) + case let .channelAdminLogEventActionChangePhoto(prev, new): + action = .changePhoto(prev: imageRepresentationsForApiChatPhoto(prev), new: imageRepresentationsForApiChatPhoto(new)) + case let .channelAdminLogEventActionToggleInvites(new): + action = .toggleInvites(boolFromApiValue(new)) + case let .channelAdminLogEventActionToggleSignatures(new): + action = .toggleSignatures(boolFromApiValue(new)) + case let .channelAdminLogEventActionUpdatePinned(new): + switch new { + case .messageEmpty: + action = .updatePinned(nil) + default: + if let message = StoreMessage(apiMessage: new), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .updatePinned(rendered) + } + } + + case let .channelAdminLogEventActionEditMessage(prev, new): + if let prev = StoreMessage(apiMessage: prev), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new), let newRendered = locallyRenderedMessage(message: new, peers: peers) { + action = .editMessage(prev: prevRendered, new: newRendered) + } + case let .channelAdminLogEventActionDeleteMessage(message): + if let message = StoreMessage(apiMessage: message), let rendered = locallyRenderedMessage(message: message, peers: peers) { + action = .deleteMessage(rendered) + } + case .channelAdminLogEventActionParticipantJoin: + action = .participantJoin + case .channelAdminLogEventActionParticipantLeave: + action = .participantLeave + case let .channelAdminLogEventActionParticipantInvite(participant): + let participant = ChannelParticipant(apiParticipant: participant) + + if let peer = peers[participant.peerId] { + action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer)) + } + case let .channelAdminLogEventActionParticipantToggleBan(prev, new): + let prevParticipant = ChannelParticipant(apiParticipant: prev) + let newParticipant = ChannelParticipant(apiParticipant: new) + + if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { + action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + } + case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new): + let prevParticipant = ChannelParticipant(apiParticipant: prev) + let newParticipant = ChannelParticipant(apiParticipant: new) + + if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] { + action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer)) + } + case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset): + action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset)) + case let .channelAdminLogEventActionTogglePreHistoryHidden(value): + action = .togglePreHistoryHidden(value == .boolTrue) + } + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + if let action = action { + events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action)) + } + } + } + return AdminLogEventsResult(peerId: peerId, peers: peers, events: events) + } + + } |> mapError {_ in return .generic} + } + + return .complete() } } diff --git a/TelegramCore/ChatContextResult.swift b/TelegramCore/ChatContextResult.swift index 29256e0afd..cb1c8ea648 100644 --- a/TelegramCore/ChatContextResult.swift +++ b/TelegramCore/ChatContextResult.swift @@ -10,7 +10,7 @@ import Foundation #endif public enum ChatContextResultMessage: PostboxCoding, Equatable { - case auto(caption: String, replyMarkup: ReplyMarkupMessageAttribute?) + case auto(caption: String, entities: TextEntitiesMessageAttribute?, replyMarkup: ReplyMarkupMessageAttribute?) case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?) case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?) case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?) @@ -18,7 +18,7 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable { public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("_v", orElse: 0) { case 0: - self = .auto(caption: decoder.decodeStringForKey("c", orElse: ""), replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) + self = .auto(caption: decoder.decodeStringForKey("c", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) case 1: self = .text(text: decoder.decodeStringForKey("t", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, disableUrlPreview: decoder.decodeInt32ForKey("du", orElse: 0) != 0, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) case 2: @@ -26,15 +26,20 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable { case 3: self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute) default: - self = .auto(caption: "", replyMarkup: nil) + self = .auto(caption: "", entities: nil, replyMarkup: nil) } } public func encode(_ encoder: PostboxEncoder) { switch self { - case let .auto(caption, replyMarkup): + case let .auto(caption, entities, replyMarkup): encoder.encodeInt32(0, forKey: "_v") encoder.encodeString(caption, forKey: "c") + if let entities = entities { + encoder.encodeObject(entities, forKey: "e") + } else { + encoder.encodeNil(forKey: "e") + } if let replyMarkup = replyMarkup { encoder.encodeObject(replyMarkup, forKey: "m") } else { @@ -75,11 +80,14 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable { public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool { switch lhs { - case let .auto(lhsCaption, lhsReplyMarkup): - if case let .auto(rhsCaption, rhsReplyMarkup) = rhs { + case let .auto(lhsCaption, lhsEntities, lhsReplyMarkup): + if case let .auto(rhsCaption, rhsEntities, rhsReplyMarkup) = rhs { if lhsCaption != rhsCaption { return false } + if lhsEntities != rhsEntities { + return false + } if lhsReplyMarkup != rhsReplyMarkup { return false } @@ -324,12 +332,16 @@ public final class ChatContextResultCollection: Equatable { extension ChatContextResultMessage { init(apiMessage: Api.BotInlineMessage) { switch apiMessage { - case let .botInlineMessageMediaAuto(_, caption, replyMarkup): + case let .botInlineMessageMediaAuto(_, message, entities, replyMarkup): + var parsedEntities: TextEntitiesMessageAttribute? + if let entities = entities, !entities.isEmpty { + parsedEntities = TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)) + } var parsedReplyMarkup: ReplyMarkupMessageAttribute? if let replyMarkup = replyMarkup { parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup) } - self = .auto(caption: caption, replyMarkup: parsedReplyMarkup) + self = .auto(caption: message, entities: parsedEntities, replyMarkup: parsedReplyMarkup) case let .botInlineMessageText(flags, message, entities, replyMarkup): var parsedEntities: TextEntitiesMessageAttribute? if let entities = entities, !entities.isEmpty { diff --git a/TelegramCore/HistoryViewChannelStateValidation.swift b/TelegramCore/HistoryViewChannelStateValidation.swift index 93bd960078..9df16c6de2 100644 --- a/TelegramCore/HistoryViewChannelStateValidation.swift +++ b/TelegramCore/HistoryViewChannelStateValidation.swift @@ -61,7 +61,7 @@ final class HistoryViewChannelStateValidationContexts { if ranges.isEmpty { ranges = [[id]] } else { - ranges[rangesToInvalidate.count - 1].append(id) + ranges[ranges.count - 1].append(id) } } @@ -122,6 +122,8 @@ final class HistoryViewChannelStateValidationContexts { var addedRanges: [[MessageId]] = [] for messages in rangesToInvalidate { for id in messages { + invalidatedMessageIds.insert(id) + if context.batchReferences[id] != nil { addRangeBreak(&addedRanges) } else { @@ -130,7 +132,7 @@ final class HistoryViewChannelStateValidationContexts { } } - if !addedRanges.isEmpty && addedRanges[rangesToInvalidate.count - 1].isEmpty { + if !addedRanges.isEmpty && addedRanges[addedRanges.count - 1].isEmpty { addedRanges.removeLast() } @@ -141,7 +143,7 @@ final class HistoryViewChannelStateValidationContexts { context.batchReferences[messageId] = batch } - disposable.set((validateBatch(postbox: self.postbox, network: self.network, messageIds: messages) + disposable.set((validateBatch(postbox: self.postbox, network: self.network, messageIds: messages, validatePts: invalidatedPts) |> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch { var completedMessageIds: [MessageId] = [] @@ -215,8 +217,10 @@ final class HistoryViewChannelStateValidationContexts { private func hashForMessages(_ messages: [Message]) -> Int32 { var acc: UInt32 = 0 - for message in messages { - acc = (acc &* 20261) &+ message.id.id + let sorted = messages.sorted(by: { $0.id > $1.id }) + + for message in sorted { + acc = (acc &* 20261) &+ UInt32(message.id.id) var timestamp = message.timestamp inner: for attribute in message.attributes { if let attribute = attribute as? EditedMessageAttribute { @@ -224,25 +228,173 @@ private func hashForMessages(_ messages: [Message]) -> Int32 { break inner } } - acc = (acc &* 20261) &+ timestamp + acc = (acc &* 20261) &+ UInt32(timestamp) } return Int32(bitPattern: acc & UInt32(0x7FFFFFFF)) } -private func validateBatch(postbox: Postbox, network: Network, messageIds: [MessageId]) -> Signal { +private func hashForMessages(_ messages: [StoreMessage]) -> Int32 { + var acc: UInt32 = 0 + + for message in messages { + if case let .Id(id) = message.id { + acc = (acc &* 20261) &+ UInt32(id.id) + var timestamp = message.timestamp + inner: for attribute in message.attributes { + if let attribute = attribute as? EditedMessageAttribute { + timestamp = attribute.date + break inner + } + } + acc = (acc &* 20261) &+ UInt32(timestamp) + } + } + return Int32(bitPattern: acc & UInt32(0x7FFFFFFF)) +} + +private func validateBatch(postbox: Postbox, network: Network, messageIds: [MessageId], validatePts: Int32) -> Signal { guard let peerId = messageIds.first?.peerId else { return .never() } return postbox.modify { modifier -> Signal in if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var messages: [Message] = [] + var previous: [MessageId: Message] = [:] for messageId in messageIds { if let message = modifier.getMessage(messageId) { messages.append(message) + previous[message.id] = message } } let hash = hashForMessages(messages) - return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: messageIds[messageIds.count - 1].id, offsetDate: 0, addOffset: 0, limit: 100, maxId: messageIds[messageIds.cout - 1].id, minId: messageIds[0].id)) + return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: messageIds[messageIds.count - 1].id + 1, offsetDate: 0, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + return postbox.modify { modifier -> Void in + if let result = result { + let messages: [Api.Message] + let chats: [Api.Chat] + let users: [Api.User] + var channelPts: Int32? + + switch result { + case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .messagesSlice(_, messages: apiMessages, chats: apiChats, users: apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + channelPts = pts + case .messagesNotModified: + for id in previous.keys { + modifier.updateMessage(id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + } + var attributes = currentMessage.attributes + /*if let channelPts = channelPts { + for i in 0 ..< attributes.count { + if let _ = attributes[i] as? ChannelMessageStateVersionAttribute { + attributes.remove(at: i) + break + } + } + attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) + }*/ + for i in 0 ..< attributes.count { + if let _ = attributes[i] as? ChannelMessageStateVersionAttribute { + attributes.remove(at: i) + break + } + } + attributes.append(ChannelMessageStateVersionAttribute(pts: validatePts)) + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + } + return + } + + var storeMessages: [StoreMessage] = [] + + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message) { + if let channelPts = channelPts { + var attributes = storeMessage.attributes + attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) + storeMessages.append(storeMessage.withUpdatedAttributes(attributes)) + } else { + storeMessages.append(storeMessage) + } + } + } + + //let updatedHash = hashForMessages(storeMessages) + + var validMessageIds = Set() + for message in storeMessages { + if case let .Id(id) = message.id { + validMessageIds.insert(id) + + if let previousMessage = previous[id] { + var updatedTimestamp = message.timestamp + inner: for attribute in message.attributes { + if let attribute = attribute as? EditedMessageAttribute { + updatedTimestamp = attribute.date + break inner + } + } + + var timestamp = previousMessage.timestamp + inner: for attribute in previousMessage.attributes { + if let attribute = attribute as? EditedMessageAttribute { + timestamp = attribute.date + break inner + } + } + + modifier.updateMessage(id, update: { currentMessage in + if updatedTimestamp != timestamp { + return .update(message) + } else { + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature) + } + var attributes = currentMessage.attributes + if let channelPts = channelPts { + for i in 0 ..< attributes.count { + if let _ = attributes[i] as? ChannelMessageStateVersionAttribute { + attributes.remove(at: i) + break + } + } + attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) + } + return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + } + }) + } + } + } + + for id in previous.keys { + if !validMessageIds.contains(id) { + modifier.deleteMessages([id]) + } + } + } + } + } } else { return .never() } diff --git a/TelegramCore/Holes.swift b/TelegramCore/Holes.swift index dc0fdc44ee..1284f8f4d2 100644 --- a/TelegramCore/Holes.swift +++ b/TelegramCore/Holes.swift @@ -88,7 +88,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos default: assertionFailure() } - request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit))) + request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0)) } else if let filter = messageFilterForTagMask(tagMask) { let offsetId: Int32 let addOffset: Int32 @@ -119,7 +119,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos minId = 1 } - request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) + request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) } else { assertionFailure() request = .never() @@ -145,7 +145,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos if hole.maxIndex.timestamp == Int32.max { let innerOffsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) let innerMaxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - maxIndexRequest = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: innerOffsetId, offsetDate: hole.maxIndex.timestamp, addOffset: 0, limit: 1, maxId: innerMaxId, minId: 1)) + maxIndexRequest = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: innerOffsetId, offsetDate: hole.maxIndex.timestamp, addOffset: 0, limit: 1, maxId: innerMaxId, minId: 1, hash: 0)) |> map(Optional.init) } case let .AroundId(id): @@ -160,7 +160,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos minId = 1 } - request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) + request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) } return combineLatest(request |> retryRequest, maxIndexRequest |> retryRequest) @@ -183,6 +183,10 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos chats = apiChats users = apiUsers channelPts = pts + case .messagesNotModified: + messages = [] + chats = [] + users = [] } var updatedMaxIndex: MessageIndex? if let maxIndexResult = maxIndexResult { @@ -194,6 +198,8 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos maxIndexMessages = apiMessages case let .channelMessages(_, _, _, apiMessages, _, _): maxIndexMessages = apiMessages + case .messagesNotModified: + maxIndexMessages = [] } if !maxIndexMessages.isEmpty { assert(maxIndexMessages.count == 1) @@ -501,7 +507,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, holeIndex: MessageInd offset = single((holeIndex.timestamp, min(holeIndex.id.id, Int32.max - 1) + 1, Api.InputPeer.inputPeerEmpty), NoError.self) return offset |> mapToSignal { (timestamp, id, peer) -> Signal in - let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0)) + let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0, hash: 0)) |> retryRequest |> mapToSignal { result -> Signal in let messages: [Api.Message] @@ -520,6 +526,10 @@ func fetchCallListHole(network: Network, postbox: Postbox, holeIndex: MessageInd messages = apiMessages chats = apiChats users = apiUsers + case .messagesNotModified: + messages = [] + chats = [] + users = [] } return postbox.modify { modifier -> Void in var storeMessages: [StoreMessage] = [] diff --git a/TelegramCore/LoadMessagesIfNecessary.swift b/TelegramCore/LoadMessagesIfNecessary.swift index fe5a872aea..68982f8ff3 100644 --- a/TelegramCore/LoadMessagesIfNecessary.swift +++ b/TelegramCore/LoadMessagesIfNecessary.swift @@ -60,12 +60,14 @@ public func getMessagesLoadIfNecessary(_ messageIds:[MessageId], postbox:Postbox if let signal = signal { signals.append(signal |> map { result in switch result { - case let .messages(messages, chats, users): - return (messages, chats, users) - case let .messagesSlice(_, messages, chats, users): - return (messages, chats, users) - case let .channelMessages(_, _, _, messages, chats, users): - return (messages, chats, users) + case let .messages(messages, chats, users): + return (messages, chats, users) + case let .messagesSlice(_, messages, chats, users): + return (messages, chats, users) + case let .channelMessages(_, _, _, messages, chats, users): + return (messages, chats, users) + case .messagesNotModified: + return ([], [], []) } } |> `catch` { _ in return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], [])) diff --git a/TelegramCore/LoadedPeerFromMessage.swift b/TelegramCore/LoadedPeerFromMessage.swift index b59e415e89..d38e3cf2d4 100644 --- a/TelegramCore/LoadedPeerFromMessage.swift +++ b/TelegramCore/LoadedPeerFromMessage.swift @@ -43,6 +43,8 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M apiUsers = users case let .channelMessages(_, _, _, _, _, users): apiUsers = users + case .messagesNotModified: + apiUsers = [] } for user in apiUsers { diff --git a/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift b/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift index 4055e80c56..a548513889 100644 --- a/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift +++ b/TelegramCore/ManagedSynchronizePinnedChatsOperations.swift @@ -119,8 +119,8 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ switch item { case let .peer(peerId): return peerId.namespace != Namespaces.Peer.SecretChat - case .group: - return false + default: + return true } } let localItemIds = modifier.getPinnedItemIds() @@ -128,17 +128,14 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ switch item { case let .peer(peerId): return peerId.namespace != Namespaces.Peer.SecretChat - case .group: - return false + default: + return true } } return network.request(Api.functions.messages.getPinnedDialogs()) |> retryRequest |> mapToSignal { dialogs -> Signal in - let dialogsChats: [Api.Chat] - let dialogsUsers: [Api.User] - var storeMessages: [StoreMessage] = [] var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] var chatStates: [PeerId: PeerChatState] = [:] @@ -146,10 +143,27 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ var remoteItemIds: [PinnedItemId] = [] + var peers: [Peer] = [] + var peerPresences: [PeerId: PeerPresence] = [:] + switch dialogs { case let .peerDialogs(dialogs, messages, chats, users, _): - dialogsChats = chats - dialogsUsers = users + var channelGroupIds: [PeerId: PeerGroupId] = [:] + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers.append(groupOrChannel) + if let channel = groupOrChannel as? TelegramChannel, let peerGroupId = channel.peerGroupId { + channelGroupIds[channel.id] = peerGroupId + } + } + } + for user in users { + let telegramUser = TelegramUser(user: user) + peers.append(telegramUser) + if let presence = TelegramUserPresence(apiUser: user) { + peerPresences[telegramUser.id] = presence + } + } loop: for dialog in dialogs { let apiPeer: Api.Peer @@ -161,6 +175,9 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ let apiNotificationSettings: Api.PeerNotifySettings switch dialog { case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _): + if channelGroupIds[peer.peerId] != nil { + continue loop + } apiPeer = peer apiTopMessage = topMessage apiReadInboxMaxId = readInboxMaxId @@ -204,21 +221,6 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ } } - var peers: [Peer] = [] - var peerPresences: [PeerId: PeerPresence] = [:] - for chat in dialogsChats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in dialogsUsers { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } - let locallyRemovedFromRemoteItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(localItemIdsWithoutSecretChats)) let remotelyRemovedItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(remoteItemIds)) diff --git a/TelegramCore/MergeLists.swift b/TelegramCore/MergeLists.swift deleted file mode 100644 index fe83f021d7..0000000000 --- a/TelegramCore/MergeLists.swift +++ /dev/null @@ -1,413 +0,0 @@ -import Foundation - -public protocol Identifiable { - associatedtype T: Hashable - var stableId: T { get } -} - -public func mergeListsStable(leftList: [T], rightList: [T]) -> ([Int], [(Int, T, Int?)]) where T: Comparable, T: Identifiable { - var removeIndices: [Int] = [] - var insertItems: [(Int, T, Int?)] = [] - - var currentList = leftList - - var i = 0 - var j = 0 - while true { - let left: T? = i < currentList.count ? currentList[i] : nil - let right: T? = j < rightList.count ? rightList[j] : nil - - if let left = left, let right = right { - if left == right { - i += 1 - j += 1 - } else if left < right { - removeIndices.append(i) - i += 1 - } else { - j += 1 - } - } else if let _ = left { - removeIndices.append(i) - i += 1 - } else if let _ = right { - j += 1 - } else { - break - } - } - - for index in removeIndices.reversed() { - currentList.remove(at: index) - } - - var previousIndices: [T.T: Int] = [:] - i = 0 - for left in leftList { - previousIndices[left.stableId] = i - i += 1 - } - - i = 0 - j = 0 - while true { - let left: T? = i < currentList.count ? currentList[i] : nil - let right: T? = j < rightList.count ? rightList[j] : nil - - if let left = left, let right = right { - if left == right { - i += 1 - j += 1 - } else if left > right { - let previousIndex = previousIndices[right.stableId] - insertItems.append((i, right, previousIndex)) - currentList.insert(right, at: i) - i += 1 - j += 1 - } else { - i += 1 - } - } else if let _ = left { - i += 1 - } else if let right = right { - let previousIndex = previousIndices[right.stableId] - insertItems.append((i, right, previousIndex)) - currentList.insert(right, at: i) - i += 1 - j += 1 - } else { - break - } - } - - assert(currentList == rightList, "currentList == rightList") - - return (removeIndices, insertItems) -} - -public func mergeListsStableWithUpdates(leftList: [T], rightList: [T]) -> ([Int], [(Int, T, Int?)], [(Int, T, Int)]) where T: Comparable, T: Identifiable { - var removeIndices: [Int] = [] - var insertItems: [(Int, T, Int?)] = [] - var updatedIndices: [(Int, T, Int)] = [] - - #if (arch(i386) || arch(x86_64)) && os(iOS) - var existingStableIds: [T.T: T] = [:] - for item in leftList { - if let _ = existingStableIds[item.stableId] { - assertionFailure() - } else { - existingStableIds[item.stableId] = item - } - } - existingStableIds.removeAll() - for item in rightList { - if let other = existingStableIds[item.stableId] { - print("\(other) has the same stableId as \(item): \(item.stableId)") - assertionFailure() - } else { - existingStableIds[item.stableId] = item - } - } - #endif - - var currentList = leftList - - var i = 0 - var previousIndices: [T.T: Int] = [:] - for left in leftList { - previousIndices[left.stableId] = i - i += 1 - } - - i = 0 - var j = 0 - while true { - let left: T? = i < currentList.count ? currentList[i] : nil - let right: T? = j < rightList.count ? rightList[j] : nil - - if let left = left, let right = right { - if left.stableId == right.stableId && left != right { - updatedIndices.append((i, right, previousIndices[left.stableId]!)) - i += 1 - j += 1 - } else { - if left == right { - i += 1 - j += 1 - } else if left < right { - removeIndices.append(i) - i += 1 - } else if !(left > right) { - removeIndices.append(i) - i += 1 - } else { - j += 1 - } - } - } else if let _ = left { - removeIndices.append(i) - i += 1 - } else if let _ = right { - j += 1 - } else { - break - } - } - - //print("remove:\n\(removeIndices)") - - for index in removeIndices.reversed() { - currentList.remove(at: index) - for i in 0 ..< updatedIndices.count { - if updatedIndices[i].0 >= index { - updatedIndices[i].0 -= 1 - } - } - } - - /*print("\n current after removes:\n") - m = 0 - for right in currentList { - print("\(m): \(right.stableId)") - m += 1 - } - - print("update:\n\(updatedIndices.map({ "\($0.0), \($0.1.stableId) (was \($0.2)))" }))")*/ - - i = 0 - j = 0 - var k = 0 - while true { - let left: T? - - //print("i=\(i), j=\(j), k=\(k)") - - if k < updatedIndices.count && updatedIndices[k].0 < i { - //print("updated[k=\(k)]=\(updatedIndices[k].0) right { - //print("\(left.stableId)>\(right.stableId)") - //print("insert \(right.stableId) at \(i)") - //print("i++, j++") - let previousIndex = previousIndices[right.stableId] - insertItems.append((i, right, previousIndex)) - currentList.insert(right, at: i) - if k < updatedIndices.count { - for l in k ..< updatedIndices.count { - updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2) - } - } - - i += 1 - j += 1 - } else { - //print("\(left.stableId)<\(right.stableId)") - //print("i++") - i += 1 - } - } else if let _ = left { - //print("\(left!.stableId)>nil") - //print("i++") - i += 1 - } else if let right = right { - //print("nil<\(right.stableId)") - //print("insert \(right.stableId) at \(i)") - //print("i++") - //print("j++") - let previousIndex = previousIndices[right.stableId] - insertItems.append((i, right, previousIndex)) - currentList.insert(right, at: i) - - if k < updatedIndices.count { - for l in k ..< updatedIndices.count { - updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2) - } - } - - i += 1 - j += 1 - } else { - break - } - } - - for (index, item, _) in updatedIndices { - currentList[index] = item - } - - assert(currentList == rightList, "currentList == rightList") - - return (removeIndices, insertItems, updatedIndices) -} - -public func mergeListsStableWithUpdatesReversed(leftList: [T], rightList: [T]) -> ([Int], [(Int, T, Int?)], [(Int, T, Int)]) where T: Comparable, T: Identifiable { - var removeIndices: [Int] = [] - var insertItems: [(Int, T, Int?)] = [] - var updatedIndices: [(Int, T, Int)] = [] - - #if (arch(i386) || arch(x86_64)) && os(iOS) - var existingStableIds: [T.T: T] = [:] - for item in leftList { - if let _ = existingStableIds[item.stableId] { - assertionFailure() - } else { - existingStableIds[item.stableId] = item - } - } - existingStableIds.removeAll() - for item in rightList { - if let other = existingStableIds[item.stableId] { - print("\(other) has the same stableId as \(item): \(item.stableId)") - assertionFailure() - } else { - existingStableIds[item.stableId] = item - } - } - #endif - - var currentList = leftList - - var i = 0 - var previousIndices: [T.T: Int] = [:] - for left in leftList { - previousIndices[left.stableId] = i - i += 1 - } - - i = 0 - var j = 0 - while true { - let left: T? = i < currentList.count ? currentList[i] : nil - let right: T? = j < rightList.count ? rightList[j] : nil - - if let left = left, let right = right { - if left.stableId == right.stableId && left != right { - updatedIndices.append((i, right, previousIndices[left.stableId]!)) - i += 1 - j += 1 - } else { - if left == right { - i += 1 - j += 1 - } else if left > right { - removeIndices.append(i) - i += 1 - } else if !(left < right) { - removeIndices.append(i) - i += 1 - } else { - j += 1 - } - } - } else if let _ = left { - removeIndices.append(i) - i += 1 - } else if let _ = right { - j += 1 - } else { - break - } - } - - //print("remove:\n\(removeIndices)") - - for index in removeIndices.reversed() { - currentList.remove(at: index) - for i in 0 ..< updatedIndices.count { - if updatedIndices[i].0 >= index { - updatedIndices[i].0 -= 1 - } - } - } - - i = 0 - j = 0 - var k = 0 - while true { - let left: T? - - if k < updatedIndices.count && updatedIndices[k].0 < i { - k += 1 - } - - if k < updatedIndices.count { - if updatedIndices[k].0 == i { - left = updatedIndices[k].1 - } else { - left = i < currentList.count ? currentList[i] : nil - } - } else { - left = i < currentList.count ? currentList[i] : nil - } - - let right: T? = j < rightList.count ? rightList[j] : nil - - if let left = left, let right = right { - if left == right { - i += 1 - j += 1 - } else if left < right { - let previousIndex = previousIndices[right.stableId] - insertItems.append((i, right, previousIndex)) - currentList.insert(right, at: i) - if k < updatedIndices.count { - for l in k ..< updatedIndices.count { - updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2) - } - } - - i += 1 - j += 1 - } else { - i += 1 - } - } else if let _ = left { - i += 1 - } else if let right = right { - let previousIndex = previousIndices[right.stableId] - insertItems.append((i, right, previousIndex)) - currentList.insert(right, at: i) - - if k < updatedIndices.count { - for l in k ..< updatedIndices.count { - updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2) - } - } - - i += 1 - j += 1 - } else { - break - } - } - - for (index, item, _) in updatedIndices { - currentList[index] = item - } - - assert(currentList == rightList, "currentList == rightList") - - return (removeIndices, insertItems, updatedIndices) -} - diff --git a/TelegramCore/OutgoingMessageWithChatContextResult.swift b/TelegramCore/OutgoingMessageWithChatContextResult.swift index 6252ab692f..172df52ae5 100644 --- a/TelegramCore/OutgoingMessageWithChatContextResult.swift +++ b/TelegramCore/OutgoingMessageWithChatContextResult.swift @@ -122,7 +122,10 @@ public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCol attributes.append(InlineBotMessageAttribute(peerId: results.botId)) switch result.message { - case let .auto(caption, replyMarkup): + case let .auto(caption, entities, replyMarkup): + if let entities = entities { + attributes.append(entities) + } if let replyMarkup = replyMarkup { attributes.append(replyMarkup) } diff --git a/TelegramCore/PendingMessageManager.swift b/TelegramCore/PendingMessageManager.swift index 8ac2d92d82..f3150bf8bf 100644 --- a/TelegramCore/PendingMessageManager.swift +++ b/TelegramCore/PendingMessageManager.swift @@ -551,8 +551,20 @@ public final class PendingMessageManager { } if let uniqueId = uniqueId { switch content { - case let .media(inputMedia): - singleMedias.append(.inputSingleMedia(media: inputMedia, randomId: uniqueId)) + case let .media(inputMedia, text): + var messageEntities: [Api.MessageEntity]? + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + messageEntities = apiTextAttributeEntities(attribute, associatedPeers: message.peers) + } + } + + var singleFlags: Int32 = 0 + if let _ = messageEntities { + singleFlags |= 1 << 0 + } + + singleMedias.append(.inputSingleMedia(media: inputMedia, flags: singleFlags, randomId: uniqueId, message: text, entities: messageEntities)) default: return .complete() } @@ -726,8 +738,8 @@ public final class PendingMessageManager { |> mapError { _ -> NoError in return NoError() } - case let .media(inputMedia): - sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil), tag: dependencyTag) + case let .media(inputMedia, text): + sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities), tag: dependencyTag) |> mapError { _ -> NoError in return NoError() } diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index 1eff35fcc1..1e452db333 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -11,7 +11,7 @@ import TelegramCorePrivateModule enum PendingMessageUploadedContent { case text(String) - case media(Api.InputMedia) + case media(Api.InputMedia, String) case forward(ForwardSourceInfoAttribute) case chatContextResult(OutgoingChatContextResultMessageAttribute) case secretMedia(Api.InputEncryptedFile, Int32, SecretFileEncryptionKey) @@ -64,19 +64,19 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods } else if let media = media.first { if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { if let reference = image.reference, case let .cloud(id, accessHash) = reference { - return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), caption: text, ttlSeconds: nil))) + return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), ttlSeconds: nil), text)) } else { return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute)) } } else if let file = media as? TelegramMediaFile { if let resource = file.resource as? CloudDocumentMediaResource { - return .ready(.media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text))) + return .ready(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)) } else { return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file)) } } else if let contact = media as? TelegramMediaContact { let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName) - return .ready(.media(input)) + return .ready(.media(input, text)) } else if let map = media as? TelegramMediaMap { let input: Api.InputMedia if let liveBroadcastingTimeout = map.liveBroadcastingTimeout { @@ -86,7 +86,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods } else { input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude)) } - return .ready(.media(input)) + return .ready(.media(input, text)) } else { return .ready(.text(text)) } @@ -194,7 +194,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI flags |= 1 << 1 ttlSeconds = autoremoveAttribute.timeout } - return .single(.progress(1.0)) |> then(.single(.content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), caption: text, ttlSeconds: ttlSeconds))))) + return .single(.progress(1.0)) |> then(.single(.content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), ttlSeconds: ttlSeconds), text)))) } case let .localReference(key): referenceKey = key @@ -221,14 +221,14 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI |> mapToSignal { inputPeer -> Signal in if let inputPeer = inputPeer { if autoremoveAttribute != nil { - return .single(.content(.media(.inputMediaUploadedPhoto(flags: flags, file: file, caption: text, stickers: nil, ttlSeconds: ttlSeconds)))) + return .single(.content(.media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds), text))) } - return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, caption: text, stickers: nil, ttlSeconds: ttlSeconds))) + return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds))) |> mapError { _ -> PendingMessageUploadError in return .generic } |> mapToSignal { result -> Signal in switch result { - case let .messageMediaPhoto(_, photo, _, _): + case let .messageMediaPhoto(_, photo, _): if let photo = photo, let mediaImage = telegramMediaImageFromApiPhoto(photo), let reference = mediaImage.reference, case let .cloud(id, accessHash) = reference { var flags: Int32 = 0 var ttlSeconds: Int32? @@ -236,7 +236,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI flags |= 1 << 1 ttlSeconds = autoremoveAttribute.timeout } - return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), caption: text, ttlSeconds: ttlSeconds))), media: mediaImage) + return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), ttlSeconds: ttlSeconds), text)), media: mediaImage) } default: break @@ -359,7 +359,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili switch result { case let .media(media): if let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource { - return .single(.progress(1.0)) |> then(.single(.content(.media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text))))) + return .single(.progress(1.0)) |> then(.single(.content(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)))) } case let .localReference(key): referenceKey = key @@ -460,7 +460,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili } if ttlSeconds != nil { - return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: ttlSeconds)))) + return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds), text))) } return postbox.modify { modifier -> Api.InputPeer? in @@ -469,13 +469,13 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili |> mapError { _ -> PendingMessageUploadError in return .generic } |> mapToSignal { inputPeer -> Signal in if let inputPeer = inputPeer { - return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: ttlSeconds))) + return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds))) |> mapError { _ -> PendingMessageUploadError in return .generic } |> mapToSignal { result -> Signal in switch result { - case let .messageMediaDocument(_, document, _, _): + case let .messageMediaDocument(_, document, _): if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource { - return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaDocument(id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text))), media: mediaFile) + return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)), media: mediaFile) } default: break diff --git a/TelegramCore/RequestUserPhotos.swift b/TelegramCore/RequestUserPhotos.swift index 4f94afeeb9..1632000afb 100644 --- a/TelegramCore/RequestUserPhotos.swift +++ b/TelegramCore/RequestUserPhotos.swift @@ -64,7 +64,7 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra } } } else if let peer = peer, let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0)) |> map {Optional($0)} + return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) |> map {Optional($0)} |> mapError {_ in} |> `catch` { return Signal.single(nil) @@ -75,18 +75,22 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra let chats: [Api.Chat] let users: [Api.User] switch result { - case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let .messages(apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let.messagesSlice(_, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers + case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .messages(apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let.messagesSlice(_, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case .messagesNotModified: + messages = [] + chats = [] + users = [] } return account.postbox.modify { modifier -> [Message] in diff --git a/TelegramCore/SearchMessages.swift b/TelegramCore/SearchMessages.swift index 7ec84b3d03..a0029d9eaa 100644 --- a/TelegramCore/SearchMessages.swift +++ b/TelegramCore/SearchMessages.swift @@ -79,7 +79,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q 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: 0, addOffset: 0, limit: 100, maxId: Int32.max - 1, minId: 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: 0, addOffset: 0, limit: 100, maxId: Int32.max - 1, minId: 0, hash: 0)) |> map {Optional($0)} |> `catch` { _ -> Signal in return .single(nil) @@ -117,6 +117,10 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q messages = apiMessages chats = apiChats users = apiUsers + case .messagesNotModified: + messages = [] + chats = [] + users = [] } return account.postbox.modify { modifier -> [Message] in @@ -183,18 +187,22 @@ public func downloadMessage(account: Account, messageId: MessageId) -> Signal Message? in @@ -239,7 +247,7 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta if peerId.namespace == Namespaces.Peer.SecretChat { return .single(modifier.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp)) } else if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) { - return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0)) + return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0)) |> map { result -> MessageId? in let messages: [Api.Message] switch result { @@ -249,6 +257,8 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta messages = apiMessages case let.messagesSlice(_, apiMessages, _, _): messages = apiMessages + case .messagesNotModified: + messages = [] } for message in messages { if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id { diff --git a/TelegramCore/Serialization.swift b/TelegramCore/Serialization.swift index ce0ac83adc..35146f1eb5 100644 --- a/TelegramCore/Serialization.swift +++ b/TelegramCore/Serialization.swift @@ -20,7 +20,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 75 + return 76 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/TelegramCore/SingleMessageView.swift b/TelegramCore/SingleMessageView.swift index af32738512..b3cea87cf5 100644 --- a/TelegramCore/SingleMessageView.swift +++ b/TelegramCore/SingleMessageView.swift @@ -64,6 +64,10 @@ private func fetchMessage(modifier: Modifier, account: Account, messageId: Messa apiMessages = messages apiChats = chats apiUsers = users + case .messagesNotModified: + apiMessages = [] + apiChats = [] + apiUsers = [] } var peers: [PeerId: Peer] = [:] diff --git a/TelegramCore/StandaloneSendMessage.swift b/TelegramCore/StandaloneSendMessage.swift index c5dbe01b6b..de6c3e8998 100644 --- a/TelegramCore/StandaloneSendMessage.swift +++ b/TelegramCore/StandaloneSendMessage.swift @@ -16,7 +16,7 @@ public enum StandaloneMedia { private enum StandaloneMessageContent { case text(String) - case media(Api.InputMedia) + case media(Api.InputMedia, String) } private enum StandaloneSendMessageEvent { @@ -33,25 +33,25 @@ public func standaloneSendMessage(account: Account, peerId: PeerId, text: String if let media = media { switch media { case let .image(data): - content = uploadedImage(account: account, text: text, data: data) + content = uploadedImage(account: account, data: data) |> mapError { _ -> StandaloneSendMessageError in return .generic } |> map { next -> StandaloneSendMessageEvent in switch next { case let .progress(progress): return .progress(progress) case let .result(media): - return .result(.media(media)) + return .result(.media(media, text)) } } case let .file(data, mimeType, attributes): - content = uploadedFile(account: account, text: text, data: data, mimeType: mimeType, attributes: attributes) + content = uploadedFile(account: account, data: data, mimeType: mimeType, attributes: attributes) |> mapError { _ -> StandaloneSendMessageError in return .generic } |> map { next -> StandaloneSendMessageEvent in switch next { case let .progress(progress): return .progress(progress) case let .result(media): - return .result(.media(media)) + return .result(.media(media, text)) } } } @@ -116,8 +116,8 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M |> mapError { _ -> NoError in return NoError() } - case let .media(inputMedia): - sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil)) + case let .media(inputMedia, text): + sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities)) |> mapError { _ -> NoError in return NoError() } @@ -141,13 +141,13 @@ private enum UploadMediaEvent { case result(Api.InputMedia) } -private func uploadedImage(account: Account, text: String, data: Data) -> Signal { +private func uploadedImage(account: Account, data: Data) -> Signal { return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image)) |> mapError { _ -> StandaloneSendMessageError in return .generic } |> map { next -> UploadMediaEvent in switch next { case let .inputFile(inputFile): - return .result(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, caption: text, stickers: nil, ttlSeconds: nil)) + return .result(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil)) case .inputSecretFile: preconditionFailure() case let .progress(progress): @@ -156,13 +156,13 @@ private func uploadedImage(account: Account, text: String, data: Data) -> Signal } } -private func uploadedFile(account: Account, text: String, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal { +private func uploadedFile(account: Account, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal { return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes))) |> mapError { _ -> PendingMessageUploadError in return .generic } |> map { next -> UploadMediaEvent in switch next { case let .inputFile(inputFile): - return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), caption: text, stickers: nil, ttlSeconds: nil)) + return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)) case .inputSecretFile: preconditionFailure() case let .progress(progress): diff --git a/TelegramCore/StandaloneUploadedMedia.swift b/TelegramCore/StandaloneUploadedMedia.swift index 9cd4516de8..f2e98d4c5e 100644 --- a/TelegramCore/StandaloneUploadedMedia.swift +++ b/TelegramCore/StandaloneUploadedMedia.swift @@ -28,11 +28,11 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { inputPeer -> Signal in if let inputPeer = inputPeer { - return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, caption: "", stickers: nil, ttlSeconds: nil))) + return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil))) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { media -> Signal in switch media { - case let .messageMediaPhoto(_, photo, _, _): + case let .messageMediaPhoto(_, photo, _): if let photo = photo { if let mediaImage = telegramMediaImageFromApiPhoto(photo) { return .single(.result(mediaImage)) @@ -67,11 +67,11 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { inputPeer -> Signal in if let inputPeer = inputPeer { - return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), caption: text, stickers: nil, ttlSeconds: nil))) + return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil))) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { media -> Signal in switch media { - case let .messageMediaDocument(_, document, _, _): + case let .messageMediaDocument(_, document, _): if let document = document { if let mediaFile = telegramMediaFileFromApiDocument(document) { return .single(.result(mediaFile)) diff --git a/TelegramCore/StoreMessage_Telegram.swift b/TelegramCore/StoreMessage_Telegram.swift index ae6a0a2d60..a7ae7ea4d3 100644 --- a/TelegramCore/StoreMessage_Telegram.swift +++ b/TelegramCore/StoreMessage_Telegram.swift @@ -266,48 +266,48 @@ extension Api.Message { } } -func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (String?, Media?, Int32?) { +func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?) { if let media = media { switch media { - case let .messageMediaPhoto(_, photo, caption, ttlSeconds): + case let .messageMediaPhoto(_, photo, ttlSeconds): if let photo = photo { if let mediaImage = telegramMediaImageFromApiPhoto(photo) { - return (caption, mediaImage, ttlSeconds) + return (mediaImage, ttlSeconds) } } else { - return (nil, TelegramMediaExpiredContent(data: .image), nil) + return (TelegramMediaExpiredContent(data: .image), nil) } case let .messageMediaContact(phoneNumber, firstName, lastName, userId): let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId) - return (nil, mediaContact, nil) + return (mediaContact, nil) case let .messageMediaGeo(geo): let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil) - return (nil, mediaMap, nil) + return (mediaMap, nil) case let .messageMediaVenue(geo, title, address, provider, venueId, venueType): let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil) - return (nil, mediaMap, nil) + return (mediaMap, nil) case let .messageMediaGeoLive(geo, period): let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period) - return (nil, mediaMap, nil) - case let .messageMediaDocument(_, document, caption, ttlSeconds): + return (mediaMap, nil) + case let .messageMediaDocument(_, document, ttlSeconds): if let document = document { if let mediaFile = telegramMediaFileFromApiDocument(document) { - return (caption, mediaFile, ttlSeconds) + return (mediaFile, ttlSeconds) } } else { - return (nil, TelegramMediaExpiredContent(data: .file), nil) + return (TelegramMediaExpiredContent(data: .file), nil) } case let .messageMediaWebPage(webpage): if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage) { - return (nil, mediaWebpage, nil) + return (mediaWebpage, nil) } case .messageMediaUnsupported: - return (nil, TelegramMediaUnsupported(), nil) + return (TelegramMediaUnsupported(), nil) case .messageMediaEmpty: break case let .messageMediaGame(game): - return (nil, TelegramMediaGame(apiGame: game), nil) + return (TelegramMediaGame(apiGame: game), nil) case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam): var parsedFlags = TelegramMediaInvoiceFlags() if (flags & (1 << 3)) != 0 { @@ -316,11 +316,11 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI if (flags & (1 << 1)) != 0 { parsedFlags.insert(.shippingAddressRequested) } - return (nil, TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil) + return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil) } } - return (nil, nil, nil) + return (nil, nil) } func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [MessageTextEntity] { @@ -432,16 +432,13 @@ extension StoreMessage { } } - var messageText = message + let messageText = message var medias: [Media] = [] var consumableContent: (Bool, Bool)? = nil if let media = media { - let (mediaText, mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId) - if let mediaText = mediaText { - messageText = mediaText - } + let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId) if let mediaValue = mediaValue { medias.append(mediaValue) diff --git a/TelegramCore/SynchronizePeerReadState.swift b/TelegramCore/SynchronizePeerReadState.swift index edb4dbc918..1f858f1ff0 100644 --- a/TelegramCore/SynchronizePeerReadState.swift +++ b/TelegramCore/SynchronizePeerReadState.swift @@ -42,7 +42,7 @@ private func inputSecretChat(postbox: Postbox, peerId: PeerId) -> Signal Signal<(Int32, Int32), VerifyReadStateError> { return inputPeer(postbox: postbox, peerId: peerId) |> mapToSignal { inputPeer -> Signal<(Int32, Int32), VerifyReadStateError> in - return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: Int32.max, offsetDate: Int32.max, addOffset: 0, limit: 1, maxId: Int32.max, minId: 1)) + return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: Int32.max, offsetDate: Int32.max, addOffset: 0, limit: 1, maxId: Int32.max, minId: 1, hash: 0)) |> retryRequest |> mapToSignalPromotingError { result -> Signal<(Int32, Int32), VerifyReadStateError> in let apiMessages: [Api.Message] @@ -53,6 +53,8 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId apiMessages = messages case let .messagesSlice(_, messages, _, _): apiMessages = messages + case .messagesNotModified: + apiMessages = [] } if let message = apiMessages.first, let timestamp = message.timestamp { return .single((message.rawId, timestamp)) diff --git a/TelegramCore/UpdatesApiUtils.swift b/TelegramCore/UpdatesApiUtils.swift index 10c4540713..bda93aa795 100644 --- a/TelegramCore/UpdatesApiUtils.swift +++ b/TelegramCore/UpdatesApiUtils.swift @@ -8,7 +8,7 @@ import Foundation extension Api.MessageMedia { var preCachedResources: [(MediaResource, Data)]? { switch self { - case let .messageMediaPhoto(_, photo, _, _): + case let .messageMediaPhoto(_, photo, _): if let photo = photo { switch photo { case let .photo(_, _, _, _, sizes): @@ -34,7 +34,7 @@ extension Api.MessageMedia { } else { return nil } - case let .messageMediaDocument(_, document, _, _): + case let .messageMediaDocument(_, document, _): if let document = document { switch document { case .document: @@ -283,6 +283,17 @@ extension Api.Update { } return nil } + + var channelPts: Int32? { + switch self { + case let .updateNewChannelMessage(_, pts, _): + return pts + case let .updateEditChannelMessage(_, pts, _): + return pts + default: + return nil + } + } } extension Api.Updates { @@ -421,6 +432,39 @@ extension Api.Updates { return [] } } + + var channelPts: Int32? { + switch self { + case let .updates(updates, _, _, _, _): + var result: Int32? + for update in updates { + if let channelPts = update.channelPts { + if result == nil || channelPts > result! { + result = channelPts + } + } + } + return result + case let .updatesCombined(updates, _, _, _, _, _): + var result: Int32? + for update in updates { + if let channelPts = update.channelPts { + if result == nil || channelPts > result! { + result = channelPts + } + } + } + return result + case let .updateShort(update, _): + if let message = update.channelPts { + return channelPts + } else { + return nil + } + default: + return nil + } + } } extension Api.Updates { diff --git a/TelegramCore/WebpagePreview.swift b/TelegramCore/WebpagePreview.swift index 2d8b487758..c4cd5e5353 100644 --- a/TelegramCore/WebpagePreview.swift +++ b/TelegramCore/WebpagePreview.swift @@ -14,7 +14,7 @@ public func webpagePreview(account: Account, url: String, webpageId: MediaId? = if let webpageId = webpageId, let webpage = modifier.getMedia(webpageId) as? TelegramMediaWebpage { return .single(webpage) } else { - return account.network.request(Api.functions.messages.getWebPagePreview(message: url)) + return account.network.request(Api.functions.messages.getWebPagePreview(flags: 0, message: url, entities: nil)) |> `catch` { _ -> Signal in return .single(.messageMediaEmpty) }