mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
no message
This commit is contained in:
@@ -200,6 +200,8 @@
|
||||
D049EAEC1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAEA1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift */; };
|
||||
D049EAF51E44DF3300A2CD3A /* AccountState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAF41E44DF3300A2CD3A /* AccountState.swift */; };
|
||||
D049EAF61E44DF3300A2CD3A /* AccountState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAF41E44DF3300A2CD3A /* AccountState.swift */; };
|
||||
D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CAA591E83310D0047E51F /* MD5.swift */; };
|
||||
D04CAA5B1E83310D0047E51F /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CAA591E83310D0047E51F /* MD5.swift */; };
|
||||
D050F2101E48AB0600988324 /* InteractivePhoneFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */; };
|
||||
D050F2111E48AB0600988324 /* InteractivePhoneFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */; };
|
||||
D050F2511E4A59C200988324 /* JoinLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D050F2501E4A59C200988324 /* JoinLink.swift */; };
|
||||
@@ -429,6 +431,14 @@
|
||||
D0E35A151DE4C6A200BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */; };
|
||||
D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; };
|
||||
D0E652201E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; };
|
||||
D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */; };
|
||||
D0F3A8A01E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */; };
|
||||
D0F3A8A21E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */; };
|
||||
D0F3A8A31E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */; };
|
||||
D0F3A8A51E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8A41E82C94C00B4C64C /* SynchronizeableChatInputState.swift */; };
|
||||
D0F3A8A61E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8A41E82C94C00B4C64C /* SynchronizeableChatInputState.swift */; };
|
||||
D0F3A8A81E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8A71E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift */; };
|
||||
D0F3A8A91E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8A71E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift */; };
|
||||
D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D711D631ABA00955575 /* SearchMessages.swift */; };
|
||||
D0F3CC7A1DDE2859008148FA /* RequestMessageActionCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC91C1DD5DA5E00E8160F /* RequestMessageActionCallback.swift */; };
|
||||
D0F3CC7B1DDE2859008148FA /* RequestEditMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC9201DD5E7E500E8160F /* RequestEditMessage.swift */; };
|
||||
@@ -604,6 +614,7 @@
|
||||
D049EAE71E44B67100A2CD3A /* RecentPeerItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentPeerItem.swift; sourceTree = "<group>"; };
|
||||
D049EAEA1E44B71B00A2CD3A /* RecentlySearchedPeerIds.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlySearchedPeerIds.swift; sourceTree = "<group>"; };
|
||||
D049EAF41E44DF3300A2CD3A /* AccountState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountState.swift; sourceTree = "<group>"; };
|
||||
D04CAA591E83310D0047E51F /* MD5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = "<group>"; };
|
||||
D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractivePhoneFormatter.swift; sourceTree = "<group>"; };
|
||||
D050F2501E4A59C200988324 /* JoinLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinLink.swift; sourceTree = "<group>"; };
|
||||
D0528E591E658B3600E2FEF5 /* ManagedLocalInputActivities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedLocalInputActivities.swift; sourceTree = "<group>"; };
|
||||
@@ -723,6 +734,10 @@
|
||||
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>"; };
|
||||
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateAccountPeerName.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>"; };
|
||||
D0F3A8A41E82C94C00B4C64C /* SynchronizeableChatInputState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeableChatInputState.swift; sourceTree = "<group>"; };
|
||||
D0F3A8A71E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatePeerChatInterfaceState.swift; sourceTree = "<group>"; };
|
||||
D0F3CC7C1DDE289E008148FA /* ResolvePeerByName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResolvePeerByName.swift; sourceTree = "<group>"; };
|
||||
D0F53BE81E784A4800117362 /* ChangeAccountPhoneNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeAccountPhoneNumber.swift; sourceTree = "<group>"; };
|
||||
D0F7AB2B1DCE889D009AD9A1 /* EditedMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditedMessageAttribute.swift; sourceTree = "<group>"; };
|
||||
@@ -874,6 +889,7 @@
|
||||
D0448C9E1E27F5EB005A61A7 /* Random.swift */,
|
||||
D050F20F1E48AB0600988324 /* InteractivePhoneFormatter.swift */,
|
||||
D03229F31E6B39700000AF9C /* ImportAccount.swift */,
|
||||
D04CAA591E83310D0047E51F /* MD5.swift */,
|
||||
);
|
||||
name = Utils;
|
||||
sourceTree = "<group>";
|
||||
@@ -914,6 +930,7 @@
|
||||
D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */,
|
||||
D00D97C61E32901700E5C2B6 /* PeerInputActivity.swift */,
|
||||
D033FEAF1E61EB0200644997 /* PeerReportStatus.swift */,
|
||||
D0F3A8A41E82C94C00B4C64C /* SynchronizeableChatInputState.swift */,
|
||||
);
|
||||
name = Peers;
|
||||
sourceTree = "<group>";
|
||||
@@ -994,6 +1011,8 @@
|
||||
D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */,
|
||||
D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */,
|
||||
D0E23DD91E806F7700B9B6D2 /* ManagedSynchronizeMarkFeaturedStickerPacksAsSeenOperations.swift */,
|
||||
D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */,
|
||||
D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */,
|
||||
);
|
||||
name = State;
|
||||
sourceTree = "<group>";
|
||||
@@ -1243,6 +1262,7 @@
|
||||
C2FD33E31E687BF1008D13D4 /* PeerPhotoUpdater.swift */,
|
||||
C2FD33EA1E696C78008D13D4 /* GroupsInCommon.swift */,
|
||||
D0C48F3B1E8142EF0075317D /* LoadedPeerFromMessage.swift */,
|
||||
D0F3A8A71E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift */,
|
||||
);
|
||||
name = Peers;
|
||||
sourceTree = "<group>";
|
||||
@@ -1475,6 +1495,7 @@
|
||||
D0B843B91DA7FF30005F29E1 /* NBMetadataCoreTest.m in Sources */,
|
||||
D09A2FE61D7CD4940018FB72 /* TelegramChannel.swift in Sources */,
|
||||
D03B0D0E1D62255C00955575 /* UpdateGroup.swift in Sources */,
|
||||
D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */,
|
||||
D01AC9231DD5E9A200E8160F /* ApplyUpdateMessage.swift in Sources */,
|
||||
D03B0CF71D62250800955575 /* TelegramMediaImage.swift in Sources */,
|
||||
D0E23DDF1E8082A400B9B6D2 /* ArchivedStickerPacks.swift in Sources */,
|
||||
@@ -1511,6 +1532,7 @@
|
||||
D0FA8B981E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */,
|
||||
D0DF0C911D81A857008AEB01 /* ImageRepresentationsUtils.swift in Sources */,
|
||||
D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */,
|
||||
D0F3A8A21E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */,
|
||||
D03B0D5A1D631A6900955575 /* Api.swift in Sources */,
|
||||
D049EAE81E44B67100A2CD3A /* RecentPeerItem.swift in Sources */,
|
||||
D02ABC7B1E30058F00CAE539 /* DeleteMessagesInteractively.swift in Sources */,
|
||||
@@ -1570,6 +1592,7 @@
|
||||
D0FA8BB91E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
|
||||
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
|
||||
D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */,
|
||||
D05452071E7B5093006EEF19 /* LoadedStickerPack.swift in Sources */,
|
||||
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
|
||||
D0B843971DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift in Sources */,
|
||||
@@ -1623,11 +1646,13 @@
|
||||
D0AB0B961D662F0B002C78E7 /* ManagedChatListHoles.swift in Sources */,
|
||||
D05A32E41E6F0B2E002760B4 /* RecentAccountSessions.swift in Sources */,
|
||||
D03E5E0C1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */,
|
||||
D0F3A8A51E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */,
|
||||
D03B0CD71D62245300955575 /* TelegramGroup.swift in Sources */,
|
||||
D0B8438C1DA7CF50005F29E1 /* BotInfo.swift in Sources */,
|
||||
D033FEB31E61F3C000644997 /* ReportPeer.swift in Sources */,
|
||||
D021E0E21DB5401A00C6B04F /* StickerManagement.swift in Sources */,
|
||||
D0BC38701E40853E0044D6FE /* UpdatePeers.swift in Sources */,
|
||||
D0F3A8A81E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */,
|
||||
D03B0CE21D62249B00955575 /* InlineBotMessageAttribute.swift in Sources */,
|
||||
D0AB0B9A1D666520002C78E7 /* ManagedSynchronizePeerReadStates.swift in Sources */,
|
||||
C27982531E73077800262BFD /* SecretChatStateBridge.swift in Sources */,
|
||||
@@ -1711,6 +1736,7 @@
|
||||
D0B844311DAB91E0005F29E1 /* NBPhoneMetaData.m in Sources */,
|
||||
C22EE61C1E67418000334C38 /* ToggleChannelSignatures.swift in Sources */,
|
||||
D0B418AC1D7E0597004562A4 /* Network.swift in Sources */,
|
||||
D04CAA5B1E83310D0047E51F /* MD5.swift in Sources */,
|
||||
D0B844141DAB91CD005F29E1 /* PhoneNumbers.swift in Sources */,
|
||||
D0E305AB1E5BA02D00D7A3A2 /* ChannelBlacklist.swift in Sources */,
|
||||
D00D97CB1E32917C00E5C2B6 /* PeerInputActivityManager.swift in Sources */,
|
||||
@@ -1754,6 +1780,7 @@
|
||||
D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */,
|
||||
D0B8442B1DAB91E0005F29E1 /* NBMetadataCore.m in Sources */,
|
||||
D00C7CD01E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */,
|
||||
D0F3A8A61E82C94C00B4C64C /* SynchronizeableChatInputState.swift in Sources */,
|
||||
D00D34431E6EDD2E0057B307 /* ManagedSynchronizeConsumeMessageContentsOperations.swift in Sources */,
|
||||
D049EAE91E44B67100A2CD3A /* RecentPeerItem.swift in Sources */,
|
||||
D001F3F31E128A1C007A8C60 /* UpdateMessageService.swift in Sources */,
|
||||
@@ -1775,6 +1802,7 @@
|
||||
C2366C8A1E4F40480097CCFF /* SupportPeerId.swift in Sources */,
|
||||
D0B844451DAB91FD005F29E1 /* AccountViewTracker.swift in Sources */,
|
||||
D0C48F3D1E8142EF0075317D /* LoadedPeerFromMessage.swift in Sources */,
|
||||
D0F3A8A01E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */,
|
||||
D0BE383F1E7C5995000079AF /* MediaPool.swift in Sources */,
|
||||
D050F2601E4A5AD500988324 /* AutoremoveTimeoutMessageAttribute.swift in Sources */,
|
||||
D049EAD91E43DAD200A2CD3A /* ManagedRecentStickers.swift in Sources */,
|
||||
@@ -1832,6 +1860,7 @@
|
||||
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||
D0613FCB1E60440600202CDB /* InvitationLinks.swift in Sources */,
|
||||
D0B844471DAB91FD005F29E1 /* ManagedServiceViews.swift in Sources */,
|
||||
D0F3A8A91E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */,
|
||||
D00D343A1E6EC9520057B307 /* TeleramMediaUnsupported.swift in Sources */,
|
||||
D03C53691DAD5CA9004C17B3 /* PeerAccessRestrictionInfo.swift in Sources */,
|
||||
C2FD33E21E680E9E008D13D4 /* RequestUserPhotos.swift in Sources */,
|
||||
@@ -1858,6 +1887,7 @@
|
||||
D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */,
|
||||
D0FA8BB41E201B02001E855B /* ProcessSecretChatIncomingEncryptedOperations.swift in Sources */,
|
||||
D0B844101DAB91CD005F29E1 /* MergeLists.swift in Sources */,
|
||||
D0F3A8A31E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */,
|
||||
D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
||||
D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */,
|
||||
D050F2641E4A5AEB00988324 /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
|
||||
|
||||
@@ -218,6 +218,7 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(FeaturedStickerPackItem.self, f: { FeaturedStickerPackItem(decoder: $0) })
|
||||
declareEncodable(SynchronizeMarkFeaturedStickerPacksAsSeenOperation.self, f: { SynchronizeMarkFeaturedStickerPacksAsSeenOperation(decoder: $0) })
|
||||
declareEncodable(ArchivedStickerPacksInfo.self, f: { ArchivedStickerPacksInfo(decoder: $0) })
|
||||
declareEncodable(SynchronizeChatInputStateOperation.self, f: { SynchronizeChatInputStateOperation(decoder: $0) })
|
||||
|
||||
return
|
||||
}()
|
||||
@@ -230,7 +231,7 @@ private func accountRecordIdPathName(_ id: AccountRecordId) -> String {
|
||||
return "account-\(UInt64(bitPattern: id.int64))"
|
||||
}
|
||||
|
||||
public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: String, testingEnvironment: Bool, shouldKeepAutoConnection: Bool = true) -> Signal<Either<UnauthorizedAccount, Account>, NoError> {
|
||||
public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: String, testingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<Either<UnauthorizedAccount, Account>, NoError> {
|
||||
return Signal<(String, Postbox, Coding?), NoError> { subscriber in
|
||||
let _ = declaredEncodables
|
||||
|
||||
@@ -271,7 +272,7 @@ public func accountWithId(apiId: Int32, id: AccountRecordId, appGroupPath: Strin
|
||||
case let authorizedState as AuthorizedAccountState:
|
||||
return initializedNetwork(apiId: apiId, datacenterId: Int(authorizedState.masterDatacenterId), keychain: keychain, networkUsageInfoPath: accountNetworkUsageInfoPath(basePath: basePath), testingEnvironment: testingEnvironment)
|
||||
|> map { network -> Either<UnauthorizedAccount, Account> in
|
||||
return .right(value: Account(id: id, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId))
|
||||
return .right(value: Account(id: id, basePath: basePath, testingEnvironment: testingEnvironment, postbox: postbox, network: network, peerId: authorizedState.peerId, auxiliaryMethods: auxiliaryMethods))
|
||||
}
|
||||
case _:
|
||||
assertionFailure("Unexpected accountState \(accountState)")
|
||||
@@ -353,6 +354,16 @@ public enum AccountNetworkState {
|
||||
case online
|
||||
}
|
||||
|
||||
public final class AccountAuxiliaryMethods {
|
||||
public let updatePeerChatInputState: (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?
|
||||
public let fetchResource: (Account, MediaResource, Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError>?
|
||||
|
||||
public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError>?) {
|
||||
self.updatePeerChatInputState = updatePeerChatInputState
|
||||
self.fetchResource = fetchResource
|
||||
}
|
||||
}
|
||||
|
||||
public class Account {
|
||||
public let id: AccountRecordId
|
||||
public let basePath: String
|
||||
@@ -361,6 +372,8 @@ public class Account {
|
||||
public let network: Network
|
||||
public let peerId: PeerId
|
||||
|
||||
public let auxiliaryMethods: AccountAuxiliaryMethods
|
||||
|
||||
private let serviceQueue = Queue()
|
||||
|
||||
public private(set) var stateManager: AccountStateManager!
|
||||
@@ -401,7 +414,7 @@ public class Account {
|
||||
|
||||
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
|
||||
|
||||
public init(id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, peerId: PeerId) {
|
||||
public init(id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, peerId: PeerId, auxiliaryMethods: AccountAuxiliaryMethods) {
|
||||
self.id = id
|
||||
self.basePath = basePath
|
||||
self.testingEnvironment = testingEnvironment
|
||||
@@ -409,8 +422,10 @@ public class Account {
|
||||
self.network = network
|
||||
self.peerId = peerId
|
||||
|
||||
self.auxiliaryMethods = auxiliaryMethods
|
||||
|
||||
self.peerInputActivityManager = PeerInputActivityManager()
|
||||
self.stateManager = AccountStateManager(account: self, peerInputActivityManager: self.peerInputActivityManager)
|
||||
self.stateManager = AccountStateManager(account: self, peerInputActivityManager: self.peerInputActivityManager, auxiliaryMethods: auxiliaryMethods)
|
||||
self.localInputActivityManager = PeerInputActivityManager()
|
||||
self.viewTracker = AccountViewTracker(account: self)
|
||||
self.pendingMessageManager = PendingMessageManager(network: network, postbox: postbox, stateManager: self.stateManager)
|
||||
@@ -551,6 +566,7 @@ public class Account {
|
||||
self.managedOperationsDisposable.add(managedRecentlyUsedInlineBots(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedLocalTypingActivities(activities: self.localInputActivityManager.allActivities(), postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||
self.managedOperationsDisposable.add(managedSynchronizeChatInputStateOperations(postbox: self.postbox, network: self.network).start())
|
||||
|
||||
|
||||
let updatedPresence = self.shouldKeepOnlinePresence.get()
|
||||
@@ -634,7 +650,13 @@ public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network:
|
||||
public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil) {
|
||||
account.postbox.mediaBox.fetchResource = { [weak account] resource, range -> Signal<MediaResourceDataFetchResult, NoError> in
|
||||
if let strongAccount = account {
|
||||
return fetchResource(account: strongAccount, resource: resource, range: range)
|
||||
if let result = fetchResource(account: strongAccount, resource: resource, range: range) {
|
||||
return result
|
||||
} else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, range) {
|
||||
return result
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ enum AccountStateMutationOperation {
|
||||
case ReadGlobalMessageContents([Int32])
|
||||
case UpdateMessageImpressionCount(MessageId, Int32)
|
||||
case UpdateInstalledStickerPacks(AccountStateUpdateStickerPacksOperation)
|
||||
case UpdateChatInputState(PeerId, SynchronizeableChatInputState?)
|
||||
}
|
||||
|
||||
struct AccountMutableState {
|
||||
@@ -248,9 +249,13 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateInstalledStickerPacks(operation))
|
||||
}
|
||||
|
||||
mutating func addUpdateChatInputState(peerId: PeerId, state: SynchronizeableChatInputState?) {
|
||||
self.addOperation(.UpdateChatInputState(peerId, state))
|
||||
}
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
switch operation {
|
||||
case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks:
|
||||
case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .ReadOutbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState:
|
||||
break
|
||||
case let .AddMessages(messages, _):
|
||||
for message in messages {
|
||||
|
||||
@@ -14,7 +14,7 @@ private enum AccountKind {
|
||||
case unauthorized
|
||||
}
|
||||
|
||||
public func currentAccount(apiId: Int32, manager: AccountManager, appGroupPath: String, testingEnvironment: Bool) -> Signal<Either<UnauthorizedAccount, Account>?, NoError> {
|
||||
public func currentAccount(apiId: Int32, manager: AccountManager, appGroupPath: String, testingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal<Either<UnauthorizedAccount, Account>?, NoError> {
|
||||
return manager.allocatedCurrentAccountId()
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
return lhs == rhs
|
||||
@@ -23,7 +23,7 @@ public func currentAccount(apiId: Int32, manager: AccountManager, appGroupPath:
|
||||
if let id = id {
|
||||
let reload = ValuePromise<Bool>(true, ignoreRepeated: false)
|
||||
return reload.get() |> mapToSignal { _ -> Signal<Either<UnauthorizedAccount, Account>?, NoError> in
|
||||
return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment)
|
||||
return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: testingEnvironment, auxiliaryMethods: auxiliaryMethods)
|
||||
|> mapToSignal { account -> Signal<Either<UnauthorizedAccount, Account>?, NoError> in
|
||||
let postbox: Postbox
|
||||
let initialKind: AccountKind
|
||||
@@ -96,7 +96,7 @@ public func logoutFromAccount(id: AccountRecordId, accountManager: AccountManage
|
||||
}
|
||||
}
|
||||
|
||||
public func managedCleanupAccounts(apiId: Int32, accountManager: AccountManager, appGroupPath: String) -> Signal<Void, NoError> {
|
||||
public func managedCleanupAccounts(apiId: Int32, accountManager: AccountManager, appGroupPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal<Void, NoError> {
|
||||
return Signal { subscriber in
|
||||
let loggedOutAccounts = Atomic<[AccountRecordId: MetaDisposable]>(value: [:])
|
||||
let disposable = accountManager.accountRecords().start(next: { view in
|
||||
@@ -139,7 +139,7 @@ public func managedCleanupAccounts(apiId: Int32, accountManager: AccountManager,
|
||||
disposable.dispose()
|
||||
}
|
||||
for (id, disposable) in beginList {
|
||||
disposable.set(cleanupAccount(apiId: apiId, accountManager: accountManager, id: id, appGroupPath: appGroupPath).start())
|
||||
disposable.set(cleanupAccount(apiId: apiId, accountManager: accountManager, id: id, appGroupPath: appGroupPath, auxiliaryMethods: auxiliaryMethods).start())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -150,8 +150,8 @@ public func managedCleanupAccounts(apiId: Int32, accountManager: AccountManager,
|
||||
}
|
||||
|
||||
|
||||
private func cleanupAccount(apiId: Int32, accountManager: AccountManager, id: AccountRecordId, appGroupPath: String) -> Signal<Void, NoError> {
|
||||
return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: false)
|
||||
private func cleanupAccount(apiId: Int32, accountManager: AccountManager, id: AccountRecordId, appGroupPath: String, auxiliaryMethods: AccountAuxiliaryMethods) -> Signal<Void, NoError> {
|
||||
return accountWithId(apiId: apiId, id: id, appGroupPath: appGroupPath, testingEnvironment: false, auxiliaryMethods: auxiliaryMethods)
|
||||
|> mapToSignal { account -> Signal<Void, NoError> in
|
||||
switch account {
|
||||
case .left:
|
||||
|
||||
@@ -982,6 +982,19 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
updatedState.addUpdateInstalledStickerPacks(.reorder(namespace, order))
|
||||
case .updateStickerSets:
|
||||
updatedState.addUpdateInstalledStickerPacks(.sync)
|
||||
case let .updateDraftMessage(peer, draft):
|
||||
let inputState: SynchronizeableChatInputState?
|
||||
switch draft {
|
||||
case .draftMessageEmpty:
|
||||
inputState = nil
|
||||
case let .draftMessage(_, replyToMsgId, message, entities, date):
|
||||
var replyToMessageId: MessageId?
|
||||
if let replyToMsgId = replyToMsgId {
|
||||
replyToMessageId = MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId)
|
||||
}
|
||||
inputState = SynchronizeableChatInputState(replyToMessageId: replyToMessageId, text: message, timestamp: date)
|
||||
}
|
||||
updatedState.addUpdateChatInputState(peerId: peer.peerId, state: inputState)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -1356,7 +1369,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
var currentAddMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ResetReadState, .UpdatePeerNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks:
|
||||
case .AddHole, .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ResetReadState, .UpdatePeerNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedPeerIds, .ReadGlobalMessageContents, .UpdateMessageImpressionCount, .UpdateInstalledStickerPacks, .UpdateChatInputState:
|
||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
@@ -1392,7 +1405,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
return result
|
||||
}
|
||||
|
||||
func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modifier, finalState: AccountFinalState) -> AccountReplayedFinalState? {
|
||||
func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modifier, auxiliaryMethods: AccountAuxiliaryMethods, finalState: AccountFinalState) -> AccountReplayedFinalState? {
|
||||
let verified = verifyTransaction(modifier, finalState: finalState.state)
|
||||
if !verified {
|
||||
return nil
|
||||
@@ -1552,6 +1565,10 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif
|
||||
})
|
||||
case let .UpdateInstalledStickerPacks(operation):
|
||||
stickerPackOperations.append(operation)
|
||||
case let .UpdateChatInputState(peerId, inputState):
|
||||
modifier.updatePeerChatInterfaceState(peerId, update: { current in
|
||||
return auxiliaryMethods.updatePeerChatInputState(current, inputState)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public final class AccountStateManager {
|
||||
private let queue = Queue()
|
||||
private let account: Account
|
||||
private let peerInputActivityManager: PeerInputActivityManager
|
||||
private let auxiliaryMethods: AccountAuxiliaryMethods
|
||||
|
||||
private var updateService: UpdateMessageService?
|
||||
private let updateServiceDisposable = MetaDisposable()
|
||||
@@ -81,9 +82,10 @@ public final class AccountStateManager {
|
||||
|
||||
private var updatedWebpageContexts: [MediaId: UpdatedWebpageSubscriberContext] = [:]
|
||||
|
||||
init(account: Account, peerInputActivityManager: PeerInputActivityManager) {
|
||||
init(account: Account, peerInputActivityManager: PeerInputActivityManager, auxiliaryMethods: AccountAuxiliaryMethods) {
|
||||
self.account = account
|
||||
self.peerInputActivityManager = peerInputActivityManager
|
||||
self.auxiliaryMethods = auxiliaryMethods
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -233,6 +235,7 @@ public final class AccountStateManager {
|
||||
let account = self.account
|
||||
let mediaBox = account.postbox.mediaBox
|
||||
let accountPeerId = account.peerId
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let signal = account.postbox.stateView()
|
||||
|> mapToSignal { view -> Signal<AuthorizedAccountState, NoError> in
|
||||
if let state = view.state as? AuthorizedAccountState {
|
||||
@@ -261,7 +264,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
return account.postbox.modify { modifier -> (Api.updates.Difference?, AccountReplayedFinalState?) in
|
||||
if let replayedState = replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, modifier: modifier, finalState: finalState) {
|
||||
if let replayedState = replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, modifier: modifier, auxiliaryMethods: auxiliaryMethods, finalState: finalState) {
|
||||
return (difference, replayedState)
|
||||
} else {
|
||||
return (nil, nil)
|
||||
@@ -345,6 +348,7 @@ public final class AccountStateManager {
|
||||
case let .processUpdateGroups(groups):
|
||||
self.operationTimer?.invalidate()
|
||||
let account = self.account
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let accountPeerId = account.peerId
|
||||
let mediaBox = account.postbox.mediaBox
|
||||
let queue = self.queue
|
||||
@@ -359,7 +363,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
|
||||
return account.postbox.modify { modifier -> AccountReplayedFinalState? in
|
||||
return replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, modifier: modifier, finalState: finalState)
|
||||
return replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, modifier: modifier, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
}
|
||||
|> map({ ($0, finalState) })
|
||||
|> deliverOn(queue)
|
||||
@@ -511,8 +515,9 @@ public final class AccountStateManager {
|
||||
|
||||
let accountPeerId = self.account.peerId
|
||||
let mediaBox = self.account.postbox.mediaBox
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let signal = self.account.postbox.modify { modifier -> AccountReplayedFinalState? in
|
||||
return replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, modifier: modifier, finalState: finalState)
|
||||
return replayFinalState(accountPeerId: accountPeerId, mediaBox: mediaBox, modifier: modifier, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
}
|
||||
|> map({ ($0, finalState) })
|
||||
|> deliverOn(self.queue)
|
||||
|
||||
@@ -299,23 +299,27 @@ public struct LocalFileReferenceMediaResourceId: MediaResourceId {
|
||||
public class LocalFileReferenceMediaResource: TelegramMediaResource {
|
||||
let localFilePath: String
|
||||
let randomId: Int64
|
||||
let isUniquelyReferencedTemporaryFile: Bool
|
||||
let size: Int32?
|
||||
|
||||
public init(localFilePath: String, randomId: Int64, size: Int32? = nil) {
|
||||
public init(localFilePath: String, randomId: Int64, isUniquelyReferencedTemporaryFile: Bool = false, size: Int32? = nil) {
|
||||
self.localFilePath = localFilePath
|
||||
self.randomId = randomId
|
||||
self.isUniquelyReferencedTemporaryFile = isUniquelyReferencedTemporaryFile
|
||||
self.size = size
|
||||
}
|
||||
|
||||
public required init(decoder: Decoder) {
|
||||
self.localFilePath = decoder.decodeStringForKey("p")
|
||||
self.randomId = decoder.decodeInt64ForKey("r")
|
||||
self.isUniquelyReferencedTemporaryFile = (decoder.decodeInt32ForKey("t") as Int32) != 0
|
||||
self.size = decoder.decodeInt32ForKey("s")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: Encoder) {
|
||||
encoder.encodeString(self.localFilePath, forKey: "p")
|
||||
encoder.encodeInt64(self.randomId, forKey: "r")
|
||||
encoder.encodeInt32(self.isUniquelyReferencedTemporaryFile ? 1 : 0, forKey: "t")
|
||||
if let size = self.size {
|
||||
encoder.encodeInt32(size, forKey: "s")
|
||||
} else {
|
||||
@@ -329,7 +333,7 @@ public class LocalFileReferenceMediaResource: TelegramMediaResource {
|
||||
|
||||
public func isEqual(to: TelegramMediaResource) -> Bool {
|
||||
if let to = to as? LocalFileReferenceMediaResource {
|
||||
return self.localFilePath == to.localFilePath && self.randomId == to.randomId && self.size == size
|
||||
return self.localFilePath == to.localFilePath && self.randomId == to.randomId && self.size == to.size && self.isUniquelyReferencedTemporaryFile == to.isUniquelyReferencedTemporaryFile
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ private func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaR
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
if let scaledImage = scaledImage, let data = UIImageJPEGRepresentation(scaledImage, 0.6) {
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: data, complete: true))
|
||||
subscriber.putNext(.dataPart(data: data, range: 0 ..< data.count, complete: true))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
@@ -57,39 +57,44 @@ private func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaR
|
||||
}
|
||||
#endif
|
||||
|
||||
private func fetchLocalFileResource(path: String) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
private func fetchLocalFileResource(path: String, move: Bool) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return Signal { subscriber in
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: data, complete: true))
|
||||
if move {
|
||||
subscriber.putNext(.moveLocalFile(path: path))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: Data(), complete: false))
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
subscriber.putNext(.dataPart(data: data, range: 0 ..< data.count, complete: true))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putNext(.dataPart(data: Data(), range: 0 ..< 0, complete: false))
|
||||
}
|
||||
}
|
||||
return EmptyDisposable
|
||||
}
|
||||
}
|
||||
|
||||
func fetchResource(account: Account, resource: MediaResource, range: Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
func fetchResource(account: Account, resource: MediaResource, range: Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError>? {
|
||||
if let _ = resource as? EmptyMediaResource {
|
||||
return .never()
|
||||
} else if let secretFileResource = resource as? SecretFileMediaResource {
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, range: range))
|
||||
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, range: range))
|
||||
} else if let cloudResource = resource as? TelegramCloudMediaResource {
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range))
|
||||
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range))
|
||||
} else if let photoLibraryResource = resource as? PhotoLibraryMediaResource {
|
||||
#if os(iOS)
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier))
|
||||
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier))
|
||||
#else
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false))
|
||||
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false))
|
||||
#endif
|
||||
} else if let localFileResource = resource as? LocalFileReferenceMediaResource {
|
||||
if false {
|
||||
//return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchLocalFileResource(path: localFileResource.localFilePath) |> delay(10.0, queue: Queue.concurrentDefaultQueue()))
|
||||
//return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchLocalFileResource(path: localFileResource.localFilePath) |> delay(10.0, queue: Queue.concurrentDefaultQueue()))
|
||||
} else {
|
||||
return fetchLocalFileResource(path: localFileResource.localFilePath)
|
||||
return fetchLocalFileResource(path: localFileResource.localFilePath, move: localFileResource.isUniquelyReferencedTemporaryFile)
|
||||
}
|
||||
} else if let httpReference = resource as? HttpReferenceMediaResource {
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchHttpResource(url: httpReference.url))
|
||||
return .single(.dataPart(data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchHttpResource(url: httpReference.url))
|
||||
}
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ func fetchHttpResource(url: String) -> Signal<MediaResourceDataFetchResult, NoEr
|
||||
let signal = MTHttpRequestOperation.data(forHttpUrl: url)!
|
||||
return Signal { subscriber in
|
||||
let disposable = signal.start(next: { next in
|
||||
let fetchResult = MediaResourceDataFetchResult(data: next as! Data, complete: true)
|
||||
let data = next as! Data
|
||||
let fetchResult: MediaResourceDataFetchResult = .dataPart(data: data, range: 0 ..< data.count, complete: true)
|
||||
subscriber.putNext(fetchResult)
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
||||
18
TelegramCore/MD5.swift
Normal file
18
TelegramCore/MD5.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
import Foundation
|
||||
import TelegramCorePrivateModule
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
#else
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public extension MemoryBuffer {
|
||||
public func md5Digest() -> Data {
|
||||
var res = Data()
|
||||
res.count = Int(CC_MD5_DIGEST_LENGTH)
|
||||
res.withUnsafeMutableBytes { mutableBytes -> Void in
|
||||
CC_MD5(self.memory, CC_LONG(self.length), mutableBytes)
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
136
TelegramCore/ManagedSynchronizeChatInputStateOperations.swift
Normal file
136
TelegramCore/ManagedSynchronizeChatInputStateOperations.swift
Normal file
@@ -0,0 +1,136 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
private final class ManagedSynchronizeChatInputStateOperationsHelper {
|
||||
var operationDisposables: [Int32: Disposable] = [:]
|
||||
|
||||
func update(_ entries: [PeerMergedOperationLogEntry]) -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) {
|
||||
var disposeOperations: [Disposable] = []
|
||||
var beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)] = []
|
||||
|
||||
var hasRunningOperationForPeerId = Set<PeerId>()
|
||||
var validMergedIndices = Set<Int32>()
|
||||
for entry in entries {
|
||||
if !hasRunningOperationForPeerId.contains(entry.peerId) {
|
||||
hasRunningOperationForPeerId.insert(entry.peerId)
|
||||
validMergedIndices.insert(entry.mergedIndex)
|
||||
|
||||
if self.operationDisposables[entry.mergedIndex] == nil {
|
||||
let disposable = MetaDisposable()
|
||||
beginOperations.append((entry, disposable))
|
||||
self.operationDisposables[entry.mergedIndex] = disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var removeMergedIndices: [Int32] = []
|
||||
for (mergedIndex, disposable) in self.operationDisposables {
|
||||
if !validMergedIndices.contains(mergedIndex) {
|
||||
removeMergedIndices.append(mergedIndex)
|
||||
disposeOperations.append(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
for mergedIndex in removeMergedIndices {
|
||||
self.operationDisposables.removeValue(forKey: mergedIndex)
|
||||
}
|
||||
|
||||
return (disposeOperations, beginOperations)
|
||||
}
|
||||
|
||||
func reset() -> [Disposable] {
|
||||
let disposables = Array(self.operationDisposables.values)
|
||||
self.operationDisposables.removeAll()
|
||||
return disposables
|
||||
}
|
||||
}
|
||||
|
||||
private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Modifier, PeerMergedOperationLogEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
|
||||
return postbox.modify { modifier -> Signal<Void, NoError> in
|
||||
var result: PeerMergedOperationLogEntry?
|
||||
modifier.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in
|
||||
if let entry = entry, let _ = entry.mergedIndex, entry.contents is SynchronizeChatInputStateOperation {
|
||||
result = entry.mergedEntry!
|
||||
return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none)
|
||||
} else {
|
||||
return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none)
|
||||
}
|
||||
})
|
||||
|
||||
return f(modifier, result)
|
||||
} |> switchToLatest
|
||||
}
|
||||
|
||||
func managedSynchronizeChatInputStateOperations(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
return Signal { _ in
|
||||
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeChatInputStates
|
||||
|
||||
let helper = Atomic<ManagedSynchronizeChatInputStateOperationsHelper>(value: ManagedSynchronizeChatInputStateOperationsHelper())
|
||||
|
||||
let disposable = postbox.mergedOperationLogView(tag: tag, limit: 10).start(next: { view in
|
||||
let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) in
|
||||
return helper.update(view.entries)
|
||||
}
|
||||
|
||||
for disposable in disposeOperations {
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
for (entry, disposable) in beginOperations {
|
||||
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { modifier, entry -> Signal<Void, NoError> in
|
||||
if let entry = entry {
|
||||
if let operation = entry.contents as? SynchronizeChatInputStateOperation {
|
||||
return synchronizeChatInputState(modifier: modifier, postbox: postbox, network: network, peerId: entry.peerId, operation: operation)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
return .complete()
|
||||
})
|
||||
|> then(postbox.modify { modifier -> Void in
|
||||
let _ = modifier.operationLogRemoveEntry(peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex)
|
||||
})
|
||||
|
||||
disposable.set(signal.start())
|
||||
}
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
let disposables = helper.with { helper -> [Disposable] in
|
||||
return helper.reset()
|
||||
}
|
||||
for disposable in disposables {
|
||||
disposable.dispose()
|
||||
}
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func synchronizeChatInputState(modifier: Modifier, postbox: Postbox, network: Network, peerId: PeerId, operation: SynchronizeChatInputStateOperation) -> Signal<Void, NoError> {
|
||||
let inputState = (modifier.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState
|
||||
if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
var flags: Int32 = 0
|
||||
if inputState?.replyToMessageId != nil {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
return network.request(Api.functions.messages.saveDraft(flags: flags, replyToMsgId: inputState?.replyToMessageId?.id, peer: inputPeer, message: inputState?.text ?? "", entities: nil))
|
||||
|> delay(2.0, queue: Queue.concurrentDefaultQueue())
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
@@ -200,9 +200,9 @@ func multipartFetch(account: Account, resource: TelegramCloudMediaResource, size
|
||||
let manager = MultipartFetchManager(size: size, range: range, encryptionKey: encryptionKey, decryptedSize: decryptedSize, fetchPart: { offset, size in
|
||||
return download.part(location: inputLocation, offset: offset, length: size)
|
||||
}, partReady: { data in
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: data, complete: false))
|
||||
subscriber.putNext(.dataPart(data: data, range: 0 ..< data.count, complete: false))
|
||||
}, completed: {
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: Data(), complete: true))
|
||||
subscriber.putNext(.dataPart(data: Data(), range: 0 ..< 0, complete: true))
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ private func md5(_ data : Data) -> Data {
|
||||
private final class MultipartUploadState {
|
||||
let aesKey: Data
|
||||
var aesIv: Data
|
||||
var md5Context = CC_MD5_CTX()
|
||||
var effectiveSize: Int = 0
|
||||
|
||||
init(encryptionKey: SecretFileEncryptionKey?) {
|
||||
@@ -72,7 +71,6 @@ private final class MultipartUploadState {
|
||||
self.aesKey = Data()
|
||||
self.aesIv = Data()
|
||||
}
|
||||
CC_MD5_Init(&self.md5Context)
|
||||
}
|
||||
|
||||
func transform(data: Data) -> Data {
|
||||
@@ -92,34 +90,17 @@ private final class MultipartUploadState {
|
||||
self.aesIv.withUnsafeMutableBytes { (iv: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
MTAesEncryptBytesInplaceAndModifyIv(bytes, encryptedData.count, self.aesKey, iv)
|
||||
}
|
||||
CC_MD5_Update(&self.md5Context, bytes, UInt32(encryptedData.count))
|
||||
}
|
||||
effectiveSize += encryptedData.count
|
||||
return encryptedData
|
||||
} else {
|
||||
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
CC_MD5_Update(&self.md5Context, bytes, UInt32(data.count))
|
||||
}
|
||||
effectiveSize += data.count
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
func finalize() -> (md5Digest: String, effectiveSize: Int) {
|
||||
var res = Data()
|
||||
res.count = Int(CC_MD5_DIGEST_LENGTH)
|
||||
res.withUnsafeMutableBytes { mutableBytes -> Void in
|
||||
CC_MD5_Final(mutableBytes, &self.md5Context)
|
||||
}
|
||||
let hashString = res.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> String in
|
||||
let hexString = NSMutableString()
|
||||
for i in 0 ..< res.count {
|
||||
let byteValue = UInt(bytes.advanced(by: i).pointee)
|
||||
hexString.appendFormat("%02x", byteValue)
|
||||
}
|
||||
return hexString as String
|
||||
}
|
||||
return (hashString, self.effectiveSize)
|
||||
func finalize() -> Int {
|
||||
return self.effectiveSize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,9 +133,11 @@ private final class MultipartUploadManager {
|
||||
let dataDisposable = MetaDisposable()
|
||||
var resourceData: MediaResourceData?
|
||||
|
||||
var headerPartReady: Bool
|
||||
|
||||
let state: MultipartUploadState
|
||||
|
||||
init(data: Signal<MediaResourceData, NoError>, encryptionKey: SecretFileEncryptionKey?, hintFileSize: Int?, uploadPart: @escaping (UploadPart) -> Signal<Void, NoError>, progress: @escaping (Float) -> Void, completed: @escaping (MultipartIntermediateResult) -> Void) {
|
||||
init(resource: MediaResource, data: Signal<MediaResourceData, NoError>, encryptionKey: SecretFileEncryptionKey?, hintFileSize: Int?, uploadPart: @escaping (UploadPart) -> Signal<Void, NoError>, progress: @escaping (Float) -> Void, completed: @escaping (MultipartIntermediateResult) -> Void) {
|
||||
self.dataSignal = data
|
||||
|
||||
var fileId: Int64 = 0
|
||||
@@ -168,12 +151,28 @@ private final class MultipartUploadManager {
|
||||
self.progress = progress
|
||||
self.completed = completed
|
||||
|
||||
self.headerPartReady = resource.headerSize == 0
|
||||
|
||||
if let hintFileSize = hintFileSize, hintFileSize > 5 * 1024 * 1024 {
|
||||
self.defaultPartSize = 512 * 1024
|
||||
self.bigTotalParts = (hintFileSize / self.defaultPartSize) + (hintFileSize % self.defaultPartSize == 0 ? 0 : 1)
|
||||
} else {
|
||||
if self.headerPartReady {
|
||||
self.defaultPartSize = 32 * 1024
|
||||
self.bigTotalParts = nil
|
||||
} else {
|
||||
self.defaultPartSize = 32 * 1024
|
||||
self.bigTotalParts = nil
|
||||
//self.defaultPartSize = 128 * 1024
|
||||
//self.bigTotalParts = -1
|
||||
}
|
||||
}
|
||||
|
||||
if self.headerPartReady {
|
||||
self.committedOffset = 0
|
||||
} else {
|
||||
self.committedOffset = self.defaultPartSize
|
||||
self.state.effectiveSize = self.defaultPartSize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,8 +212,29 @@ private final class MultipartUploadManager {
|
||||
}
|
||||
|
||||
if let resourceData = self.resourceData, resourceData.complete, self.committedOffset >= resourceData.size {
|
||||
let (md5Digest, effectiveSize) = self.state.finalize()
|
||||
self.completed(MultipartIntermediateResult(id: self.fileId, partCount: Int32(effectiveSize / self.defaultPartSize + (effectiveSize % self.defaultPartSize == 0 ? 0 : 1)), md5Digest: md5Digest, size: Int32(resourceData.size), bigTotalParts: self.bigTotalParts))
|
||||
if self.headerPartReady {
|
||||
let effectiveSize = self.state.finalize()
|
||||
self.completed(MultipartIntermediateResult(id: self.fileId, partCount: Int32(effectiveSize / self.defaultPartSize + (effectiveSize % self.defaultPartSize == 0 ? 0 : 1)), md5Digest: "", size: Int32(resourceData.size), bigTotalParts: self.bigTotalParts))
|
||||
} else {
|
||||
let partOffset = 0
|
||||
let partSize = min(resourceData.size - partOffset, self.defaultPartSize)
|
||||
let partIndex = partOffset / self.defaultPartSize
|
||||
let fileData = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.alwaysMapped])
|
||||
let partData = fileData!.subdata(in: partOffset ..< (partOffset + partSize))
|
||||
var currentBigTotalParts = self.bigTotalParts
|
||||
if let _ = self.bigTotalParts {
|
||||
currentBigTotalParts = (resourceData.size / self.defaultPartSize) + (resourceData.size % self.defaultPartSize == 0 ? 0 : 1)
|
||||
}
|
||||
let part = self.uploadPart(UploadPart(fileId: self.fileId, index: partIndex, data: partData, bigTotalParts: currentBigTotalParts))
|
||||
|> deliverOn(self.queue)
|
||||
self.uploadingParts[0] = (partSize, part.start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = strongSelf.uploadingParts.removeValue(forKey: 0)
|
||||
strongSelf.headerPartReady = true
|
||||
strongSelf.checkState()
|
||||
}
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
while uploadingParts.count < self.parallelParts {
|
||||
var nextOffset = self.committedOffset
|
||||
@@ -225,13 +245,19 @@ private final class MultipartUploadManager {
|
||||
nextOffset = max(nextOffset, offset + partSize)
|
||||
}
|
||||
|
||||
if let resourceData = self.resourceData, nextOffset < resourceData.size, (resourceData.complete || nextOffset % 1024 == 0) {
|
||||
if let resourceData = self.resourceData {
|
||||
let partOffset = nextOffset
|
||||
let partSize = min(resourceData.size - partOffset, self.defaultPartSize)
|
||||
|
||||
if nextOffset < resourceData.size && partSize > 0 && (resourceData.complete || partSize == self.defaultPartSize) {
|
||||
let partIndex = partOffset / self.defaultPartSize
|
||||
let fileData = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.alwaysMapped])
|
||||
let partData = self.state.transform(data: fileData!.subdata(in: partOffset ..< (partOffset + partSize)))
|
||||
let part = self.uploadPart(UploadPart(fileId: self.fileId, index: partIndex, data: partData, bigTotalParts: self.bigTotalParts))
|
||||
var currentBigTotalParts = self.bigTotalParts
|
||||
if let _ = self.bigTotalParts, resourceData.complete && partOffset + partSize == resourceData.size {
|
||||
currentBigTotalParts = (resourceData.size / self.defaultPartSize) + (resourceData.size % self.defaultPartSize == 0 ? 0 : 1)
|
||||
}
|
||||
let part = self.uploadPart(UploadPart(fileId: self.fileId, index: partIndex, data: partData, bigTotalParts: currentBigTotalParts))
|
||||
|> deliverOn(self.queue)
|
||||
self.uploadingParts[nextOffset] = (partSize, part.start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
@@ -243,6 +269,9 @@ private final class MultipartUploadManager {
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,7 +304,7 @@ func multipartUpload(network: Network, postbox: Postbox, resource: MediaResource
|
||||
|
||||
let resourceData = postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: true))
|
||||
|
||||
let manager = MultipartUploadManager(data: resourceData, encryptionKey: encryptionKey, hintFileSize: hintFileSize, uploadPart: { part in
|
||||
let manager = MultipartUploadManager(resource: resource, data: resourceData, encryptionKey: encryptionKey, hintFileSize: hintFileSize, uploadPart: { part in
|
||||
return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data, bigTotalParts: part.bigTotalParts)
|
||||
}, progress: { progress in
|
||||
subscriber.putNext(.progress(progress))
|
||||
|
||||
@@ -78,6 +78,7 @@ struct OperationLogTags {
|
||||
static let SynchronizeInstalledStickerPacks = PeerOperationLogTag(value: 8)
|
||||
static let SynchronizeInstalledMasks = PeerOperationLogTag(value: 9)
|
||||
static let SynchronizeMarkFeaturedStickerPacksAsSeen = PeerOperationLogTag(value: 10)
|
||||
static let SynchronizeChatInputStates = PeerOperationLogTag(value: 11)
|
||||
}
|
||||
|
||||
private enum PreferencesKeyValues: Int32 {
|
||||
|
||||
@@ -474,6 +474,8 @@ public final class PendingMessageManager {
|
||||
|
||||
var flags: Int32 = 0
|
||||
|
||||
flags |= (1 << 7)
|
||||
|
||||
for attribute in message.attributes {
|
||||
if let replyAttribute = attribute as? ReplyMessageAttribute {
|
||||
replyMessageId = replyAttribute.messageId.id
|
||||
|
||||
51
TelegramCore/SynchronizeChatInputStateOperation.swift
Normal file
51
TelegramCore/SynchronizeChatInputStateOperation.swift
Normal file
@@ -0,0 +1,51 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
#else
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
final class SynchronizeChatInputStateOperation: Coding {
|
||||
let previousState: SynchronizeableChatInputState?
|
||||
|
||||
init(previousState: SynchronizeableChatInputState?) {
|
||||
self.previousState = previousState
|
||||
}
|
||||
|
||||
init(decoder: Decoder) {
|
||||
self.previousState = decoder.decodeObjectForKey("p", decoder: { SynchronizeableChatInputState(decoder: $0) }) as? SynchronizeableChatInputState
|
||||
}
|
||||
|
||||
func encode(_ encoder: Encoder) {
|
||||
if let previousState = self.previousState {
|
||||
encoder.encodeObject(previousState, forKey: "p")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "p")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addSynchronizeChatInputStateOperation(modifier: Modifier, peerId: PeerId) {
|
||||
var updateLocalIndex: Int32?
|
||||
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeChatInputStates
|
||||
|
||||
var previousOperation: SynchronizeChatInputStateOperation?
|
||||
modifier.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in
|
||||
updateLocalIndex = entry.tagLocalIndex
|
||||
if let operation = entry.contents as? SynchronizeChatInputStateOperation {
|
||||
previousOperation = operation
|
||||
}
|
||||
return false
|
||||
})
|
||||
var previousState: SynchronizeableChatInputState?
|
||||
if let previousOperation = previousOperation {
|
||||
previousState = previousOperation.previousState
|
||||
} else if let peerChatInterfaceState = modifier.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState {
|
||||
previousState = peerChatInterfaceState.synchronizeableInputState
|
||||
}
|
||||
let operationContents = SynchronizeChatInputStateOperation(previousState: previousState)
|
||||
if let updateLocalIndex = updateLocalIndex {
|
||||
let _ = modifier.operationLogRemoveEntry(peerId: peerId, tag: tag, tagLocalIndex: updateLocalIndex)
|
||||
}
|
||||
modifier.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: operationContents)
|
||||
}
|
||||
60
TelegramCore/SynchronizeableChatInputState.swift
Normal file
60
TelegramCore/SynchronizeableChatInputState.swift
Normal file
@@ -0,0 +1,60 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
#else
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public struct SynchronizeableChatInputState: Coding, Equatable {
|
||||
public let replyToMessageId: MessageId?
|
||||
public let text: String
|
||||
public let timestamp: Int32
|
||||
|
||||
public init(replyToMessageId: MessageId?, text: String, timestamp: Int32) {
|
||||
self.replyToMessageId = replyToMessageId
|
||||
self.text = text
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
public init(decoder: Decoder) {
|
||||
self.text = decoder.decodeStringForKey("t")
|
||||
self.timestamp = decoder.decodeInt32ForKey("s")
|
||||
if let messageIdPeerId = (decoder.decodeInt64ForKey("m.p") as Int64?), let messageIdNamespace = (decoder.decodeInt32ForKey("m.n") as Int32?), let messageIdId = (decoder.decodeInt32ForKey("m.i") as Int32?) {
|
||||
self.replyToMessageId = MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId)
|
||||
} else {
|
||||
self.replyToMessageId = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: Encoder) {
|
||||
encoder.encodeString(self.text, forKey: "t")
|
||||
encoder.encodeInt32(self.timestamp, forKey: "s")
|
||||
if let replyToMessageId = self.replyToMessageId {
|
||||
encoder.encodeInt64(replyToMessageId.peerId.toInt64(), forKey: "m.p")
|
||||
encoder.encodeInt32(replyToMessageId.namespace, forKey: "m.n")
|
||||
encoder.encodeInt32(replyToMessageId.id, forKey: "m.i")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "m.p")
|
||||
encoder.encodeNil(forKey: "m.n")
|
||||
encoder.encodeNil(forKey: "m.i")
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: SynchronizeableChatInputState, rhs: SynchronizeableChatInputState) -> Bool {
|
||||
if lhs.replyToMessageId != rhs.replyToMessageId {
|
||||
return false
|
||||
}
|
||||
if lhs.text != rhs.text {
|
||||
return true
|
||||
}
|
||||
if lhs.timestamp != rhs.timestamp {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public protocol SynchronizeableChatInterfaceState: PeerChatInterfaceState {
|
||||
var synchronizeableInputState: SynchronizeableChatInputState? { get }
|
||||
func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> SynchronizeableChatInterfaceState
|
||||
}
|
||||
24
TelegramCore/UpdatePeerChatInterfaceState.swift
Normal file
24
TelegramCore/UpdatePeerChatInterfaceState.swift
Normal file
@@ -0,0 +1,24 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
#endif
|
||||
|
||||
public func updatePeerChatInterfaceState(account: Account, peerId: PeerId, state: SynchronizeableChatInterfaceState) -> Signal<Void, NoError> {
|
||||
return account.postbox.modify { modifier -> Void in
|
||||
let currentInputState = (modifier.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState
|
||||
let updatedInputState = state.synchronizeableInputState
|
||||
|
||||
if currentInputState != updatedInputState {
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
addSynchronizeChatInputStateOperation(modifier: modifier, peerId: peerId)
|
||||
}
|
||||
}
|
||||
modifier.updatePeerChatInterfaceState(peerId, update: { _ in
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user