mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-26 01:52:25 +00:00
Merge commit '50c01586839b0113730b0aaa9a4011b954868da2'
This commit is contained in:
commit
3024d9c43a
@ -151,7 +151,6 @@
|
|||||||
D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; };
|
D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; };
|
||||||
D033FEB71E61F3F900644997 /* 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 */; };
|
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 */; };
|
D03B0CBD1D62234300955575 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBC1D62234300955575 /* Regex.swift */; };
|
||||||
D03B0CBF1D62234A00955575 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; };
|
D03B0CBF1D62234A00955575 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; };
|
||||||
D03B0CC11D62235000955575 /* StringFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CC01D62235000955575 /* StringFormat.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 */; };
|
D0B8440D1DAB91CD005F29E1 /* ImageRepresentationsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */; };
|
||||||
D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */; };
|
D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */; };
|
||||||
D0B8440F1DAB91CD005F29E1 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CB81D62233400955575 /* Either.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 */; };
|
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBC1D62234300955575 /* Regex.swift */; };
|
||||||
D0B844121DAB91CD005F29E1 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; };
|
D0B844121DAB91CD005F29E1 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; };
|
||||||
D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CC01D62235000955575 /* StringFormat.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 */; };
|
D0E35A151DE4C6A200BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */; };
|
||||||
D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; };
|
D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; };
|
||||||
D0E652201E3A364A004EEA91 /* 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 */; };
|
D0F02CE51E9926C40065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; };
|
||||||
D0F02CE61E9926C50065DEE2 /* 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 */; };
|
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 = "<group>"; };
|
D033FEB21E61F3C000644997 /* ReportPeer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportPeer.swift; sourceTree = "<group>"; };
|
||||||
D033FEB51E61F3F900644997 /* BlockedPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeers.swift; sourceTree = "<group>"; };
|
D033FEB51E61F3F900644997 /* BlockedPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeers.swift; sourceTree = "<group>"; };
|
||||||
D03B0CB81D62233400955575 /* Either.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = "<group>"; };
|
D03B0CB81D62233400955575 /* Either.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = "<group>"; };
|
||||||
D03B0CBA1D62233C00955575 /* MergeLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergeLists.swift; sourceTree = "<group>"; };
|
|
||||||
D03B0CBC1D62234300955575 /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = "<group>"; };
|
D03B0CBC1D62234300955575 /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = "<group>"; };
|
||||||
D03B0CBE1D62234A00955575 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
D03B0CBE1D62234A00955575 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||||
D03B0CC01D62235000955575 /* StringFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringFormat.swift; sourceTree = "<group>"; };
|
D03B0CC01D62235000955575 /* StringFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringFormat.swift; sourceTree = "<group>"; };
|
||||||
@ -930,6 +929,7 @@
|
|||||||
D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageWithChatContextResult.swift; sourceTree = "<group>"; };
|
D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageWithChatContextResult.swift; sourceTree = "<group>"; };
|
||||||
D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingChatContextResultMessageAttribute.swift; sourceTree = "<group>"; };
|
D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingChatContextResultMessageAttribute.swift; sourceTree = "<group>"; };
|
||||||
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateAccountPeerName.swift; sourceTree = "<group>"; };
|
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateAccountPeerName.swift; sourceTree = "<group>"; };
|
||||||
|
D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelAdminEventLogContext.swift; sourceTree = "<group>"; };
|
||||||
D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConfigurationUpdates.swift; sourceTree = "<group>"; };
|
D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConfigurationUpdates.swift; sourceTree = "<group>"; };
|
||||||
D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeChatInputStateOperation.swift; sourceTree = "<group>"; };
|
D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeChatInputStateOperation.swift; sourceTree = "<group>"; };
|
||||||
D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeChatInputStateOperations.swift; sourceTree = "<group>"; };
|
D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeChatInputStateOperations.swift; sourceTree = "<group>"; };
|
||||||
@ -1090,7 +1090,6 @@
|
|||||||
D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */,
|
D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */,
|
||||||
D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */,
|
D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */,
|
||||||
D03B0CB81D62233400955575 /* Either.swift */,
|
D03B0CB81D62233400955575 /* Either.swift */,
|
||||||
D03B0CBA1D62233C00955575 /* MergeLists.swift */,
|
|
||||||
D03B0CBC1D62234300955575 /* Regex.swift */,
|
D03B0CBC1D62234300955575 /* Regex.swift */,
|
||||||
D03B0CBE1D62234A00955575 /* Log.swift */,
|
D03B0CBE1D62234A00955575 /* Log.swift */,
|
||||||
D03B0CC01D62235000955575 /* StringFormat.swift */,
|
D03B0CC01D62235000955575 /* StringFormat.swift */,
|
||||||
@ -1575,6 +1574,7 @@
|
|||||||
C23BC3861E9BE3CA00D79F92 /* ImportContact.swift */,
|
C23BC3861E9BE3CA00D79F92 /* ImportContact.swift */,
|
||||||
C205FEA71EB3B75900455808 /* ExportMessageLink.swift */,
|
C205FEA71EB3B75900455808 /* ExportMessageLink.swift */,
|
||||||
C230BEB51EE9A3760029586C /* ChannelAdminEventLogs.swift */,
|
C230BEB51EE9A3760029586C /* ChannelAdminEventLogs.swift */,
|
||||||
|
D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */,
|
||||||
D0A472B51F4CBE8B00E0EEDA /* LoadedPeer.swift */,
|
D0A472B51F4CBE8B00E0EEDA /* LoadedPeer.swift */,
|
||||||
D0DA1D311F7043D50034E892 /* ManagedPendingPeerNotificationSettings.swift */,
|
D0DA1D311F7043D50034E892 /* ManagedPendingPeerNotificationSettings.swift */,
|
||||||
D02395D51F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift */,
|
D02395D51F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift */,
|
||||||
@ -1950,6 +1950,7 @@
|
|||||||
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
||||||
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
|
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
|
||||||
D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */,
|
D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */,
|
||||||
|
D0E817492010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */,
|
||||||
D05452071E7B5093006EEF19 /* LoadedStickerPack.swift in Sources */,
|
D05452071E7B5093006EEF19 /* LoadedStickerPack.swift in Sources */,
|
||||||
D01C7F041EFC1C49008305F1 /* DeviceContact.swift in Sources */,
|
D01C7F041EFC1C49008305F1 /* DeviceContact.swift in Sources */,
|
||||||
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
||||||
@ -2040,7 +2041,6 @@
|
|||||||
D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */,
|
D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */,
|
||||||
D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */,
|
D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */,
|
||||||
D0C48F391E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */,
|
D0C48F391E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */,
|
||||||
D03B0CBB1D62233C00955575 /* MergeLists.swift in Sources */,
|
|
||||||
C239BE971E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift in Sources */,
|
C239BE971E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift in Sources */,
|
||||||
D03B0CC11D62235000955575 /* StringFormat.swift in Sources */,
|
D03B0CC11D62235000955575 /* StringFormat.swift in Sources */,
|
||||||
D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */,
|
D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */,
|
||||||
@ -2289,6 +2289,7 @@
|
|||||||
D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */,
|
D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */,
|
||||||
D0DFD5E01FCDBCFD0039B3B1 /* CachedSentMediaReferences.swift in Sources */,
|
D0DFD5E01FCDBCFD0039B3B1 /* CachedSentMediaReferences.swift in Sources */,
|
||||||
D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */,
|
D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */,
|
||||||
|
D0E8174A2010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */,
|
||||||
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||||
D0AD02E41FFFA14800C1DCFF /* PeerLiveLocationsContext.swift in Sources */,
|
D0AD02E41FFFA14800C1DCFF /* PeerLiveLocationsContext.swift in Sources */,
|
||||||
D0613FCB1E60440600202CDB /* InvitationLinks.swift in Sources */,
|
D0613FCB1E60440600202CDB /* InvitationLinks.swift in Sources */,
|
||||||
@ -2324,7 +2325,6 @@
|
|||||||
D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */,
|
D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */,
|
||||||
D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */,
|
D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */,
|
||||||
D0FA8BB41E201B02001E855B /* ProcessSecretChatIncomingEncryptedOperations.swift in Sources */,
|
D0FA8BB41E201B02001E855B /* ProcessSecretChatIncomingEncryptedOperations.swift in Sources */,
|
||||||
D0B844101DAB91CD005F29E1 /* MergeLists.swift in Sources */,
|
|
||||||
D0F3A8A31E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */,
|
D0F3A8A31E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */,
|
||||||
D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
||||||
D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */,
|
D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */,
|
||||||
|
|||||||
@ -802,13 +802,10 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
|||||||
if !entities.isEmpty {
|
if !entities.isEmpty {
|
||||||
attributes.append(TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)))
|
attributes.append(TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)))
|
||||||
}
|
}
|
||||||
var messageText = text
|
let messageText = text
|
||||||
var medias: [Media] = []
|
var medias: [Media] = []
|
||||||
|
|
||||||
let (mediaText, mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
||||||
if let mediaText = mediaText {
|
|
||||||
messageText = mediaText
|
|
||||||
}
|
|
||||||
if let mediaValue = mediaValue {
|
if let mediaValue = mediaValue {
|
||||||
medias.append(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))
|
updatedState.readInbox(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: maxId))
|
||||||
case let .updateReadHistoryOutbox(peer, maxId, _, _):
|
case let .updateReadHistoryOutbox(peer, maxId, _, _):
|
||||||
updatedState.readOutbox(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: 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 {
|
switch maxPosition {
|
||||||
case let .feedPosition(date, peer, id):
|
case let .feedPosition(date, peer, id):
|
||||||
let index = MessageIndex(id: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: id), timestamp: date)
|
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)
|
return (messages, chats, users)
|
||||||
case let .channelMessages(_, _, _, messages, chats, users):
|
case let .channelMessages(_, _, _, messages, chats, users):
|
||||||
return (messages, chats, users)
|
return (messages, chats, users)
|
||||||
|
case .messagesNotModified:
|
||||||
|
return ([], [], [])
|
||||||
}
|
}
|
||||||
} |> `catch` { _ in
|
} |> `catch` { _ in
|
||||||
return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], []))
|
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))
|
return account.network.request(Api.functions.messages.getPeerDialogs(peers: inputPeers))
|
||||||
|> retryRequest
|
|> map(Optional.init)
|
||||||
|
|> `catch` { error -> Signal<Api.messages.PeerDialogs?, Void> in
|
||||||
|
if error.errorDescription == "CHANNEL_PRIVATE" && inputPeers.count == 1 {
|
||||||
|
return .single(nil)
|
||||||
|
} else {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|> map { result -> AccountMutableState in
|
|> map { result -> AccountMutableState in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
|
|
||||||
@ -1331,6 +1337,7 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta
|
|||||||
var channelStates: [PeerId: ChannelState] = [:]
|
var channelStates: [PeerId: ChannelState] = [:]
|
||||||
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||||
|
|
||||||
|
if let result = result {
|
||||||
switch result {
|
switch result {
|
||||||
case let .peerDialogs(dialogs, messages, chats, users, _):
|
case let .peerDialogs(dialogs, messages, chats, users, _):
|
||||||
dialogsChats.append(contentsOf: chats)
|
dialogsChats.append(contentsOf: chats)
|
||||||
@ -1400,6 +1407,7 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updatedState.mergeChats(dialogsChats)
|
updatedState.mergeChats(dialogsChats)
|
||||||
updatedState.mergeUsers(dialogsUsers)
|
updatedState.mergeUsers(dialogsUsers)
|
||||||
@ -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)
|
modifier.deleteMessagesInRange(peerId: id.peerId, namespace: id.namespace, minId: 1, maxId: id.id)
|
||||||
case let .EditMessage(id, message):
|
case let .EditMessage(id, message):
|
||||||
modifier.updateMessage(id, update: { previousMessage in
|
modifier.updateMessage(id, update: { previousMessage in
|
||||||
|
var updatedFlags = message.flags
|
||||||
var updatedLocalTags = message.localTags
|
var updatedLocalTags = message.localTags
|
||||||
if previousMessage.localTags.contains(.OutgoingLiveLocation) {
|
if previousMessage.localTags.contains(.OutgoingLiveLocation) {
|
||||||
updatedLocalTags.insert(.OutgoingLiveLocation)
|
updatedLocalTags.insert(.OutgoingLiveLocation)
|
||||||
}
|
}
|
||||||
|
if message.flags.contains(.Incoming) {
|
||||||
|
updatedFlags.insert(.Incoming)
|
||||||
|
} else {
|
||||||
|
updatedFlags.remove(.Incoming)
|
||||||
|
}
|
||||||
return .update(message.withUpdatedLocalTags(updatedLocalTags))
|
return .update(message.withUpdatedLocalTags(updatedLocalTags))
|
||||||
})
|
})
|
||||||
case let .UpdateMedia(id, media):
|
case let .UpdateMedia(id, media):
|
||||||
|
|||||||
@ -75,8 +75,8 @@ public final class AccountStateManager {
|
|||||||
return self.isUpdatingValue.get()
|
return self.isUpdatingValue.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let notificationMessagesPipe = ValuePipe<[Message]>()
|
private let notificationMessagesPipe = ValuePipe<[(Message, PeerGroupId?)]>()
|
||||||
public var notificationMessages: Signal<[Message], NoError> {
|
public var notificationMessages: Signal<[(Message, PeerGroupId?)], NoError> {
|
||||||
return self.notificationMessagesPipe.signal()
|
return self.notificationMessagesPipe.signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,12 +460,12 @@ public final class AccountStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let signal = self.account.postbox.modify { modifier -> [Message] in
|
let signal = self.account.postbox.modify { modifier -> [(Message, PeerGroupId?)] in
|
||||||
var messages: [Message] = []
|
var messages: [(Message, PeerGroupId?)] = []
|
||||||
for id in events.addedIncomingMessageIds {
|
for id in events.addedIncomingMessageIds {
|
||||||
let (message, notify, _, _) = messageForNotification(modifier: modifier, id: id, alwaysReturnMessage: false)
|
let (message, notify, _, _) = messageForNotification(modifier: modifier, id: id, alwaysReturnMessage: false)
|
||||||
if let message = message, notify {
|
if let message = message, notify {
|
||||||
messages.append(message)
|
messages.append((message, modifier.getPeerGroupId(message.id.peerId)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messages
|
return messages
|
||||||
@ -473,7 +473,7 @@ public final class AccountStateManager {
|
|||||||
|
|
||||||
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in
|
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
for message in messages {
|
for (message, _) in messages {
|
||||||
Logger.shared.log("State" , "notify: \(String(describing: messageMainPeer(message)?.displayTitle)): \(message.id)")
|
Logger.shared.log("State" , "notify: \(String(describing: messageMainPeer(message)?.displayTitle)): \(message.id)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,6 +77,10 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal<Void
|
|||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
chats = apiChats
|
chats = apiChats
|
||||||
users = apiUsers
|
users = apiUsers
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
|
chats = []
|
||||||
|
users = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.postbox.modify { modifier -> Void in
|
return account.postbox.modify { modifier -> Void in
|
||||||
|
|||||||
@ -108,7 +108,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
|||||||
case .chat, .chatEmpty, .chatForbidden, .channelForbidden:
|
case .chat, .chatEmpty, .chatForbidden, .channelForbidden:
|
||||||
return parseTelegramGroupOrChannel(chat: rhs)
|
return parseTelegramGroupOrChannel(chat: rhs)
|
||||||
case let .channel(flags, _, accessHash, title, username, photo, date, version, restrictionReason, adminRights, bannedRights, _, feedId):
|
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)
|
return parseTelegramGroupOrChannel(chat: rhs)
|
||||||
} else if let lhs = lhs as? TelegramChannel {
|
} else if let lhs = lhs as? TelegramChannel {
|
||||||
var channelFlags = lhs.flags
|
var channelFlags = lhs.flags
|
||||||
@ -128,7 +128,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
|||||||
}
|
}
|
||||||
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
|
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 {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,8 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let channelPts = result.channelPts
|
||||||
|
|
||||||
var sentStickers: [TelegramMediaFile] = []
|
var sentStickers: [TelegramMediaFile] = []
|
||||||
var sentGifs: [TelegramMediaFile] = []
|
var sentGifs: [TelegramMediaFile] = []
|
||||||
|
|
||||||
@ -62,14 +64,14 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
|||||||
}
|
}
|
||||||
|
|
||||||
let media: [Media]
|
let media: [Media]
|
||||||
let attributes: [MessageAttribute]
|
var attributes: [MessageAttribute]
|
||||||
let text: String
|
let text: String
|
||||||
if let apiMessage = apiMessage, let updatedMessage = StoreMessage(apiMessage: apiMessage) {
|
if let apiMessage = apiMessage, let updatedMessage = StoreMessage(apiMessage: apiMessage) {
|
||||||
media = updatedMessage.media
|
media = updatedMessage.media
|
||||||
attributes = updatedMessage.attributes
|
attributes = updatedMessage.attributes
|
||||||
text = updatedMessage.text
|
text = updatedMessage.text
|
||||||
} else if case let .updateShortSentMessage(_, _, _, _, _, apiMedia, entities) = result {
|
} 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 {
|
if let mediaValue = mediaValue {
|
||||||
media = [mediaValue]
|
media = [mediaValue]
|
||||||
} else {
|
} else {
|
||||||
@ -95,6 +97,16 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
|||||||
text = currentMessage.text
|
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?
|
var storeForwardInfo: StoreMessageForwardInfo?
|
||||||
if let forwardInfo = currentMessage.forwardInfo {
|
if let forwardInfo = currentMessage.forwardInfo {
|
||||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature)
|
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature)
|
||||||
|
|||||||
124
TelegramCore/ChannelAdminEventLogContext.swift
Normal file
124
TelegramCore/ChannelAdminEventLogContext.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,17 +8,29 @@
|
|||||||
|
|
||||||
public typealias AdminLogEventId = Int64
|
public typealias AdminLogEventId = Int64
|
||||||
|
|
||||||
public struct AdminLogEvent {
|
public struct AdminLogEvent: Comparable {
|
||||||
public let id: AdminLogEventId
|
public let id: AdminLogEventId
|
||||||
public let peerId:PeerId
|
public let peerId: PeerId
|
||||||
public let date:Int32
|
public let date: Int32
|
||||||
public let action: AdminLogEventAction
|
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 struct AdminLogEventsResult {
|
||||||
public let peerId: PeerId
|
public let peerId: PeerId
|
||||||
public let peers:[PeerId: Peer]
|
public let peers: [PeerId: Peer]
|
||||||
public let events:[AdminLogEvent]
|
public let events: [AdminLogEvent]
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AdminLogEventAction {
|
public enum AdminLogEventAction {
|
||||||
@ -29,7 +41,7 @@ public enum AdminLogEventAction {
|
|||||||
case toggleInvites(Bool)
|
case toggleInvites(Bool)
|
||||||
case toggleSignatures(Bool)
|
case toggleSignatures(Bool)
|
||||||
case updatePinned(Message?)
|
case updatePinned(Message?)
|
||||||
case editMessage(prev: Message, new:Message)
|
case editMessage(prev: Message, new: Message)
|
||||||
case deleteMessage(Message)
|
case deleteMessage(Message)
|
||||||
case participantJoin
|
case participantJoin
|
||||||
case participantLeave
|
case participantLeave
|
||||||
@ -69,10 +81,10 @@ public struct AdminLogEventsFlags : OptionSet {
|
|||||||
public static let editMessages = AdminLogEventsFlags(rawValue: 1 << 12)
|
public static let editMessages = AdminLogEventsFlags(rawValue: 1 << 12)
|
||||||
public static let deleteMessages = AdminLogEventsFlags(rawValue: 1 << 13)
|
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]
|
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]
|
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .pinnedMessages, .editMessages, .deleteMessages]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,17 +98,17 @@ private func boolFromApiValue(_ value: Api.Bool) -> Bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func channelAdminLogEvents(_ account:Account, peerId:PeerId, maxId:AdminLogEventId, minId:AdminLogEventId, limit:Int32 = 100, query:String? = nil, filter:AdminLogEventsFlags? = nil, admins:[PeerId]? = nil) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> {
|
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<AdminLogEventsResult, ChannelAdminLogEventError> {
|
||||||
|
return postbox.modify { modifier -> (Peer?, [Peer]?) in
|
||||||
return account.postbox.modify { modifier -> (Peer?, [Peer]?) in
|
return (modifier.getPeer(peerId), admins?.flatMap { modifier.getPeer($0) })
|
||||||
return (modifier.getPeer(peerId), admins?.flatMap {modifier.getPeer($0)})
|
}
|
||||||
} |> mapError {return .generic} |> mapToSignal { (peer, admins) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> in
|
|> mapError { return .generic }
|
||||||
|
|> mapToSignal { (peer, admins) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> in
|
||||||
if let peer = peer, let inputChannel = apiInputChannel(peer) {
|
if let peer = peer, let inputChannel = apiInputChannel(peer) {
|
||||||
let inputAdmins = admins?.flatMap {apiInputUser($0)}
|
let inputAdmins = admins?.flatMap {apiInputUser($0)}
|
||||||
|
|
||||||
var flags:Int32 = 0
|
var flags: Int32 = 0
|
||||||
var eventsFilter:Api.ChannelAdminLogEventsFilter? = nil
|
var eventsFilter: Api.ChannelAdminLogEventsFilter? = nil
|
||||||
if let filter = filter {
|
if let filter = filter {
|
||||||
flags += Int32(1 << 0)
|
flags += Int32(1 << 0)
|
||||||
eventsFilter = Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: Int32(filter.rawValue))
|
eventsFilter = Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: Int32(filter.rawValue))
|
||||||
@ -104,7 +116,7 @@ public func channelAdminLogEvents(_ account:Account, peerId:PeerId, maxId:AdminL
|
|||||||
if let _ = inputAdmins {
|
if let _ = inputAdmins {
|
||||||
flags += Int32(1 << 1)
|
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
|
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 {
|
switch result {
|
||||||
case let .adminLogResults(apiEvents, apiChats, apiUsers):
|
case let .adminLogResults(apiEvents, apiChats, apiUsers):
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import Foundation
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
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 text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?)
|
||||||
case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?)
|
case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?)
|
||||||
case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?)
|
case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?)
|
||||||
@ -18,7 +18,7 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
|||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
|
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
|
||||||
case 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:
|
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)
|
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:
|
case 2:
|
||||||
@ -26,15 +26,20 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
|||||||
case 3:
|
case 3:
|
||||||
self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
|
self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
|
||||||
default:
|
default:
|
||||||
self = .auto(caption: "", replyMarkup: nil)
|
self = .auto(caption: "", entities: nil, replyMarkup: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
switch self {
|
switch self {
|
||||||
case let .auto(caption, replyMarkup):
|
case let .auto(caption, entities, replyMarkup):
|
||||||
encoder.encodeInt32(0, forKey: "_v")
|
encoder.encodeInt32(0, forKey: "_v")
|
||||||
encoder.encodeString(caption, forKey: "c")
|
encoder.encodeString(caption, forKey: "c")
|
||||||
|
if let entities = entities {
|
||||||
|
encoder.encodeObject(entities, forKey: "e")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "e")
|
||||||
|
}
|
||||||
if let replyMarkup = replyMarkup {
|
if let replyMarkup = replyMarkup {
|
||||||
encoder.encodeObject(replyMarkup, forKey: "m")
|
encoder.encodeObject(replyMarkup, forKey: "m")
|
||||||
} else {
|
} else {
|
||||||
@ -75,11 +80,14 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
|
|||||||
|
|
||||||
public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool {
|
public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .auto(lhsCaption, lhsReplyMarkup):
|
case let .auto(lhsCaption, lhsEntities, lhsReplyMarkup):
|
||||||
if case let .auto(rhsCaption, rhsReplyMarkup) = rhs {
|
if case let .auto(rhsCaption, rhsEntities, rhsReplyMarkup) = rhs {
|
||||||
if lhsCaption != rhsCaption {
|
if lhsCaption != rhsCaption {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsEntities != rhsEntities {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsReplyMarkup != rhsReplyMarkup {
|
if lhsReplyMarkup != rhsReplyMarkup {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -324,12 +332,16 @@ public final class ChatContextResultCollection: Equatable {
|
|||||||
extension ChatContextResultMessage {
|
extension ChatContextResultMessage {
|
||||||
init(apiMessage: Api.BotInlineMessage) {
|
init(apiMessage: Api.BotInlineMessage) {
|
||||||
switch apiMessage {
|
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?
|
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
|
||||||
if let replyMarkup = replyMarkup {
|
if let replyMarkup = replyMarkup {
|
||||||
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: 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):
|
case let .botInlineMessageText(flags, message, entities, replyMarkup):
|
||||||
var parsedEntities: TextEntitiesMessageAttribute?
|
var parsedEntities: TextEntitiesMessageAttribute?
|
||||||
if let entities = entities, !entities.isEmpty {
|
if let entities = entities, !entities.isEmpty {
|
||||||
|
|||||||
@ -61,7 +61,7 @@ final class HistoryViewChannelStateValidationContexts {
|
|||||||
if ranges.isEmpty {
|
if ranges.isEmpty {
|
||||||
ranges = [[id]]
|
ranges = [[id]]
|
||||||
} else {
|
} else {
|
||||||
ranges[rangesToInvalidate.count - 1].append(id)
|
ranges[ranges.count - 1].append(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +122,8 @@ final class HistoryViewChannelStateValidationContexts {
|
|||||||
var addedRanges: [[MessageId]] = []
|
var addedRanges: [[MessageId]] = []
|
||||||
for messages in rangesToInvalidate {
|
for messages in rangesToInvalidate {
|
||||||
for id in messages {
|
for id in messages {
|
||||||
|
invalidatedMessageIds.insert(id)
|
||||||
|
|
||||||
if context.batchReferences[id] != nil {
|
if context.batchReferences[id] != nil {
|
||||||
addRangeBreak(&addedRanges)
|
addRangeBreak(&addedRanges)
|
||||||
} else {
|
} 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()
|
addedRanges.removeLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +143,7 @@ final class HistoryViewChannelStateValidationContexts {
|
|||||||
context.batchReferences[messageId] = batch
|
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
|
|> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in
|
||||||
if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch {
|
if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch {
|
||||||
var completedMessageIds: [MessageId] = []
|
var completedMessageIds: [MessageId] = []
|
||||||
@ -215,8 +217,10 @@ final class HistoryViewChannelStateValidationContexts {
|
|||||||
private func hashForMessages(_ messages: [Message]) -> Int32 {
|
private func hashForMessages(_ messages: [Message]) -> Int32 {
|
||||||
var acc: UInt32 = 0
|
var acc: UInt32 = 0
|
||||||
|
|
||||||
for message in messages {
|
let sorted = messages.sorted(by: { $0.id > $1.id })
|
||||||
acc = (acc &* 20261) &+ message.id.id
|
|
||||||
|
for message in sorted {
|
||||||
|
acc = (acc &* 20261) &+ UInt32(message.id.id)
|
||||||
var timestamp = message.timestamp
|
var timestamp = message.timestamp
|
||||||
inner: for attribute in message.attributes {
|
inner: for attribute in message.attributes {
|
||||||
if let attribute = attribute as? EditedMessageAttribute {
|
if let attribute = attribute as? EditedMessageAttribute {
|
||||||
@ -224,25 +228,173 @@ private func hashForMessages(_ messages: [Message]) -> Int32 {
|
|||||||
break inner
|
break inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acc = (acc &* 20261) &+ timestamp
|
acc = (acc &* 20261) &+ UInt32(timestamp)
|
||||||
}
|
}
|
||||||
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))
|
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validateBatch(postbox: Postbox, network: Network, messageIds: [MessageId]) -> Signal<Void, NoError> {
|
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<Void, NoError> {
|
||||||
guard let peerId = messageIds.first?.peerId else {
|
guard let peerId = messageIds.first?.peerId else {
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
return postbox.modify { modifier -> Signal<Void, NoError> in
|
return postbox.modify { modifier -> Signal<Void, NoError> in
|
||||||
if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||||
var messages: [Message] = []
|
var messages: [Message] = []
|
||||||
|
var previous: [MessageId: Message] = [:]
|
||||||
for messageId in messageIds {
|
for messageId in messageIds {
|
||||||
if let message = modifier.getMessage(messageId) {
|
if let message = modifier.getMessage(messageId) {
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
|
previous[message.id] = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let hash = hashForMessages(messages)
|
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<Api.messages.Messages?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> mapToSignal { result -> Signal<Void, NoError> 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<MessageId>()
|
||||||
|
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 {
|
} else {
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
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) {
|
} else if let filter = messageFilterForTagMask(tagMask) {
|
||||||
let offsetId: Int32
|
let offsetId: Int32
|
||||||
let addOffset: Int32
|
let addOffset: Int32
|
||||||
@ -119,7 +119,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
|||||||
minId = 1
|
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 {
|
} else {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
request = .never()
|
request = .never()
|
||||||
@ -145,7 +145,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
|||||||
if hole.maxIndex.timestamp == Int32.max {
|
if hole.maxIndex.timestamp == Int32.max {
|
||||||
let innerOffsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1)
|
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)
|
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)
|
|> map(Optional.init)
|
||||||
}
|
}
|
||||||
case let .AroundId(id):
|
case let .AroundId(id):
|
||||||
@ -160,7 +160,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
|||||||
minId = 1
|
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)
|
return combineLatest(request |> retryRequest, maxIndexRequest |> retryRequest)
|
||||||
@ -183,6 +183,10 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
|||||||
chats = apiChats
|
chats = apiChats
|
||||||
users = apiUsers
|
users = apiUsers
|
||||||
channelPts = pts
|
channelPts = pts
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
|
chats = []
|
||||||
|
users = []
|
||||||
}
|
}
|
||||||
var updatedMaxIndex: MessageIndex?
|
var updatedMaxIndex: MessageIndex?
|
||||||
if let maxIndexResult = maxIndexResult {
|
if let maxIndexResult = maxIndexResult {
|
||||||
@ -194,6 +198,8 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
|
|||||||
maxIndexMessages = apiMessages
|
maxIndexMessages = apiMessages
|
||||||
case let .channelMessages(_, _, _, apiMessages, _, _):
|
case let .channelMessages(_, _, _, apiMessages, _, _):
|
||||||
maxIndexMessages = apiMessages
|
maxIndexMessages = apiMessages
|
||||||
|
case .messagesNotModified:
|
||||||
|
maxIndexMessages = []
|
||||||
}
|
}
|
||||||
if !maxIndexMessages.isEmpty {
|
if !maxIndexMessages.isEmpty {
|
||||||
assert(maxIndexMessages.count == 1)
|
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)
|
offset = single((holeIndex.timestamp, min(holeIndex.id.id, Int32.max - 1) + 1, Api.InputPeer.inputPeerEmpty), NoError.self)
|
||||||
return offset
|
return offset
|
||||||
|> mapToSignal { (timestamp, id, peer) -> Signal<Void, NoError> in
|
|> mapToSignal { (timestamp, id, peer) -> Signal<Void, NoError> 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
|
|> retryRequest
|
||||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
let messages: [Api.Message]
|
let messages: [Api.Message]
|
||||||
@ -520,6 +526,10 @@ func fetchCallListHole(network: Network, postbox: Postbox, holeIndex: MessageInd
|
|||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
chats = apiChats
|
chats = apiChats
|
||||||
users = apiUsers
|
users = apiUsers
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
|
chats = []
|
||||||
|
users = []
|
||||||
}
|
}
|
||||||
return postbox.modify { modifier -> Void in
|
return postbox.modify { modifier -> Void in
|
||||||
var storeMessages: [StoreMessage] = []
|
var storeMessages: [StoreMessage] = []
|
||||||
|
|||||||
@ -66,6 +66,8 @@ public func getMessagesLoadIfNecessary(_ messageIds:[MessageId], postbox:Postbox
|
|||||||
return (messages, chats, users)
|
return (messages, chats, users)
|
||||||
case let .channelMessages(_, _, _, messages, chats, users):
|
case let .channelMessages(_, _, _, messages, chats, users):
|
||||||
return (messages, chats, users)
|
return (messages, chats, users)
|
||||||
|
case .messagesNotModified:
|
||||||
|
return ([], [], [])
|
||||||
}
|
}
|
||||||
} |> `catch` { _ in
|
} |> `catch` { _ in
|
||||||
return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], []))
|
return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], []))
|
||||||
|
|||||||
@ -43,6 +43,8 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M
|
|||||||
apiUsers = users
|
apiUsers = users
|
||||||
case let .channelMessages(_, _, _, _, _, users):
|
case let .channelMessages(_, _, _, _, _, users):
|
||||||
apiUsers = users
|
apiUsers = users
|
||||||
|
case .messagesNotModified:
|
||||||
|
apiUsers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
for user in apiUsers {
|
for user in apiUsers {
|
||||||
|
|||||||
@ -119,8 +119,8 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
|
|||||||
switch item {
|
switch item {
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
return peerId.namespace != Namespaces.Peer.SecretChat
|
return peerId.namespace != Namespaces.Peer.SecretChat
|
||||||
case .group:
|
default:
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let localItemIds = modifier.getPinnedItemIds()
|
let localItemIds = modifier.getPinnedItemIds()
|
||||||
@ -128,17 +128,14 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
|
|||||||
switch item {
|
switch item {
|
||||||
case let .peer(peerId):
|
case let .peer(peerId):
|
||||||
return peerId.namespace != Namespaces.Peer.SecretChat
|
return peerId.namespace != Namespaces.Peer.SecretChat
|
||||||
case .group:
|
default:
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return network.request(Api.functions.messages.getPinnedDialogs())
|
return network.request(Api.functions.messages.getPinnedDialogs())
|
||||||
|> retryRequest
|
|> retryRequest
|
||||||
|> mapToSignal { dialogs -> Signal<Void, NoError> in
|
|> mapToSignal { dialogs -> Signal<Void, NoError> in
|
||||||
let dialogsChats: [Api.Chat]
|
|
||||||
let dialogsUsers: [Api.User]
|
|
||||||
|
|
||||||
var storeMessages: [StoreMessage] = []
|
var storeMessages: [StoreMessage] = []
|
||||||
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
|
||||||
var chatStates: [PeerId: PeerChatState] = [:]
|
var chatStates: [PeerId: PeerChatState] = [:]
|
||||||
@ -146,10 +143,27 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
|
|||||||
|
|
||||||
var remoteItemIds: [PinnedItemId] = []
|
var remoteItemIds: [PinnedItemId] = []
|
||||||
|
|
||||||
|
var peers: [Peer] = []
|
||||||
|
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||||
|
|
||||||
switch dialogs {
|
switch dialogs {
|
||||||
case let .peerDialogs(dialogs, messages, chats, users, _):
|
case let .peerDialogs(dialogs, messages, chats, users, _):
|
||||||
dialogsChats = chats
|
var channelGroupIds: [PeerId: PeerGroupId] = [:]
|
||||||
dialogsUsers = users
|
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 {
|
loop: for dialog in dialogs {
|
||||||
let apiPeer: Api.Peer
|
let apiPeer: Api.Peer
|
||||||
@ -161,6 +175,9 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
|
|||||||
let apiNotificationSettings: Api.PeerNotifySettings
|
let apiNotificationSettings: Api.PeerNotifySettings
|
||||||
switch dialog {
|
switch dialog {
|
||||||
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
|
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
|
||||||
|
if channelGroupIds[peer.peerId] != nil {
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
apiPeer = peer
|
apiPeer = peer
|
||||||
apiTopMessage = topMessage
|
apiTopMessage = topMessage
|
||||||
apiReadInboxMaxId = readInboxMaxId
|
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 locallyRemovedFromRemoteItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(localItemIdsWithoutSecretChats))
|
||||||
let remotelyRemovedItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(remoteItemIds))
|
let remotelyRemovedItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(remoteItemIds))
|
||||||
|
|
||||||
|
|||||||
@ -1,413 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
public protocol Identifiable {
|
|
||||||
associatedtype T: Hashable
|
|
||||||
var stableId: T { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func mergeListsStable<T>(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<T>(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)<i=\(i), k++")
|
|
||||||
k += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if k < updatedIndices.count {
|
|
||||||
if updatedIndices[k].0 == i {
|
|
||||||
left = updatedIndices[k].1
|
|
||||||
//print("override left = \(updatedIndices[k].1.stableId)")
|
|
||||||
} 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 {
|
|
||||||
//print("\(left.stableId)==\(right.stableId)")
|
|
||||||
//print("i++, j++")
|
|
||||||
i += 1
|
|
||||||
j += 1
|
|
||||||
} else if left > 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<T>(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -122,7 +122,10 @@ public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCol
|
|||||||
attributes.append(InlineBotMessageAttribute(peerId: results.botId))
|
attributes.append(InlineBotMessageAttribute(peerId: results.botId))
|
||||||
|
|
||||||
switch result.message {
|
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 {
|
if let replyMarkup = replyMarkup {
|
||||||
attributes.append(replyMarkup)
|
attributes.append(replyMarkup)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -551,8 +551,20 @@ public final class PendingMessageManager {
|
|||||||
}
|
}
|
||||||
if let uniqueId = uniqueId {
|
if let uniqueId = uniqueId {
|
||||||
switch content {
|
switch content {
|
||||||
case let .media(inputMedia):
|
case let .media(inputMedia, text):
|
||||||
singleMedias.append(.inputSingleMedia(media: inputMedia, randomId: uniqueId))
|
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:
|
default:
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -726,8 +738,8 @@ public final class PendingMessageManager {
|
|||||||
|> mapError { _ -> NoError in
|
|> mapError { _ -> NoError in
|
||||||
return NoError()
|
return NoError()
|
||||||
}
|
}
|
||||||
case let .media(inputMedia):
|
case let .media(inputMedia, text):
|
||||||
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil), tag: dependencyTag)
|
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
|
|> mapError { _ -> NoError in
|
||||||
return NoError()
|
return NoError()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import TelegramCorePrivateModule
|
|||||||
|
|
||||||
enum PendingMessageUploadedContent {
|
enum PendingMessageUploadedContent {
|
||||||
case text(String)
|
case text(String)
|
||||||
case media(Api.InputMedia)
|
case media(Api.InputMedia, String)
|
||||||
case forward(ForwardSourceInfoAttribute)
|
case forward(ForwardSourceInfoAttribute)
|
||||||
case chatContextResult(OutgoingChatContextResultMessageAttribute)
|
case chatContextResult(OutgoingChatContextResultMessageAttribute)
|
||||||
case secretMedia(Api.InputEncryptedFile, Int32, SecretFileEncryptionKey)
|
case secretMedia(Api.InputEncryptedFile, Int32, SecretFileEncryptionKey)
|
||||||
@ -64,19 +64,19 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
|
|||||||
} else if let media = media.first {
|
} else if let media = media.first {
|
||||||
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
|
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
|
||||||
if let reference = image.reference, case let .cloud(id, accessHash) = reference {
|
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 {
|
} else {
|
||||||
return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute))
|
return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute))
|
||||||
}
|
}
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
if let resource = file.resource as? CloudDocumentMediaResource {
|
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 {
|
} else {
|
||||||
return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file))
|
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 {
|
} else if let contact = media as? TelegramMediaContact {
|
||||||
let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName)
|
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 {
|
} else if let map = media as? TelegramMediaMap {
|
||||||
let input: Api.InputMedia
|
let input: Api.InputMedia
|
||||||
if let liveBroadcastingTimeout = map.liveBroadcastingTimeout {
|
if let liveBroadcastingTimeout = map.liveBroadcastingTimeout {
|
||||||
@ -86,7 +86,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
|
|||||||
} else {
|
} else {
|
||||||
input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude))
|
input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude))
|
||||||
}
|
}
|
||||||
return .ready(.media(input))
|
return .ready(.media(input, text))
|
||||||
} else {
|
} else {
|
||||||
return .ready(.text(text))
|
return .ready(.text(text))
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI
|
|||||||
flags |= 1 << 1
|
flags |= 1 << 1
|
||||||
ttlSeconds = autoremoveAttribute.timeout
|
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):
|
case let .localReference(key):
|
||||||
referenceKey = key
|
referenceKey = key
|
||||||
@ -221,14 +221,14 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI
|
|||||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
if let inputPeer = inputPeer {
|
if let inputPeer = inputPeer {
|
||||||
if autoremoveAttribute != nil {
|
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 }
|
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
switch result {
|
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 {
|
if let photo = photo, let mediaImage = telegramMediaImageFromApiPhoto(photo), let reference = mediaImage.reference, case let .cloud(id, accessHash) = reference {
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
var ttlSeconds: Int32?
|
var ttlSeconds: Int32?
|
||||||
@ -236,7 +236,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI
|
|||||||
flags |= 1 << 1
|
flags |= 1 << 1
|
||||||
ttlSeconds = autoremoveAttribute.timeout
|
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:
|
default:
|
||||||
break
|
break
|
||||||
@ -359,7 +359,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
switch result {
|
switch result {
|
||||||
case let .media(media):
|
case let .media(media):
|
||||||
if let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
|
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):
|
case let .localReference(key):
|
||||||
referenceKey = key
|
referenceKey = key
|
||||||
@ -460,7 +460,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ttlSeconds != nil {
|
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
|
return postbox.modify { modifier -> Api.InputPeer? in
|
||||||
@ -469,13 +469,13 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
if let inputPeer = inputPeer {
|
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 }
|
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .messageMediaDocument(_, document, _, _):
|
case let .messageMediaDocument(_, document, _):
|
||||||
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource {
|
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:
|
default:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -64,7 +64,7 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let peer = peer, let inputPeer = apiInputPeer(peer) {
|
} 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}
|
|> mapError {_ in}
|
||||||
|> `catch` {
|
|> `catch` {
|
||||||
return Signal<Api.messages.Messages?, Void>.single(nil)
|
return Signal<Api.messages.Messages?, Void>.single(nil)
|
||||||
@ -87,6 +87,10 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra
|
|||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
chats = apiChats
|
chats = apiChats
|
||||||
users = apiUsers
|
users = apiUsers
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
|
chats = []
|
||||||
|
users = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.postbox.modify { modifier -> [Message] in
|
return account.postbox.modify { modifier -> [Message] in
|
||||||
|
|||||||
@ -79,7 +79,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
|||||||
flags |= (1 << 0)
|
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)}
|
|> map {Optional($0)}
|
||||||
|> `catch` { _ -> Signal<Api.messages.Messages?, MTRpcError> in
|
|> `catch` { _ -> Signal<Api.messages.Messages?, MTRpcError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -117,6 +117,10 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
|
|||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
chats = apiChats
|
chats = apiChats
|
||||||
users = apiUsers
|
users = apiUsers
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
|
chats = []
|
||||||
|
users = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.postbox.modify { modifier -> [Message] in
|
return account.postbox.modify { modifier -> [Message] in
|
||||||
@ -195,6 +199,10 @@ public func downloadMessage(account: Account, messageId: MessageId) -> Signal<Me
|
|||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
chats = apiChats
|
chats = apiChats
|
||||||
users = apiUsers
|
users = apiUsers
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
|
chats = []
|
||||||
|
users = []
|
||||||
}
|
}
|
||||||
|
|
||||||
let postboxSignal = account.postbox.modify { modifier -> Message? in
|
let postboxSignal = account.postbox.modify { modifier -> Message? in
|
||||||
@ -239,7 +247,7 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
|
|||||||
if peerId.namespace == Namespaces.Peer.SecretChat {
|
if peerId.namespace == Namespaces.Peer.SecretChat {
|
||||||
return .single(modifier.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp))
|
return .single(modifier.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp))
|
||||||
} else if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
} 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
|
|> map { result -> MessageId? in
|
||||||
let messages: [Api.Message]
|
let messages: [Api.Message]
|
||||||
switch result {
|
switch result {
|
||||||
@ -249,6 +257,8 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
|
|||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
case let.messagesSlice(_, apiMessages, _, _):
|
case let.messagesSlice(_, apiMessages, _, _):
|
||||||
messages = apiMessages
|
messages = apiMessages
|
||||||
|
case .messagesNotModified:
|
||||||
|
messages = []
|
||||||
}
|
}
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id {
|
if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id {
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public class BoxedMessage: NSObject {
|
|||||||
|
|
||||||
public class Serialization: NSObject, MTSerialization {
|
public class Serialization: NSObject, MTSerialization {
|
||||||
public func currentLayer() -> UInt {
|
public func currentLayer() -> UInt {
|
||||||
return 75
|
return 76
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseMessage(_ data: Data!) -> Any! {
|
public func parseMessage(_ data: Data!) -> Any! {
|
||||||
|
|||||||
@ -64,6 +64,10 @@ private func fetchMessage(modifier: Modifier, account: Account, messageId: Messa
|
|||||||
apiMessages = messages
|
apiMessages = messages
|
||||||
apiChats = chats
|
apiChats = chats
|
||||||
apiUsers = users
|
apiUsers = users
|
||||||
|
case .messagesNotModified:
|
||||||
|
apiMessages = []
|
||||||
|
apiChats = []
|
||||||
|
apiUsers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
var peers: [PeerId: Peer] = [:]
|
var peers: [PeerId: Peer] = [:]
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public enum StandaloneMedia {
|
|||||||
|
|
||||||
private enum StandaloneMessageContent {
|
private enum StandaloneMessageContent {
|
||||||
case text(String)
|
case text(String)
|
||||||
case media(Api.InputMedia)
|
case media(Api.InputMedia, String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum StandaloneSendMessageEvent {
|
private enum StandaloneSendMessageEvent {
|
||||||
@ -33,25 +33,25 @@ public func standaloneSendMessage(account: Account, peerId: PeerId, text: String
|
|||||||
if let media = media {
|
if let media = media {
|
||||||
switch media {
|
switch media {
|
||||||
case let .image(data):
|
case let .image(data):
|
||||||
content = uploadedImage(account: account, text: text, data: data)
|
content = uploadedImage(account: account, data: data)
|
||||||
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|
||||||
|> map { next -> StandaloneSendMessageEvent in
|
|> map { next -> StandaloneSendMessageEvent in
|
||||||
switch next {
|
switch next {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
return .progress(progress)
|
return .progress(progress)
|
||||||
case let .result(media):
|
case let .result(media):
|
||||||
return .result(.media(media))
|
return .result(.media(media, text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .file(data, mimeType, attributes):
|
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 }
|
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|
||||||
|> map { next -> StandaloneSendMessageEvent in
|
|> map { next -> StandaloneSendMessageEvent in
|
||||||
switch next {
|
switch next {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
return .progress(progress)
|
return .progress(progress)
|
||||||
case let .result(media):
|
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
|
|> mapError { _ -> NoError in
|
||||||
return NoError()
|
return NoError()
|
||||||
}
|
}
|
||||||
case let .media(inputMedia):
|
case let .media(inputMedia, text):
|
||||||
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil))
|
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
|
|> mapError { _ -> NoError in
|
||||||
return NoError()
|
return NoError()
|
||||||
}
|
}
|
||||||
@ -141,13 +141,13 @@ private enum UploadMediaEvent {
|
|||||||
case result(Api.InputMedia)
|
case result(Api.InputMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func uploadedImage(account: Account, text: String, data: Data) -> Signal<UploadMediaEvent, StandaloneSendMessageError> {
|
private func uploadedImage(account: Account, data: Data) -> Signal<UploadMediaEvent, StandaloneSendMessageError> {
|
||||||
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|
||||||
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|
||||||
|> map { next -> UploadMediaEvent in
|
|> map { next -> UploadMediaEvent in
|
||||||
switch next {
|
switch next {
|
||||||
case let .inputFile(inputFile):
|
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:
|
case .inputSecretFile:
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
case let .progress(progress):
|
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<UploadMediaEvent, PendingMessageUploadError> {
|
private func uploadedFile(account: Account, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal<UploadMediaEvent, PendingMessageUploadError> {
|
||||||
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes)))
|
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes)))
|
||||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||||
|> map { next -> UploadMediaEvent in
|
|> map { next -> UploadMediaEvent in
|
||||||
switch next {
|
switch next {
|
||||||
case let .inputFile(inputFile):
|
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:
|
case .inputSecretFile:
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
|
|||||||
@ -28,11 +28,11 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri
|
|||||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
if let inputPeer = inputPeer {
|
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 }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
switch media {
|
switch media {
|
||||||
case let .messageMediaPhoto(_, photo, _, _):
|
case let .messageMediaPhoto(_, photo, _):
|
||||||
if let photo = photo {
|
if let photo = photo {
|
||||||
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
|
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
|
||||||
return .single(.result(mediaImage))
|
return .single(.result(mediaImage))
|
||||||
@ -67,11 +67,11 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
|
|||||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
if let inputPeer = inputPeer {
|
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 }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
switch media {
|
switch media {
|
||||||
case let .messageMediaDocument(_, document, _, _):
|
case let .messageMediaDocument(_, document, _):
|
||||||
if let document = document {
|
if let document = document {
|
||||||
if let mediaFile = telegramMediaFileFromApiDocument(document) {
|
if let mediaFile = telegramMediaFileFromApiDocument(document) {
|
||||||
return .single(.result(mediaFile))
|
return .single(.result(mediaFile))
|
||||||
|
|||||||
@ -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 {
|
if let media = media {
|
||||||
switch media {
|
switch media {
|
||||||
case let .messageMediaPhoto(_, photo, caption, ttlSeconds):
|
case let .messageMediaPhoto(_, photo, ttlSeconds):
|
||||||
if let photo = photo {
|
if let photo = photo {
|
||||||
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
|
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
|
||||||
return (caption, mediaImage, ttlSeconds)
|
return (mediaImage, ttlSeconds)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (nil, TelegramMediaExpiredContent(data: .image), nil)
|
return (TelegramMediaExpiredContent(data: .image), nil)
|
||||||
}
|
}
|
||||||
case let .messageMediaContact(phoneNumber, firstName, lastName, userId):
|
case let .messageMediaContact(phoneNumber, firstName, lastName, userId):
|
||||||
let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: 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)
|
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId)
|
||||||
return (nil, mediaContact, nil)
|
return (mediaContact, nil)
|
||||||
case let .messageMediaGeo(geo):
|
case let .messageMediaGeo(geo):
|
||||||
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil)
|
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):
|
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)
|
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):
|
case let .messageMediaGeoLive(geo, period):
|
||||||
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period)
|
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period)
|
||||||
return (nil, mediaMap, nil)
|
return (mediaMap, nil)
|
||||||
case let .messageMediaDocument(_, document, caption, ttlSeconds):
|
case let .messageMediaDocument(_, document, ttlSeconds):
|
||||||
if let document = document {
|
if let document = document {
|
||||||
if let mediaFile = telegramMediaFileFromApiDocument(document) {
|
if let mediaFile = telegramMediaFileFromApiDocument(document) {
|
||||||
return (caption, mediaFile, ttlSeconds)
|
return (mediaFile, ttlSeconds)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (nil, TelegramMediaExpiredContent(data: .file), nil)
|
return (TelegramMediaExpiredContent(data: .file), nil)
|
||||||
}
|
}
|
||||||
case let .messageMediaWebPage(webpage):
|
case let .messageMediaWebPage(webpage):
|
||||||
if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage) {
|
if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage) {
|
||||||
return (nil, mediaWebpage, nil)
|
return (mediaWebpage, nil)
|
||||||
}
|
}
|
||||||
case .messageMediaUnsupported:
|
case .messageMediaUnsupported:
|
||||||
return (nil, TelegramMediaUnsupported(), nil)
|
return (TelegramMediaUnsupported(), nil)
|
||||||
case .messageMediaEmpty:
|
case .messageMediaEmpty:
|
||||||
break
|
break
|
||||||
case let .messageMediaGame(game):
|
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):
|
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
|
||||||
var parsedFlags = TelegramMediaInvoiceFlags()
|
var parsedFlags = TelegramMediaInvoiceFlags()
|
||||||
if (flags & (1 << 3)) != 0 {
|
if (flags & (1 << 3)) != 0 {
|
||||||
@ -316,11 +316,11 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
|||||||
if (flags & (1 << 1)) != 0 {
|
if (flags & (1 << 1)) != 0 {
|
||||||
parsedFlags.insert(.shippingAddressRequested)
|
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] {
|
func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [MessageTextEntity] {
|
||||||
@ -432,16 +432,13 @@ extension StoreMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageText = message
|
let messageText = message
|
||||||
var medias: [Media] = []
|
var medias: [Media] = []
|
||||||
|
|
||||||
var consumableContent: (Bool, Bool)? = nil
|
var consumableContent: (Bool, Bool)? = nil
|
||||||
|
|
||||||
if let media = media {
|
if let media = media {
|
||||||
let (mediaText, mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
|
||||||
if let mediaText = mediaText {
|
|
||||||
messageText = mediaText
|
|
||||||
}
|
|
||||||
if let mediaValue = mediaValue {
|
if let mediaValue = mediaValue {
|
||||||
medias.append(mediaValue)
|
medias.append(mediaValue)
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ private func inputSecretChat(postbox: Postbox, peerId: PeerId) -> Signal<Api.Inp
|
|||||||
private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(Int32, Int32), VerifyReadStateError> {
|
private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(Int32, Int32), VerifyReadStateError> {
|
||||||
return inputPeer(postbox: postbox, peerId: peerId)
|
return inputPeer(postbox: postbox, peerId: peerId)
|
||||||
|> mapToSignal { inputPeer -> Signal<(Int32, Int32), VerifyReadStateError> in
|
|> 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
|
|> retryRequest
|
||||||
|> mapToSignalPromotingError { result -> Signal<(Int32, Int32), VerifyReadStateError> in
|
|> mapToSignalPromotingError { result -> Signal<(Int32, Int32), VerifyReadStateError> in
|
||||||
let apiMessages: [Api.Message]
|
let apiMessages: [Api.Message]
|
||||||
@ -53,6 +53,8 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId
|
|||||||
apiMessages = messages
|
apiMessages = messages
|
||||||
case let .messagesSlice(_, messages, _, _):
|
case let .messagesSlice(_, messages, _, _):
|
||||||
apiMessages = messages
|
apiMessages = messages
|
||||||
|
case .messagesNotModified:
|
||||||
|
apiMessages = []
|
||||||
}
|
}
|
||||||
if let message = apiMessages.first, let timestamp = message.timestamp {
|
if let message = apiMessages.first, let timestamp = message.timestamp {
|
||||||
return .single((message.rawId, timestamp))
|
return .single((message.rawId, timestamp))
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import Foundation
|
|||||||
extension Api.MessageMedia {
|
extension Api.MessageMedia {
|
||||||
var preCachedResources: [(MediaResource, Data)]? {
|
var preCachedResources: [(MediaResource, Data)]? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .messageMediaPhoto(_, photo, _, _):
|
case let .messageMediaPhoto(_, photo, _):
|
||||||
if let photo = photo {
|
if let photo = photo {
|
||||||
switch photo {
|
switch photo {
|
||||||
case let .photo(_, _, _, _, sizes):
|
case let .photo(_, _, _, _, sizes):
|
||||||
@ -34,7 +34,7 @@ extension Api.MessageMedia {
|
|||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case let .messageMediaDocument(_, document, _, _):
|
case let .messageMediaDocument(_, document, _):
|
||||||
if let document = document {
|
if let document = document {
|
||||||
switch document {
|
switch document {
|
||||||
case .document:
|
case .document:
|
||||||
@ -283,6 +283,17 @@ extension Api.Update {
|
|||||||
}
|
}
|
||||||
return nil
|
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 {
|
extension Api.Updates {
|
||||||
@ -421,6 +432,39 @@ extension Api.Updates {
|
|||||||
return []
|
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 {
|
extension Api.Updates {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ public func webpagePreview(account: Account, url: String, webpageId: MediaId? =
|
|||||||
if let webpageId = webpageId, let webpage = modifier.getMedia(webpageId) as? TelegramMediaWebpage {
|
if let webpageId = webpageId, let webpage = modifier.getMedia(webpageId) as? TelegramMediaWebpage {
|
||||||
return .single(webpage)
|
return .single(webpage)
|
||||||
} else {
|
} 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<Api.MessageMedia, NoError> in
|
|> `catch` { _ -> Signal<Api.MessageMedia, NoError> in
|
||||||
return .single(.messageMediaEmpty)
|
return .single(.messageMediaEmpty)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user