mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-17 11:00:07 +00:00
no message
This commit is contained in:
parent
9523bfa138
commit
5bd28cbc65
@ -203,6 +203,12 @@
|
||||
D050F26E1E4A5B6D00988324 /* RemovePeerChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38741E40A7F70044D6FE /* RemovePeerChat.swift */; };
|
||||
D0528E5A1E658B3600E2FEF5 /* ManagedLocalInputActivities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E591E658B3600E2FEF5 /* ManagedLocalInputActivities.swift */; };
|
||||
D0528E5B1E658B3600E2FEF5 /* ManagedLocalInputActivities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E591E658B3600E2FEF5 /* ManagedLocalInputActivities.swift */; };
|
||||
D0528E601E65B94E00E2FEF5 /* SingleMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */; };
|
||||
D0528E611E65B94E00E2FEF5 /* SingleMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */; };
|
||||
D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E641E65C82400E2FEF5 /* UpdateContactName.swift */; };
|
||||
D0528E661E65C82400E2FEF5 /* UpdateContactName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E641E65C82400E2FEF5 /* UpdateContactName.swift */; };
|
||||
D0528E6A1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */; };
|
||||
D0528E6B1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */; };
|
||||
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; };
|
||||
D0561DE41E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */; };
|
||||
D0561DEA1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */; };
|
||||
@ -533,6 +539,9 @@
|
||||
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>"; };
|
||||
D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleMessageView.swift; sourceTree = "<group>"; };
|
||||
D0528E641E65C82400E2FEF5 /* UpdateContactName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateContactName.swift; sourceTree = "<group>"; };
|
||||
D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebpagePreview.swift; sourceTree = "<group>"; };
|
||||
D0561DE21E5737FC00E6B9E9 /* UpdatePeerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatePeerInfo.swift; sourceTree = "<group>"; };
|
||||
D0561DE91E5754FA00E6B9E9 /* ChannelAdmins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChannelAdmins.swift; sourceTree = "<group>"; };
|
||||
D0613FC91E60440600202CDB /* InvitationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvitationLinks.swift; sourceTree = "<group>"; };
|
||||
@ -963,6 +972,8 @@
|
||||
D0AAD1A91E32638500D5B9DE /* ApplyMaxReadIndexInteractively.swift */,
|
||||
D00C7CDF1E3785700080C3D5 /* MarkMessageContentAsConsumedInteractively.swift */,
|
||||
C239BE961E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift */,
|
||||
D0528E5F1E65B94E00E2FEF5 /* SingleMessageView.swift */,
|
||||
D0528E691E65DD2100E2FEF5 /* WebpagePreview.swift */,
|
||||
);
|
||||
name = Messages;
|
||||
sourceTree = "<group>";
|
||||
@ -1105,6 +1116,7 @@
|
||||
D033FEB21E61F3C000644997 /* ReportPeer.swift */,
|
||||
D033FEB51E61F3F900644997 /* BlockedPeers.swift */,
|
||||
C239BE9B1E630CA700C2C453 /* UpdatePinnedMessage.swift */,
|
||||
D0528E641E65C82400E2FEF5 /* UpdateContactName.swift */,
|
||||
);
|
||||
name = Peers;
|
||||
sourceTree = "<group>";
|
||||
@ -1427,6 +1439,7 @@
|
||||
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */,
|
||||
D0BC38771E40BAAA0044D6FE /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
|
||||
D0B843851DA6EDC4005F29E1 /* CachedChannelData.swift in Sources */,
|
||||
D0528E6A1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */,
|
||||
D0BEAF5D1E54941B00BD963D /* Authorization.swift in Sources */,
|
||||
D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */,
|
||||
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
|
||||
@ -1469,6 +1482,7 @@
|
||||
D03B0D5B1D631A6900955575 /* Buffer.swift in Sources */,
|
||||
D0B843891DA7AB96005F29E1 /* ExportedInvitation.swift in Sources */,
|
||||
D03B0E441D631E6600955575 /* NetworkLogging.m in Sources */,
|
||||
D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */,
|
||||
D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */,
|
||||
D03B0CBB1D62233C00955575 /* MergeLists.swift in Sources */,
|
||||
C239BE971E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift in Sources */,
|
||||
@ -1476,6 +1490,7 @@
|
||||
D0B843C31DA7FF30005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */,
|
||||
C2366C861E4F403C0097CCFF /* AddressNames.swift in Sources */,
|
||||
D0B843C11DA7FF30005F29E1 /* NBPhoneMetaData.m in Sources */,
|
||||
D0528E601E65B94E00E2FEF5 /* SingleMessageView.swift in Sources */,
|
||||
D0528E5A1E658B3600E2FEF5 /* ManagedLocalInputActivities.swift in Sources */,
|
||||
D0FA8BA41E1FA341001E855B /* SecretChatKeychain.swift in Sources */,
|
||||
D01749601E118FC30057C89A /* AccountIntermediateState.swift in Sources */,
|
||||
@ -1623,6 +1638,7 @@
|
||||
D00C7CCD1E3620C30080C3D5 /* CachedChannelParticipants.swift in Sources */,
|
||||
D03C536E1DAD5CA9004C17B3 /* PhoneNumber.swift in Sources */,
|
||||
D0BC387C1E40D2880044D6FE /* TogglePeerChatPinned.swift in Sources */,
|
||||
D0528E6B1E65DD2100E2FEF5 /* WebpagePreview.swift in Sources */,
|
||||
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */,
|
||||
D0B844321DAB91E0005F29E1 /* NBPhoneMetaDataGenerator.m in Sources */,
|
||||
D0BEAF5E1E54941B00BD963D /* Authorization.swift in Sources */,
|
||||
@ -1665,6 +1681,7 @@
|
||||
D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
|
||||
D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */,
|
||||
D050F2641E4A5AEB00988324 /* ManagedSynchronizePinnedChatsOperations.swift in Sources */,
|
||||
D0528E661E65C82400E2FEF5 /* UpdateContactName.swift in Sources */,
|
||||
D0F7B1E81E045C87007EB8A5 /* PeerParticipants.swift in Sources */,
|
||||
D049EAD61E43D98500A2CD3A /* RecentMediaItem.swift in Sources */,
|
||||
D0B844331DAB91E0005F29E1 /* NBPhoneNumber.m in Sources */,
|
||||
@ -1672,6 +1689,7 @@
|
||||
D001F3F61E128A1C007A8C60 /* PendingMessageUploadedContent.swift in Sources */,
|
||||
C2366C871E4F403C0097CCFF /* AddressNames.swift in Sources */,
|
||||
D02ABC7F1E3109F000CAE539 /* CloudChatRemoveMessagesOperation.swift in Sources */,
|
||||
D0528E611E65B94E00E2FEF5 /* SingleMessageView.swift in Sources */,
|
||||
D0528E5B1E658B3600E2FEF5 /* ManagedLocalInputActivities.swift in Sources */,
|
||||
D0FA8BA51E1FA341001E855B /* SecretChatKeychain.swift in Sources */,
|
||||
D0F7B1E71E045C87007EB8A5 /* JoinChannel.swift in Sources */,
|
||||
|
@ -376,6 +376,8 @@ public class Account {
|
||||
return self.networkStateValue.get()
|
||||
}
|
||||
|
||||
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
|
||||
|
||||
public init(id: AccountRecordId, basePath: String, testingEnvironment: Bool, postbox: Postbox, network: Network, peerId: PeerId) {
|
||||
self.id = id
|
||||
self.basePath = basePath
|
||||
@ -594,7 +596,10 @@ public class Account {
|
||||
}
|
||||
}
|
||||
|
||||
public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: ((_ account: Account, _ resource: MediaResource, _ resourceData: MediaResourceData, _ representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError>)? = nil) {
|
||||
public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ resourceData: MediaResourceData, _ representation: CachedMediaResourceRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError>
|
||||
public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: Media, _ userInteractive: Bool) -> Signal<Media?, NoError>
|
||||
|
||||
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)
|
||||
@ -611,6 +616,9 @@ public func setupAccount(_ account: Account, fetchCachedResourceRepresentation:
|
||||
}
|
||||
}
|
||||
|
||||
account.transformOutgoingMessageMedia = transformOutgoingMessageMedia
|
||||
account.pendingMessageManager.transformOutgoingMessageMedia = transformOutgoingMessageMedia
|
||||
|
||||
account.managedContactsDisposable.set(manageContacts(network: account.network, postbox: account.postbox).start())
|
||||
account.managedStickerPacksDisposable.set(manageStickerPacks(network: account.network, postbox: account.postbox).start())
|
||||
|
||||
|
@ -42,7 +42,7 @@ public func checkAddressNameFormat(_ value: String) -> AddressNameFormatError? {
|
||||
if index == 0 && char >= "0" && char <= "9" {
|
||||
return .startsWithDigit
|
||||
}
|
||||
if (!((char >= "a" && char <= "z") || (char >= "A" && char <= "Z") || (char >= "0" && char <= "9"))) {
|
||||
if (!((char >= "a" && char <= "z") || (char >= "A" && char <= "Z") || (char >= "0" && char <= "9") || char == "_")) {
|
||||
return .invalidCharacters
|
||||
}
|
||||
index += 1
|
||||
|
@ -173,7 +173,7 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource {
|
||||
|
||||
public func isEqual(to: TelegramMediaResource) -> Bool {
|
||||
if let to = to as? CloudDocumentMediaResource {
|
||||
return self.datacenterId != to.datacenterId && self.fileId != to.fileId && self.accessHash != to.accessHash && self.size != to.size
|
||||
return self.datacenterId == to.datacenterId && self.fileId == to.fileId && self.accessHash == to.accessHash && self.size == to.size
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
@ -24,15 +24,15 @@ public enum EnqueueMessage {
|
||||
private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAttribute]) -> [MessageAttribute] {
|
||||
return attributes.filter { attribute in
|
||||
switch attribute {
|
||||
case let _ as TextEntitiesMessageAttribute:
|
||||
case _ as TextEntitiesMessageAttribute:
|
||||
return true
|
||||
case let _ as InlineBotMessageAttribute:
|
||||
case _ as InlineBotMessageAttribute:
|
||||
return true
|
||||
case let _ as OutgoingMessageInfoAttribute:
|
||||
case _ as OutgoingMessageInfoAttribute:
|
||||
return true
|
||||
case let _ as ReplyMarkupMessageAttribute:
|
||||
case _ as ReplyMarkupMessageAttribute:
|
||||
return true
|
||||
case let _ as OutgoingChatContextResultMessageAttribute:
|
||||
case _ as OutgoingChatContextResultMessageAttribute:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@ -43,9 +43,9 @@ private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAtt
|
||||
private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAttribute]) -> [MessageAttribute] {
|
||||
return attributes.filter { attribute in
|
||||
switch attribute {
|
||||
case let _ as TextEntitiesMessageAttribute:
|
||||
case _ as TextEntitiesMessageAttribute:
|
||||
return true
|
||||
case let _ as InlineBotMessageAttribute:
|
||||
case _ as InlineBotMessageAttribute:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@ -53,25 +53,83 @@ private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAt
|
||||
}
|
||||
}
|
||||
|
||||
func opportunisticallyTransformMessageWithMedia(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia, media: Media, userInteractive: Bool) -> Signal<Media?, NoError> {
|
||||
return transformOutgoingMessageMedia(postbox, network, media, userInteractive)
|
||||
|> timeout(2.0, queue: Queue.concurrentDefaultQueue(), alternate: .single(nil))
|
||||
}
|
||||
|
||||
private func opportunisticallyTransformOutgoingMedia(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia, messages: [EnqueueMessage], userInteractive: Bool) -> Signal<[(Bool, EnqueueMessage)], NoError> {
|
||||
var hasMedia = false
|
||||
loop: for message in messages {
|
||||
switch message {
|
||||
case let .message(_, _, media, _):
|
||||
if media != nil {
|
||||
hasMedia = true
|
||||
break loop
|
||||
}
|
||||
case .forward:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasMedia {
|
||||
return .single(messages.map { (true, $0) })
|
||||
}
|
||||
|
||||
var signals: [Signal<(Bool, EnqueueMessage), NoError>] = []
|
||||
for message in messages {
|
||||
switch message {
|
||||
case let .message(text, attributes, media, replyToMessageId):
|
||||
if let media = media {
|
||||
signals.append(opportunisticallyTransformMessageWithMedia(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, media: media, userInteractive: userInteractive) |> map { result -> (Bool, EnqueueMessage) in
|
||||
if let result = result {
|
||||
return (true, .message(text: text, attributes: attributes, media: result, replyToMessageId: replyToMessageId))
|
||||
} else {
|
||||
return (false, .message(text: text, attributes: attributes, media: media, replyToMessageId: replyToMessageId))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
signals.append(.single((false, message)))
|
||||
}
|
||||
case .forward:
|
||||
signals.append(.single((false, message)))
|
||||
}
|
||||
}
|
||||
return combineLatest(signals)
|
||||
}
|
||||
|
||||
public func enqueueMessages(account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> Signal<[MessageId?], NoError> {
|
||||
let signal: Signal<[(Bool, EnqueueMessage)], NoError>
|
||||
if let transformOutgoingMessageMedia = account.transformOutgoingMessageMedia {
|
||||
signal = opportunisticallyTransformOutgoingMedia(network: account.network, postbox: account.postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messages: messages, userInteractive: true)
|
||||
} else {
|
||||
signal = .single(messages.map { (false, $0) })
|
||||
}
|
||||
return signal
|
||||
|> mapToSignal { messages -> Signal<[MessageId?], NoError> in
|
||||
return account.postbox.modify { modifier -> [MessageId?] in
|
||||
return enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messages: [EnqueueMessage]) -> [MessageId?] {
|
||||
func enqueueMessages(modifier: Modifier, account: Account, peerId: PeerId, messages: [(Bool, EnqueueMessage)]) -> [MessageId?] {
|
||||
if let peer = modifier.getPeer(peerId) {
|
||||
var storeMessages: [StoreMessage] = []
|
||||
let timestamp = Int32(account.network.context.globalTime())
|
||||
var globallyUniqueIds: [Int64] = []
|
||||
for message in messages {
|
||||
for (transformedMedia, message) in messages {
|
||||
var attributes: [MessageAttribute] = []
|
||||
var flags = StoreMessageFlags()
|
||||
flags.insert(.Unsent)
|
||||
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId))
|
||||
var infoFlags = OutgoingMessageInfoFlags()
|
||||
if transformedMedia {
|
||||
infoFlags.insert(.transformedMedia)
|
||||
}
|
||||
attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: infoFlags))
|
||||
globallyUniqueIds.append(randomId)
|
||||
|
||||
switch message {
|
||||
|
@ -16,7 +16,6 @@ private func fetchCloudMediaLocation(account: Account, resource: TelegramCloudMe
|
||||
#if os(iOS)
|
||||
private func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
return Signal { subscriber in
|
||||
let options = PHFetchOptions()
|
||||
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
|
||||
var requestId: PHImageRequestID?
|
||||
if fetchResult.count != 0 {
|
||||
@ -63,6 +62,8 @@ private func fetchLocalFileResource(path: String) -> Signal<MediaResourceDataFet
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: data, complete: true))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putNext(MediaResourceDataFetchResult(data: Data(), complete: false))
|
||||
}
|
||||
return EmptyDisposable
|
||||
}
|
||||
@ -70,21 +71,25 @@ private func fetchLocalFileResource(path: String) -> Signal<MediaResourceDataFet
|
||||
|
||||
func fetchResource(account: Account, resource: MediaResource, range: Range<Int>) -> Signal<MediaResourceDataFetchResult, NoError> {
|
||||
if let _ = resource as? EmptyMediaResource {
|
||||
return .never()
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false))
|
||||
} else if let secretFileResource = resource as? SecretFileMediaResource {
|
||||
return fetchSecretFileResource(account: account, resource: secretFileResource, range: range)
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchSecretFileResource(account: account, resource: secretFileResource, range: range))
|
||||
} else if let cloudResource = resource as? TelegramCloudMediaResource {
|
||||
return fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range)
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, size: resource.size, range: range))
|
||||
} else if let photoLibraryResource = resource as? PhotoLibraryMediaResource {
|
||||
#if os(iOS)
|
||||
return fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier)
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier))
|
||||
#else
|
||||
return .never()
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), 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()))
|
||||
} else {
|
||||
return fetchLocalFileResource(path: localFileResource.localFilePath)
|
||||
}
|
||||
} else if let httpReference = resource as? HttpReferenceMediaResource {
|
||||
return fetchHttpResource(url: httpReference.url)
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false)) |> then(fetchHttpResource(url: httpReference.url))
|
||||
}
|
||||
return .never()
|
||||
return .single(MediaResourceDataFetchResult(data: Data(), complete: false))
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ func managedSecretChatOutgoingOperations(postbox: Postbox, network: Network) ->
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .noop(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .readMessagesContent(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .readMessageContents(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, globallyUniqueIds: globallyUniqueIds), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, timeout: timeout), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout,messageId):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, timeout: timeout, messageId: messageId), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .resendOperations(layer, actionGloballyUniqueId, fromSeqNo, toSeqNo):
|
||||
return sendServiceActionMessage(postbox: postbox, network: network, peerId: entry.peerId, action: .resendOperations(layer: layer, actionGloballyUniqueId: actionGloballyUniqueId, fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), tagLocalIndex: entry.tagLocalIndex, wasDelivered: operation.delivered)
|
||||
case let .screenshotMessages(layer, actionGloballyUniqueId, globallyUniqueIds):
|
||||
@ -339,7 +339,7 @@ private enum SecretMessageAction {
|
||||
case pfsCommitKey(layer: SecretChatSequenceBasedLayer, actionGloballyUniqueId: Int64, rekeySessionId: Int64, keyFingerprint: Int64)
|
||||
case noop(layer: SecretChatSequenceBasedLayer, actionGloballyUniqueId: Int64)
|
||||
case readMessageContents(layer: SecretChatLayer, actionGloballyUniqueId: Int64, globallyUniqueIds: [Int64])
|
||||
case setMessageAutoremoveTimeout(layer: SecretChatLayer, actionGloballyUniqueId: Int64, timeout: Int32)
|
||||
case setMessageAutoremoveTimeout(layer: SecretChatLayer, actionGloballyUniqueId: Int64, timeout: Int32, messageId: MessageId)
|
||||
|
||||
var globallyUniqueId: Int64 {
|
||||
switch self {
|
||||
@ -365,10 +365,19 @@ private enum SecretMessageAction {
|
||||
return actionGloballyUniqueId
|
||||
case let .readMessageContents(_, actionGloballyUniqueId, _):
|
||||
return actionGloballyUniqueId
|
||||
case let .setMessageAutoremoveTimeout(_, actionGloballyUniqueId, _):
|
||||
case let .setMessageAutoremoveTimeout(_, actionGloballyUniqueId, _, _):
|
||||
return actionGloballyUniqueId
|
||||
}
|
||||
}
|
||||
|
||||
var messageId: MessageId? {
|
||||
switch self {
|
||||
case let .setMessageAutoremoveTimeout(_, _, _, messageId):
|
||||
return messageId
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func decryptedAttributes46(_ attributes: [TelegramMediaFileAttribute]) -> [SecretApi46.DocumentAttribute] {
|
||||
@ -580,7 +589,7 @@ private func boxedDecryptedSecretMessageAction(action: SecretMessageAction) -> B
|
||||
case .layer46:
|
||||
return .layer46(.decryptedMessageService(randomId: actionGloballyUniqueId, action: .decryptedMessageActionReadMessages(randomIds: globallyUniqueIds)))
|
||||
}
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout):
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout, _):
|
||||
switch layer {
|
||||
case .layer8:
|
||||
let randomBytesData = malloc(15)!
|
||||
@ -670,6 +679,29 @@ private func sendServiceActionMessage(postbox: Postbox, network: Network, peerId
|
||||
|> mapToSignal { result in
|
||||
return postbox.modify { modifier -> Void in
|
||||
markOutgoingOperationAsCompleted(modifier: modifier, peerId: peerId, tagLocalIndex: tagLocalIndex)
|
||||
if let messageId = action.messageId {
|
||||
modifier.updateMessage(messageId, update: { currentMessage in
|
||||
var flags = StoreMessageFlags(currentMessage.flags)
|
||||
var timestamp = currentMessage.timestamp
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .sentEncryptedMessage(date):
|
||||
timestamp = date
|
||||
case let .sentEncryptedFile(date, _):
|
||||
timestamp = date
|
||||
}
|
||||
flags.remove(.Unsent)
|
||||
flags.remove(.Sending)
|
||||
} else {
|
||||
flags = [.Failed]
|
||||
}
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date)
|
||||
}
|
||||
return StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: timestamp, flags: flags, tags: currentMessage.tags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -157,7 +157,7 @@ private final class MultipartFetchManager {
|
||||
data = data.subdata(in: 0 ..< partSize)
|
||||
}
|
||||
strongSelf.receivedSize += data.count
|
||||
if let completeSize = strongSelf.completeSize {
|
||||
if let _ = strongSelf.completeSize {
|
||||
if data.count != partSize {
|
||||
assertionFailure()
|
||||
return
|
||||
|
@ -262,7 +262,7 @@ func multipartUpload(network: Network, postbox: Postbox, resource: MediaResource
|
||||
encryptionKey = SecretFileEncryptionKey(aesKey: aesKey, aesIv: aesIv)
|
||||
}
|
||||
|
||||
let resourceData = postbox.mediaBox.resourceData(resource, complete: true)
|
||||
let resourceData = postbox.mediaBox.resourceData(resource, option: .incremental(waitUntilFetchStatus: false))
|
||||
|
||||
let manager = MultipartUploadManager(data: resourceData, encryptionKey: encryptionKey, uploadPart: { part in
|
||||
return download.uploadPart(fileId: part.fileId, index: part.index, data: part.data)
|
||||
|
@ -5,18 +5,40 @@ import Foundation
|
||||
import Postbox
|
||||
#endif
|
||||
|
||||
public struct OutgoingMessageInfoFlags: OptionSet {
|
||||
public var rawValue: Int32
|
||||
|
||||
public init() {
|
||||
self.rawValue = 0
|
||||
}
|
||||
|
||||
public init(rawValue: Int32) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
public static var transformedMedia = OutgoingMessageInfoFlags(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
public class OutgoingMessageInfoAttribute: MessageAttribute {
|
||||
public let uniqueId: Int64
|
||||
public let flags: OutgoingMessageInfoFlags
|
||||
|
||||
init(uniqueId: Int64) {
|
||||
init(uniqueId: Int64, flags: OutgoingMessageInfoFlags) {
|
||||
self.uniqueId = uniqueId
|
||||
self.flags = flags
|
||||
}
|
||||
|
||||
required public init(decoder: Decoder) {
|
||||
self.uniqueId = decoder.decodeInt64ForKey("u")
|
||||
self.flags = OutgoingMessageInfoFlags(rawValue: decoder.decodeInt32ForKey("f"))
|
||||
}
|
||||
|
||||
public func encode(_ encoder: Encoder) {
|
||||
encoder.encodeInt64(self.uniqueId, forKey: "u")
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "f")
|
||||
}
|
||||
|
||||
public func withUpdatedFlags(_ flags: OutgoingMessageInfoFlags) -> OutgoingMessageInfoAttribute {
|
||||
return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: flags)
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ public final class PendingMessageManager {
|
||||
|
||||
private var peerSummaryContexts: [PeerId: PeerPendingMessagesSummaryContext] = [:]
|
||||
|
||||
var transformOutgoingMessageMedia: TransformOutgoingMessageMedia?
|
||||
|
||||
init(network: Network, postbox: Postbox, stateManager: AccountStateManager) {
|
||||
self.network = network
|
||||
self.postbox = postbox
|
||||
@ -170,7 +172,7 @@ public final class PendingMessageManager {
|
||||
continue
|
||||
}
|
||||
|
||||
let uploadedContent = uploadedMessageContent(network: strongSelf.network, postbox: strongSelf.postbox, message: message)
|
||||
let uploadedContent = uploadedMessageContent(network: strongSelf.network, postbox: strongSelf.postbox, transformOutgoingMessageMedia: strongSelf.transformOutgoingMessageMedia, message: message)
|
||||
|
||||
let sendMessage = uploadedContent
|
||||
|> mapToSignal { contentResult -> Signal<PendingMessageResult, NoError> in
|
||||
@ -255,13 +257,15 @@ public final class PendingMessageManager {
|
||||
if let media = media as? TelegramMediaAction {
|
||||
if case let .messageAutoremoveTimeoutUpdated(value) = media.action {
|
||||
sentAsAction = true
|
||||
let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: message.id.peerId, operation: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: message.globallyUniqueId!, timeout: value), state: state)
|
||||
let updatedState = addSecretChatOutgoingOperation(modifier: modifier, peerId: message.id.peerId, operation: .setMessageAutoremoveTimeout(layer: layer, actionGloballyUniqueId: message.globallyUniqueId!, timeout: value, messageId: message.id), state: state)
|
||||
if updatedState != state {
|
||||
modifier.setPeerChatState(message.id.peerId, state: updatedState)
|
||||
}
|
||||
modifier.updateMessage(message.id, update: { currentMessage in
|
||||
var flags = StoreMessageFlags(message.flags)
|
||||
flags.remove(.Sending)
|
||||
if !flags.contains(.Failed) {
|
||||
flags.insert(.Sending)
|
||||
}
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date)
|
||||
|
@ -20,7 +20,7 @@ enum PendingMessageUploadedContentResult {
|
||||
case content(Message, PendingMessageUploadedContent)
|
||||
}
|
||||
|
||||
func uploadedMessageContent(network: Network, postbox: Postbox, message: Message) -> Signal<PendingMessageUploadedContentResult, NoError> {
|
||||
func uploadedMessageContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, message: Message) -> Signal<PendingMessageUploadedContentResult, NoError> {
|
||||
var outgoingChatContextResultAttribute: OutgoingChatContextResultMessageAttribute?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? OutgoingChatContextResultMessageAttribute {
|
||||
@ -28,7 +28,7 @@ func uploadedMessageContent(network: Network, postbox: Postbox, message: Message
|
||||
}
|
||||
}
|
||||
|
||||
if let forwardInfo = message.forwardInfo {
|
||||
if let _ = message.forwardInfo {
|
||||
var forwardSourceInfo: ForwardSourceInfoAttribute?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ForwardSourceInfoAttribute {
|
||||
@ -44,13 +44,13 @@ func uploadedMessageContent(network: Network, postbox: Postbox, message: Message
|
||||
} else if let outgoingChatContextResultAttribute = outgoingChatContextResultAttribute {
|
||||
return .single(.content(message, .chatContextResult(outgoingChatContextResultAttribute)))
|
||||
} else if let media = message.media.first {
|
||||
if let image = media as? TelegramMediaImage, let largestRepresentation = largestImageRepresentation(image.representations) {
|
||||
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
|
||||
return uploadedMediaImageContent(network: network, postbox: postbox, image: image, message: message)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
if let resource = file.resource as? CloudDocumentMediaResource {
|
||||
return .single(.content(message, .media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: message.text))))
|
||||
} else {
|
||||
return uploadedMediaFileContent(network: network, postbox: postbox, file: file, message: message)
|
||||
return uploadedMediaFileContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, file: file, message: message)
|
||||
}
|
||||
} else if let contact = media as? TelegramMediaContact {
|
||||
let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName)
|
||||
@ -129,9 +129,33 @@ private func inputDocumentAttributesFromFile(_ file: TelegramMediaFile) -> [Api.
|
||||
return attributes
|
||||
}
|
||||
|
||||
private func uploadedMediaFileContent(network: Network, postbox: Postbox, file: TelegramMediaFile, message: Message) -> Signal<PendingMessageUploadedContentResult, NoError> {
|
||||
return multipartUpload(network: network, postbox: postbox, resource: file.resource, encrypt: message.id.peerId.namespace == Namespaces.Peer.SecretChat)
|
||||
|> map { next -> PendingMessageUploadedContentResult in
|
||||
private enum UploadedMediaTransform {
|
||||
case pending
|
||||
case done(Media?)
|
||||
}
|
||||
|
||||
private enum UploadedMediaThumbnail {
|
||||
case pending
|
||||
case done(Api.InputFile?)
|
||||
}
|
||||
|
||||
private func uploadedThumbnail(network: Network, postbox: Postbox, image: TelegramMediaImageRepresentation) -> Signal<Api.InputFile?, NoError> {
|
||||
return multipartUpload(network: network, postbox: postbox, resource: image.resource, encrypt: false)
|
||||
|> mapToSignal { result -> Signal<Api.InputFile?, NoError> in
|
||||
switch result {
|
||||
case .progress:
|
||||
return .complete()
|
||||
case let .inputFile(inputFile):
|
||||
return .single(inputFile)
|
||||
case let .inputSecretFile(file, size, key):
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func uploadedMediaFileContent(network: Network, postbox: Postbox, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, file: TelegramMediaFile, message: Message) -> Signal<PendingMessageUploadedContentResult, NoError> {
|
||||
let upload = multipartUpload(network: network, postbox: postbox, resource: file.resource, encrypt: message.id.peerId.namespace == Namespaces.Peer.SecretChat)
|
||||
/*|> map { next -> UploadedMediaFileContent in
|
||||
switch next {
|
||||
case let .progress(progress):
|
||||
return .progress(progress)
|
||||
@ -140,5 +164,90 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, file:
|
||||
case let .inputSecretFile(file, size, key):
|
||||
return .content(message, .secretMedia(file, size, key))
|
||||
}
|
||||
}*/
|
||||
var alreadyTransformed = false
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? OutgoingMessageInfoAttribute {
|
||||
if attribute.flags.contains(.transformedMedia) {
|
||||
alreadyTransformed = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let transform: Signal<UploadedMediaTransform, Void>
|
||||
if let transformOutgoingMessageMedia = transformOutgoingMessageMedia, !alreadyTransformed {
|
||||
transform = .single(.pending) |> then(transformOutgoingMessageMedia(postbox, network, file, false)
|
||||
|> mapToSignal { media -> Signal<UploadedMediaTransform, NoError> in
|
||||
return postbox.modify { modifier -> UploadedMediaTransform in
|
||||
if let media = media {
|
||||
if let id = media.id {
|
||||
modifier.updateMedia(id, update: media)
|
||||
modifier.updateMessage(message.id, update: { currentMessage in
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date)
|
||||
}
|
||||
var updatedAttributes = currentMessage.attributes
|
||||
if let index = updatedAttributes.index(where: { $0 is OutgoingMessageInfoAttribute }){
|
||||
let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute
|
||||
updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia]))
|
||||
} else {
|
||||
updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: arc4random64(), flags: [.transformedMedia]))
|
||||
}
|
||||
return StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)
|
||||
})
|
||||
}
|
||||
return .done(media)
|
||||
} else {
|
||||
return .done(file)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
transform = .single(.done(file))
|
||||
}
|
||||
|
||||
let thumbnail: Signal<UploadedMediaThumbnail, NoError> = .single(.pending) |> then(transform
|
||||
|> mapToSignal { media -> Signal<UploadedMediaThumbnail, NoError> in
|
||||
switch media {
|
||||
case .pending:
|
||||
return .single(.pending)
|
||||
case let .done(media):
|
||||
if let media = media as? TelegramMediaFile, let smallestThumbnail = smallestImageRepresentation(media.previewRepresentations) {
|
||||
return uploadedThumbnail(network: network, postbox: postbox, image: smallestThumbnail)
|
||||
|> map { result in
|
||||
return .done(result)
|
||||
}
|
||||
} else {
|
||||
return .single(.done(nil))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return combineLatest(upload, thumbnail)
|
||||
|> mapToSignal { content, media -> Signal<PendingMessageUploadedContentResult, NoError> in
|
||||
switch content {
|
||||
case let .progress(progress):
|
||||
return .single(.progress(progress))
|
||||
case let .inputFile(inputFile):
|
||||
if case let .done(thumbnail) = media {
|
||||
let inputMedia: Api.InputMedia
|
||||
if let thumbnail = thumbnail {
|
||||
inputMedia = Api.InputMedia.inputMediaUploadedThumbDocument(flags: 0, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: message.text, stickers: nil)
|
||||
} else {
|
||||
inputMedia = Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFile(file), caption: message.text, stickers: nil)
|
||||
}
|
||||
return .single(.content(message, .media(inputMedia)))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
case let .inputSecretFile(file, size, key):
|
||||
if case .done = media {
|
||||
return .single(.content(message, .secretMedia(file, size, key)))
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ enum SecretChatOutgoingOperationContents: Coding {
|
||||
case pfsAbortSession(layer: SecretChatSequenceBasedLayer, actionGloballyUniqueId: Int64, rekeySessionId: Int64)
|
||||
case pfsCommitKey(layer: SecretChatSequenceBasedLayer, actionGloballyUniqueId: Int64, rekeySessionId: Int64, keyFingerprint: Int64)
|
||||
case noop(layer: SecretChatSequenceBasedLayer, actionGloballyUniqueId: Int64)
|
||||
case setMessageAutoremoveTimeout(layer: SecretChatLayer, actionGloballyUniqueId: Int64, timeout: Int32)
|
||||
case setMessageAutoremoveTimeout(layer: SecretChatLayer, actionGloballyUniqueId: Int64, timeout: Int32, messageId: MessageId)
|
||||
case terminate
|
||||
|
||||
init(decoder: Decoder) {
|
||||
@ -144,7 +144,7 @@ enum SecretChatOutgoingOperationContents: Coding {
|
||||
case SecretChatOutgoingOperationValue.noop.rawValue:
|
||||
self = .noop(layer: SecretChatSequenceBasedLayer(rawValue: decoder.decodeInt32ForKey("l"))!, actionGloballyUniqueId: decoder.decodeInt64ForKey("i"))
|
||||
case SecretChatOutgoingOperationValue.setMessageAutoremoveTimeout.rawValue:
|
||||
self = .setMessageAutoremoveTimeout(layer: SecretChatLayer(rawValue: decoder.decodeInt32ForKey("l"))!, actionGloballyUniqueId: decoder.decodeInt64ForKey("i"), timeout: decoder.decodeInt32ForKey("t"))
|
||||
self = .setMessageAutoremoveTimeout(layer: SecretChatLayer(rawValue: decoder.decodeInt32ForKey("l"))!, actionGloballyUniqueId: decoder.decodeInt64ForKey("i"), timeout: decoder.decodeInt32ForKey("t"), messageId: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("m.p")), namespace: decoder.decodeInt32ForKey("m.n"), id: decoder.decodeInt32ForKey("m.i")))
|
||||
case SecretChatOutgoingOperationValue.terminate.rawValue:
|
||||
self = .terminate
|
||||
default:
|
||||
@ -229,11 +229,14 @@ enum SecretChatOutgoingOperationContents: Coding {
|
||||
encoder.encodeInt32(SecretChatOutgoingOperationValue.noop.rawValue, forKey: "r")
|
||||
encoder.encodeInt32(layer.rawValue, forKey: "l")
|
||||
encoder.encodeInt64(actionGloballyUniqueId, forKey: "i")
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout):
|
||||
case let .setMessageAutoremoveTimeout(layer, actionGloballyUniqueId, timeout, messageId):
|
||||
encoder.encodeInt32(SecretChatOutgoingOperationValue.setMessageAutoremoveTimeout.rawValue, forKey: "r")
|
||||
encoder.encodeInt32(layer.rawValue, forKey: "l")
|
||||
encoder.encodeInt64(actionGloballyUniqueId, forKey: "i")
|
||||
encoder.encodeInt32(timeout, forKey: "t")
|
||||
encoder.encodeInt64(messageId.peerId.toInt64(), forKey: "m.p")
|
||||
encoder.encodeInt32(messageId.namespace, forKey: "m.n")
|
||||
encoder.encodeInt32(messageId.id, forKey: "m.i")
|
||||
case .terminate:
|
||||
encoder.encodeInt32(SecretChatOutgoingOperationValue.terminate.rawValue, forKey: "r")
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public func setSecretChatMessageAutoremoveTimeoutInteractively(account: Account,
|
||||
modifier.setPeerChatState(peerId, state: updatedState)
|
||||
}
|
||||
|
||||
enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: [.message(text: "", attributes: [], media: TelegramMediaAction(action: TelegramMediaActionType.messageAutoremoveTimeoutUpdated(timeout == nil ? 0 : timeout!)), replyToMessageId: nil)])
|
||||
enqueueMessages(modifier: modifier, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], media: TelegramMediaAction(action: TelegramMediaActionType.messageAutoremoveTimeoutUpdated(timeout == nil ? 0 : timeout!)), replyToMessageId: nil))])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
TelegramCore/SingleMessageView.swift
Normal file
100
TelegramCore/SingleMessageView.swift
Normal file
@ -0,0 +1,100 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
public func singleMessageView(account: Account, messageId: MessageId, loadIfNotExists: Bool) -> Signal<MessageView, NoError> {
|
||||
return Signal { subscriber in
|
||||
let loadedMessage = account.postbox.modify { modifier -> Signal<Void, NoError> in
|
||||
if modifier.getMessage(messageId) == nil, loadIfNotExists {
|
||||
return fetchMessage(modifier: modifier, account: account, messageId: messageId)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} |> switchToLatest
|
||||
|
||||
let disposable = loadedMessage.start()
|
||||
let viewDisposable = account.postbox.messageView(messageId).start(next: { view in
|
||||
subscriber.putNext(view)
|
||||
})
|
||||
|
||||
return ActionDisposable {
|
||||
disposable.dispose()
|
||||
viewDisposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchMessage(modifier: Modifier, account: Account, messageId: MessageId) -> Signal<Void, NoError> {
|
||||
if let peer = modifier.getPeer(messageId.peerId) {
|
||||
var signal: Signal<Api.messages.Messages, MTRpcError>?
|
||||
if messageId.peerId.namespace == Namespaces.Peer.CloudUser || messageId.peerId.namespace == Namespaces.Peer.CloudGroup {
|
||||
signal = account.network.request(Api.functions.messages.getMessages(id: [messageId.id]))
|
||||
} else if messageId.peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
if let inputChannel = apiInputChannel(peer) {
|
||||
signal = account.network.request(Api.functions.channels.getMessages(channel: inputChannel, id: [messageId.id]))
|
||||
}
|
||||
}
|
||||
if let signal = signal {
|
||||
return signal
|
||||
|> `catch` { _ -> Signal<Api.messages.Messages, NoError> in
|
||||
return .single(.messages(messages: [], chats: [], users: []))
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||
return account.postbox.modify { modifier -> Void in
|
||||
let apiMessages: [Api.Message]
|
||||
let apiChats: [Api.Chat]
|
||||
let apiUsers: [Api.User]
|
||||
switch result {
|
||||
case let .messages(messages, chats, users):
|
||||
apiMessages = messages
|
||||
apiChats = chats
|
||||
apiUsers = users
|
||||
case let .messagesSlice(_, messages, chats, users):
|
||||
apiMessages = messages
|
||||
apiChats = chats
|
||||
apiUsers = users
|
||||
case let .channelMessages(_, _, _, messages, chats, users):
|
||||
apiMessages = messages
|
||||
apiChats = chats
|
||||
apiUsers = users
|
||||
}
|
||||
|
||||
var peers: [PeerId: Peer] = [:]
|
||||
|
||||
for user in apiUsers {
|
||||
if let user = TelegramUser.merge(modifier.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
peers[user.id] = user
|
||||
}
|
||||
}
|
||||
|
||||
for chat in apiChats {
|
||||
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers[groupOrChannel.id] = groupOrChannel
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(modifier: modifier, peers: Array(peers.values), update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
|
||||
for message in apiMessages {
|
||||
if let message = StoreMessage(apiMessage: message) {
|
||||
let _ = modifier.addMessages([message], location: .Random)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
@ -245,12 +245,43 @@ public final class TelegramMediaFile: Media, Equatable {
|
||||
}
|
||||
|
||||
public func isEqual(_ other: Media) -> Bool {
|
||||
if let other = other as? TelegramMediaFile {
|
||||
if self.fileId == other.fileId {
|
||||
guard let other = other as? TelegramMediaFile else {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.fileId != other.fileId {
|
||||
return false
|
||||
}
|
||||
|
||||
if !self.resource.isEqual(to: other.resource) {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.previewRepresentations != other.previewRepresentations {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.size != other.size {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.mimeType != other.mimeType {
|
||||
return false
|
||||
}
|
||||
|
||||
/*if self.attributes != other.attributes {
|
||||
return false
|
||||
}*/
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public func withUpdatedSize(_ size: Int?) -> TelegramMediaFile {
|
||||
return TelegramMediaFile(fileId: self.fileId, resource: self.resource, previewRepresentations: self.previewRepresentations, mimeType: self.mimeType, size: size, attributes: self.attributes)
|
||||
}
|
||||
return false
|
||||
|
||||
public func withUpdatedPreviewRepresentations(_ previewRepresentations: [TelegramMediaImageRepresentation]) -> TelegramMediaFile {
|
||||
return TelegramMediaFile(fileId: self.fileId, resource: self.resource, previewRepresentations: previewRepresentations, mimeType: self.mimeType, size: self.size, attributes: self.attributes)
|
||||
}
|
||||
}
|
||||
|
||||
|
40
TelegramCore/UpdateContactName.swift
Normal file
40
TelegramCore/UpdateContactName.swift
Normal file
@ -0,0 +1,40 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
public enum UpdateContactNameError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func updateContactName(account: Account, peerId: PeerId, firstName: String, lastName: String) -> Signal<Void, UpdateContactNameError> {
|
||||
return account.postbox.modify { modifier -> Signal<Void, UpdateContactNameError> in
|
||||
if let peer = modifier.getPeer(peerId) as? TelegramUser, let phone = peer.phone, !phone.isEmpty {
|
||||
return account.network.request(Api.functions.contacts.importContacts(contacts: [Api.InputContact.inputPhoneContact(clientId: 1, phone: phone, firstName: firstName, lastName: lastName)], replace: .boolFalse))
|
||||
|> mapError { _ -> UpdateContactNameError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, UpdateContactNameError> in
|
||||
return account.postbox.modify { modifier -> Void in
|
||||
switch result {
|
||||
case let .importedContacts(_, _, users):
|
||||
if let first = users.first {
|
||||
let user = TelegramUser(user: first)
|
||||
updatePeers(modifier: modifier, peers: [user], update: { _, updated in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
} |> mapError { _ -> UpdateContactNameError in return .generic }
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
} |> mapError { _ -> UpdateContactNameError in return .generic } |> switchToLatest
|
||||
}
|
25
TelegramCore/WebpagePreview.swift
Normal file
25
TelegramCore/WebpagePreview.swift
Normal file
@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import MtProtoKitDynamic
|
||||
#endif
|
||||
|
||||
public func webpagePreview(account: Account, url: String) -> Signal<TelegramMediaWebpage?, NoError> {
|
||||
return account.network.request(Api.functions.messages.getWebPagePreview(message: url))
|
||||
|> `catch` { _ -> Signal<Api.MessageMedia, NoError> in
|
||||
return .single(.messageMediaEmpty)
|
||||
}
|
||||
|> map { result -> TelegramMediaWebpage? in
|
||||
switch result {
|
||||
case let .messageMediaWebPage(webpage):
|
||||
return telegramMediaWebpageFromApiWebpage(webpage)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user