diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index ec2cb4a88a..ed954b3c6e 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1024,7 +1024,7 @@ public protocol SharedAccountContext: AnyObject { func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any?, UIView?, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController - func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController + func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (FileMediaReference) -> Void) -> ViewController func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController diff --git a/submodules/DrawingUI/Sources/DrawingReactionView.swift b/submodules/DrawingUI/Sources/DrawingReactionView.swift index a86449a87e..8ecbabc19d 100644 --- a/submodules/DrawingUI/Sources/DrawingReactionView.swift +++ b/submodules/DrawingUI/Sources/DrawingReactionView.swift @@ -189,7 +189,7 @@ public class DrawingReactionEntityView: DrawingStickerEntityView { } if case let .file(_, type) = self.stickerEntity.content, case let .reaction(_, style) = type { - self.stickerEntity.content = .file(animation, .reaction(updateReaction.reaction, style)) + self.stickerEntity.content = .file(.standalone(media: animation), .reaction(updateReaction.reaction, style)) } var nodeToTransitionOut: ASDisplayNode? diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index 15232b7a07..e5d661d92e 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -2893,13 +2893,13 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U for entity in self.entitiesView.entities { if let sticker = entity as? DrawingStickerEntity, case let .file(file, _) = sticker.content { let coder = PostboxEncoder() - coder.encodeRootObject(file) + coder.encodeRootObject(file.media) stickers.append(coder.makeData()) } else if let text = entity as? DrawingTextEntity, let subEntities = text.renderSubEntities { for sticker in subEntities { if let sticker = sticker as? DrawingStickerEntity, case let .file(file, _) = sticker.content { let coder = PostboxEncoder() - coder.encodeRootObject(file) + coder.encodeRootObject(file.media) stickers.append(coder.makeData()) } } diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift index a6b7dd50b1..e2c9d4e880 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift @@ -118,7 +118,7 @@ public class DrawingStickerEntityView: DrawingEntityView { private var file: TelegramMediaFile? { if case let .file(file, _) = self.stickerEntity.content { - return file + return file.media } else { return nil } @@ -158,7 +158,7 @@ public class DrawingStickerEntityView: DrawingEntityView { private var dimensions: CGSize { switch self.stickerEntity.content { case let .file(file, _): - return file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0) + return file.media.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0) case let .image(image, _): return image.size case let .animatedImage(_, thumbnailImage): @@ -174,7 +174,7 @@ public class DrawingStickerEntityView: DrawingEntityView { private func updateAnimationColor() { let color: UIColor? - if case let .file(file, type) = self.stickerEntity.content, file.isCustomTemplateEmoji { + if case let .file(file, type) = self.stickerEntity.content, file.media.isCustomTemplateEmoji { if case let .reaction(_, style) = type { if case .white = style { color = UIColor(rgb: 0x000000) diff --git a/submodules/DrawingUI/Sources/DrawingTextEntityView.swift b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift index 5321abea5a..c8ee6138ca 100644 --- a/submodules/DrawingUI/Sources/DrawingTextEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingTextEntityView.swift @@ -725,7 +725,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate let itemSize: CGFloat = floor(24.0 * fontSize * 0.78 / 17.0) let emojiTextPosition = emojiRect.center.offsetBy(dx: -textSize.width / 2.0, dy: -textSize.height / 2.0) - let entity = DrawingStickerEntity(content: .file(file, .sticker)) + let entity = DrawingStickerEntity(content: .file(.standalone(media: file), .sticker)) entity.referenceDrawingSize = CGSize(width: itemSize * 4.0, height: itemSize * 4.0) entity.scale = scale entity.position = textPosition.offsetBy( diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift index 0d5d6c1957..f988ba1df5 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift @@ -625,9 +625,9 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, ASScroll if let localResource = item.stickerItem.resource { self.context.account.postbox.mediaBox.copyResourceData(from: localResource._asResource().id, to: resource._asResource().id) } - stickers.append(ImportSticker(resource: resource._asResource(), emojis: item.stickerItem.emojis, dimensions: dimensions, mimeType: item.stickerItem.mimeType, keywords: item.stickerItem.keywords)) + stickers.append(ImportSticker(resource: .standalone(resource: resource._asResource()), emojis: item.stickerItem.emojis, dimensions: dimensions, mimeType: item.stickerItem.mimeType, keywords: item.stickerItem.keywords)) } else if let resource = item.stickerItem.resource { - stickers.append(ImportSticker(resource: resource._asResource(), emojis: item.stickerItem.emojis, dimensions: dimensions, mimeType: item.stickerItem.mimeType, keywords: item.stickerItem.keywords)) + stickers.append(ImportSticker(resource: .standalone(resource: resource._asResource()), emojis: item.stickerItem.emojis, dimensions: dimensions, mimeType: item.stickerItem.mimeType, keywords: item.stickerItem.keywords)) } } var thumbnailSticker: ImportSticker? @@ -638,7 +638,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, ASScroll } let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnail.data) - thumbnailSticker = ImportSticker(resource: resource, emojis: [], dimensions: dimensions, mimeType: thumbnail.mimeType, keywords: thumbnail.keywords) + thumbnailSticker = ImportSticker(resource: .standalone(resource: resource), emojis: [], dimensions: dimensions, mimeType: thumbnail.mimeType, keywords: thumbnail.keywords) } let firstStickerItem = thumbnailSticker ?? stickers.first diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index e749b0f6ea..145a408982 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -102,7 +102,8 @@ private class LegacyPaintStickerEntity: LegacyPaintEntity { self.animated = entity.isAnimated switch entity.content { - case let .file(file, _): + case let .file(fileReference, _): + let file = fileReference.media self.file = file if file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" { self.source = AnimatedStickerResourceSource(postbox: postbox, resource: file.resource, isVideo: file.isVideoSticker || file.mimeType == "video/webm") diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 7644877303..983ddcda93 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -25,6 +25,8 @@ import Pasteboard import StickerPackEditTitleController import EntityKeyboard +private let maxStickersCount = 120 + private enum StickerPackPreviewGridEntry: Comparable, Identifiable { case sticker(index: Int, stableId: Int, stickerItem: StickerPackItem?, isEmpty: Bool, isPremium: Bool, isLocked: Bool, isEditing: Bool, isAdd: Bool) case add @@ -1251,7 +1253,7 @@ private final class StickerPackContainer: ASDisplayNode { completion: { file, emoji, commit in dismissImpl?() let sticker = ImportSticker( - resource: file.resource, + resource: .standalone(resource: file.resource), emojis: emoji, dimensions: file.dimensions ?? PixelDimensions(width: 512, height: 512), mimeType: file.mimeType, @@ -1292,18 +1294,18 @@ private final class StickerPackContainer: ASDisplayNode { let context = self.context let controller = self.context.sharedContext.makeStickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData, completion: { file in var emoji = "🫥" - for attribute in file.attributes { - if case let .Sticker(displayText, _, _) = attribute { + for attribute in file.media.attributes { + if case let .Sticker(displayText, _, _) = attribute, !displayText.isEmpty { emoji = displayText break } } let sticker = ImportSticker( - resource: file.resource, + resource: file.resourceReference(file.media.resource), emojis: [emoji], - dimensions: file.dimensions ?? PixelDimensions(width: 512, height: 512), - mimeType: file.mimeType, + dimensions: file.media.dimensions ?? PixelDimensions(width: 512, height: 512), + mimeType: file.media.mimeType, keywords: "" ) let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) @@ -1313,7 +1315,7 @@ private final class StickerPackContainer: ASDisplayNode { (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { - packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file, loop: true, title: nil, text: "Sticker added to **\(info.title)** sticker set.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + packController.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: context, file: file.media, loop: true, title: nil, text: "Sticker added to **\(info.title)** sticker set.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) } }) }) @@ -1342,7 +1344,7 @@ private final class StickerPackContainer: ASDisplayNode { transitionArguments: nil, completion: { file, emoji, commit in let sticker = ImportSticker( - resource: file.resource, + resource: .standalone(resource: file.resource), emojis: emoji, dimensions: file.dimensions ?? PixelDimensions(width: 512, height: 512), mimeType: file.mimeType, @@ -1787,7 +1789,7 @@ private final class StickerPackContainer: ASDisplayNode { self.updateButton(count: count) } - if GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) { + if GlobalExperimentalSettings.enableWIPStickers && info.flags.contains(.isCreator) && !info.flags.contains(.isEmoji) && entries.count < maxStickersCount { entries.append(.add) } } @@ -1883,7 +1885,9 @@ private final class StickerPackContainer: ASDisplayNode { } } - entries.append(.add) + if entries.count < maxStickersCount { + entries.append(.add) + } self.currentEntries = entries diff --git a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift index a54d8a540b..d31307d64d 100644 --- a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift @@ -299,6 +299,8 @@ private enum MediaReferenceRevalidationKey: Hashable { case webPage(webPage: WebpageReference) case stickerPack(stickerPack: StickerPackReference) case savedGifs + case savedStickers + case recentStickers case peer(peer: PeerReference) case wallpaper(wallpaper: WallpaperReference) case wallpapers @@ -544,6 +546,66 @@ final class MediaReferenceRevalidationContext { } } + func savedStickers(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> { + return self.genericItem(key: .savedStickers, background: background, request: { next, error in + let loadSavedStickers: Signal<[TelegramMediaFile], NoError> = postbox.transaction { transaction -> [TelegramMediaFile] in + return transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudSavedStickers).compactMap({ item -> TelegramMediaFile? in + if let contents = item.contents.get(RecentMediaItem.self) { + let file = contents.media + return file + } + return nil + }) + } + return (managedSavedStickers(postbox: postbox, network: network, forceFetch: true) + |> mapToSignal { _ -> Signal<[TelegramMediaFile], NoError> in + return .complete() + } + |> then(loadSavedStickers) + |> castError(RevalidateMediaReferenceError.self)).start(next: { value in + next(value) + }, error: { _ in + error(.generic) + }) + }) |> mapToSignal { next -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> in + if let next = next as? [TelegramMediaFile] { + return .single(next) + } else { + return .fail(.generic) + } + } + } + + func recentStickers(postbox: Postbox, network: Network, background: Bool) -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> { + return self.genericItem(key: .recentStickers, background: background, request: { next, error in + let loadRecentStickers: Signal<[TelegramMediaFile], NoError> = postbox.transaction { transaction -> [TelegramMediaFile] in + return transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudRecentStickers).compactMap({ item -> TelegramMediaFile? in + if let contents = item.contents.get(RecentMediaItem.self) { + let file = contents.media + return file + } + return nil + }) + } + return (managedRecentStickers(postbox: postbox, network: network, forceFetch: true) + |> mapToSignal { _ -> Signal<[TelegramMediaFile], NoError> in + return .complete() + } + |> then(loadRecentStickers) + |> castError(RevalidateMediaReferenceError.self)).start(next: { value in + next(value) + }, error: { _ in + error(.generic) + }) + }) |> mapToSignal { next -> Signal<[TelegramMediaFile], RevalidateMediaReferenceError> in + if let next = next as? [TelegramMediaFile] { + return .single(next) + } else { + return .fail(.generic) + } + } + } + func peer(accountPeerId: PeerId, postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal { return self.genericItem(key: .peer(peer: peer), background: background, request: { next, error in return (_internal_updatedRemotePeer(accountPeerId: accountPeerId, postbox: postbox, network: network, peer: peer) @@ -811,6 +873,30 @@ func revalidateMediaResourceReference(accountPeerId: PeerId, postbox: Postbox, n } return .fail(.generic) } + case let .savedSticker(media): + return revalidationContext.savedStickers(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation) + |> mapToSignal { result -> Signal in + for file in result { + if media.id != nil && file.id == media.id { + if let updatedResource = findUpdatedMediaResource(media: file, previousMedia: media, resource: resource) { + return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil)) + } + } + } + return .fail(.generic) + } + case let .recentSticker(media): + return revalidationContext.recentStickers(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation) + |> mapToSignal { result -> Signal in + for file in result { + if media.id != nil && file.id == media.id { + if let updatedResource = findUpdatedMediaResource(media: file, previousMedia: media, resource: resource) { + return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil)) + } + } + } + return .fail(.generic) + } case let .avatarList(peer, media): return revalidationContext.peerAvatars(accountPeerId: accountPeerId, postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) |> mapToSignal { result -> Signal in diff --git a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift index 4b73712944..bac8b35cff 100644 --- a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift +++ b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift @@ -42,8 +42,8 @@ private func managedRecentMedia(postbox: Postbox, network: Network, collectionId } |> switchToLatest } -func managedRecentStickers(postbox: Postbox, network: Network) -> Signal { - return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in +func managedRecentStickers(postbox: Postbox, network: Network, forceFetch: Bool = false) -> Signal { + return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: forceFetch, fetch: { hash in return network.request(Api.functions.messages.getRecentStickers(flags: 0, hash: hash)) |> retryRequest |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in @@ -88,8 +88,8 @@ func managedRecentGifs(postbox: Postbox, network: Network, forceFetch: Bool = fa }) } -func managedSavedStickers(postbox: Postbox, network: Network) -> Signal { - return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudSavedStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: true, forceFetch: false, fetch: { hash in +func managedSavedStickers(postbox: Postbox, network: Network, forceFetch: Bool = false) -> Signal { + return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudSavedStickers, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: true, forceFetch: forceFetch, fetch: { hash in return network.request(Api.functions.messages.getFavedStickers(hash: hash)) |> retryRequest |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift index 57a8473844..9d6213c71c 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift @@ -262,6 +262,8 @@ public enum AnyMediaReference: Equatable { case webPage(webPage: WebpageReference, media: Media) case stickerPack(stickerPack: StickerPackReference, media: Media) case savedGif(media: Media) + case savedSticker(media: Media) + case recentSticker(media: Media) case avatarList(peer: PeerReference, media: Media) case attachBot(peer: PeerReference, media: Media) case customEmoji(media: Media) @@ -299,6 +301,18 @@ public enum AnyMediaReference: Equatable { } else { return false } + case let .savedSticker(lhsMedia): + if case let .savedSticker(rhsMedia) = rhs, lhsMedia.isEqual(to: rhsMedia) { + return true + } else { + return false + } + case let .recentSticker(lhsMedia): + if case let .recentSticker(rhsMedia) = rhs, lhsMedia.isEqual(to: rhsMedia) { + return true + } else { + return false + } case let .avatarList(lhsPeer, lhsMedia): if case let .avatarList(rhsPeer, rhsMedia) = rhs, lhsPeer == rhsPeer, lhsMedia.isEqual(to: rhsMedia) { return true @@ -338,6 +352,10 @@ public enum AnyMediaReference: Equatable { return .stickerPack(stickerPack: stickerPack) case .savedGif: return .savedGif + case .savedSticker: + return .savedSticker + case .recentSticker: + return .recentSticker case .avatarList: return nil case .attachBot: @@ -371,6 +389,14 @@ public enum AnyMediaReference: Equatable { if let media = media as? T { return .savedGif(media: media) } + case let .savedSticker(media): + if let media = media as? T { + return .savedSticker(media: media) + } + case let .recentSticker(media): + if let media = media as? T { + return .recentSticker(media: media) + } case let .avatarList(peer, media): if let media = media as? T { return .avatarList(peer: peer, media: media) @@ -403,6 +429,10 @@ public enum AnyMediaReference: Equatable { return media case let .savedGif(media): return media + case let .savedSticker(media): + return media + case let .recentSticker(media): + return media case let .avatarList(_, media): return media case let .attachBot(_, media): @@ -425,12 +455,16 @@ public enum PartialMediaReference: Equatable { case webPage case stickerPack case savedGif + case savedSticker + case recentSticker } case message(message: MessageReference) case webPage(webPage: WebpageReference) case stickerPack(stickerPack: StickerPackReference) case savedGif + case savedSticker + case recentSticker public init?(decoder: PostboxDecoder) { guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else { @@ -448,6 +482,10 @@ public enum PartialMediaReference: Equatable { self = .stickerPack(stickerPack: stickerPack) case .savedGif: self = .savedGif + case .savedSticker: + self = .savedSticker + case .recentSticker: + self = .recentSticker } } @@ -464,6 +502,10 @@ public enum PartialMediaReference: Equatable { encoder.encodeObject(stickerPack, forKey: "spk") case .savedGif: encoder.encodeInt32(CodingCase.savedGif.rawValue, forKey: "_r") + case .savedSticker: + encoder.encodeInt32(CodingCase.savedSticker.rawValue, forKey: "_r") + case .recentSticker: + encoder.encodeInt32(CodingCase.recentSticker.rawValue, forKey: "_r") } } @@ -477,6 +519,10 @@ public enum PartialMediaReference: Equatable { return .stickerPack(stickerPack: stickerPack, media: media) case .savedGif: return .savedGif(media: media) + case .savedSticker: + return .savedSticker(media: media) + case .recentSticker: + return .recentSticker(media: media) } } } @@ -488,6 +534,8 @@ public enum MediaReference { case webPage case stickerPack case savedGif + case savedSticker + case recentSticker case avatarList case attachBot case customEmoji @@ -499,6 +547,8 @@ public enum MediaReference { case webPage(webPage: WebpageReference, media: T) case stickerPack(stickerPack: StickerPackReference, media: T) case savedGif(media: T) + case savedSticker(media: T) + case recentSticker(media: T) case avatarList(peer: PeerReference, media: T) case attachBot(peer: PeerReference, media: T) case customEmoji(media: T) @@ -537,6 +587,16 @@ public enum MediaReference { return nil } self = .savedGif(media: media) + case .savedSticker: + guard let media = decoder.decodeObjectForKey("m") as? T else { + return nil + } + self = .savedSticker(media: media) + case .recentSticker: + guard let media = decoder.decodeObjectForKey("m") as? T else { + return nil + } + self = .recentSticker(media: media) case .avatarList: let peer = decoder.decodeObjectForKey("pr", decoder: { PeerReference(decoder: $0) }) as! PeerReference guard let media = decoder.decodeObjectForKey("m") as? T else { @@ -584,6 +644,12 @@ public enum MediaReference { case let .savedGif(media): encoder.encodeInt32(CodingCase.savedGif.rawValue, forKey: "_r") encoder.encodeObject(media, forKey: "m") + case let .savedSticker(media): + encoder.encodeInt32(CodingCase.savedSticker.rawValue, forKey: "_r") + encoder.encodeObject(media, forKey: "m") + case let .recentSticker(media): + encoder.encodeInt32(CodingCase.recentSticker.rawValue, forKey: "_r") + encoder.encodeObject(media, forKey: "m") case let .avatarList(peer, media): encoder.encodeInt32(CodingCase.avatarList.rawValue, forKey: "_r") encoder.encodeObject(peer, forKey: "pr") @@ -615,6 +681,10 @@ public enum MediaReference { return .stickerPack(stickerPack: stickerPack, media: media) case let .savedGif(media): return .savedGif(media: media) + case let .savedSticker(media): + return .savedSticker(media: media) + case let .recentSticker(media): + return .recentSticker(media: media) case let .avatarList(peer, media): return .avatarList(peer: peer, media: media) case let .attachBot(peer, media): @@ -642,6 +712,10 @@ public enum MediaReference { return media case let .savedGif(media): return media + case let .savedSticker(media): + return media + case let .recentSticker(media): + return media case let .avatarList(_, media): return media case let .attachBot(_, media): diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift index 183e1be0f9..bd0e213582 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift @@ -2,7 +2,7 @@ import Foundation import Postbox import SwiftSignalKit import TelegramApi - +import MtProtoKit public enum UploadStickerStatus { case progress(Float) @@ -77,13 +77,13 @@ public enum CreateStickerSetError { } public struct ImportSticker { - public let resource: MediaResource + public let resource: MediaResourceReference let emojis: [String] public let dimensions: PixelDimensions public let mimeType: String public let keywords: String - public init(resource: MediaResource, emojis: [String], dimensions: PixelDimensions, mimeType: String, keywords: String) { + public init(resource: MediaResourceReference, emojis: [String], dimensions: PixelDimensions, mimeType: String, keywords: String) { self.resource = resource self.emojis = emojis self.dimensions = dimensions @@ -94,7 +94,7 @@ public struct ImportSticker { public extension ImportSticker { var stickerPackItem: StickerPackItem? { - guard let resource = self.resource as? TelegramMediaResource else { + guard let resource = self.resource.resource as? TelegramMediaResource else { return nil } var fileAttributes: [TelegramMediaFileAttribute] = [] @@ -150,10 +150,10 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri stickers.append(thumbnail) } for sticker in stickers { - if let resource = sticker.resource as? CloudDocumentMediaResource { + if let resource = sticker.resource.resource as? CloudDocumentMediaResource { uploadStickers.append(.single(.complete(resource, sticker.mimeType))) } else { - uploadStickers.append(_internal_uploadSticker(account: account, peer: peer, resource: sticker.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) + uploadStickers.append(_internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) |> mapError { _ -> CreateStickerSetError in return .generic }) @@ -294,13 +294,13 @@ public enum AddStickerToSetError { func _internal_addStickerToStickerSet(account: Account, packReference: StickerPackReference, sticker: ImportSticker) -> Signal { let uploadSticker: Signal - if let resource = sticker.resource as? CloudDocumentMediaResource { + if let resource = sticker.resource.resource as? CloudDocumentMediaResource { uploadSticker = .single(.complete(resource, sticker.mimeType)) } else { uploadSticker = account.postbox.loadedPeerWithId(account.peerId) |> castError(AddStickerToSetError.self) |> mapToSignal { peer in - return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) + return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) |> mapError { _ -> AddStickerToSetError in return .generic } @@ -317,8 +317,27 @@ func _internal_addStickerToStickerSet(account: Account, packReference: StickerPa flags |= (1 << 1) } let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords) - + return account.network.request(Api.functions.stickers.addStickerToSet(stickerset: packReference.apiInputStickerSet, sticker: inputSticker)) + |> `catch` { error -> Signal in + if error.errorDescription == "FILE_REFERENCE_EXPIRED" { + return revalidateMediaResourceReference(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: sticker.resource, preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: sticker.resource.resource) + |> mapError { _ -> MTRpcError in + return MTRpcError(errorCode: 500, errorDescription: "Internal") + } + |> mapToSignal { result -> Signal in + guard let resource = result.updatedResource as? CloudDocumentMediaResource else { + return .fail(MTRpcError(errorCode: 500, errorDescription: "Internal")) + } + + let inputSticker: Api.InputStickerSetItem = .inputStickerSetItem(flags: flags, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), emoji: sticker.emojis.joined(), maskCoords: nil, keywords: sticker.keywords) + + return account.network.request(Api.functions.stickers.addStickerToSet(stickerset: packReference.apiInputStickerSet, sticker: inputSticker)) + } + } else { + return .fail(error) + } + } |> mapError { error -> AddStickerToSetError in return .generic } @@ -403,13 +422,13 @@ func _internal_replaceSticker(account: Account, previousSticker: FileMediaRefere } let uploadSticker: Signal - if let resource = sticker.resource as? CloudDocumentMediaResource { + if let resource = sticker.resource.resource as? CloudDocumentMediaResource { uploadSticker = .single(.complete(resource, sticker.mimeType)) } else { uploadSticker = account.postbox.loadedPeerWithId(account.peerId) |> castError(ReplaceStickerError.self) |> mapToSignal { peer in - return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) + return _internal_uploadSticker(account: account, peer: peer, resource: sticker.resource.resource, alt: sticker.emojis.first ?? "", dimensions: sticker.dimensions, mimeType: sticker.mimeType) |> mapError { _ -> ReplaceStickerError in return .generic } diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index cfab79f187..4b545754f9 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -1401,7 +1401,7 @@ final class AvatarEditorScreenComponent: Component { try? backgroundImage.jpegData(compressionQuality: 0.8)?.write(to: tempUrl) let drawingSize = CGSize(width: 1920.0, height: 1920.0) - let entity = DrawingStickerEntity(content: .file(file, .sticker)) + let entity = DrawingStickerEntity(content: .file(.standalone(media: file), .sticker)) entity.referenceDrawingSize = drawingSize entity.position = CGPoint(x: drawingSize.width / 2.0, y: drawingSize.height / 2.0) entity.scale = 3.3 @@ -1409,7 +1409,8 @@ final class AvatarEditorScreenComponent: Component { var fileId: Int64 = 0 var stickerPackId: Int64 = 0 var stickerPackAccessHash: Int64 = 0 - if case let .file(file, _) = entity.content { + if case let .file(fileReference, _) = entity.content { + let file = fileReference.media if file.isCustomEmoji { fileId = file.fileId.id } else if file.isAnimatedSticker { diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index d0e9c13b51..5f187d2a3f 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -1195,7 +1195,16 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { if let id = groupId.base as? ItemCollectionId, context.sharedContext.currentStickerSettings.with({ $0 }).dynamicPackOrder { bubbleUpEmojiOrStickersets.append(id) } - let _ = interaction.sendSticker(.standalone(media: file), false, false, nil, false, view, rect, layer, bubbleUpEmojiOrStickersets) + + let reference: FileMediaReference + if groupId == AnyHashable("saved") { + reference = .savedSticker(media: file) + } else if groupId == AnyHashable("recent") { + reference = .recentSticker(media: file) + } else { + reference = .standalone(media: file) + } + let _ = interaction.sendSticker(reference, false, false, nil, false, view, rect, layer, bubbleUpEmojiOrStickersets) } }) }, diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift index 92015ddc47..2779b41157 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/DrawingStickerEntity.swift @@ -32,7 +32,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { case sticker case reaction(MessageReaction.Reaction, ReactionStyle) } - case file(TelegramMediaFile, FileType) + case file(FileMediaReference, FileType) case image(UIImage, ImageType) case animatedImage(Data, UIImage) case video(TelegramMediaFile) @@ -43,7 +43,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { switch lhs { case let .file(lhsFile, lhsFileType): if case let .file(rhsFile, rhsFileType) = rhs { - return lhsFile.fileId == rhsFile.fileId && lhsFileType == rhsFileType + return lhsFile.media.fileId == rhsFile.media.fileId && lhsFileType == rhsFileType } else { return false } @@ -152,7 +152,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { if case .reaction = type { dimensions = CGSize(width: 512.0, height: 512.0) } else { - dimensions = file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0) + dimensions = file.media.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0) } case let .video(file): dimensions = file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0) @@ -176,7 +176,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { case .reaction: return false default: - return file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" + return file.media.isAnimatedSticker || file.media.isVideoSticker || file.media.mimeType == "video/webm" } } case .image: @@ -248,7 +248,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { } else { fileType = .sticker } - self.content = .file(file, fileType) + self.content = .file(.standalone(media: file), fileType) } else if let imagePath = try container.decodeIfPresent(String.self, forKey: .imagePath), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) { let isRectangle = try container.decodeIfPresent(Bool.self, forKey: .isRectangle) ?? false let isDualPhoto = try container.decodeIfPresent(Bool.self, forKey: .isDualPhoto) ?? false @@ -290,7 +290,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable { try container.encode(self.uuid, forKey: .uuid) switch self.content { case let .file(file, fileType): - try container.encode(file, forKey: .file) + try container.encode(file.media, forKey: .file) switch fileType { case let .reaction(reaction, reactionStyle): try container.encode(reaction, forKey: .reaction) diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift index ab1c4569b5..1fc6ac6656 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposerEntity.swift @@ -69,7 +69,7 @@ func composerEntitiesForDrawingEntity(postbox: Postbox, textScale: CGFloat, enti let content: MediaEditorComposerStickerEntity.Content switch entity.content { case let .file(file, _): - content = .file(file) + content = .file(file.media) case let .image(image, _): content = .image(image) case let .animatedImage(data, _): diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 4fc58e4315..494f8c2c4c 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3051,7 +3051,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate }) } else if case let .sticker(sticker, emoji) = effectiveSubject { controller.stickerSelectedEmoji = emoji - let stickerEntity = DrawingStickerEntity(content: .file(sticker, .sticker)) + let stickerEntity = DrawingStickerEntity(content: .file(.standalone(media: sticker), .sticker)) stickerEntity.referenceDrawingSize = storyDimensions stickerEntity.scale = 4.0 stickerEntity.position = CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0) @@ -4382,7 +4382,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let reaction = self.availableReactions.first(where: { reaction in return reaction.reaction.rawValue == .builtin(heart) }) { - let stickerEntity = DrawingStickerEntity(content: .file(reaction.stillAnimation, .reaction(.builtin(heart), .white))) + let stickerEntity = DrawingStickerEntity(content: .file(.standalone(media: reaction.stillAnimation), .reaction(.builtin(heart), .white))) self.interaction?.insertEntity(stickerEntity, scale: 1.175) } @@ -4530,11 +4530,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let self { if let content { if case let .file(file, _) = content { - if file.isCustomEmoji { - self.defaultToEmoji = true - } else { - self.defaultToEmoji = false - } + self.defaultToEmoji = file.media.isCustomEmoji } let stickerEntity = DrawingStickerEntity(content: content) @@ -5823,7 +5819,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) } for entity in entities { if let stickerEntity = entity as? DrawingStickerEntity, case let .file(file, type) = stickerEntity.content, case let .reaction(reaction, _) = type, case .custom = reaction { - self.presentUnavailableReactionPremiumSuggestion(file: file) + self.presentUnavailableReactionPremiumSuggestion(file: file.media) return false } } @@ -5879,13 +5875,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate switch entity { case let .sticker(stickerEntity): if case let .file(file, fileType) = stickerEntity.content, case .sticker = fileType { - stickers.append(file) + stickers.append(file.media) } case let .text(textEntity): if let subEntities = textEntity.renderSubEntities { for entity in subEntities { if let stickerEntity = entity as? DrawingStickerEntity, case let .file(file, fileType) = stickerEntity.content, case .sticker = fileType { - stickers.append(file) + stickers.append(file.media) } } } @@ -6467,7 +6463,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate shortName: "", stickers: [ ImportSticker( - resource: file.resource, + resource: .standalone(resource: file.resource), emojis: self.effectiveStickerEmoji(), dimensions: PixelDimensions(width: 512, height: 512), mimeType: "image/webp", @@ -6602,7 +6598,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } case let .createStickerPack(title): let sticker = ImportSticker( - resource: resource, + resource: .standalone(resource: resource), emojis: self.effectiveStickerEmoji(), dimensions: dimensions, mimeType: mimeType, @@ -6621,7 +6617,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } case let .addToStickerPack(pack, _): let sticker = ImportSticker( - resource: resource, + resource: .standalone(resource: resource), emojis: self.effectiveStickerEmoji(), dimensions: dimensions, mimeType: mimeType, diff --git a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift index c5828da772..e99ea5ec77 100644 --- a/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift +++ b/submodules/TelegramUI/Components/StickerPickerScreen/Sources/StickerPickerScreen.swift @@ -147,7 +147,7 @@ private final class StickerSelectionComponent: Component { c.dismiss(animated: true) } }) - if controller.completion(.file(file.media, .sticker)) { + if controller.completion(.file(file, .sticker)) { controller.dismiss(animated: true) } } @@ -884,7 +884,7 @@ public class StickerPickerScreen: ViewController { }) }) } else if let file = item.itemFile { - if controller.completion(.file(file, .sticker)) { + if controller.completion(.file(.standalone(media: file), .sticker)) { controller.dismiss(animated: true) } } else if case let .staticEmoji(emoji) = item.content { @@ -1299,7 +1299,7 @@ public class StickerPickerScreen: ViewController { guard let self, let controller = self.controller else { return false } - if controller.completion(.file(fileReference.media, .sticker)) { + if controller.completion(.file(fileReference, .sticker)) { controller.dismiss(animated: true) } return true @@ -1311,7 +1311,15 @@ public class StickerPickerScreen: ViewController { } }) } else { - let _ = controller.completion(.file(file, .sticker)) + let reference: FileMediaReference + if groupId == AnyHashable("saved") { + reference = .savedSticker(media: file) + } else if groupId == AnyHashable("recent") { + reference = .recentSticker(media: file) + } else { + reference = .standalone(media: file) + } + let _ = controller.completion(.file(reference, .sticker)) controller.dismiss(animated: true) } }, diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index b828eba274..dc2d9621cd 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2421,7 +2421,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return stickerMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed) } - public func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (TelegramMediaFile) -> Void) -> ViewController { + public func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (FileMediaReference) -> Void) -> ViewController { let controller = StickerPickerScreen(context: context, inputData: inputData.get(), expanded: true, hasGifs: false, hasInteractiveStickers: false) controller.completion = { content in if let content, case let .file(file, _) = content {