mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-05 04:10:16 +00:00
Merge branch 'devel' of https://github.com/peter-iakovlev/TelegramCore into devel
Conflicts: TelegramCore/ChannelAdmins.swift TelegramCore/ChannelBlacklist.swift TelegramCore/ChannelMembers.swift TelegramCore/ChannelParticipants.swift
This commit is contained in:
commit
16c84c3d2a
@ -296,6 +296,8 @@
|
||||
D073CEA11DCBF3D3007511FD /* StickerPack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021E0DE1DB539FC00C6B04F /* StickerPack.swift */; };
|
||||
D073CEA41DCBF3EA007511FD /* MultipartUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03C53761DAFF20F004C17B3 /* MultipartUpload.swift */; };
|
||||
D073CEA51DCBF3F5007511FD /* StickerManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D021E0E11DB5401A00C6B04F /* StickerManagement.swift */; };
|
||||
D0754D2A1EEE10FC00884F6E /* BotPaymentForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0754D291EEE10FC00884F6E /* BotPaymentForm.swift */; };
|
||||
D0754D2B1EEE10FC00884F6E /* BotPaymentForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0754D291EEE10FC00884F6E /* BotPaymentForm.swift */; };
|
||||
D07827BB1E00451F00071108 /* SearchPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07827BA1E00451F00071108 /* SearchPeers.swift */; };
|
||||
D07827C91E02F59C00071108 /* InstantPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07827C81E02F59C00071108 /* InstantPage.swift */; };
|
||||
D07827CB1E02F5B200071108 /* RichText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07827CA1E02F5B200071108 /* RichText.swift */; };
|
||||
@ -315,6 +317,10 @@
|
||||
D08F4A671E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */; };
|
||||
D08F4A691E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */; };
|
||||
D08F4A6A1E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */; };
|
||||
D099D7461EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */; };
|
||||
D099D7471EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */; };
|
||||
D099D7491EEF418D00A3128C /* HistoryViewChannelStateValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */; };
|
||||
D099D74A1EEF418D00A3128C /* HistoryViewChannelStateValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */; };
|
||||
D099EA1C1DE72867001AF5A8 /* PeerCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = D099EA1B1DE72867001AF5A8 /* PeerCommands.swift */; };
|
||||
D09A2FE61D7CD4940018FB72 /* TelegramChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09A2FE51D7CD4940018FB72 /* TelegramChannel.swift */; };
|
||||
D09A2FEB1D7CDC320018FB72 /* PeerAccessRestrictionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09A2FEA1D7CDC320018FB72 /* PeerAccessRestrictionInfo.swift */; };
|
||||
@ -703,6 +709,7 @@
|
||||
D067066E1D512AEB00DED3E3 /* MtProtoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MtProtoKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/MtProtoKit.framework"; sourceTree = "<group>"; };
|
||||
D073CE5C1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForwardSourceInfoAttribute.swift; sourceTree = "<group>"; };
|
||||
D073CE5F1DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageInfoAttribute.swift; sourceTree = "<group>"; };
|
||||
D0754D291EEE10FC00884F6E /* BotPaymentForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BotPaymentForm.swift; sourceTree = "<group>"; };
|
||||
D07827BA1E00451F00071108 /* SearchPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchPeers.swift; sourceTree = "<group>"; };
|
||||
D07827C81E02F59C00071108 /* InstantPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantPage.swift; sourceTree = "<group>"; };
|
||||
D07827CA1E02F5B200071108 /* RichText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RichText.swift; sourceTree = "<group>"; };
|
||||
@ -715,6 +722,8 @@
|
||||
D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localizations.swift; sourceTree = "<group>"; };
|
||||
D08F4A651E79CC4A00A2AA15 /* SynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeInstalledStickerPacksOperations.swift; sourceTree = "<group>"; };
|
||||
D08F4A681E79CECB00A2AA15 /* ManagedSynchronizeInstalledStickerPacksOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeInstalledStickerPacksOperations.swift; sourceTree = "<group>"; };
|
||||
D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelMessageStateVersionAttribute.swift; sourceTree = "<group>"; };
|
||||
D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryViewChannelStateValidation.swift; sourceTree = "<group>"; };
|
||||
D099EA1B1DE72867001AF5A8 /* PeerCommands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerCommands.swift; sourceTree = "<group>"; };
|
||||
D09A2FE51D7CD4940018FB72 /* TelegramChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramChannel.swift; sourceTree = "<group>"; };
|
||||
D09A2FEA1D7CDC320018FB72 /* PeerAccessRestrictionInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerAccessRestrictionInfo.swift; sourceTree = "<group>"; };
|
||||
@ -1044,6 +1053,7 @@
|
||||
D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */,
|
||||
D0458C871E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift */,
|
||||
D00D343E1E6ED6E50057B307 /* ConsumableContentMessageAttribute.swift */,
|
||||
D099D7451EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift */,
|
||||
);
|
||||
name = Attributes;
|
||||
sourceTree = "<group>";
|
||||
@ -1166,6 +1176,7 @@
|
||||
D0BEAF5C1E54941B00BD963D /* Authorization.swift */,
|
||||
D0BEAF5F1E54ACF900BD963D /* AccountManager.swift */,
|
||||
D0528E591E658B3600E2FEF5 /* ManagedLocalInputActivities.swift */,
|
||||
D099D7481EEF418D00A3128C /* HistoryViewChannelStateValidation.swift */,
|
||||
);
|
||||
name = Account;
|
||||
sourceTree = "<group>";
|
||||
@ -1255,6 +1266,14 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D0754D281EEE10D800884F6E /* Bot Payments */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0754D291EEE10FC00884F6E /* BotPaymentForm.swift */,
|
||||
);
|
||||
name = "Bot Payments";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D08CAA821ED816290000FDA8 /* Localization */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1309,6 +1328,7 @@
|
||||
D03B0D691D631A9200955575 /* Contacts */,
|
||||
D03B0D6E1D631AA900955575 /* Messages */,
|
||||
D0DF0C881D819C5F008AEB01 /* Peers */,
|
||||
D0754D281EEE10D800884F6E /* Bot Payments */,
|
||||
D0C50E2F1E93A83B00F62E39 /* Calls */,
|
||||
D021E0E01DB5400200C6B04F /* Sticker Management */,
|
||||
D05A32DF1E6F096B002760B4 /* Settings */,
|
||||
@ -1661,6 +1681,7 @@
|
||||
D00DBBDA1E64E67E00DB5485 /* UpdateSecretChat.swift in Sources */,
|
||||
D0E23DD51E8042F500B9B6D2 /* FeaturedStickerPack.swift in Sources */,
|
||||
D0FA8B981E1E955C001E855B /* SecretChatOutgoingOperation.swift in Sources */,
|
||||
D0754D2A1EEE10FC00884F6E /* BotPaymentForm.swift in Sources */,
|
||||
D0DF0C911D81A857008AEB01 /* ImageRepresentationsUtils.swift in Sources */,
|
||||
D08CAA8C1ED81EDF0000FDA8 /* Localizations.swift in Sources */,
|
||||
D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */,
|
||||
@ -1686,6 +1707,7 @@
|
||||
D049EAD51E43D98500A2CD3A /* RecentMediaItem.swift in Sources */,
|
||||
D0C50E341E93A86600F62E39 /* CallSessionManager.swift in Sources */,
|
||||
D00D34421E6EDD2E0057B307 /* ManagedSynchronizeConsumeMessageContentsOperations.swift in Sources */,
|
||||
D099D7491EEF418D00A3128C /* HistoryViewChannelStateValidation.swift in Sources */,
|
||||
C23BC3871E9BE3CA00D79F92 /* ImportContact.swift in Sources */,
|
||||
D03B0D0A1D62255C00955575 /* Holes.swift in Sources */,
|
||||
D0B843CB1DA7FF30005F29E1 /* NBPhoneNumberUtil.m in Sources */,
|
||||
@ -1774,6 +1796,7 @@
|
||||
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
|
||||
D0FA8B9E1E1F973B001E855B /* SecretChatIncomingEncryptedOperation.swift in Sources */,
|
||||
D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||
D099D7461EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */,
|
||||
D0613FCA1E60440600202CDB /* InvitationLinks.swift in Sources */,
|
||||
D03B0D721D631ABA00955575 /* SearchMessages.swift in Sources */,
|
||||
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */,
|
||||
@ -1889,6 +1912,7 @@
|
||||
D0F7B1E91E045C87007EB8A5 /* PeerCommands.swift in Sources */,
|
||||
D0B477741EBF54A20033A0AB /* RecentCalls.swift in Sources */,
|
||||
D00D97C81E32901700E5C2B6 /* PeerInputActivity.swift in Sources */,
|
||||
D0754D2B1EEE10FC00884F6E /* BotPaymentForm.swift in Sources */,
|
||||
D0B844311DAB91E0005F29E1 /* NBPhoneMetaData.m in Sources */,
|
||||
C22EE61C1E67418000334C38 /* ToggleChannelSignatures.swift in Sources */,
|
||||
D0B418AC1D7E0597004562A4 /* Network.swift in Sources */,
|
||||
@ -1956,6 +1980,7 @@
|
||||
D0E35A131DE4C69100BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
||||
D0B418961D7E0580004562A4 /* TelegramMediaFile.swift in Sources */,
|
||||
D08CAA881ED81DD40000FDA8 /* LocalizationInfo.swift in Sources */,
|
||||
D099D74A1EEF418D00A3128C /* HistoryViewChannelStateValidation.swift in Sources */,
|
||||
D001F3EC1E128A1C007A8C60 /* Holes.swift in Sources */,
|
||||
D0B4189B1D7E0580004562A4 /* TelegramMediaWebpage.swift in Sources */,
|
||||
D00C7CE11E3785710080C3D5 /* MarkMessageContentAsConsumedInteractively.swift in Sources */,
|
||||
@ -2056,6 +2081,7 @@
|
||||
D0F7B1E41E045C7B007EB8A5 /* InstantPage.swift in Sources */,
|
||||
D03E5E0D1E55E02D0029569A /* LoggedOutAccountAttribute.swift in Sources */,
|
||||
D0B418AD1D7E0597004562A4 /* Serialization.swift in Sources */,
|
||||
D099D7471EEF0C2700A3128C /* ChannelMessageStateVersionAttribute.swift in Sources */,
|
||||
D058E0D21E8AD65C00A442DE /* StandaloneSendMessage.swift in Sources */,
|
||||
D03C536F1DAD5CA9004C17B3 /* BotInfo.swift in Sources */,
|
||||
D0FA8BBA1E2240B4001E855B /* SecretChatIncomingDecryptedOperation.swift in Sources */,
|
||||
|
@ -230,6 +230,7 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(LocalizationSettings.self, f: { LocalizationSettings(decoder: $0) })
|
||||
declareEncodable(SuggestedLocalizationEntry.self, f: { SuggestedLocalizationEntry(decoder: $0) })
|
||||
declareEncodable(SynchronizeLocalizationUpdatesOperation.self, f: { SynchronizeLocalizationUpdatesOperation(decoder: $0) })
|
||||
declareEncodable(ChannelMessageStateVersionAttribute.self, f: { ChannelMessageStateVersionAttribute(decoder: $0) })
|
||||
|
||||
return
|
||||
}()
|
||||
|
@ -17,8 +17,9 @@ final class AccountInitialState {
|
||||
let peerNotificationSettings: [PeerId: PeerNotificationSettings]
|
||||
let peerIdsWithNewMessages: Set<PeerId>
|
||||
let locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]]
|
||||
let cloudReadStates: [PeerId: PeerReadState]
|
||||
|
||||
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, messageIds: Set<MessageId>, peerIdsWithNewMessages: Set<PeerId>, channelStates: [PeerId: ChannelState], peerNotificationSettings: [PeerId: PeerNotificationSettings], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]]) {
|
||||
init(state: AuthorizedAccountState.State, peerIds: Set<PeerId>, messageIds: Set<MessageId>, peerIdsWithNewMessages: Set<PeerId>, channelStates: [PeerId: ChannelState], peerNotificationSettings: [PeerId: PeerNotificationSettings], locallyGeneratedMessageTimestamps: [PeerId: [(MessageId.Namespace, Int32)]], cloudReadStates: [PeerId: PeerReadState]) {
|
||||
self.state = state
|
||||
self.peerIds = peerIds
|
||||
self.messageIds = messageIds
|
||||
@ -26,6 +27,7 @@ final class AccountInitialState {
|
||||
self.peerIdsWithNewMessages = peerIdsWithNewMessages
|
||||
self.peerNotificationSettings = peerNotificationSettings
|
||||
self.locallyGeneratedMessageTimestamps = locallyGeneratedMessageTimestamps
|
||||
self.cloudReadStates = cloudReadStates
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +284,7 @@ private func initialStateWithPeerIds(_ modifier: Modifier, peerIds: Set<PeerId>,
|
||||
|
||||
var peerNotificationSettings: [PeerId: PeerNotificationSettings] = [:]
|
||||
var readInboxMaxIds: [PeerId: MessageId] = [:]
|
||||
var cloudReadStates: [PeerId: PeerReadState] = [:]
|
||||
|
||||
for peerId in peerIdsWithNewMessages {
|
||||
if let notificationSettings = modifier.getPeerNotificationSettings(peerId) {
|
||||
@ -292,6 +293,7 @@ private func initialStateWithPeerIds(_ modifier: Modifier, peerIds: Set<PeerId>,
|
||||
if let readStates = modifier.getPeerReadStates(peerId) {
|
||||
for (namespace, state) in readStates {
|
||||
if namespace == Namespaces.Message.Cloud {
|
||||
cloudReadStates[peerId] = state
|
||||
switch state {
|
||||
case let .idBased(maxIncomingReadId, _, _, _):
|
||||
readInboxMaxIds[peerId] = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: maxIncomingReadId)
|
||||
@ -304,7 +306,7 @@ private func initialStateWithPeerIds(_ modifier: Modifier, peerIds: Set<PeerId>,
|
||||
}
|
||||
}
|
||||
|
||||
return AccountMutableState(initialState: AccountInitialState(state: (modifier.getState() as? AuthorizedAccountState)!.state!, peerIds: peerIds, messageIds: associatedMessageIds, peerIdsWithNewMessages: peerIdsWithNewMessages, channelStates: channelStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: locallyGeneratedMessageTimestamps), initialPeers: peers, initialStoredMessages: storedMessages, initialReadInboxMaxIds: readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: storedMessagesByPeerIdAndTimestamp)
|
||||
return AccountMutableState(initialState: AccountInitialState(state: (modifier.getState() as? AuthorizedAccountState)!.state!, peerIds: peerIds, messageIds: associatedMessageIds, peerIdsWithNewMessages: peerIdsWithNewMessages, channelStates: channelStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: locallyGeneratedMessageTimestamps, cloudReadStates: cloudReadStates), initialPeers: peers, initialStoredMessages: storedMessages, initialReadInboxMaxIds: readInboxMaxIds, storedMessagesByPeerIdAndTimestamp: storedMessagesByPeerIdAndTimestamp)
|
||||
}
|
||||
|
||||
func initialStateWithUpdateGroups(_ account: Account, groups: [UpdateGroup]) -> Signal<AccountMutableState, NoError> {
|
||||
@ -620,7 +622,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
//Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old delete update")
|
||||
} else if previousState.pts + ptsCount == pts {
|
||||
updatedState.deleteMessages(messages.map({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }))
|
||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||
updatedState.updateChannelState(peerId, state: previousState.withUpdatedPts(pts))
|
||||
} else {
|
||||
if !channelsToPoll.contains(peerId) {
|
||||
Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) delete pts hole")
|
||||
@ -646,8 +648,10 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
updatedState.addPreCachedResource(resource, data: data)
|
||||
}
|
||||
}
|
||||
updatedState.editMessage(messageId, message: message)
|
||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||
var attributes = message.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: pts))
|
||||
updatedState.editMessage(messageId, message: message.withUpdatedAttributes(attributes))
|
||||
updatedState.updateChannelState(peerId, state: previousState.withUpdatedPts(pts))
|
||||
} else {
|
||||
if !channelsToPoll.contains(peerId) {
|
||||
Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) edit message pts hole")
|
||||
@ -678,7 +682,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
}
|
||||
}
|
||||
|
||||
updatedState.updateChannelState(peerId, state: previousState.setPts(pts))
|
||||
updatedState.updateChannelState(peerId, state: previousState.withUpdatedPts(pts))
|
||||
} else {
|
||||
if !channelsToPoll.contains(peerId) {
|
||||
Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) updateWebPage pts hole")
|
||||
@ -712,8 +716,10 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
updatedState.addPreCachedResource(resource, data: data)
|
||||
}
|
||||
}
|
||||
updatedState.addMessages([message], location: .UpperHistoryBlock)
|
||||
updatedState.updateChannelState(message.id.peerId, state: previousState.setPts(pts))
|
||||
var attributes = message.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: pts))
|
||||
updatedState.addMessages([message.withUpdatedAttributes(attributes)], location: .UpperHistoryBlock)
|
||||
updatedState.updateChannelState(message.id.peerId, state: previousState.withUpdatedPts(pts))
|
||||
} else {
|
||||
if !channelsToPoll.contains(message.id.peerId) {
|
||||
Logger.shared.log("State", "channel \(message.id.peerId) (\((updatedState.peers[message.id.peerId] as? TelegramChannel)?.title ?? "nil")) message pts hole")
|
||||
@ -919,7 +925,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
case let .updateUserStatus(userId, status):
|
||||
updatedState.mergePeerPresences([PeerId(namespace: Namespaces.Peer.CloudUser, id: userId): TelegramUserPresence(apiStatus: status)])
|
||||
case let .updateUserName(userId, firstName, lastName, username):
|
||||
//TODO add contact checing for apply first and last name
|
||||
//TODO add contact checking for apply first and last name
|
||||
updatedState.updatePeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), { peer in
|
||||
if let user = peer as? TelegramUser {
|
||||
return user.withUpdatedUsername(username)
|
||||
@ -1021,8 +1027,11 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
|
||||
return resolveAssociatedMessages(account: account, state: finalState)
|
||||
|> mapToSignal { resultingState -> Signal<AccountFinalState, NoError> in
|
||||
return resolveMissingPeerNotificationSettings(account: account, state: resultingState)
|
||||
|> map { resultingState -> AccountFinalState in
|
||||
return AccountFinalState(state: resultingState, shouldPoll: shouldPoll || hadError, incomplete: missingUpdates)
|
||||
|> mapToSignal { resultingState -> Signal<AccountFinalState, NoError> in
|
||||
return resolveMissingPeerCloudReadStates(account: account, state: resultingState)
|
||||
|> map { resultingState -> AccountFinalState in
|
||||
return AccountFinalState(state: resultingState, shouldPoll: shouldPoll || hadError, incomplete: missingUpdates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1136,6 +1145,47 @@ private func resolveMissingPeerNotificationSettings(account: Account, state: Acc
|
||||
}
|
||||
}
|
||||
|
||||
private func resolveMissingPeerCloudReadStates(account: Account, state: AccountMutableState) -> Signal<AccountMutableState, NoError> {
|
||||
var missingPeers: [PeerId: Api.InputPeer] = [:]
|
||||
|
||||
for peerId in state.initialState.peerIdsWithNewMessages {
|
||||
if state.initialState.cloudReadStates[peerId] == nil && (peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup) {
|
||||
if let peer = state.peers[peerId], let inputPeer = apiInputPeer(peer) {
|
||||
missingPeers[peerId] = inputPeer
|
||||
} else {
|
||||
Logger.shared.log("State", "can't fetch notification settings for peer \(peerId): can't create inputPeer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if missingPeers.isEmpty {
|
||||
return .single(state)
|
||||
} else {
|
||||
Logger.shared.log("State", "will fetch cloud read states for \(missingPeers.count) peers")
|
||||
|
||||
var signals: [Signal<(PeerId, PeerReadState)?, NoError>] = []
|
||||
for (peerId, inputPeer) in missingPeers {
|
||||
let fetchSettings = fetchPeerCloudReadState(network: account.network, postbox: account.postbox, peerId: peerId, inputPeer: inputPeer)
|
||||
|> map { state -> (PeerId, PeerReadState)? in
|
||||
return state.flatMap { (peerId, $0) }
|
||||
}
|
||||
signals.append(fetchSettings)
|
||||
}
|
||||
return combineLatest(signals)
|
||||
|> map { peersAndSettings -> AccountMutableState in
|
||||
var updatedState = state
|
||||
for pair in peersAndSettings {
|
||||
if let (peerId, state) = pair {
|
||||
if case let .idBased(maxIncomingReadId, maxOutgoingReadId, maxKnownId, count) = state {
|
||||
updatedState.resetReadState(peerId, namespace: Namespaces.Message.Cloud, maxIncomingReadId: maxIncomingReadId, maxOutgoingReadId: maxOutgoingReadId, maxKnownId: maxKnownId, count: count)
|
||||
}
|
||||
}
|
||||
}
|
||||
return updatedState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func keepPollingChannel(account: Account, peerId: PeerId, stateManager: AccountStateManager) -> Signal<Void, NoError> {
|
||||
return account.postbox.modify { modifier -> Signal<Void, NoError> in
|
||||
if let accountState = (modifier.getState() as? AuthorizedAccountState)?.state, let peer = modifier.getPeer(peerId) {
|
||||
@ -1148,7 +1198,7 @@ func keepPollingChannel(account: Account, peerId: PeerId, stateManager: AccountS
|
||||
if let notificationSettings = modifier.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
|
||||
peerNotificationSettings[peerId] = notificationSettings
|
||||
}
|
||||
let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), messageIds: Set(), peerIdsWithNewMessages: Set(), channelStates: channelStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: [:]), initialPeers: initialPeers, initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:])
|
||||
let initialState = AccountMutableState(initialState: AccountInitialState(state: accountState, peerIds: Set(), messageIds: Set(), peerIdsWithNewMessages: Set(), channelStates: channelStates, peerNotificationSettings: peerNotificationSettings, locallyGeneratedMessageTimestamps: [:], cloudReadStates: [:]), initialPeers: initialPeers, initialStoredMessages: Set(), initialReadInboxMaxIds: [:], storedMessagesByPeerIdAndTimestamp: [:])
|
||||
return pollChannel(account, peer: peer, state: initialState)
|
||||
|> mapToSignal { (finalState, _, timeout) -> Signal<Void, NoError> in
|
||||
return resolveAssociatedMessages(account: account, state: finalState)
|
||||
@ -1173,7 +1223,11 @@ func keepPollingChannel(account: Account, peerId: PeerId, stateManager: AccountS
|
||||
|
||||
private func pollChannel(_ account: Account, peer: Peer, state: AccountMutableState) -> Signal<(AccountMutableState, Bool, Int32?), NoError> {
|
||||
if let inputChannel = apiInputChannel(peer) {
|
||||
return (account.network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: state.channelStates[peer.id]?.pts ?? 1, limit: 20))
|
||||
var limit: Int32 = 20
|
||||
#if (arch(i386) || arch(x86_64)) && os(iOS)
|
||||
limit = 3
|
||||
#endif
|
||||
return (account.network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilterEmpty, pts: state.channelStates[peer.id]?.pts ?? 1, limit: limit))
|
||||
|> map { Optional($0) }
|
||||
|> `catch` { error -> Signal<Api.updates.ChannelDifference?, MTRpcError> in
|
||||
if error.errorDescription == "CHANNEL_PRIVATE" {
|
||||
@ -1192,9 +1246,9 @@ private func pollChannel(_ account: Account, peer: Peer, state: AccountMutableSt
|
||||
apiTimeout = timeout
|
||||
let channelState: ChannelState
|
||||
if let previousState = updatedState.channelStates[peer.id] {
|
||||
channelState = previousState.setPts(pts)
|
||||
channelState = previousState.withUpdatedPts(pts)
|
||||
} else {
|
||||
channelState = ChannelState(pts: pts)
|
||||
channelState = ChannelState(pts: pts, invalidatedPts: nil)
|
||||
}
|
||||
updatedState.updateChannelState(peer.id, state: channelState)
|
||||
|
||||
@ -1225,20 +1279,15 @@ private func pollChannel(_ account: Account, peer: Peer, state: AccountMutableSt
|
||||
|
||||
let channelState: ChannelState
|
||||
if let previousState = updatedState.channelStates[peer.id] {
|
||||
channelState = previousState.setPts(pts)
|
||||
channelState = previousState.withUpdatedPts(pts)
|
||||
} else {
|
||||
channelState = ChannelState(pts: pts)
|
||||
channelState = ChannelState(pts: pts, invalidatedPts: nil)
|
||||
}
|
||||
updatedState.updateChannelState(peer.id, state: channelState)
|
||||
case let .channelDifferenceTooLong(_, pts, timeout, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, messages, chats, users):
|
||||
apiTimeout = timeout
|
||||
|
||||
let channelState: ChannelState
|
||||
if let previousState = updatedState.channelStates[peer.id] {
|
||||
channelState = previousState.setPts(pts)
|
||||
} else {
|
||||
channelState = ChannelState(pts: pts)
|
||||
}
|
||||
let channelState = ChannelState(pts: pts, invalidatedPts: pts)
|
||||
updatedState.updateChannelState(peer.id, state: channelState)
|
||||
|
||||
updatedState.mergeChats(chats)
|
||||
|
@ -133,6 +133,16 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal<Void
|
||||
}
|
||||
}
|
||||
|
||||
private func wrappedHistoryViewAdditionalData(peerId: PeerId, additionalData: [AdditionalMessageHistoryViewData]) -> [AdditionalMessageHistoryViewData] {
|
||||
var result = additionalData
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if result.index(where: { if case .peerChatState = $0 { return true } else { return false } }) == nil {
|
||||
result.append(.peerChatState(peerId))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private final class PeerCachedDataContext {
|
||||
var viewIds = Set<Int32>()
|
||||
var timestamp: Double?
|
||||
@ -196,8 +206,12 @@ public final class AccountViewTracker {
|
||||
private var channelPollingContexts: [PeerId: ChannelPollingContext] = [:]
|
||||
private var featuredStickerPacksContext: FeaturedStickerPacksContext?
|
||||
|
||||
private let historyViewChannelStateValidationContexts: HistoryViewChannelStateValidationContexts
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
|
||||
self.historyViewChannelStateValidationContexts = HistoryViewChannelStateValidationContexts(queue: self.queue, postbox: account.postbox, network: account.network)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -482,10 +496,16 @@ public final class AccountViewTracker {
|
||||
if let strongSelf = self {
|
||||
let messageIds = pendingWebpages(entries: next.0.entries)
|
||||
strongSelf.updatePendingWebpages(viewId: viewId, messageIds: messageIds)
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
strongSelf.historyViewChannelStateValidationContexts.updateView(id: viewId, view: next.0)
|
||||
}
|
||||
}
|
||||
}, disposed: { [weak self] viewId in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updatePendingWebpages(viewId: viewId, messageIds: [])
|
||||
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
strongSelf.historyViewChannelStateValidationContexts.updateView(id: viewId, view: nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -511,7 +531,7 @@ public final class AccountViewTracker {
|
||||
|
||||
public func aroundUnreadMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
|
||||
if let account = self.account {
|
||||
let signal = account.postbox.aroundUnreadMessageHistoryViewForPeerId(peerId, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData)
|
||||
let signal = account.postbox.aroundUnreadMessageHistoryViewForPeerId(peerId, count: count, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(peerId: peerId, additionalData: additionalData))
|
||||
return wrappedMessageHistorySignal(peerId: peerId, signal: signal)
|
||||
} else {
|
||||
return .never()
|
||||
@ -520,7 +540,7 @@ public final class AccountViewTracker {
|
||||
|
||||
public func aroundIdMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, messageId: MessageId, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
|
||||
if let account = self.account {
|
||||
let signal = account.postbox.aroundIdMessageHistoryViewForPeerId(peerId, count: count, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData)
|
||||
let signal = account.postbox.aroundIdMessageHistoryViewForPeerId(peerId, count: count, messageId: messageId, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(peerId: peerId, additionalData: additionalData))
|
||||
return wrappedMessageHistorySignal(peerId: peerId, signal: signal)
|
||||
} else {
|
||||
return .never()
|
||||
@ -529,7 +549,7 @@ public final class AccountViewTracker {
|
||||
|
||||
public func aroundMessageHistoryViewForPeerId(_ peerId: PeerId, index: MessageIndex, count: Int, anchorIndex: MessageIndex, fixedCombinedReadState: CombinedPeerReadState?, tagMask: MessageTags? = nil, orderStatistics: MessageHistoryViewOrderStatistics = [], additionalData: [AdditionalMessageHistoryViewData] = []) -> Signal<(MessageHistoryView, ViewUpdateType, InitialMessageHistoryData?), NoError> {
|
||||
if let account = self.account {
|
||||
let signal = account.postbox.aroundMessageHistoryViewForPeerId(peerId, index: index, count: count, anchorIndex: anchorIndex, fixedCombinedReadState: fixedCombinedReadState, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: additionalData)
|
||||
let signal = account.postbox.aroundMessageHistoryViewForPeerId(peerId, index: index, count: count, anchorIndex: anchorIndex, fixedCombinedReadState: fixedCombinedReadState, topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tagMask: tagMask, orderStatistics: orderStatistics, additionalData: wrappedHistoryViewAdditionalData(peerId: peerId, additionalData: additionalData))
|
||||
return wrappedMessageHistorySignal(peerId: peerId, signal: signal)
|
||||
} else {
|
||||
return .never()
|
||||
|
@ -52,6 +52,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[145955919] = { return Api.PageBlock.parse_pageBlockCollage($0) }
|
||||
dict[319588707] = { return Api.PageBlock.parse_pageBlockSlideshow($0) }
|
||||
dict[-283684427] = { return Api.PageBlock.parse_pageBlockChannel($0) }
|
||||
dict[834148991] = { return Api.PageBlock.parse_pageBlockAudio($0) }
|
||||
dict[-614138572] = { return Api.account.TmpPassword.parse_tmpPassword($0) }
|
||||
dict[590459437] = { return Api.Photo.parse_photoEmpty($0) }
|
||||
dict[-1836524247] = { return Api.Photo.parse_photo($0) }
|
||||
@ -352,8 +353,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1182234929] = { return Api.InputUser.parse_inputUserEmpty($0) }
|
||||
dict[-138301121] = { return Api.InputUser.parse_inputUserSelf($0) }
|
||||
dict[-668391402] = { return Api.InputUser.parse_inputUser($0) }
|
||||
dict[-1913754556] = { return Api.Page.parse_pagePart($0) }
|
||||
dict[-677274263] = { return Api.Page.parse_pageFull($0) }
|
||||
dict[-1908433218] = { return Api.Page.parse_pagePart($0) }
|
||||
dict[1433323434] = { return Api.Page.parse_pageFull($0) }
|
||||
dict[157948117] = { return Api.upload.File.parse_file($0) }
|
||||
dict[352864346] = { return Api.upload.File.parse_fileCdnRedirect($0) }
|
||||
dict[182649427] = { return Api.MessageRange.parse_messageRange($0) }
|
||||
@ -2872,6 +2873,7 @@ public struct Api {
|
||||
case pageBlockCollage(items: [Api.PageBlock], caption: Api.RichText)
|
||||
case pageBlockSlideshow(items: [Api.PageBlock], caption: Api.RichText)
|
||||
case pageBlockChannel(channel: Api.Chat)
|
||||
case pageBlockAudio(audioId: Int64, caption: Api.RichText)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool {
|
||||
switch self {
|
||||
@ -3045,6 +3047,13 @@ public struct Api {
|
||||
}
|
||||
channel.serialize(buffer, true)
|
||||
break
|
||||
case .pageBlockAudio(let audioId, let caption):
|
||||
if boxed {
|
||||
buffer.appendInt32(834148991)
|
||||
}
|
||||
serializeInt64(audioId, buffer: buffer, boxed: false)
|
||||
caption.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -3391,6 +3400,22 @@ public struct Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fileprivate static func parse_pageBlockAudio(_ reader: BufferReader) -> PageBlock? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Api.RichText?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.RichText
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.PageBlock.pageBlockAudio(audioId: _1!, caption: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
get {
|
||||
@ -3439,6 +3464,8 @@ public struct Api {
|
||||
return "(pageBlockSlideshow items: \(items), caption: \(caption))"
|
||||
case .pageBlockChannel(let channel):
|
||||
return "(pageBlockChannel channel: \(channel))"
|
||||
case .pageBlockAudio(let audioId, let caption):
|
||||
return "(pageBlockAudio audioId: \(audioId), caption: \(caption))"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10795,14 +10822,14 @@ public struct Api {
|
||||
}
|
||||
|
||||
public enum Page: CustomStringConvertible {
|
||||
case pagePart(blocks: [Api.PageBlock], photos: [Api.Photo], videos: [Api.Document])
|
||||
case pageFull(blocks: [Api.PageBlock], photos: [Api.Photo], videos: [Api.Document])
|
||||
case pagePart(blocks: [Api.PageBlock], photos: [Api.Photo], documents: [Api.Document])
|
||||
case pageFull(blocks: [Api.PageBlock], photos: [Api.Photo], documents: [Api.Document])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) -> Swift.Bool {
|
||||
switch self {
|
||||
case .pagePart(let blocks, let photos, let videos):
|
||||
case .pagePart(let blocks, let photos, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1913754556)
|
||||
buffer.appendInt32(-1908433218)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(blocks.count))
|
||||
@ -10815,14 +10842,14 @@ public struct Api {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(videos.count))
|
||||
for item in videos {
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .pageFull(let blocks, let photos, let videos):
|
||||
case .pageFull(let blocks, let photos, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(-677274263)
|
||||
buffer.appendInt32(1433323434)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(blocks.count))
|
||||
@ -10835,8 +10862,8 @@ public struct Api {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(videos.count))
|
||||
for item in videos {
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
@ -10861,7 +10888,7 @@ public struct Api {
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Page.pagePart(blocks: _1!, photos: _2!, videos: _3!)
|
||||
return Api.Page.pagePart(blocks: _1!, photos: _2!, documents: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -10884,7 +10911,7 @@ public struct Api {
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Page.pageFull(blocks: _1!, photos: _2!, videos: _3!)
|
||||
return Api.Page.pageFull(blocks: _1!, photos: _2!, documents: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -10894,10 +10921,10 @@ public struct Api {
|
||||
public var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .pagePart(let blocks, let photos, let videos):
|
||||
return "(pagePart blocks: \(blocks), photos: \(photos), videos: \(videos))"
|
||||
case .pageFull(let blocks, let photos, let videos):
|
||||
return "(pageFull blocks: \(blocks), photos: \(photos), videos: \(videos))"
|
||||
case .pagePart(let blocks, let photos, let documents):
|
||||
return "(pagePart blocks: \(blocks), photos: \(photos), documents: \(documents))"
|
||||
case .pageFull(let blocks, let photos, let documents):
|
||||
return "(pageFull blocks: \(blocks), photos: \(photos), documents: \(documents))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
168
TelegramCore/BotPaymentForm.swift
Normal file
168
TelegramCore/BotPaymentForm.swift
Normal file
@ -0,0 +1,168 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import MtProtoKitMac
|
||||
import SwiftSignalKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import MtProtoKitDynamic
|
||||
import SwiftSignalKit
|
||||
#endif
|
||||
|
||||
public struct BotPaymentInvoiceFields: OptionSet {
|
||||
public var rawValue: Int32
|
||||
|
||||
public init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
public static let name = BotPaymentInvoiceFields(rawValue: 1 << 0)
|
||||
public static let phone = BotPaymentInvoiceFields(rawValue: 1 << 1)
|
||||
public static let email = BotPaymentInvoiceFields(rawValue: 1 << 2)
|
||||
public static let shippingAddress = BotPaymentInvoiceFields(rawValue: 1 << 3)
|
||||
public static let flexibleShipping = BotPaymentInvoiceFields(rawValue: 1 << 4)
|
||||
}
|
||||
|
||||
public struct BotPaymentPrice {
|
||||
public let label: String
|
||||
public let amount: Int64
|
||||
|
||||
public init(label: String, amount: Int64) {
|
||||
self.label = label
|
||||
self.amount = amount
|
||||
}
|
||||
}
|
||||
|
||||
public struct BotPaymentInvoice {
|
||||
public let isTest: Bool
|
||||
public let requestedFields: BotPaymentInvoiceFields
|
||||
public let currency: String
|
||||
public let prices: [BotPaymentPrice]
|
||||
}
|
||||
|
||||
public struct BotPaymentNativeProvider {
|
||||
public let name: String
|
||||
public let params: String
|
||||
}
|
||||
|
||||
public struct BotPaymentShippingAddress {
|
||||
public let streetLine1: String
|
||||
public let streetLine2: String
|
||||
public let city: String
|
||||
public let state: String
|
||||
public let countryIso2: String
|
||||
public let postCode: String
|
||||
|
||||
public init(streetLine1: String, streetLine2: String, city: String, state: String, countryIso2: String, postCode: String) {
|
||||
self.streetLine1 = streetLine1
|
||||
self.streetLine2 = streetLine2
|
||||
self.city = city
|
||||
self.state = state
|
||||
self.countryIso2 = countryIso2
|
||||
self.postCode = postCode
|
||||
}
|
||||
}
|
||||
|
||||
public struct BotPaymentRequestedInfo {
|
||||
public let name: String?
|
||||
public let phone: String?
|
||||
public let email: String?
|
||||
public let shippingAddress: BotPaymentShippingAddress?
|
||||
|
||||
public init(name: String?, phone: String?, email: String?, shippingAddress: BotPaymentShippingAddress?) {
|
||||
self.name = name
|
||||
self.phone = phone
|
||||
self.email = email
|
||||
self.shippingAddress = shippingAddress
|
||||
}
|
||||
}
|
||||
|
||||
public enum BotPaymentSavedCredentials {
|
||||
case card(id: String, title: String)
|
||||
}
|
||||
|
||||
public struct BotPaymentForm {
|
||||
public let canSaveCredentials: Bool
|
||||
public let passwordMissing: Bool
|
||||
public let invoice: BotPaymentInvoice
|
||||
public let providerId: Int32
|
||||
public let url: String
|
||||
public let nativeProvider: BotPaymentNativeProvider?
|
||||
public let savedInfo: BotPaymentRequestedInfo?
|
||||
public let savedCredentials: BotPaymentSavedCredentials?
|
||||
}
|
||||
|
||||
public enum BotPaymentFormReuestError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func fetchBotPaymentForm(postbox: Postbox, network: Network, messageId: MessageId) -> Signal<BotPaymentForm, BotPaymentFormReuestError> {
|
||||
return network.request(Api.functions.payments.getPaymentForm(msgId: messageId.id))
|
||||
|> `catch` { _ -> Signal<Api.payments.PaymentForm, BotPaymentFormReuestError> in
|
||||
return .fail(.generic)
|
||||
}
|
||||
|> map { result -> BotPaymentForm in
|
||||
switch result {
|
||||
case let .paymentForm(flags, _, invoice, providerId, url, nativeProvider, nativeParams, savedInfo, savedCredentials, _):
|
||||
let parsedInvoice: BotPaymentInvoice
|
||||
switch invoice {
|
||||
case let .invoice(flags, currency, prices):
|
||||
var fields = BotPaymentInvoiceFields()
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
fields.insert(.name)
|
||||
}
|
||||
if (flags & (1 << 2)) != 0 {
|
||||
fields.insert(.phone)
|
||||
}
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
fields.insert(.email)
|
||||
}
|
||||
if (flags & (1 << 4)) != 0 {
|
||||
fields.insert(.shippingAddress)
|
||||
}
|
||||
if (flags & (1 << 5)) != 0 {
|
||||
fields.insert(.flexibleShipping)
|
||||
}
|
||||
parsedInvoice = BotPaymentInvoice(isTest: (flags & (1 << 0)) != 0, requestedFields: fields, currency: currency, prices: prices.map {
|
||||
switch $0 {
|
||||
case let .labeledPrice(label, amount):
|
||||
return BotPaymentPrice(label: label, amount: amount)
|
||||
}
|
||||
})
|
||||
}
|
||||
var parsedNativeProvider: BotPaymentNativeProvider?
|
||||
if let nativeProvider = nativeProvider, let nativeParams = nativeParams {
|
||||
switch nativeParams {
|
||||
case let .dataJSON(data):
|
||||
parsedNativeProvider = BotPaymentNativeProvider(name: nativeProvider, params: data)
|
||||
}
|
||||
}
|
||||
var parsedSavedInfo: BotPaymentRequestedInfo?
|
||||
if let savedInfo = savedInfo {
|
||||
switch savedInfo {
|
||||
case let .paymentRequestedInfo(_, name, phone, email, shippingAddress):
|
||||
var parsedShippingAddress: BotPaymentShippingAddress?
|
||||
if let shippingAddress = shippingAddress {
|
||||
switch shippingAddress {
|
||||
case let .postAddress(streetLine1, streetLine2, city, state, countryIso2, postCode):
|
||||
parsedShippingAddress = BotPaymentShippingAddress(streetLine1: streetLine1, streetLine2: streetLine2, city: city, state: state, countryIso2: countryIso2, postCode: postCode)
|
||||
}
|
||||
}
|
||||
parsedSavedInfo = BotPaymentRequestedInfo(name: name, phone: phone, email: email, shippingAddress: parsedShippingAddress)
|
||||
}
|
||||
}
|
||||
var parsedSavedCredentials: BotPaymentSavedCredentials?
|
||||
if let savedCredentials = savedCredentials {
|
||||
switch savedCredentials {
|
||||
case let .paymentSavedCredentialsCard(id, title):
|
||||
parsedSavedCredentials = .card(id: id, title: title)
|
||||
}
|
||||
}
|
||||
return BotPaymentForm(canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, providerId: providerId, url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials)
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ public func channelAdmins(account: Account, peerId: PeerId) -> Signal<[RenderedC
|
||||
|
||||
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
|
||||
if let peer = peers[participant.peerId] {
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, presence: status[peer.id]))
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presence: status[peer.id]))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ private func fetchChannelBlacklist(account: Account, peerId: PeerId, filter: Cha
|
||||
|
||||
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
|
||||
if let peer = peers[participant.peerId] {
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, presence: status[peer.id]))
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presence: status[peer.id]))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public func channelMembers(account: Account, peerId: PeerId, filter: ChannelMemb
|
||||
|
||||
for participant in CachedChannelParticipants(apiParticipants: participants).participants {
|
||||
if let peer = peers[participant.peerId] {
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, presence: status[peer.id]))
|
||||
items.append(RenderedChannelParticipant(participant: participant, peer: peer, peers: peers, presence: status[peer.id]))
|
||||
}
|
||||
|
||||
}
|
||||
|
22
TelegramCore/ChannelMessageStateVersionAttribute.swift
Normal file
22
TelegramCore/ChannelMessageStateVersionAttribute.swift
Normal file
@ -0,0 +1,22 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
#else
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public class ChannelMessageStateVersionAttribute: MessageAttribute {
|
||||
public let pts: Int32
|
||||
|
||||
public init(pts: Int32) {
|
||||
self.pts = pts
|
||||
}
|
||||
|
||||
required public init(decoder: Decoder) {
|
||||
self.pts = decoder.decodeInt32ForKey("p", orElse: 0)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: Encoder) {
|
||||
encoder.encodeInt32(self.pts, forKey: "p")
|
||||
}
|
||||
}
|
@ -12,24 +12,25 @@ import Foundation
|
||||
public struct RenderedChannelParticipant: Equatable {
|
||||
public let participant: ChannelParticipant
|
||||
public let peer: Peer
|
||||
public let presence: PeerPresence?
|
||||
public init(participant: ChannelParticipant, peer: Peer, presence: PeerPresence? = nil) {
|
||||
public let peers: [PeerId: Peer]
|
||||
public let presence:PeerPresence?
|
||||
public init(participant: ChannelParticipant, peer: Peer, peers: [PeerId: Peer] = [:], presence: PeerPresence? = nil) {
|
||||
self.participant = participant
|
||||
self.peer = peer
|
||||
self.peers = peers
|
||||
self.presence = presence
|
||||
}
|
||||
|
||||
public static func ==(lhs: RenderedChannelParticipant, rhs: RenderedChannelParticipant) -> Bool {
|
||||
if let lhsStatus = lhs.presence, let rhsStatus = rhs.presence {
|
||||
if !lhsStatus.isEqual(to: rhsStatus) {
|
||||
if let lhsPresence = lhs.presence, let rhsPresence = rhs.presence {
|
||||
if !lhsPresence.isEqual(to: rhsPresence) {
|
||||
return false
|
||||
}
|
||||
} else if (lhs.presence != nil) != (rhs.presence != nil) {
|
||||
} else if(lhs.presence != nil) != (rhs.presence != nil) {
|
||||
return false
|
||||
}
|
||||
return lhs.participant == rhs.participant && lhs.peer.isEqual(rhs.peer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func updateChannelParticipantsSummary(account: Account, peerId: PeerId) -> Signal<Void, NoError> {
|
||||
|
@ -7,21 +7,33 @@ import Foundation
|
||||
|
||||
final class ChannelState: PeerChatState, Equatable, CustomStringConvertible {
|
||||
let pts: Int32
|
||||
let invalidatedPts: Int32?
|
||||
|
||||
init(pts: Int32) {
|
||||
init(pts: Int32, invalidatedPts: Int32?) {
|
||||
self.pts = pts
|
||||
self.invalidatedPts = invalidatedPts
|
||||
}
|
||||
|
||||
init(decoder: Decoder) {
|
||||
self.pts = decoder.decodeInt32ForKey("pts", orElse: 0)
|
||||
self.invalidatedPts = decoder.decodeOptionalInt32ForKey("ipts")
|
||||
}
|
||||
|
||||
func encode(_ encoder: Encoder) {
|
||||
encoder.encodeInt32(self.pts, forKey: "pts")
|
||||
if let invalidatedPts = self.invalidatedPts {
|
||||
encoder.encodeInt32(invalidatedPts, forKey: "ipts")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "ipts")
|
||||
}
|
||||
}
|
||||
|
||||
func setPts(_ pts: Int32) -> ChannelState {
|
||||
return ChannelState(pts: pts)
|
||||
func withUpdatedPts(_ pts: Int32) -> ChannelState {
|
||||
return ChannelState(pts: pts, invalidatedPts: self.invalidatedPts)
|
||||
}
|
||||
|
||||
func withUpdatedInvalidatedPts(_ invalidatedPts: Int32?) -> ChannelState {
|
||||
return ChannelState(pts: self.pts, invalidatedPts: invalidatedPts)
|
||||
}
|
||||
|
||||
func equals(_ other: PeerChatState) -> Bool {
|
||||
@ -37,7 +49,7 @@ final class ChannelState: PeerChatState, Equatable, CustomStringConvertible {
|
||||
}
|
||||
|
||||
func ==(lhs: ChannelState, rhs: ChannelState) -> Bool {
|
||||
return lhs.pts == rhs.pts
|
||||
return lhs.pts == rhs.pts && lhs.invalidatedPts == rhs.invalidatedPts
|
||||
}
|
||||
|
||||
struct ChannelUpdate {
|
||||
|
286
TelegramCore/HistoryViewChannelStateValidation.swift
Normal file
286
TelegramCore/HistoryViewChannelStateValidation.swift
Normal file
@ -0,0 +1,286 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
private final class ChannelStateValidationBatch {
|
||||
private let disposable: Disposable
|
||||
let invalidatedPts: Int32
|
||||
|
||||
var cancelledMessageIds = Set<MessageId>()
|
||||
|
||||
init(disposable: Disposable, invalidatedPts: Int32) {
|
||||
self.disposable = disposable
|
||||
self.invalidatedPts = invalidatedPts
|
||||
}
|
||||
|
||||
deinit {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChannelStateValidationContext {
|
||||
var batchReferences: [MessageId: ChannelStateValidationBatch] = [:]
|
||||
}
|
||||
|
||||
final class HistoryViewChannelStateValidationContexts {
|
||||
private let queue: Queue
|
||||
private let postbox: Postbox
|
||||
private let network: Network
|
||||
|
||||
private var contexts: [Int32: ChannelStateValidationContext] = [:]
|
||||
|
||||
init(queue: Queue, postbox: Postbox, network: Network) {
|
||||
self.queue = queue
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
}
|
||||
|
||||
func updateView(id: Int32, view: MessageHistoryView?) {
|
||||
if let view = view {
|
||||
var channelState: ChannelState?
|
||||
for entry in view.additionalData {
|
||||
if case let .peerChatState(_, chatState) = entry {
|
||||
if let chatState = chatState as? ChannelState {
|
||||
channelState = chatState
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let invalidatedPts = channelState?.invalidatedPts {
|
||||
var invalidatedMessageIds: [MessageId] = []
|
||||
var minValidatedPts: Int32?
|
||||
|
||||
for entry in view.entries {
|
||||
switch entry {
|
||||
case let .MessageEntry(message, _, _, _):
|
||||
if message.id.namespace == Namespaces.Message.Cloud {
|
||||
var messagePts: Int32?
|
||||
inner: for attribute in message.attributes {
|
||||
if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
||||
messagePts = attribute.pts
|
||||
break inner
|
||||
}
|
||||
}
|
||||
if let messagePts = messagePts {
|
||||
if messagePts < invalidatedPts {
|
||||
if minValidatedPts == nil || minValidatedPts! > messagePts {
|
||||
minValidatedPts = messagePts
|
||||
}
|
||||
invalidatedMessageIds.append(message.id)
|
||||
}
|
||||
} else {
|
||||
invalidatedMessageIds.append(message.id)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !invalidatedMessageIds.isEmpty {
|
||||
let context: ChannelStateValidationContext
|
||||
if let current = self.contexts[id] {
|
||||
context = current
|
||||
} else {
|
||||
context = ChannelStateValidationContext()
|
||||
self.contexts[id] = context
|
||||
}
|
||||
var messageIdsForBatch: [MessageId] = []
|
||||
for messageId in invalidatedMessageIds {
|
||||
if let batch = context.batchReferences[messageId] {
|
||||
if batch.invalidatedPts < invalidatedPts {
|
||||
batch.cancelledMessageIds.insert(messageId)
|
||||
messageIdsForBatch.append(messageId)
|
||||
}
|
||||
} else {
|
||||
messageIdsForBatch.append(messageId)
|
||||
}
|
||||
}
|
||||
if !messageIdsForBatch.isEmpty {
|
||||
let disposable = MetaDisposable()
|
||||
let batch = ChannelStateValidationBatch(disposable: disposable, invalidatedPts: invalidatedPts)
|
||||
for messageId in messageIdsForBatch {
|
||||
context.batchReferences[messageId] = batch
|
||||
}
|
||||
|
||||
disposable.set((validateBatch(postbox: self.postbox, network: self.network, messageIds: messageIdsForBatch, minValidatedPts: minValidatedPts)
|
||||
|> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in
|
||||
if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch {
|
||||
var completedMessageIds: [MessageId] = []
|
||||
for (messageId, messageBatch) in context.batchReferences {
|
||||
if messageBatch === batch {
|
||||
completedMessageIds.append(messageId)
|
||||
}
|
||||
}
|
||||
for messageId in completedMessageIds {
|
||||
context.batchReferences.removeValue(forKey: messageId)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if let context = self.contexts[id] {
|
||||
let messageIds = Set(invalidatedMessageIds)
|
||||
var removeIds: [MessageId] = []
|
||||
|
||||
for batchMessageId in context.batchReferences.keys {
|
||||
if !messageIds.contains(batchMessageId) {
|
||||
removeIds.append(batchMessageId)
|
||||
}
|
||||
}
|
||||
|
||||
for messageId in removeIds {
|
||||
context.batchReferences.removeValue(forKey: messageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if self.contexts[id] != nil {
|
||||
self.contexts.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func validateBatch(postbox: Postbox, network: Network, messageIds: [MessageId], minValidatedPts: Int32?) -> Signal<Void, NoError> {
|
||||
guard let peerId = messageIds.first?.peerId else {
|
||||
return .never()
|
||||
}
|
||||
return postbox.modify { modifier -> Signal<Void, NoError> in
|
||||
if let peer = modifier.getPeer(peerId), let inputChannel = apiInputChannel(peer) {
|
||||
var ranges: [Api.MessageRange] = []
|
||||
var currentRange: (Int32, Int32)?
|
||||
for id in messageIds.sorted() {
|
||||
if let (minId, maxId) = currentRange {
|
||||
if maxId == id.id - 1 {
|
||||
currentRange = (minId, id.id)
|
||||
} else {
|
||||
ranges.append(Api.MessageRange.messageRange(minId: minId - 1, maxId: maxId + 1))
|
||||
currentRange = (id.id, id.id)
|
||||
}
|
||||
} else {
|
||||
currentRange = (id.id, id.id)
|
||||
}
|
||||
}
|
||||
if let (minId, maxId) = currentRange {
|
||||
ranges.append(Api.MessageRange.messageRange(minId: minId - 1, maxId: maxId + 1))
|
||||
}
|
||||
return network.request(Api.functions.updates.getChannelDifference(flags: 0, channel: inputChannel, filter: .channelMessagesFilter(flags: 1 << 1, ranges: ranges), pts: minValidatedPts ?? 1, limit: 100))
|
||||
|> `catch` { _ -> Signal<Api.updates.ChannelDifference, NoError> in
|
||||
return .never()
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
return postbox.modify { modifier -> Void in
|
||||
let finalPts: Int32
|
||||
var deletedMessageIds: [MessageId] = []
|
||||
var updatedMessages: [MessageId: StoreMessage] = [:]
|
||||
|
||||
var apiChats: [Api.Chat] = []
|
||||
var apiUsers: [Api.User] = []
|
||||
|
||||
switch result {
|
||||
case let .channelDifference(_, pts, _, newMessages, otherUpdates, chats, users):
|
||||
finalPts = pts
|
||||
apiChats = chats
|
||||
apiUsers = users
|
||||
|
||||
for message in newMessages {
|
||||
if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id {
|
||||
updatedMessages[id] = message
|
||||
}
|
||||
}
|
||||
for update in otherUpdates {
|
||||
switch update {
|
||||
case let .updateDeleteChannelMessages(_, messages, _, _):
|
||||
for messageId in messages {
|
||||
deletedMessageIds.append(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: messageId))
|
||||
}
|
||||
case let .updateNewChannelMessage(message, _, _):
|
||||
if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id {
|
||||
updatedMessages[id] = message
|
||||
}
|
||||
case let .updateEditChannelMessage(message, _, _):
|
||||
if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id {
|
||||
updatedMessages[id] = message
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
case let .channelDifferenceEmpty(_, pts, _):
|
||||
finalPts = pts
|
||||
case let .channelDifferenceTooLong(_, pts, _, _, _, _, _, _, _, _):
|
||||
finalPts = pts
|
||||
}
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
for chat in apiChats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(groupOrChannel)
|
||||
}
|
||||
}
|
||||
for user in apiUsers {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
if let presence = TelegramUserPresence(apiUser: user) {
|
||||
peerPresences[telegramUser.id] = presence
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(modifier: modifier, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
modifier.updatePeerPresences(peerPresences)
|
||||
|
||||
if !deletedMessageIds.isEmpty {
|
||||
modifier.deleteMessages(deletedMessageIds)
|
||||
}
|
||||
|
||||
for (messageId, message) in updatedMessages {
|
||||
modifier.updateMessage(messageId, update: { _ in
|
||||
var attributes = message.attributes
|
||||
for j in 0 ..< attributes.count {
|
||||
if let _ = attributes[j] as? ChannelMessageStateVersionAttribute {
|
||||
attributes.remove(at: j)
|
||||
break
|
||||
}
|
||||
}
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: finalPts))
|
||||
return .update(StoreMessage(id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: attributes, media: message.media))
|
||||
})
|
||||
}
|
||||
|
||||
for messageId in messageIds {
|
||||
if updatedMessages[messageId] == nil {
|
||||
modifier.updateMessage(messageId, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date)
|
||||
}
|
||||
var attributes = currentMessage.attributes
|
||||
for j in 0 ..< attributes.count {
|
||||
if let _ = attributes[j] as? ChannelMessageStateVersionAttribute {
|
||||
attributes.remove(at: j)
|
||||
break
|
||||
}
|
||||
}
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: finalPts))
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
} |> switchToLatest
|
||||
}
|
@ -80,6 +80,7 @@ func fetchMessageHistoryHole(network: Network, postbox: Postbox, hole: MessageHi
|
||||
let messages: [Api.Message]
|
||||
let chats: [Api.Chat]
|
||||
let users: [Api.User]
|
||||
var channelPts: Int32?
|
||||
switch result {
|
||||
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
|
||||
messages = apiMessages
|
||||
@ -89,17 +90,24 @@ func fetchMessageHistoryHole(network: Network, postbox: Postbox, hole: MessageHi
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
|
||||
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
|
||||
messages = apiMessages
|
||||
chats = apiChats
|
||||
users = apiUsers
|
||||
channelPts = pts
|
||||
}
|
||||
return postbox.modify { modifier in
|
||||
var storeMessages: [StoreMessage] = []
|
||||
|
||||
for message in messages {
|
||||
if let storeMessage = StoreMessage(apiMessage: message) {
|
||||
storeMessages.append(storeMessage)
|
||||
if let channelPts = channelPts {
|
||||
var attributes = storeMessage.attributes
|
||||
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
|
||||
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
|
||||
} else {
|
||||
storeMessages.append(storeMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +226,7 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) -
|
||||
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount)
|
||||
|
||||
if let apiChannelPts = apiChannelPts {
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts)
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil)
|
||||
}
|
||||
|
||||
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
|
||||
@ -340,7 +348,7 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) -
|
||||
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount)
|
||||
|
||||
if let apiChannelPts = apiChannelPts {
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts)
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil)
|
||||
}
|
||||
|
||||
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
|
||||
@ -391,7 +399,15 @@ func fetchChatListHole(network: Network, postbox: Postbox, hole: ChatListHole) -
|
||||
modifier.resetIncomingReadStates(readStates)
|
||||
|
||||
for (peerId, chatState) in chatStates {
|
||||
modifier.setPeerChatState(peerId, state: chatState)
|
||||
if let chatState = chatState as? ChannelState {
|
||||
if let current = modifier.getPeerChatState(peerId) as? ChannelState {
|
||||
modifier.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts))
|
||||
} else {
|
||||
modifier.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
} else {
|
||||
modifier.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
}
|
||||
|
||||
if let replacePinnedPeerIds = replacePinnedPeerIds {
|
||||
|
@ -28,6 +28,7 @@ private enum InstantPageBlockType: Int32 {
|
||||
case collage = 19
|
||||
case slideshow = 20
|
||||
case channelBanner = 21
|
||||
case audio = 22
|
||||
}
|
||||
|
||||
public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
@ -47,8 +48,9 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
case pullQuote(text: RichText, caption: RichText)
|
||||
case image(id: MediaId, caption: RichText)
|
||||
case video(id: MediaId, caption: RichText, autoplay: Bool, loop: Bool)
|
||||
case audio(id: MediaId, caption: RichText)
|
||||
case cover(InstantPageBlock)
|
||||
case webEmbed(url: String?, html: String?, dimensions: CGSize, caption: RichText, stretchToWidth: Bool, allowScrolling: Bool)
|
||||
case webEmbed(url: String?, html: String?, dimensions: CGSize, caption: RichText, stretchToWidth: Bool, allowScrolling: Bool, coverId: MediaId?)
|
||||
case postEmbed(url: String, webpageId: MediaId?, avatarId: MediaId?, author: String, date: Int32, blocks: [InstantPageBlock], caption: RichText)
|
||||
case collage(items: [InstantPageBlock], caption: RichText)
|
||||
case slideshow(items: [InstantPageBlock], caption: RichText)
|
||||
@ -91,7 +93,11 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
case InstantPageBlockType.cover.rawValue:
|
||||
self = .cover(decoder.decodeObjectForKey("c", decoder: { InstantPageBlock(decoder: $0) }) as! InstantPageBlock)
|
||||
case InstantPageBlockType.webEmbed.rawValue:
|
||||
self = .webEmbed(url: decoder.decodeOptionalStringForKey("u"), html: decoder.decodeOptionalStringForKey("h"), dimensions: CGSize(width: CGFloat(decoder.decodeInt32ForKey("sw", orElse: 0)), height: CGFloat(decoder.decodeInt32ForKey("sh", orElse: 0))), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText, stretchToWidth: decoder.decodeInt32ForKey("st", orElse: 0) != 0, allowScrolling: decoder.decodeInt32ForKey("as", orElse: 0) != 0)
|
||||
var coverId: MediaId?
|
||||
if let coverIdNamespace = decoder.decodeOptionalInt32ForKey("ci.n"), let coverIdId = decoder.decodeOptionalInt64ForKey("ci.i") {
|
||||
coverId = MediaId(namespace: coverIdNamespace, id: coverIdId)
|
||||
}
|
||||
self = .webEmbed(url: decoder.decodeOptionalStringForKey("u"), html: decoder.decodeOptionalStringForKey("h"), dimensions: CGSize(width: CGFloat(decoder.decodeInt32ForKey("sw", orElse: 0)), height: CGFloat(decoder.decodeInt32ForKey("sh", orElse: 0))), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText, stretchToWidth: decoder.decodeInt32ForKey("st", orElse: 0) != 0, allowScrolling: decoder.decodeInt32ForKey("as", orElse: 0) != 0, coverId: coverId)
|
||||
case InstantPageBlockType.postEmbed.rawValue:
|
||||
var avatarId: MediaId?
|
||||
let avatarIdNamespace: Int32? = decoder.decodeOptionalInt32ForKey("av.n")
|
||||
@ -106,6 +112,8 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
self = .slideshow(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText)
|
||||
case InstantPageBlockType.channelBanner.rawValue:
|
||||
self = .channelBanner(decoder.decodeObjectForKey("c") as? TelegramChannel)
|
||||
case InstantPageBlockType.audio.rawValue:
|
||||
self = .audio(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText)
|
||||
default:
|
||||
self = .unsupported
|
||||
}
|
||||
@ -172,8 +180,15 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
case let .cover(block):
|
||||
encoder.encodeInt32(InstantPageBlockType.cover.rawValue, forKey: "r")
|
||||
encoder.encodeObject(block, forKey: "c")
|
||||
case let .webEmbed(url, html, dimensions, caption, stretchToWidth, allowScrolling):
|
||||
case let .webEmbed(url, html, dimensions, caption, stretchToWidth, allowScrolling, coverId):
|
||||
encoder.encodeInt32(InstantPageBlockType.webEmbed.rawValue, forKey: "r")
|
||||
if let coverId = coverId {
|
||||
encoder.encodeInt32(coverId.namespace, forKey: "ci.n")
|
||||
encoder.encodeInt64(coverId.id, forKey: "ci.i")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "ci.n")
|
||||
encoder.encodeNil(forKey: "ci.i")
|
||||
}
|
||||
if let url = url {
|
||||
encoder.encodeString(url, forKey: "u")
|
||||
} else {
|
||||
@ -225,6 +240,11 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "c")
|
||||
}
|
||||
case let .audio(id, caption):
|
||||
encoder.encodeInt32(InstantPageBlockType.audio.rawValue, forKey: "r")
|
||||
encoder.encodeInt32(id.namespace, forKey: "i.n")
|
||||
encoder.encodeInt64(id.id, forKey: "i.i")
|
||||
encoder.encodeObject(caption, forKey: "c")
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,8 +352,8 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .webEmbed(lhsUrl, lhsHtml, lhsDimensions, lhsCaption, lhsStretchToWidth, lhsAllowScrolling):
|
||||
if case let .webEmbed(rhsUrl, rhsHtml, rhsDimensions, rhsCaption, rhsStretchToWidth, rhsAllowScrolling) = rhs, lhsUrl == rhsUrl && lhsHtml == rhsHtml && lhsDimensions == rhsDimensions && lhsCaption == rhsCaption && lhsStretchToWidth == rhsStretchToWidth && lhsAllowScrolling == rhsAllowScrolling {
|
||||
case let .webEmbed(lhsUrl, lhsHtml, lhsDimensions, lhsCaption, lhsStretchToWidth, lhsAllowScrolling, lhsCoverId):
|
||||
if case let .webEmbed(rhsUrl, rhsHtml, rhsDimensions, rhsCaption, rhsStretchToWidth, rhsAllowScrolling, rhsCoverId) = rhs, lhsUrl == rhsUrl && lhsHtml == rhsHtml && lhsDimensions == rhsDimensions && lhsCaption == rhsCaption && lhsStretchToWidth == rhsStretchToWidth && lhsAllowScrolling == rhsAllowScrolling && lhsCoverId == rhsCoverId {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -369,6 +389,12 @@ public indirect enum InstantPageBlock: Coding, Equatable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .audio(id, caption):
|
||||
if case .audio(id, caption) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -387,7 +413,9 @@ private final class MediaDictionary: Coding {
|
||||
var dict: [MediaId: Media] = [:]
|
||||
assert(mediaIds.count == medias.count)
|
||||
for i in 0 ..< mediaIds.count {
|
||||
dict[mediaIds[i]] = medias[i] as! Media
|
||||
if let media = medias[i] as? Media {
|
||||
dict[mediaIds[i]] = media
|
||||
}
|
||||
}
|
||||
self.dict = dict
|
||||
}
|
||||
@ -489,11 +517,11 @@ extension InstantPageBlock {
|
||||
case let .pageBlockPhoto(photoId, caption):
|
||||
self = .image(id: MediaId(namespace: Namespaces.Media.CloudImage, id: photoId), caption: RichText(apiText: caption))
|
||||
case let .pageBlockVideo(flags, videoId, caption):
|
||||
self = .video(id: MediaId(namespace: Namespaces.Media.CloudVideo, id: videoId), caption: RichText(apiText: caption), autoplay: (flags & (1 << 0)) != 0, loop: (flags & (1 << 1)) != 0)
|
||||
self = .video(id: MediaId(namespace: Namespaces.Media.CloudFile, id: videoId), caption: RichText(apiText: caption), autoplay: (flags & (1 << 0)) != 0, loop: (flags & (1 << 1)) != 0)
|
||||
case let .pageBlockCover(cover):
|
||||
self = .cover(InstantPageBlock(apiBlock: cover))
|
||||
case let .pageBlockEmbed(flags, url, html, posterPhotoId, w, h, caption):
|
||||
self = .webEmbed(url: url, html: html, dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), caption: RichText(apiText: caption), stretchToWidth: (flags & (1 << 0)) != 0, allowScrolling: (flags & (1 << 3)) != 0)
|
||||
self = .webEmbed(url: url, html: html, dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), caption: RichText(apiText: caption), stretchToWidth: (flags & (1 << 0)) != 0, allowScrolling: (flags & (1 << 3)) != 0, coverId: posterPhotoId.flatMap { MediaId(namespace: Namespaces.Media.CloudImage, id: $0) })
|
||||
case let .pageBlockEmbedPost(url, webpageId, authorPhotoId, author, date, blocks, caption):
|
||||
self = .postEmbed(url: url, webpageId: webpageId == 0 ? nil : MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId), avatarId: authorPhotoId == 0 ? nil : MediaId(namespace: Namespaces.Media.CloudImage, id: authorPhotoId), author: author, date: date, blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: caption))
|
||||
case let .pageBlockCollage(items, caption):
|
||||
@ -502,6 +530,8 @@ extension InstantPageBlock {
|
||||
self = .slideshow(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: caption))
|
||||
case let .pageBlockChannel(channel: apiChat):
|
||||
self = .channelBanner(parseTelegramGroupOrChannel(chat: apiChat) as? TelegramChannel)
|
||||
case let .pageBlockAudio(audioId, caption):
|
||||
self = .audio(id: MediaId(namespace: Namespaces.Media.CloudFile, id: audioId), caption: RichText(apiText: caption))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
|
||||
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount)
|
||||
|
||||
if let apiChannelPts = apiChannelPts {
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts)
|
||||
chatStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: nil)
|
||||
}
|
||||
|
||||
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
|
||||
@ -234,7 +234,15 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
|
||||
modifier.resetIncomingReadStates(readStates)
|
||||
|
||||
for (peerId, chatState) in chatStates {
|
||||
modifier.setPeerChatState(peerId, state: chatState)
|
||||
if let chatState = chatState as? ChannelState {
|
||||
if let current = modifier.getPeerChatState(peerId) as? ChannelState {
|
||||
modifier.setPeerChatState(peerId, state: current.withUpdatedPts(chatState.pts))
|
||||
} else {
|
||||
modifier.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
} else {
|
||||
modifier.setPeerChatState(peerId, state: chatState)
|
||||
}
|
||||
}
|
||||
|
||||
if remotePeerIds == resultingPeerIds {
|
||||
|
@ -14,7 +14,6 @@ public struct Namespaces {
|
||||
|
||||
public struct Media {
|
||||
public static let CloudImage: Int32 = 0
|
||||
public static let CloudVideo: Int32 = 1
|
||||
public static let CloudAudio: Int32 = 2
|
||||
public static let CloudContact: Int32 = 3
|
||||
public static let CloudMap: Int32 = 4
|
||||
|
@ -128,7 +128,9 @@ public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCol
|
||||
}
|
||||
switch result {
|
||||
case let .internalReference(id, type, title, description, image, file, message):
|
||||
if let image = image {
|
||||
if type == "game" {
|
||||
return .message(text: "", attributes: attributes, media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: title ?? "", description: description ?? "", image: image, file: file), replyToMessageId: nil)
|
||||
} else if let image = image {
|
||||
return .message(text: caption, attributes: attributes, media: image, replyToMessageId: nil)
|
||||
} else if let file = file {
|
||||
return .message(text: caption, attributes: attributes, media: file, replyToMessageId: nil)
|
||||
|
@ -268,8 +268,15 @@ func textAndMediaFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (S
|
||||
break
|
||||
case let .messageMediaGame(game):
|
||||
return (nil, TelegramMediaGame(apiGame: game))
|
||||
case let .messageMediaInvoice(_, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
|
||||
return (nil, TelegramMediaInvoice(title: title, description: description, photo: photo != nil ? TelegramMediaWebFile(photo!) : nil, receiptMessageId: receiptMsgId != nil ? MessageId(peerId: peerId, namespace: 0, id: receiptMsgId!) : nil, currency: currency, totalAmount: totalAmount, startParam: startParam))
|
||||
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
|
||||
var parsedFlags = TelegramMediaInvoiceFlags()
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
parsedFlags.insert(.isTest)
|
||||
}
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
parsedFlags.insert(.shippingAddressRequested)
|
||||
}
|
||||
return (nil, TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,35 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId
|
||||
}
|
||||
}
|
||||
|
||||
func fetchPeerCloudReadState(network: Network, postbox: Postbox, peerId: PeerId, inputPeer: Api.InputPeer) -> Signal<PeerReadState?, NoError> {
|
||||
return network.request(Api.functions.messages.getPeerDialogs(peers: [inputPeer]))
|
||||
|> map { result -> PeerReadState? in
|
||||
switch result {
|
||||
case let .peerDialogs(dialogs, _, _, _, _):
|
||||
if let dialog = dialogs.filter({ $0.peerId == peerId }).first {
|
||||
let apiTopMessage: Int32
|
||||
let apiReadInboxMaxId: Int32
|
||||
let apiReadOutboxMaxId: Int32
|
||||
let apiUnreadCount: Int32
|
||||
switch dialog {
|
||||
case let .dialog(_, _, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, _, _, _):
|
||||
apiTopMessage = topMessage
|
||||
apiReadInboxMaxId = readInboxMaxId
|
||||
apiReadOutboxMaxId = readOutboxMaxId
|
||||
apiUnreadCount = unreadCount
|
||||
}
|
||||
|
||||
return .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|> `catch` { _ -> Signal<PeerReadState?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func dialogReadState(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(PeerReadState, PeerReadStateMarker), VerifyReadStateError> {
|
||||
return dialogTopMessage(network: network, postbox: postbox, peerId: peerId)
|
||||
|> mapToSignal { topMessage -> Signal<(PeerReadState, PeerReadStateMarker), VerifyReadStateError> in
|
||||
|
@ -136,6 +136,20 @@ public enum TelegramMediaFileAttribute: Coding {
|
||||
}
|
||||
}
|
||||
|
||||
func dimensionsForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) -> CGSize? {
|
||||
for attribute in attributes {
|
||||
switch attribute {
|
||||
case let .Video(_, size, _):
|
||||
return size
|
||||
case let .ImageSize(size):
|
||||
return size
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public final class TelegramMediaFile: Media, Equatable {
|
||||
public let fileId: MediaId
|
||||
public let resource: TelegramMediaResource
|
||||
@ -255,17 +269,7 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
}
|
||||
|
||||
public var dimensions: CGSize? {
|
||||
for attribute in self.attributes {
|
||||
switch attribute {
|
||||
case let .Video(_, size, _):
|
||||
return size
|
||||
case let .ImageSize(size):
|
||||
return size
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return dimensionsForFileAttributes(self.attributes)
|
||||
}
|
||||
|
||||
public func isEqual(_ other: Media) -> Bool {
|
||||
|
@ -5,6 +5,21 @@ import Foundation
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public struct TelegramMediaInvoiceFlags: OptionSet {
|
||||
public var rawValue: Int32
|
||||
|
||||
public init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
public static let isTest = TelegramMediaInvoiceFlags(rawValue: 1 << 0)
|
||||
public static let shippingAddressRequested = TelegramMediaInvoiceFlags(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
//flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String
|
||||
//messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
|
||||
|
||||
@ -13,16 +28,16 @@ public final class TelegramMediaInvoice: Media {
|
||||
|
||||
public var id: MediaId? = nil
|
||||
|
||||
public let title:String
|
||||
public let description:String
|
||||
// public let photo:
|
||||
public let receiptMessageId:MessageId?
|
||||
public let currency:String
|
||||
public let totalAmount:Int64
|
||||
public let startParam:String
|
||||
public let photo:TelegramMediaWebFile?
|
||||
public let title: String
|
||||
public let description: String
|
||||
public let receiptMessageId: MessageId?
|
||||
public let currency: String
|
||||
public let totalAmount: Int64
|
||||
public let startParam: String
|
||||
public let photo: TelegramMediaWebFile?
|
||||
public let flags: TelegramMediaInvoiceFlags
|
||||
|
||||
public init(title:String, description:String, photo:TelegramMediaWebFile?, receiptMessageId:MessageId?, currency:String, totalAmount:Int64, startParam:String) {
|
||||
public init(title: String, description: String, photo: TelegramMediaWebFile?, receiptMessageId: MessageId?, currency: String, totalAmount: Int64, startParam: String, flags: TelegramMediaInvoiceFlags) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.photo = photo
|
||||
@ -30,9 +45,9 @@ public final class TelegramMediaInvoice: Media {
|
||||
self.currency = currency
|
||||
self.totalAmount = totalAmount
|
||||
self.startParam = startParam
|
||||
self.flags = flags
|
||||
}
|
||||
|
||||
|
||||
public init(decoder: Decoder) {
|
||||
self.title = decoder.decodeStringForKey("t", orElse: "")
|
||||
self.description = decoder.decodeStringForKey("d", orElse: "")
|
||||
@ -40,6 +55,7 @@ public final class TelegramMediaInvoice: Media {
|
||||
self.totalAmount = decoder.decodeInt64ForKey("ta", orElse: 0)
|
||||
self.startParam = decoder.decodeStringForKey("sp", orElse: "")
|
||||
self.photo = decoder.decodeObjectForKey("p") as? TelegramMediaWebFile
|
||||
self.flags = TelegramMediaInvoiceFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0))
|
||||
|
||||
if let receiptMessageIdPeerId = decoder.decodeOptionalInt64ForKey("r.p") as Int64?, let receiptMessageIdNamespace = decoder.decodeOptionalInt32ForKey("r.n") as Int32?, let receiptMessageIdId = decoder.decodeOptionalInt32ForKey("r.i") as Int32? {
|
||||
self.receiptMessageId = MessageId(peerId: PeerId(receiptMessageIdPeerId), namespace: receiptMessageIdNamespace, id: receiptMessageIdId)
|
||||
@ -54,6 +70,7 @@ public final class TelegramMediaInvoice: Media {
|
||||
encoder.encodeString(self.currency, forKey: "nc")
|
||||
encoder.encodeInt64(self.totalAmount, forKey: "ta")
|
||||
encoder.encodeString(self.startParam, forKey: "sp")
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "f")
|
||||
|
||||
if let photo = photo {
|
||||
encoder.encodeObject(photo, forKey: "p")
|
||||
@ -101,6 +118,10 @@ public final class TelegramMediaInvoice: Media {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.flags != other.flags {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,6 @@ import Foundation
|
||||
#endif
|
||||
|
||||
public class TelegramMediaWebFile: Media {
|
||||
|
||||
|
||||
|
||||
|
||||
public let resource: TelegramMediaResource
|
||||
public let mimeType: String
|
||||
public let size: Int32
|
||||
@ -61,14 +57,16 @@ public class TelegramMediaWebFile: Media {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
public var dimensions: CGSize? {
|
||||
return dimensionsForFileAttributes(self.attributes)
|
||||
}
|
||||
}
|
||||
|
||||
extension TelegramMediaWebFile {
|
||||
convenience init(_ document:Api.WebDocument) {
|
||||
switch document {
|
||||
case let .webDocument(data):
|
||||
self.init(resource: WebFileReferenceMediaResource(url: data.url, size: data.size, datacenterId: data.dcId, accessHash: data.accessHash), mimeType: data.mimeType, size: data.size, attributes: telegramMediaFileAttributesFromApiAttributes(data.attributes))
|
||||
case let .webDocument(data):
|
||||
self.init(resource: WebFileReferenceMediaResource(url: data.url, size: data.size, datacenterId: data.dcId, accessHash: data.accessHash), mimeType: data.mimeType, size: data.size, attributes: telegramMediaFileAttributesFromApiAttributes(data.attributes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user