mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Paid media improvements
This commit is contained in:
parent
813a913bca
commit
e47b5a89ef
@ -12266,6 +12266,8 @@ Sorry for the inconvenience.";
|
|||||||
"Stars.Intro.Transaction.FragmentTopUp.Subtitle" = "via Fragment";
|
"Stars.Intro.Transaction.FragmentTopUp.Subtitle" = "via Fragment";
|
||||||
"Stars.Intro.Transaction.FragmentWithdrawal.Title" = "Withdrawal";
|
"Stars.Intro.Transaction.FragmentWithdrawal.Title" = "Withdrawal";
|
||||||
"Stars.Intro.Transaction.FragmentWithdrawal.Subtitle" = "via Fragment";
|
"Stars.Intro.Transaction.FragmentWithdrawal.Subtitle" = "via Fragment";
|
||||||
|
"Stars.Intro.Transaction.TelegramAds.Title" = "Withdrawal";
|
||||||
|
"Stars.Intro.Transaction.TelegramAds.Subtitle" = "via Telegram Ads";
|
||||||
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
|
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
|
||||||
"Stars.Intro.Transaction.Refund" = "Refund";
|
"Stars.Intro.Transaction.Refund" = "Refund";
|
||||||
|
|
||||||
@ -12310,9 +12312,19 @@ Sorry for the inconvenience.";
|
|||||||
"Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment";
|
"Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment";
|
||||||
"Stars.Transaction.FragmentWithdrawal.Title" = "Stars Withdrawal";
|
"Stars.Transaction.FragmentWithdrawal.Title" = "Stars Withdrawal";
|
||||||
"Stars.Transaction.FragmentWithdrawal.Subtitle" = "Fragment";
|
"Stars.Transaction.FragmentWithdrawal.Subtitle" = "Fragment";
|
||||||
|
"Stars.Transaction.TelegramAds.Title" = "Stars Withdrawal";
|
||||||
|
"Stars.Transaction.TelegramAds.Subtitle" = "Telegram Ads";
|
||||||
"Stars.Transaction.Unsupported.Title" = "Unsupported";
|
"Stars.Transaction.Unsupported.Title" = "Unsupported";
|
||||||
"Stars.Transaction.Refund" = "Refund";
|
"Stars.Transaction.Refund" = "Refund";
|
||||||
|
|
||||||
|
"Stars.Transaction.Photos_1" = "%@ Photo";
|
||||||
|
"Stars.Transaction.Photos_any" = "%@ Photos";
|
||||||
|
"Stars.Transaction.Videos_1" = "%@ Video";
|
||||||
|
"Stars.Transaction.Videos_any" = "%@ Videos";
|
||||||
|
"Stars.Transaction.SinglePhoto" = "Photo";
|
||||||
|
"Stars.Transaction.SingleVideo" = "Video";
|
||||||
|
"Stars.Transaction.MediaAnd" = "%1$@ and %2$@";
|
||||||
|
|
||||||
"Stars.Transfer.Title" = "Confirm Your Purchase";
|
"Stars.Transfer.Title" = "Confirm Your Purchase";
|
||||||
"Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?";
|
"Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?";
|
||||||
"Stars.Transfer.Info.Stars_1" = "%@ Star";
|
"Stars.Transfer.Info.Stars_1" = "%@ Star";
|
||||||
@ -12329,6 +12341,14 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Stars.Transfer.Unavailable" = "Sorry, no star purchases are available from your country.";
|
"Stars.Transfer.Unavailable" = "Sorry, no star purchases are available from your country.";
|
||||||
|
|
||||||
|
"Stars.Transfer.Photos_1" = "%@ photo";
|
||||||
|
"Stars.Transfer.Photos_any" = "%@ photos";
|
||||||
|
"Stars.Transfer.Videos_1" = "%@ video";
|
||||||
|
"Stars.Transfer.Videos_any" = "%@ videos";
|
||||||
|
"Stars.Transfer.SinglePhoto" = "photo";
|
||||||
|
"Stars.Transfer.SingleVideo" = "video";
|
||||||
|
"Stars.Transfer.MediaAnd" = "%1$@ and %2$@";
|
||||||
|
|
||||||
"Settings.Stars" = "Your Stars";
|
"Settings.Stars" = "Your Stars";
|
||||||
|
|
||||||
"Chat.MessageEffectMenu.TitleAddEffect" = "Add an animated effect";
|
"Chat.MessageEffectMenu.TitleAddEffect" = "Add an animated effect";
|
||||||
|
@ -1063,6 +1063,7 @@ public enum FileMediaResourceMediaStatus: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public protocol ChatMessageItemNodeProtocol: ListViewItemNode {
|
public protocol ChatMessageItemNodeProtocol: ListViewItemNode {
|
||||||
|
func makeProgress() -> Promise<Bool>?
|
||||||
func targetReactionView(value: MessageReaction.Reaction) -> UIView?
|
func targetReactionView(value: MessageReaction.Reaction) -> UIView?
|
||||||
func targetForStoryTransition(id: StoryId) -> UIView?
|
func targetForStoryTransition(id: StoryId) -> UIView?
|
||||||
func contentFrame() -> CGRect
|
func contentFrame() -> CGRect
|
||||||
|
@ -67,7 +67,7 @@ public func messageFileMediaResourceStatus(context: AccountContext, file: Telegr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let pendingStatus = pendingStatus {
|
} else if let pendingStatus = pendingStatus {
|
||||||
mediaStatus = .fetchStatus(.Fetching(isActive: pendingStatus.isRunning, progress: pendingStatus.progress))
|
mediaStatus = .fetchStatus(.Fetching(isActive: pendingStatus.isRunning, progress: pendingStatus.progress.progress))
|
||||||
} else {
|
} else {
|
||||||
mediaStatus = .fetchStatus(EngineMediaResource.FetchStatus(resourceStatus))
|
mediaStatus = .fetchStatus(EngineMediaResource.FetchStatus(resourceStatus))
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ public func messageImageMediaResourceStatus(context: AccountContext, image: Tele
|
|||||||
|> map { resourceStatus, pendingStatus -> FileMediaResourceStatus in
|
|> map { resourceStatus, pendingStatus -> FileMediaResourceStatus in
|
||||||
let mediaStatus: FileMediaResourceMediaStatus
|
let mediaStatus: FileMediaResourceMediaStatus
|
||||||
if let pendingStatus = pendingStatus {
|
if let pendingStatus = pendingStatus {
|
||||||
mediaStatus = .fetchStatus(.Fetching(isActive: pendingStatus.isRunning, progress: pendingStatus.progress))
|
mediaStatus = .fetchStatus(.Fetching(isActive: pendingStatus.isRunning, progress: pendingStatus.progress.progress))
|
||||||
} else {
|
} else {
|
||||||
mediaStatus = .fetchStatus(EngineMediaResource.FetchStatus(resourceStatus))
|
mediaStatus = .fetchStatus(EngineMediaResource.FetchStatus(resourceStatus))
|
||||||
}
|
}
|
||||||
|
@ -1077,7 +1077,7 @@ private final class DemoSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
//TODO:localize
|
|
||||||
availableItems[.messageEffects] = DemoPagerComponent.Item(
|
availableItems[.messageEffects] = DemoPagerComponent.Item(
|
||||||
AnyComponentWithIdentity(
|
AnyComponentWithIdentity(
|
||||||
id: PremiumDemoScreen.Subject.messageEffects,
|
id: PremiumDemoScreen.Subject.messageEffects,
|
||||||
@ -1193,8 +1193,7 @@ private final class DemoSheetContent: CombinedComponent {
|
|||||||
case .folderTags:
|
case .folderTags:
|
||||||
text = strings.Premium_FolderTagsStandaloneInfo
|
text = strings.Premium_FolderTagsStandaloneInfo
|
||||||
case .messageEffects:
|
case .messageEffects:
|
||||||
//TODO:localize
|
text = strings.Premium_MessageEffectsInfo
|
||||||
text = "Add over 500 animated effects to private messages."
|
|
||||||
default:
|
default:
|
||||||
text = ""
|
text = ""
|
||||||
}
|
}
|
||||||
|
@ -254,8 +254,8 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_PremiumBotTopUp_Title
|
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_PremiumBotTopUp_Title
|
||||||
itemSubtitle = item.presentationData.strings.Stars_Intro_Transaction_PremiumBotTopUp_Subtitle
|
itemSubtitle = item.presentationData.strings.Stars_Intro_Transaction_PremiumBotTopUp_Subtitle
|
||||||
case .ads:
|
case .ads:
|
||||||
itemTitle = "Withdrawal"
|
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_TelegramAds_Title
|
||||||
itemSubtitle = "via Telegram Ads"
|
itemSubtitle = item.presentationData.strings.Stars_Intro_Transaction_TelegramAds_Subtitle
|
||||||
case .unsupported:
|
case .unsupported:
|
||||||
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_Unsupported_Title
|
itemTitle = item.presentationData.strings.Stars_Intro_Transaction_Unsupported_Title
|
||||||
itemSubtitle = nil
|
itemSubtitle = nil
|
||||||
|
@ -153,7 +153,13 @@ private func areResourcesEqual(_ lhs: MediaResource, _ rhs: MediaResource) -> Bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func findMediaResource(media: Media, previousMedia: Media?, resource: MediaResource) -> TelegramMediaResource? {
|
private func findMediaResource(media: Media, previousMedia: Media?, resource: MediaResource) -> TelegramMediaResource? {
|
||||||
if let image = media as? TelegramMediaImage {
|
if let paidContent = media as? TelegramMediaPaidContent {
|
||||||
|
for case let .full(fullMedia) in paidContent.extendedMedia {
|
||||||
|
if let resource = findMediaResource(media: fullMedia, previousMedia: previousMedia, resource: resource) {
|
||||||
|
return resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let image = media as? TelegramMediaImage {
|
||||||
for representation in image.representations {
|
for representation in image.representations {
|
||||||
if let updatedResource = representation.resource as? CloudPhotoSizeMediaResource, let previousResource = resource as? CloudPhotoSizeMediaResource {
|
if let updatedResource = representation.resource as? CloudPhotoSizeMediaResource, let previousResource = resource as? CloudPhotoSizeMediaResource {
|
||||||
if updatedResource.photoId == previousResource.photoId && updatedResource.sizeSpec == previousResource.sizeSpec {
|
if updatedResource.photoId == previousResource.photoId && updatedResource.sizeSpec == previousResource.sizeSpec {
|
||||||
|
@ -23,8 +23,18 @@ struct PendingMessageUploadedContentAndReuploadInfo {
|
|||||||
let cacheReferenceKey: CachedSentMediaReferenceKey?
|
let cacheReferenceKey: CachedSentMediaReferenceKey?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PendingMessageUploadedContentProgress {
|
||||||
|
let progress: Float
|
||||||
|
let mediaProgress: [MediaId: Float]
|
||||||
|
|
||||||
|
init(progress: Float, mediaProgress: [MediaId: Float] = [:]) {
|
||||||
|
self.progress = progress
|
||||||
|
self.mediaProgress = mediaProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum PendingMessageUploadedContentResult {
|
enum PendingMessageUploadedContentResult {
|
||||||
case progress(Float)
|
case progress(PendingMessageUploadedContentProgress)
|
||||||
case content(PendingMessageUploadedContentAndReuploadInfo)
|
case content(PendingMessageUploadedContentAndReuploadInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +100,7 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po
|
|||||||
} else if let media = media.first as? TelegramMediaStory {
|
} else if let media = media.first as? TelegramMediaStory {
|
||||||
return .signal(postbox.transaction { transaction -> PendingMessageUploadedContentResult in
|
return .signal(postbox.transaction { transaction -> PendingMessageUploadedContentResult in
|
||||||
guard let inputPeer = transaction.getPeer(media.storyId.peerId).flatMap(apiInputPeer) else {
|
guard let inputPeer = transaction.getPeer(media.storyId.peerId).flatMap(apiInputPeer) else {
|
||||||
return .progress(0.0)
|
return .progress(PendingMessageUploadedContentProgress(progress: 0.0))
|
||||||
}
|
}
|
||||||
return .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaStory(peer: inputPeer, id: media.storyId.id), ""), reuploadInfo: nil, cacheReferenceKey: nil))
|
return .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaStory(peer: inputPeer, id: media.storyId.id), ""), reuploadInfo: nil, cacheReferenceKey: nil))
|
||||||
}
|
}
|
||||||
@ -121,7 +131,12 @@ func messageContentToUpload(accountPeerId: PeerId, network: Network, postbox: Po
|
|||||||
func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute], mediaReference: AnyMediaReference?) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
|
func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, isGrouped: Bool, passFetchProgress: Bool, forceNoBigParts: Bool, peerId: PeerId, media: Media, text: String, autoremoveMessageAttribute: AutoremoveTimeoutMessageAttribute?, autoclearMessageAttribute: AutoclearTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute], mediaReference: AnyMediaReference?) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? {
|
||||||
if let paidContent = media as? TelegramMediaPaidContent {
|
if let paidContent = media as? TelegramMediaPaidContent {
|
||||||
var signals: [Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>] = []
|
var signals: [Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>] = []
|
||||||
|
var mediaIds: [MediaId] = []
|
||||||
for case let .full(media) in paidContent.extendedMedia {
|
for case let .full(media) in paidContent.extendedMedia {
|
||||||
|
guard let id = media.id else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mediaIds.append(id)
|
||||||
if let image = media as? TelegramMediaImage {
|
if let image = media as? TelegramMediaImage {
|
||||||
signals.append(uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, forceReupload: forceReupload, isGrouped: false, peerId: peerId, image: image, messageId: messageId, text: "", attributes: [], autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, auxiliaryMethods: auxiliaryMethods))
|
signals.append(uploadedMediaImageContent(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, forceReupload: forceReupload, isGrouped: false, peerId: peerId, image: image, messageId: messageId, text: "", attributes: [], autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, auxiliaryMethods: auxiliaryMethods))
|
||||||
} else if let file = media as? TelegramMediaFile {
|
} else if let file = media as? TelegramMediaFile {
|
||||||
@ -132,13 +147,16 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
|
|||||||
|> map { results -> PendingMessageUploadedContentResult in
|
|> map { results -> PendingMessageUploadedContentResult in
|
||||||
var currentProgress: Float = 0.0
|
var currentProgress: Float = 0.0
|
||||||
var media: [Api.InputMedia] = []
|
var media: [Api.InputMedia] = []
|
||||||
for result in results {
|
var mediaProgress: [MediaId: Float] = [:]
|
||||||
|
for (mediaId, result) in zip(mediaIds, results) {
|
||||||
switch result {
|
switch result {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
currentProgress += progress
|
currentProgress += progress.progress
|
||||||
|
mediaProgress[mediaId] = progress.progress
|
||||||
case let .content(content):
|
case let .content(content):
|
||||||
if case let .media(resultMedia, _) = content.content {
|
if case let .media(resultMedia, _) = content.content {
|
||||||
media.append(resultMedia)
|
media.append(resultMedia)
|
||||||
|
mediaProgress[mediaId] = 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +171,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
|
|||||||
cacheReferenceKey: nil
|
cacheReferenceKey: nil
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
return .progress(normalizedProgress)
|
return .progress(PendingMessageUploadedContentProgress(progress: normalizedProgress, mediaProgress: mediaProgress))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,7 +482,7 @@ if "".isEmpty {
|
|||||||
flags |= 1 << 1
|
flags |= 1 << 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .single(.progress(1.0))
|
return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0)))
|
||||||
|> then(
|
|> then(
|
||||||
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||||
)
|
)
|
||||||
@ -500,13 +518,43 @@ if "".isEmpty {
|
|||||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: nil, psaType: nil, flags: [])
|
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: nil, psaType: nil, flags: [])
|
||||||
}
|
}
|
||||||
var updatedAttributes = currentMessage.attributes
|
var updatedAttributes = currentMessage.attributes
|
||||||
|
|
||||||
|
var markTransformedMedia = true
|
||||||
|
var updatedMedia = currentMessage.media
|
||||||
|
if let paidContent = updatedMedia.first(where: { $0 is TelegramMediaPaidContent }) as? TelegramMediaPaidContent {
|
||||||
|
var extendedMedia = paidContent.extendedMedia
|
||||||
|
if let index = extendedMedia.firstIndex(where: { media in
|
||||||
|
if case let .full(fullMedia) = media, fullMedia.id == id {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
extendedMedia[index] = .full(media: media)
|
||||||
|
}
|
||||||
|
updatedMedia = [TelegramMediaPaidContent(amount: paidContent.amount, extendedMedia: extendedMedia)]
|
||||||
|
|
||||||
|
if extendedMedia.contains(where: { media in
|
||||||
|
if case .preview = media {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
markTransformedMedia = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if markTransformedMedia {
|
||||||
if let index = updatedAttributes.firstIndex(where: { $0 is OutgoingMessageInfoAttribute }){
|
if let index = updatedAttributes.firstIndex(where: { $0 is OutgoingMessageInfoAttribute }){
|
||||||
let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute
|
let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute
|
||||||
updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia]))
|
updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia]))
|
||||||
} else {
|
} else {
|
||||||
updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||||
}
|
}
|
||||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media))
|
}
|
||||||
|
|
||||||
|
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return .done(media)
|
return .done(media)
|
||||||
@ -526,7 +574,7 @@ if "".isEmpty {
|
|||||||
|> mapToSignal { transformResult -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { transformResult -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
switch transformResult {
|
switch transformResult {
|
||||||
case .pending:
|
case .pending:
|
||||||
return .single(.progress(0.0))
|
return .single(.progress(PendingMessageUploadedContentProgress(progress: 0.0)))
|
||||||
case let .done(transformedMedia):
|
case let .done(transformedMedia):
|
||||||
let transformedImage = (transformedMedia as? TelegramMediaImage) ?? image
|
let transformedImage = (transformedMedia as? TelegramMediaImage) ?? image
|
||||||
guard let largestRepresentation = largestImageRepresentation(transformedImage.representations) else {
|
guard let largestRepresentation = largestImageRepresentation(transformedImage.representations) else {
|
||||||
@ -543,7 +591,7 @@ if "".isEmpty {
|
|||||||
|> mapToSignal { next -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { next -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
switch next {
|
switch next {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
return .single(.progress(progress))
|
return .single(.progress(PendingMessageUploadedContentProgress(progress: progress)))
|
||||||
case let .inputFile(file):
|
case let .inputFile(file):
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
var ttlSeconds: Int32?
|
var ttlSeconds: Int32?
|
||||||
@ -770,7 +818,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return .single(.progress(1.0))
|
return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0)))
|
||||||
|> then(
|
|> then(
|
||||||
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||||
)
|
)
|
||||||
@ -844,13 +892,43 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: nil, psaType: nil, flags: [])
|
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: nil, psaType: nil, flags: [])
|
||||||
}
|
}
|
||||||
var updatedAttributes = currentMessage.attributes
|
var updatedAttributes = currentMessage.attributes
|
||||||
|
|
||||||
|
var markTransformedMedia = true
|
||||||
|
var updatedMedia = currentMessage.media
|
||||||
|
if let paidContent = updatedMedia.first(where: { $0 is TelegramMediaPaidContent }) as? TelegramMediaPaidContent {
|
||||||
|
var extendedMedia = paidContent.extendedMedia
|
||||||
|
if let index = extendedMedia.firstIndex(where: { media in
|
||||||
|
if case let .full(fullMedia) = media, fullMedia.id == id {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
extendedMedia[index] = .full(media: media)
|
||||||
|
}
|
||||||
|
updatedMedia = [TelegramMediaPaidContent(amount: paidContent.amount, extendedMedia: extendedMedia)]
|
||||||
|
|
||||||
|
if extendedMedia.contains(where: { media in
|
||||||
|
if case .preview = media {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
markTransformedMedia = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if markTransformedMedia {
|
||||||
if let index = updatedAttributes.firstIndex(where: { $0 is OutgoingMessageInfoAttribute }){
|
if let index = updatedAttributes.firstIndex(where: { $0 is OutgoingMessageInfoAttribute }){
|
||||||
let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute
|
let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute
|
||||||
updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia]))
|
updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia]))
|
||||||
} else {
|
} else {
|
||||||
updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil, bubbleUpEmojiOrStickersets: []))
|
||||||
}
|
}
|
||||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media))
|
}
|
||||||
|
|
||||||
|
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: updatedMedia))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return .done(media)
|
return .done(media)
|
||||||
@ -901,7 +979,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
|> mapToSignal { content, fileAndThumbnailResult, resourceStatus -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
|> mapToSignal { content, fileAndThumbnailResult, resourceStatus -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||||
guard let content = content else {
|
guard let content = content else {
|
||||||
if let resourceStatus = resourceStatus, case let .Fetching(_, progress) = resourceStatus {
|
if let resourceStatus = resourceStatus, case let .Fetching(_, progress) = resourceStatus {
|
||||||
return .single(.progress(progress * 0.33))
|
return .single(.progress(PendingMessageUploadedContentProgress(progress: progress * 0.33)))
|
||||||
}
|
}
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -911,7 +989,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
|||||||
if passFetchProgress {
|
if passFetchProgress {
|
||||||
progress = 0.33 + progress * 0.67
|
progress = 0.33 + progress * 0.67
|
||||||
}
|
}
|
||||||
return .single(.progress(progress))
|
return .single(.progress(PendingMessageUploadedContentProgress(progress: progress)))
|
||||||
case let .inputFile(inputFile):
|
case let .inputFile(inputFile):
|
||||||
if case let .done(file, thumbnail) = fileAndThumbnailResult {
|
if case let .done(file, thumbnail) = fileAndThumbnailResult {
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
|
@ -54,7 +54,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox,
|
|||||||
let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError>
|
let uploadedMedia: Signal<PendingMessageUploadedContentResult?, NoError>
|
||||||
switch media {
|
switch media {
|
||||||
case .keep:
|
case .keep:
|
||||||
uploadedMedia = .single(.progress(0.0))
|
uploadedMedia = .single(.progress(PendingMessageUploadedContentProgress(progress: 0.0)))
|
||||||
|> then(.single(nil))
|
|> then(.single(nil))
|
||||||
case let .update(media):
|
case let .update(media):
|
||||||
let generateUploadSignal: (Bool) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? = { forceReupload in
|
let generateUploadSignal: (Bool) -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>? = { forceReupload in
|
||||||
@ -69,12 +69,12 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox,
|
|||||||
return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, passFetchProgress: false, forceNoBigParts: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: attributes, mediaReference: nil)
|
return mediaContentToUpload(accountPeerId: accountPeerId, network: network, postbox: postbox, auxiliaryMethods: stateManager.auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, revalidationContext: mediaReferenceRevalidationContext, forceReupload: forceReupload, isGrouped: false, passFetchProgress: false, forceNoBigParts: false, peerId: messageId.peerId, media: augmentedMedia, text: "", autoremoveMessageAttribute: nil, autoclearMessageAttribute: nil, messageId: nil, attributes: attributes, mediaReference: nil)
|
||||||
}
|
}
|
||||||
if let uploadSignal = generateUploadSignal(forceReupload) {
|
if let uploadSignal = generateUploadSignal(forceReupload) {
|
||||||
uploadedMedia = .single(.progress(0.027))
|
uploadedMedia = .single(.progress(PendingMessageUploadedContentProgress(progress: 0.027)))
|
||||||
|> then(uploadSignal)
|
|> then(uploadSignal)
|
||||||
|> map { result -> PendingMessageUploadedContentResult? in
|
|> map { result -> PendingMessageUploadedContentResult? in
|
||||||
switch result {
|
switch result {
|
||||||
case let .progress(value):
|
case let .progress(value):
|
||||||
return .progress(max(value, 0.027))
|
return .progress(PendingMessageUploadedContentProgress(progress: max(value.progress, 0.027)))
|
||||||
case let .content(content):
|
case let .content(content):
|
||||||
return .content(content)
|
return .content(content)
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ private func requestEditMessageInternal(accountPeerId: PeerId, postbox: Postbox,
|
|||||||
if let uploadedMediaResult = uploadedMediaResult {
|
if let uploadedMediaResult = uploadedMediaResult {
|
||||||
switch uploadedMediaResult {
|
switch uploadedMediaResult {
|
||||||
case let .progress(value):
|
case let .progress(value):
|
||||||
return .single(.progress(value))
|
return .single(.progress(value.progress))
|
||||||
case let .content(content):
|
case let .content(content):
|
||||||
pendingMediaContent = content.content
|
pendingMediaContent = content.content
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ public func standaloneSendEnqueueMessages(
|
|||||||
switch result.result {
|
switch result.result {
|
||||||
case let .progress(value):
|
case let .progress(value):
|
||||||
allDone = false
|
allDone = false
|
||||||
progressSum += value
|
progressSum += value.progress
|
||||||
case let .content(content):
|
case let .content(content):
|
||||||
allResults.append((content, result.media))
|
allResults.append((content, result.media))
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,23 @@ import MtProtoKit
|
|||||||
|
|
||||||
|
|
||||||
public struct PendingMessageStatus: Equatable {
|
public struct PendingMessageStatus: Equatable {
|
||||||
public let isRunning: Bool
|
public struct Progress: Equatable {
|
||||||
public let progress: Float
|
public let progress: Float
|
||||||
|
public let mediaProgress: [MediaId: Float]
|
||||||
|
|
||||||
|
public init(progress: Float, mediaProgress: [MediaId: Float] = [:]) {
|
||||||
|
self.progress = progress
|
||||||
|
self.mediaProgress = mediaProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ contentProgress: PendingMessageUploadedContentProgress) {
|
||||||
|
self.progress = contentProgress.progress
|
||||||
|
self.mediaProgress = contentProgress.mediaProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let isRunning: Bool
|
||||||
|
public let progress: Progress
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum PendingMessageState {
|
private enum PendingMessageState {
|
||||||
@ -364,7 +379,7 @@ public final class PendingMessageManager {
|
|||||||
self.messageContexts[id] = messageContext
|
self.messageContexts[id] = messageContext
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = PendingMessageStatus(isRunning: false, progress: 0.0)
|
let status = PendingMessageStatus(isRunning: false, progress: PendingMessageStatus.Progress(progress: 0.0))
|
||||||
if status != messageContext.status {
|
if status != messageContext.status {
|
||||||
messageContext.status = status
|
messageContext.status = status
|
||||||
for subscriber in messageContext.statusSubscribers.copyItems() {
|
for subscriber in messageContext.statusSubscribers.copyItems() {
|
||||||
@ -618,7 +633,7 @@ public final class PendingMessageManager {
|
|||||||
switch next {
|
switch next {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
if let current = strongSelf.messageContexts[messageId] {
|
if let current = strongSelf.messageContexts[messageId] {
|
||||||
let status = PendingMessageStatus(isRunning: true, progress: progress)
|
let status = PendingMessageStatus(isRunning: true, progress: PendingMessageStatus.Progress(progress: progress))
|
||||||
current.status = status
|
current.status = status
|
||||||
for subscriber in current.statusSubscribers.copyItems() {
|
for subscriber in current.statusSubscribers.copyItems() {
|
||||||
subscriber(current.status, current.error)
|
subscriber(current.status, current.error)
|
||||||
@ -636,7 +651,7 @@ public final class PendingMessageManager {
|
|||||||
private func beginUploadingMessage(messageContext: PendingMessageContext, id: MessageId, threadId: Int64?, groupId: Int64?, uploadSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>) {
|
private func beginUploadingMessage(messageContext: PendingMessageContext, id: MessageId, threadId: Int64?, groupId: Int64?, uploadSignal: Signal<PendingMessageUploadedContentResult, PendingMessageUploadError>) {
|
||||||
messageContext.state = .uploading(groupId: groupId)
|
messageContext.state = .uploading(groupId: groupId)
|
||||||
|
|
||||||
let status = PendingMessageStatus(isRunning: true, progress: 0.0)
|
let status = PendingMessageStatus(isRunning: true, progress: PendingMessageStatus.Progress(progress: 0.0))
|
||||||
messageContext.status = status
|
messageContext.status = status
|
||||||
for subscriber in messageContext.statusSubscribers.copyItems() {
|
for subscriber in messageContext.statusSubscribers.copyItems() {
|
||||||
subscriber(messageContext.status, messageContext.error)
|
subscriber(messageContext.status, messageContext.error)
|
||||||
@ -678,7 +693,7 @@ public final class PendingMessageManager {
|
|||||||
switch next {
|
switch next {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
if let current = strongSelf.messageContexts[id] {
|
if let current = strongSelf.messageContexts[id] {
|
||||||
let status = PendingMessageStatus(isRunning: true, progress: progress)
|
let status = PendingMessageStatus(isRunning: true, progress: PendingMessageStatus.Progress(progress))
|
||||||
current.status = status
|
current.status = status
|
||||||
for subscriber in current.statusSubscribers.copyItems() {
|
for subscriber in current.statusSubscribers.copyItems() {
|
||||||
subscriber(current.status, current.error)
|
subscriber(current.status, current.error)
|
||||||
@ -712,7 +727,7 @@ public final class PendingMessageManager {
|
|||||||
if case let .waitingForUploadToStart(groupId, uploadSignal) = context.state {
|
if case let .waitingForUploadToStart(groupId, uploadSignal) = context.state {
|
||||||
if self.canBeginUploadingMessage(id: contextId, type: context.contentType ?? .media) {
|
if self.canBeginUploadingMessage(id: contextId, type: context.contentType ?? .media) {
|
||||||
context.state = .uploading(groupId: groupId)
|
context.state = .uploading(groupId: groupId)
|
||||||
let status = PendingMessageStatus(isRunning: true, progress: 0.0)
|
let status = PendingMessageStatus(isRunning: true, progress: PendingMessageStatus.Progress(progress: 0.0))
|
||||||
context.status = status
|
context.status = status
|
||||||
for subscriber in context.statusSubscribers.copyItems() {
|
for subscriber in context.statusSubscribers.copyItems() {
|
||||||
subscriber(context.status, context.error)
|
subscriber(context.status, context.error)
|
||||||
@ -734,7 +749,7 @@ public final class PendingMessageManager {
|
|||||||
switch next {
|
switch next {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
if let current = strongSelf.messageContexts[contextId] {
|
if let current = strongSelf.messageContexts[contextId] {
|
||||||
let status = PendingMessageStatus(isRunning: true, progress: progress)
|
let status = PendingMessageStatus(isRunning: true, progress: PendingMessageStatus.Progress(progress))
|
||||||
current.status = status
|
current.status = status
|
||||||
for subscriber in current.statusSubscribers.copyItems() {
|
for subscriber in current.statusSubscribers.copyItems() {
|
||||||
subscriber(context.status, context.error)
|
subscriber(context.status, context.error)
|
||||||
|
@ -1093,7 +1093,7 @@ func _internal_uploadStoryImpl(
|
|||||||
|> mapToSignal { result -> Signal<StoryUploadResult, NoError> in
|
|> mapToSignal { result -> Signal<StoryUploadResult, NoError> in
|
||||||
switch result {
|
switch result {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
return .single(.progress(progress))
|
return .single(.progress(progress.progress))
|
||||||
case let .content(content):
|
case let .content(content):
|
||||||
return postbox.transaction { transaction -> Signal<StoryUploadResult, NoError> in
|
return postbox.transaction { transaction -> Signal<StoryUploadResult, NoError> in
|
||||||
let privacyRules = apiInputPrivacyRules(privacy: privacy, transaction: transaction)
|
let privacyRules = apiInputPrivacyRules(privacy: privacy, transaction: transaction)
|
||||||
@ -1278,7 +1278,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng
|
|||||||
return contentSignal
|
return contentSignal
|
||||||
|> mapToSignal { result -> Signal<StoryUploadResult, NoError> in
|
|> mapToSignal { result -> Signal<StoryUploadResult, NoError> in
|
||||||
if let result = result, case let .progress(progress) = result {
|
if let result = result, case let .progress(progress) = result {
|
||||||
return .single(.progress(progress))
|
return .single(.progress(progress.progress))
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputMedia: Api.InputMedia?
|
let inputMedia: Api.InputMedia?
|
||||||
|
@ -5911,6 +5911,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||||||
item.controllerInteraction.openMessageContextMenu(item.message, true, self, subFrame, nil, nil)
|
item.controllerInteraction.openMessageContextMenu(item.message, true, self, subFrame, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func makeProgress() -> Promise<Bool>? {
|
||||||
|
return self.unlockButtonNode?.makeProgress()
|
||||||
|
}
|
||||||
|
|
||||||
override public func targetReactionView(value: MessageReaction.Reaction) -> UIView? {
|
override public func targetReactionView(value: MessageReaction.Reaction) -> UIView? {
|
||||||
if let result = self.reactionButtonsNode?.reactionTargetView(value: value) {
|
if let result = self.reactionButtonsNode?.reactionTargetView(value: value) {
|
||||||
return result
|
return result
|
||||||
|
@ -473,7 +473,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
updatedPlaybackStatus = combineLatest(messageFileMediaResourceStatus(context: item.context, file: updatedFile, message: EngineMessage(item.message), isRecentActions: item.associatedData.isRecentActions), item.context.account.pendingMessageManager.pendingMessageStatus(item.message.id) |> map { $0.0 })
|
updatedPlaybackStatus = combineLatest(messageFileMediaResourceStatus(context: item.context, file: updatedFile, message: EngineMessage(item.message), isRecentActions: item.associatedData.isRecentActions), item.context.account.pendingMessageManager.pendingMessageStatus(item.message.id) |> map { $0.0 })
|
||||||
|> map { resourceStatus, pendingStatus -> FileMediaResourceStatus in
|
|> map { resourceStatus, pendingStatus -> FileMediaResourceStatus in
|
||||||
if let pendingStatus = pendingStatus {
|
if let pendingStatus = pendingStatus {
|
||||||
var progress = pendingStatus.progress
|
var progress = pendingStatus.progress.progress
|
||||||
if pendingStatus.isRunning {
|
if pendingStatus.isRunning {
|
||||||
progress = max(progress, 0.27)
|
progress = max(progress, 0.27)
|
||||||
}
|
}
|
||||||
|
@ -1475,7 +1475,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
updatedStatusSignal = combineLatest(chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image)), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|
updatedStatusSignal = combineLatest(chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image)), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|
||||||
|> map { resourceStatus, pendingStatus -> (MediaResourceStatus, MediaResourceStatus?) in
|
|> map { resourceStatus, pendingStatus -> (MediaResourceStatus, MediaResourceStatus?) in
|
||||||
if let pendingStatus = pendingStatus {
|
if let pendingStatus = pendingStatus {
|
||||||
let adjustedProgress = max(pendingStatus.progress, 0.027)
|
var progress: Float = pendingStatus.progress.progress
|
||||||
|
if let id = media.id, let mediaProgress = pendingStatus.progress.mediaProgress[id] {
|
||||||
|
progress = mediaProgress
|
||||||
|
}
|
||||||
|
let adjustedProgress = max(progress, 0.027)
|
||||||
return (.Fetching(isActive: pendingStatus.isRunning, progress: adjustedProgress), resourceStatus)
|
return (.Fetching(isActive: pendingStatus.isRunning, progress: adjustedProgress), resourceStatus)
|
||||||
} else {
|
} else {
|
||||||
return (resourceStatus, nil)
|
return (resourceStatus, nil)
|
||||||
@ -1491,7 +1495,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
updatedStatusSignal = combineLatest(messageMediaFileStatus(context: context, messageId: message.id, file: file, adjustForVideoThumbnail: true), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|
updatedStatusSignal = combineLatest(messageMediaFileStatus(context: context, messageId: message.id, file: file, adjustForVideoThumbnail: true), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|
||||||
|> map { resourceStatus, pendingStatus -> (MediaResourceStatus, MediaResourceStatus?) in
|
|> map { resourceStatus, pendingStatus -> (MediaResourceStatus, MediaResourceStatus?) in
|
||||||
if let pendingStatus = pendingStatus {
|
if let pendingStatus = pendingStatus {
|
||||||
let adjustedProgress = max(pendingStatus.progress, 0.027)
|
var progress: Float = pendingStatus.progress.progress
|
||||||
|
if let id = media.id, let mediaProgress = pendingStatus.progress.mediaProgress[id] {
|
||||||
|
progress = mediaProgress
|
||||||
|
}
|
||||||
|
let adjustedProgress = max(progress, 0.027)
|
||||||
return (.Fetching(isActive: pendingStatus.isRunning, progress: adjustedProgress), resourceStatus)
|
return (.Fetching(isActive: pendingStatus.isRunning, progress: adjustedProgress), resourceStatus)
|
||||||
} else {
|
} else {
|
||||||
return (resourceStatus, nil)
|
return (resourceStatus, nil)
|
||||||
|
@ -847,7 +847,7 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
|
|||||||
item.controllerInteraction.activateSwitchInline(peerId, "@\(addressName) \(query)", peerTypes)
|
item.controllerInteraction.activateSwitchInline(peerId, "@\(addressName) \(query)", peerTypes)
|
||||||
}
|
}
|
||||||
case .payment:
|
case .payment:
|
||||||
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
|
item.controllerInteraction.openCheckoutOrReceipt(item.message.id, nil)
|
||||||
case let .urlAuth(url, buttonId):
|
case let .urlAuth(url, buttonId):
|
||||||
item.controllerInteraction.requestMessageActionUrlAuth(url, .message(id: item.message.id, buttonId: buttonId))
|
item.controllerInteraction.requestMessageActionUrlAuth(url, .message(id: item.message.id, buttonId: buttonId))
|
||||||
case .setupPoll:
|
case .setupPoll:
|
||||||
@ -881,6 +881,10 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
|
|||||||
open func openMessageContextMenu() {
|
open func openMessageContextMenu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open func makeProgress() -> Promise<Bool>? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
open func targetReactionView(value: MessageReaction.Reaction) -> UIView? {
|
open func targetReactionView(value: MessageReaction.Reaction) -> UIView? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,8 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
case .automaticPlayback:
|
case .automaticPlayback:
|
||||||
openChatMessageMode = .automaticPlayback
|
openChatMessageMode = .automaticPlayback
|
||||||
}
|
}
|
||||||
let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: openChatMessageMode, mediaIndex: self.mediaIndex))
|
|
||||||
|
let _ = item.controllerInteraction.openMessage(item.message, OpenMessageParams(mode: openChatMessageMode, mediaIndex: self.mediaIndex, progress: self.itemNode?.makeProgress()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.interactiveImageNode.activateAgeRestrictedMedia = { [weak self] in
|
self.interactiveImageNode.activateAgeRestrictedMedia = { [weak self] in
|
||||||
|
@ -239,9 +239,12 @@ public class ChatMessageStarsMediaInfoNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
text = NSMutableAttributedString(string: "Purchased", font: textFont, textColor: .white)
|
text = NSMutableAttributedString(string: "Purchased", font: textFont, textColor: .white)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
if let range = text.string.range(of: "⭐️") {
|
if let range = text.string.range(of: "⭐️") {
|
||||||
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: NSRange(range, in: text.string))
|
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: NSRange(range, in: text.string))
|
||||||
text.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: text.string))
|
text.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: text.string))
|
||||||
|
offset -= 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: arguments.constrainedSize.width, height: arguments.constrainedSize.height), alignment: .natural, cutout: nil, insets: .zero))
|
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: text, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: arguments.constrainedSize.width, height: arguments.constrainedSize.height), alignment: .natural, cutout: nil, insets: .zero))
|
||||||
@ -278,7 +281,7 @@ public class ChatMessageStarsMediaInfoNode: ASDisplayNode {
|
|||||||
|
|
||||||
node.contentBackgroundNode.frame = CGRect(origin: .zero, size: size)
|
node.contentBackgroundNode.frame = CGRect(origin: .zero, size: size)
|
||||||
|
|
||||||
let textFrame = CGRect(origin: CGPoint(x: padding, y: floorToScreenPixels((size.height - textLayout.size.height) / 2.0) + UIScreenPixel), size: textLayout.size)
|
let textFrame = CGRect(origin: CGPoint(x: padding + offset, y: floorToScreenPixels((size.height - textLayout.size.height) / 2.0) + UIScreenPixel), size: textLayout.size)
|
||||||
textNode.textNode.frame = textFrame
|
textNode.textNode.frame = textFrame
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
@ -16,6 +16,7 @@ import AnimationCache
|
|||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
import ChatControllerInteraction
|
import ChatControllerInteraction
|
||||||
|
import HierarchyTrackingLayer
|
||||||
|
|
||||||
public class ChatMessageUnlockMediaNode: ASDisplayNode {
|
public class ChatMessageUnlockMediaNode: ASDisplayNode {
|
||||||
public class Arguments {
|
public class Arguments {
|
||||||
@ -63,11 +64,14 @@ public class ChatMessageUnlockMediaNode: ASDisplayNode {
|
|||||||
private let contentNode: HighlightTrackingButtonNode
|
private let contentNode: HighlightTrackingButtonNode
|
||||||
private let backgroundNode: NavigationBackgroundNode
|
private let backgroundNode: NavigationBackgroundNode
|
||||||
private var textNode: TextNodeWithEntities?
|
private var textNode: TextNodeWithEntities?
|
||||||
|
private var loadingView: LoadingEffectView?
|
||||||
|
|
||||||
private var pressed = { }
|
private var pressed = { }
|
||||||
|
|
||||||
private var absolutePosition: (CGRect, CGSize)?
|
private var absolutePosition: (CGRect, CGSize)?
|
||||||
|
|
||||||
|
private var currentProgressDisposable: Disposable?
|
||||||
|
|
||||||
override public init() {
|
override public init() {
|
||||||
self.contentNode = HighlightTrackingButtonNode()
|
self.contentNode = HighlightTrackingButtonNode()
|
||||||
|
|
||||||
@ -83,10 +87,41 @@ public class ChatMessageUnlockMediaNode: ASDisplayNode {
|
|||||||
self.contentNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
self.contentNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.currentProgressDisposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func buttonPressed() {
|
@objc private func buttonPressed() {
|
||||||
self.pressed()
|
self.pressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeProgress() -> Promise<Bool> {
|
||||||
|
let progress = Promise<Bool>()
|
||||||
|
self.currentProgressDisposable?.dispose()
|
||||||
|
self.currentProgressDisposable = (progress.get()
|
||||||
|
|> distinctUntilChanged
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] hasProgress in
|
||||||
|
guard let self, let loadingView = self.loadingView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
if hasProgress {
|
||||||
|
if loadingView.superview == nil {
|
||||||
|
loadingView.alpha = 0.0
|
||||||
|
self.view.addSubview(loadingView)
|
||||||
|
transition.updateAlpha(layer: loadingView.layer, alpha: 1.0)
|
||||||
|
}
|
||||||
|
} else if loadingView.superview != nil {
|
||||||
|
transition.updateAlpha(layer: loadingView.layer, alpha: 0.0, beginWithCurrentState: true, completion: { finished in
|
||||||
|
if finished {
|
||||||
|
loadingView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return progress
|
||||||
|
}
|
||||||
|
|
||||||
public class func asyncLayout(_ maybeNode: ChatMessageUnlockMediaNode?) -> (_ arguments: Arguments) -> (CGSize, (Bool) -> ChatMessageUnlockMediaNode) {
|
public class func asyncLayout(_ maybeNode: ChatMessageUnlockMediaNode?) -> (_ arguments: Arguments) -> (CGSize, (Bool) -> ChatMessageUnlockMediaNode) {
|
||||||
let textNodeLayout = TextNodeWithEntities.asyncLayout(maybeNode?.textNode)
|
let textNodeLayout = TextNodeWithEntities.asyncLayout(maybeNode?.textNode)
|
||||||
|
|
||||||
@ -139,8 +174,171 @@ public class ChatMessageUnlockMediaNode: ASDisplayNode {
|
|||||||
node.backgroundNode.update(size: size, cornerRadius: size.height / 2.0, transition: .immediate)
|
node.backgroundNode.update(size: size, cornerRadius: size.height / 2.0, transition: .immediate)
|
||||||
node.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
node.contentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
|
|
||||||
|
let loadingView: LoadingEffectView
|
||||||
|
if let current = node.loadingView {
|
||||||
|
loadingView = current
|
||||||
|
} else {
|
||||||
|
loadingView = LoadingEffectView()
|
||||||
|
node.loadingView = loadingView
|
||||||
|
}
|
||||||
|
loadingView.frame = CGRect(origin: .zero, size: size)
|
||||||
|
loadingView.update(color: UIColor.white, rect: CGRect(origin: .zero, size: size))
|
||||||
|
|
||||||
return node
|
return node
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let shadowImage: UIImage? = {
|
||||||
|
UIImage(named: "Stories/PanelGradient")
|
||||||
|
}()
|
||||||
|
|
||||||
|
public final class LoadingEffectView: UIView {
|
||||||
|
let hierarchyTrackingLayer: HierarchyTrackingLayer
|
||||||
|
|
||||||
|
private let maskContentsView: UIView
|
||||||
|
private let maskHighlightNode: LinkHighlightingNode
|
||||||
|
|
||||||
|
private let maskBorderContentsView: UIView
|
||||||
|
private let maskBorderHighlightNode: LinkHighlightingNode
|
||||||
|
|
||||||
|
private let backgroundView: UIImageView
|
||||||
|
private let borderBackgroundView: UIImageView
|
||||||
|
|
||||||
|
private var duration: Double
|
||||||
|
private var gradientWidth: CGFloat
|
||||||
|
|
||||||
|
private var inHierarchy = false
|
||||||
|
private var size: CGSize?
|
||||||
|
|
||||||
|
override public init(frame: CGRect) {
|
||||||
|
self.hierarchyTrackingLayer = HierarchyTrackingLayer()
|
||||||
|
|
||||||
|
self.maskContentsView = UIView()
|
||||||
|
self.maskHighlightNode = LinkHighlightingNode(color: .black)
|
||||||
|
self.maskHighlightNode.useModernPathCalculation = true
|
||||||
|
|
||||||
|
self.maskBorderContentsView = UIView()
|
||||||
|
self.maskBorderHighlightNode = LinkHighlightingNode(color: .black)
|
||||||
|
self.maskBorderHighlightNode.borderOnly = true
|
||||||
|
self.maskBorderHighlightNode.useModernPathCalculation = true
|
||||||
|
|
||||||
|
self.maskBorderContentsView.addSubview(self.maskBorderHighlightNode.view)
|
||||||
|
|
||||||
|
self.backgroundView = UIImageView()
|
||||||
|
self.borderBackgroundView = UIImageView()
|
||||||
|
|
||||||
|
self.gradientWidth = 120.0
|
||||||
|
self.duration = 1.0
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.maskContentsView.mask = self.maskHighlightNode.view
|
||||||
|
self.maskContentsView.addSubview(self.backgroundView)
|
||||||
|
self.addSubview(self.maskContentsView)
|
||||||
|
|
||||||
|
self.maskBorderContentsView.mask = self.maskBorderHighlightNode.view
|
||||||
|
self.maskBorderContentsView.addSubview(self.borderBackgroundView)
|
||||||
|
self.addSubview(self.maskBorderContentsView)
|
||||||
|
|
||||||
|
let generateGradient: (CGFloat) -> UIImage? = { baseAlpha in
|
||||||
|
return generateImage(CGSize(width: self.gradientWidth, height: 16.0), opaque: false, scale: 1.0, rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let foregroundColor = UIColor(white: 1.0, alpha: min(1.0, baseAlpha * 4.0))
|
||||||
|
|
||||||
|
if let shadowImage {
|
||||||
|
UIGraphicsPushContext(context)
|
||||||
|
|
||||||
|
for i in 0 ..< 2 {
|
||||||
|
let shadowFrame = CGRect(origin: CGPoint(x: CGFloat(i) * (size.width * 0.5), y: 0.0), size: CGSize(width: size.width * 0.5, height: size.height))
|
||||||
|
|
||||||
|
context.saveGState()
|
||||||
|
context.translateBy(x: shadowFrame.midX, y: shadowFrame.midY)
|
||||||
|
context.rotate(by: CGFloat(i == 0 ? 1.0 : -1.0) * CGFloat.pi * 0.5)
|
||||||
|
let adjustedRect = CGRect(origin: CGPoint(x: -shadowFrame.height * 0.5, y: -shadowFrame.width * 0.5), size: CGSize(width: shadowFrame.height, height: shadowFrame.width))
|
||||||
|
|
||||||
|
context.clip(to: adjustedRect, mask: shadowImage.cgImage!)
|
||||||
|
context.setFillColor(foregroundColor.cgColor)
|
||||||
|
context.fill(adjustedRect)
|
||||||
|
|
||||||
|
context.restoreGState()
|
||||||
|
}
|
||||||
|
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
}
|
||||||
|
})?.withRenderingMode(.alwaysTemplate)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.backgroundView.image = generateGradient(0.5)
|
||||||
|
self.borderBackgroundView.image = generateGradient(1.0)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.hierarchyTrackingLayer)
|
||||||
|
self.hierarchyTrackingLayer.isInHierarchyUpdated = { [weak self] inHierarchy in
|
||||||
|
guard let self, let size = self.size else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.inHierarchy = inHierarchy
|
||||||
|
self.updateAnimations(size: size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateAnimations(size: CGSize) {
|
||||||
|
if self.inHierarchy {
|
||||||
|
if self.backgroundView.layer.animation(forKey: "shimmer") != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let animation = self.backgroundView.layer.makeAnimation(from: 0.0 as NSNumber, to: (size.width + self.gradientWidth + size.width * 0.0) as NSNumber, keyPath: "position.x", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: self.duration, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: true)
|
||||||
|
animation.repeatCount = Float.infinity
|
||||||
|
self.backgroundView.layer.add(animation, forKey: "shimmer")
|
||||||
|
self.borderBackgroundView.layer.add(animation, forKey: "shimmer")
|
||||||
|
} else {
|
||||||
|
self.backgroundView.layer.removeAllAnimations()
|
||||||
|
self.borderBackgroundView.layer.removeAllAnimations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(color: UIColor, rect: CGRect) {
|
||||||
|
let maskFrame = CGRect(origin: CGPoint(), size: rect.size).insetBy(dx: -4.0, dy: -4.0)
|
||||||
|
|
||||||
|
self.gradientWidth = 260.0
|
||||||
|
self.duration = 1.2
|
||||||
|
|
||||||
|
self.maskContentsView.backgroundColor = .clear
|
||||||
|
self.maskBorderContentsView.backgroundColor = color.withAlphaComponent(0.12)
|
||||||
|
|
||||||
|
// self.backgroundView.alpha = 0.25
|
||||||
|
self.backgroundView.tintColor = color
|
||||||
|
|
||||||
|
self.borderBackgroundView.tintColor = color
|
||||||
|
|
||||||
|
self.maskContentsView.frame = maskFrame
|
||||||
|
self.maskBorderContentsView.frame = maskFrame
|
||||||
|
|
||||||
|
let rectsSet: [CGRect] = [rect]
|
||||||
|
|
||||||
|
self.maskHighlightNode.outerRadius = rect.height / 2.0
|
||||||
|
self.maskHighlightNode.updateRects(rectsSet)
|
||||||
|
self.maskHighlightNode.frame = CGRect(origin: CGPoint(x: -maskFrame.minX, y: -maskFrame.minY), size: CGSize())
|
||||||
|
|
||||||
|
self.maskBorderHighlightNode.outerRadius = rect.height / 2.0
|
||||||
|
self.maskBorderHighlightNode.updateRects(rectsSet)
|
||||||
|
self.maskBorderHighlightNode.frame = CGRect(origin: CGPoint(x: -maskFrame.minX, y: -maskFrame.minY), size: CGSize())
|
||||||
|
|
||||||
|
if self.size != maskFrame.size {
|
||||||
|
self.size = maskFrame.size
|
||||||
|
|
||||||
|
self.backgroundView.frame = CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: maskFrame.height))
|
||||||
|
self.borderBackgroundView.frame = CGRect(origin: CGPoint(x: -self.gradientWidth, y: 0.0), size: CGSize(width: self.gradientWidth, height: maskFrame.height))
|
||||||
|
|
||||||
|
self.updateAnimations(size: maskFrame.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -557,7 +557,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, openCheckoutOrReceipt: { _ in
|
}, openCheckoutOrReceipt: { _, _ in
|
||||||
}, openSearch: {
|
}, openSearch: {
|
||||||
}, setupReply: { _ in
|
}, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
|
@ -428,7 +428,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess
|
|||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _, _ in }, openSearch: { }, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
return .none
|
return .none
|
||||||
}, canSendMessages: {
|
}, canSendMessages: {
|
||||||
|
@ -207,7 +207,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
public let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
public let presentGlobalOverlayController: (ViewController, Any?) -> Void
|
||||||
public let callPeer: (PeerId, Bool) -> Void
|
public let callPeer: (PeerId, Bool) -> Void
|
||||||
public let longTap: (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void
|
public let longTap: (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void
|
||||||
public let openCheckoutOrReceipt: (MessageId) -> Void
|
public let openCheckoutOrReceipt: (MessageId, OpenMessageParams?) -> Void
|
||||||
public let openSearch: () -> Void
|
public let openSearch: () -> Void
|
||||||
public let setupReply: (MessageId) -> Void
|
public let setupReply: (MessageId) -> Void
|
||||||
public let canSetupReply: (Message) -> ChatControllerInteractionSwipeAction
|
public let canSetupReply: (Message) -> ChatControllerInteractionSwipeAction
|
||||||
@ -336,7 +336,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
|
|||||||
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void,
|
||||||
callPeer: @escaping (PeerId, Bool) -> Void,
|
callPeer: @escaping (PeerId, Bool) -> Void,
|
||||||
longTap: @escaping (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void,
|
longTap: @escaping (ChatControllerInteractionLongTapAction, LongTapParams?) -> Void,
|
||||||
openCheckoutOrReceipt: @escaping (MessageId) -> Void,
|
openCheckoutOrReceipt: @escaping (MessageId, OpenMessageParams?) -> Void,
|
||||||
openSearch: @escaping () -> Void,
|
openSearch: @escaping () -> Void,
|
||||||
setupReply: @escaping (MessageId) -> Void,
|
setupReply: @escaping (MessageId) -> Void,
|
||||||
canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction,
|
canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction,
|
||||||
|
@ -3295,7 +3295,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}, openCheckoutOrReceipt: { _ in
|
}, openCheckoutOrReceipt: { _, _ in
|
||||||
}, openSearch: {
|
}, openSearch: {
|
||||||
}, setupReply: { _ in
|
}, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
|
@ -245,12 +245,18 @@ public final class StarsAvatarComponent: Component {
|
|||||||
self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment")
|
self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment")
|
||||||
iconOffset = 2.0
|
iconOffset = 2.0
|
||||||
case .ads:
|
case .ads:
|
||||||
self.backgroundView.image = generateFilledCircleImage(diameter: size.width, color: UIColor(rgb: 0x1b1f24))
|
self.backgroundView.image = generateGradientFilledCircleImage(
|
||||||
|
diameter: size.width,
|
||||||
|
colors: [
|
||||||
|
UIColor(rgb: 0xffa85c).cgColor,
|
||||||
|
UIColor(rgb: 0xffcd6a).cgColor
|
||||||
|
],
|
||||||
|
direction: .mirroredDiagonal
|
||||||
|
)
|
||||||
self.backgroundView.isHidden = false
|
self.backgroundView.isHidden = false
|
||||||
self.iconView.isHidden = false
|
self.iconView.isHidden = false
|
||||||
self.avatarNode.isHidden = true
|
self.avatarNode.isHidden = true
|
||||||
self.iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment")
|
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white)
|
||||||
iconOffset = 2.0
|
|
||||||
case .premiumBot:
|
case .premiumBot:
|
||||||
iconInset = 7.0
|
iconInset = 7.0
|
||||||
self.backgroundView.image = generateGradientFilledCircleImage(
|
self.backgroundView.image = generateGradientFilledCircleImage(
|
||||||
|
@ -720,12 +720,15 @@ public final class StarsImageComponent: Component {
|
|||||||
iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment")
|
iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment")
|
||||||
iconOffset = 5.0
|
iconOffset = 5.0
|
||||||
case .ads:
|
case .ads:
|
||||||
iconBackgroundView.image = generateFilledCircleImage(
|
iconBackgroundView.image = generateGradientFilledCircleImage(
|
||||||
diameter: imageSize.width,
|
diameter: imageSize.width,
|
||||||
color: UIColor(rgb: 0x1b1f24)
|
colors: [
|
||||||
|
UIColor(rgb: 0xffa85c).cgColor,
|
||||||
|
UIColor(rgb: 0xffcd6a).cgColor
|
||||||
|
],
|
||||||
|
direction: .mirroredDiagonal
|
||||||
)
|
)
|
||||||
iconView.image = UIImage(bundleImageName: "Premium/Stars/Fragment")
|
iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white)
|
||||||
iconOffset = 5.0
|
|
||||||
case .premiumBot:
|
case .premiumBot:
|
||||||
iconInset = 15.0
|
iconInset = 15.0
|
||||||
iconBackgroundView.image = generateGradientFilledCircleImage(
|
iconBackgroundView.image = generateGradientFilledCircleImage(
|
||||||
|
@ -216,17 +216,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
via = strings.Stars_Transaction_FragmentWithdrawal_Subtitle
|
via = strings.Stars_Transaction_FragmentWithdrawal_Subtitle
|
||||||
}
|
}
|
||||||
case .ads:
|
case .ads:
|
||||||
//TODO:localize
|
titleText = strings.Stars_Transaction_TelegramAds_Title
|
||||||
titleText = "Stars Withdrawal"
|
via = strings.Stars_Transaction_TelegramAds_Subtitle
|
||||||
via = "Telegram Ads"
|
|
||||||
case .unsupported:
|
case .unsupported:
|
||||||
titleText = strings.Stars_Transaction_Unsupported_Title
|
titleText = strings.Stars_Transaction_Unsupported_Title
|
||||||
via = nil
|
via = nil
|
||||||
}
|
}
|
||||||
if !transaction.media.isEmpty {
|
if !transaction.media.isEmpty {
|
||||||
var description: String = ""
|
var description: String = ""
|
||||||
var photoCount: Int = 0
|
var photoCount: Int32 = 0
|
||||||
var videoCount: Int = 0
|
var videoCount: Int32 = 0
|
||||||
for media in transaction.media {
|
for media in transaction.media {
|
||||||
if let _ = media as? TelegramMediaFile {
|
if let _ = media as? TelegramMediaFile {
|
||||||
videoCount += 1
|
videoCount += 1
|
||||||
@ -234,33 +233,22 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
photoCount += 1
|
photoCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO:localize
|
|
||||||
if photoCount > 0 && videoCount > 0 {
|
if photoCount > 0 && videoCount > 0 {
|
||||||
if photoCount > 1 {
|
description += strings.Stars_Transaction_MediaAnd(strings.Stars_Transaction_Photos(photoCount), strings.Stars_Transaction_Videos(videoCount)).string
|
||||||
description += "\(photoCount) photos**"
|
|
||||||
} else {
|
|
||||||
description += "**\(photoCount) photo**"
|
|
||||||
}
|
|
||||||
description += " and "
|
|
||||||
if videoCount > 1 {
|
|
||||||
description += "**\(videoCount) videos**"
|
|
||||||
} else {
|
|
||||||
description += "**\(videoCount) video**"
|
|
||||||
}
|
|
||||||
} else if photoCount > 0 {
|
} else if photoCount > 0 {
|
||||||
if photoCount > 1 {
|
if photoCount > 1 {
|
||||||
description += "**\(photoCount) photos**"
|
description += strings.Stars_Transaction_Photos(photoCount)
|
||||||
} else {
|
} else {
|
||||||
description += "**Photo**"
|
description += strings.Stars_Transaction_SinglePhoto
|
||||||
}
|
}
|
||||||
} else if videoCount > 0 {
|
} else if videoCount > 0 {
|
||||||
if videoCount > 1 {
|
if videoCount > 1 {
|
||||||
description += "**\(videoCount) videos**"
|
description += strings.Stars_Transaction_Videos(videoCount)
|
||||||
} else {
|
} else {
|
||||||
description += "**Video**"
|
description += strings.Stars_Transaction_SingleVideo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
descriptionText = description.replacingOccurrences(of: "**", with: "")
|
descriptionText = description
|
||||||
} else {
|
} else {
|
||||||
descriptionText = transaction.description ?? ""
|
descriptionText = transaction.description ?? ""
|
||||||
}
|
}
|
||||||
@ -416,7 +404,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let messageId {
|
if let messageId {
|
||||||
//TODO:localize
|
|
||||||
let peerName: String
|
let peerName: String
|
||||||
if case let .transaction(_, parentPeer) = component.subject {
|
if case let .transaction(_, parentPeer) = component.subject {
|
||||||
if parentPeer.id == component.context.account.peerId {
|
if parentPeer.id == component.context.account.peerId {
|
||||||
@ -432,7 +419,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
peerName = ""
|
peerName = ""
|
||||||
}
|
}
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "via",
|
id: "media",
|
||||||
title: strings.Stars_Transaction_Media,
|
title: strings.Stars_Transaction_Media,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
Button(
|
Button(
|
||||||
|
@ -209,8 +209,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
switch item.peer {
|
switch item.peer {
|
||||||
case let .peer(peer):
|
case let .peer(peer):
|
||||||
if !item.media.isEmpty {
|
if !item.media.isEmpty {
|
||||||
//TODO:localize
|
itemTitle = environment.strings.Stars_Intro_Transaction_MediaPurchase
|
||||||
itemTitle = "Media Purchase"
|
|
||||||
itemSubtitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
itemSubtitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||||
} else if let title = item.title {
|
} else if let title = item.title {
|
||||||
itemTitle = title
|
itemTitle = title
|
||||||
@ -237,8 +236,8 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
itemTitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Title
|
itemTitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Title
|
||||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Subtitle
|
itemSubtitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Subtitle
|
||||||
case .ads:
|
case .ads:
|
||||||
itemTitle = "Withdrawal"
|
itemTitle = environment.strings.Stars_Intro_Transaction_TelegramAds_Title
|
||||||
itemSubtitle = "via Telegram Ads"
|
itemSubtitle = environment.strings.Stars_Intro_Transaction_TelegramAds_Subtitle
|
||||||
case .unsupported:
|
case .unsupported:
|
||||||
itemTitle = environment.strings.Stars_Intro_Transaction_Unsupported_Title
|
itemTitle = environment.strings.Stars_Intro_Transaction_Unsupported_Title
|
||||||
itemSubtitle = nil
|
itemSubtitle = nil
|
||||||
|
@ -335,10 +335,9 @@ private final class SheetContent: CombinedComponent {
|
|||||||
let amount = component.invoice.totalAmount
|
let amount = component.invoice.totalAmount
|
||||||
let infoText: String
|
let infoText: String
|
||||||
if !component.extendedMedia.isEmpty {
|
if !component.extendedMedia.isEmpty {
|
||||||
//TODO:localize
|
|
||||||
var description: String = ""
|
var description: String = ""
|
||||||
var photoCount: Int = 0
|
var photoCount: Int32 = 0
|
||||||
var videoCount: Int = 0
|
var videoCount: Int32 = 0
|
||||||
for media in component.extendedMedia {
|
for media in component.extendedMedia {
|
||||||
if case let .preview(_, _, videoDuration) = media, videoDuration != nil {
|
if case let .preview(_, _, videoDuration) = media, videoDuration != nil {
|
||||||
videoCount += 1
|
videoCount += 1
|
||||||
@ -347,28 +346,18 @@ private final class SheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if photoCount > 0 && videoCount > 0 {
|
if photoCount > 0 && videoCount > 0 {
|
||||||
if photoCount > 1 {
|
description = strings.Stars_Transfer_MediaAnd("**\(strings.Stars_Transfer_Photos(photoCount))**", "**\(strings.Stars_Transfer_Videos(videoCount))**").string
|
||||||
description += "**\(photoCount) photos**"
|
|
||||||
} else {
|
|
||||||
description += "**\(photoCount) photo**"
|
|
||||||
}
|
|
||||||
description += " and "
|
|
||||||
if videoCount > 1 {
|
|
||||||
description += "**\(videoCount) videos**"
|
|
||||||
} else {
|
|
||||||
description += "**\(videoCount) video**"
|
|
||||||
}
|
|
||||||
} else if photoCount > 0 {
|
} else if photoCount > 0 {
|
||||||
if photoCount > 1 {
|
if photoCount > 1 {
|
||||||
description += "**\(photoCount) photos**"
|
description += "**\(strings.Stars_Transfer_Photos(photoCount))**"
|
||||||
} else {
|
} else {
|
||||||
description += "**photo**"
|
description += "**\(strings.Stars_Transfer_SinglePhoto)**"
|
||||||
}
|
}
|
||||||
} else if videoCount > 0 {
|
} else if videoCount > 0 {
|
||||||
if videoCount > 1 {
|
if videoCount > 1 {
|
||||||
description += "**\(videoCount) videos**"
|
description += "**\(strings.Stars_Transfer_Videos(videoCount))**"
|
||||||
} else {
|
} else {
|
||||||
description += "**video**"
|
description += "**\(strings.Stars_Transfer_SingleVideo)**"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infoText = strings.Stars_Transfer_UnlockInfo(
|
infoText = strings.Stars_Transfer_UnlockInfo(
|
||||||
|
@ -96,6 +96,8 @@ func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, no
|
|||||||
return true
|
return true
|
||||||
} else if let file = media as? TelegramMediaFile, file.isVideo {
|
} else if let file = media as? TelegramMediaFile, file.isVideo {
|
||||||
return true
|
return true
|
||||||
|
} else if media is TelegramMediaPaidContent {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
@ -905,7 +905,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
switch extendedMedia {
|
switch extendedMedia {
|
||||||
case .preview:
|
case .preview:
|
||||||
if displayVoiceMessageDiscardAlert() {
|
if displayVoiceMessageDiscardAlert() {
|
||||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id)
|
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id, params)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -917,7 +917,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
switch extendedMedia {
|
switch extendedMedia {
|
||||||
case .preview:
|
case .preview:
|
||||||
if displayVoiceMessageDiscardAlert() {
|
if displayVoiceMessageDiscardAlert() {
|
||||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id)
|
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id, nil)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -2586,7 +2586,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let self {
|
if let self {
|
||||||
self.openLinkLongTap(action, params: params)
|
self.openLinkLongTap(action, params: params)
|
||||||
}
|
}
|
||||||
}, openCheckoutOrReceipt: { [weak self] messageId in
|
}, openCheckoutOrReceipt: { [weak self] messageId, params in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2610,6 +2610,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let paidContent = media as? TelegramMediaPaidContent {
|
if let paidContent = media as? TelegramMediaPaidContent {
|
||||||
|
let progressSignal = Signal<Never, NoError> { _ in
|
||||||
|
params?.progress?.set(.single(true))
|
||||||
|
return ActionDisposable {
|
||||||
|
params?.progress?.set(.single(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(Queue.mainQueue())
|
||||||
|
|> delay(0.25, queue: Queue.mainQueue())
|
||||||
|
let progressDisposable = progressSignal.startStrict()
|
||||||
|
|
||||||
strongSelf.chatDisplayNode.dismissInput()
|
strongSelf.chatDisplayNode.dismissInput()
|
||||||
let inputData = Promise<BotCheckoutController.InputData?>()
|
let inputData = Promise<BotCheckoutController.InputData?>()
|
||||||
inputData.set(BotCheckoutController.InputData.fetch(context: strongSelf.context, source: .message(message.id))
|
inputData.set(BotCheckoutController.InputData.fetch(context: strongSelf.context, source: .message(message.id))
|
||||||
@ -2636,6 +2646,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: paidContent.amount, startParam: "", extendedMedia: .preview(dimensions:dimensions, immediateThumbnailData: immediateThumbnailData, videoDuration: nil), flags: [], version: 0)
|
let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: paidContent.amount, startParam: "", extendedMedia: .preview(dimensions:dimensions, immediateThumbnailData: immediateThumbnailData, videoDuration: nil), flags: [], version: 0)
|
||||||
let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .message(messageId), extendedMedia: paidContent.extendedMedia, inputData: starsInputData, completion: { _ in })
|
let controller = strongSelf.context.sharedContext.makeStarsTransferScreen(context: strongSelf.context, starsContext: starsContext, invoice: invoice, source: .message(messageId), extendedMedia: paidContent.extendedMedia, inputData: starsInputData, completion: { _ in })
|
||||||
strongSelf.push(controller)
|
strongSelf.push(controller)
|
||||||
|
|
||||||
|
progressDisposable.dispose()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if let invoice = media as? TelegramMediaInvoice {
|
} else if let invoice = media as? TelegramMediaInvoice {
|
||||||
|
@ -955,7 +955,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
controllerInteraction.activateSwitchInline(peerId, "@\(addressName) \(query)", peerTypes)
|
controllerInteraction.activateSwitchInline(peerId, "@\(addressName) \(query)", peerTypes)
|
||||||
}
|
}
|
||||||
case .payment:
|
case .payment:
|
||||||
controllerInteraction.openCheckoutOrReceipt(message.id)
|
controllerInteraction.openCheckoutOrReceipt(message.id, nil)
|
||||||
case let .urlAuth(url, buttonId):
|
case let .urlAuth(url, buttonId):
|
||||||
controllerInteraction.requestMessageActionUrlAuth(url, .message(id: message.id, buttonId: buttonId))
|
controllerInteraction.requestMessageActionUrlAuth(url, .message(id: message.id, buttonId: buttonId))
|
||||||
case .setupPoll:
|
case .setupPoll:
|
||||||
|
@ -118,7 +118,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}, presentGlobalOverlayController: { _, _ in
|
}, presentGlobalOverlayController: { _, _ in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
}, longTap: { _, _ in
|
}, longTap: { _, _ in
|
||||||
}, openCheckoutOrReceipt: { _ in
|
}, openCheckoutOrReceipt: { _, _ in
|
||||||
}, openSearch: {
|
}, openSearch: {
|
||||||
}, setupReply: { _ in
|
}, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
|
@ -1722,7 +1722,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return nil
|
return nil
|
||||||
}, chatControllerNode: {
|
}, chatControllerNode: {
|
||||||
return nil
|
return nil
|
||||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _, _ in }, openSearch: { }, setupReply: { _ in
|
||||||
}, canSetupReply: { _ in
|
}, canSetupReply: { _ in
|
||||||
return .none
|
return .none
|
||||||
}, canSendMessages: {
|
}, canSendMessages: {
|
||||||
|
@ -8,6 +8,24 @@ import PhotoResources
|
|||||||
import ImageCompression
|
import ImageCompression
|
||||||
|
|
||||||
public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, media: AnyMediaReference, opportunistic: Bool) -> Signal<AnyMediaReference?, NoError> {
|
public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, media: AnyMediaReference, opportunistic: Bool) -> Signal<AnyMediaReference?, NoError> {
|
||||||
|
if let paidContent = media.media as? TelegramMediaPaidContent {
|
||||||
|
var signals: [Signal<AnyMediaReference?, NoError>] = []
|
||||||
|
for case let .full(fullMedia) in paidContent.extendedMedia {
|
||||||
|
signals.append(transformOutgoingMessageMedia(postbox: postbox, network: network, media: media.withUpdatedMedia(fullMedia), opportunistic: opportunistic))
|
||||||
|
}
|
||||||
|
return combineLatest(signals)
|
||||||
|
|> mapToSignal { results -> Signal<AnyMediaReference?, NoError> in
|
||||||
|
let mediaResults = results.compactMap { $0?.media }
|
||||||
|
if mediaResults.count == signals.count {
|
||||||
|
return .single(media.withUpdatedMedia(TelegramMediaPaidContent(amount: paidContent.amount, extendedMedia: mediaResults.map { .full(media: $0) })))
|
||||||
|
} else if opportunistic {
|
||||||
|
return .single(nil)
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch media.media {
|
switch media.media {
|
||||||
case let file as TelegramMediaFile:
|
case let file as TelegramMediaFile:
|
||||||
let signal = Signal<MediaResourceData, NoError> { subscriber in
|
let signal = Signal<MediaResourceData, NoError> { subscriber in
|
||||||
|
Loading…
x
Reference in New Issue
Block a user