no message

This commit is contained in:
Peter
2017-03-23 21:26:36 +03:00
parent f755f19769
commit c55a138e0b
18 changed files with 487 additions and 77 deletions

View File

@@ -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 */,

View File

@@ -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()
}

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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)
})
}
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
View 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
}
}

View 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()
}
}

View File

@@ -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()
})

View File

@@ -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))

View File

@@ -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 {

View File

@@ -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

View 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)
}

View 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
}

View 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
})
}
}