diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 593a53b698..3c593ac9f0 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11904,3 +11904,5 @@ Sorry for the inconvenience."; "Conversation.ViewStickers" = "VIEW STICKERS"; "Conversation.ViewEmojis" = "VIEW EMOJIS"; + +"MediaEditor.StickersTooMuch" = "Sorry, you've reached the maximum number of stickers in this set. Try a different one."; diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift index a981865d2b..e98d2366d8 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift @@ -314,7 +314,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode { if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker { let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) if stickerItem.file.isVideoSticker { - self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: stickerItem.file, small: true)) + self.imageNode.setSignal(chatMessageSticker(account: context.account, userLocation: .other, file: stickerItem.file, small: true, fetched: true)) } else { self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .other, file: stickerItem.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)))) } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 5d2a9aada7..9cc1fcd669 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -1250,6 +1250,7 @@ private final class StickerPackContainer: ASDisplayNode { let presentationData = self.presentationData let updatedPresentationData = self.controller?.updatedPresentationData let navigationController = self.controller?.parentNavigationController as? NavigationController + let sendSticker = self.controller?.sendSticker var dismissImpl: (() -> Void)? let mainController = context.sharedContext.makeStickerMediaPickerScreen( @@ -1275,7 +1276,7 @@ private final class StickerPackContainer: ASDisplayNode { |> deliverOnMainQueue).start(completed: { commit() - let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { @@ -1301,6 +1302,7 @@ private final class StickerPackContainer: ASDisplayNode { let presentationData = self.presentationData let updatedPresentationData = self.controller?.updatedPresentationData let navigationController = self.controller?.parentNavigationController as? NavigationController + let sendSticker = self.controller?.sendSticker let context = self.context let controller = self.context.sharedContext.makeStickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData, completion: { file in @@ -1323,7 +1325,7 @@ private final class StickerPackContainer: ASDisplayNode { let packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash) let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker) |> deliverOnMainQueue).start(completed: { - let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root)) Queue.mainQueue().after(0.1) { @@ -1345,8 +1347,10 @@ private final class StickerPackContainer: ASDisplayNode { } let context = self.context + let presentationData = self.presentationData let updatedPresentationData = self.controller?.updatedPresentationData let navigationController = self.controller?.parentNavigationController as? NavigationController + let sendSticker = self.controller?.sendSticker self.controller?.dismiss() @@ -1369,8 +1373,12 @@ private final class StickerPackContainer: ASDisplayNode { |> deliverOnMainQueue).start(completed: { commit() - let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: nil, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) + let packController = StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], loadedStickerPacks: [], expandIfNeeded: true, parentNavigationController: navigationController, sendSticker: sendSticker, sendEmoji: nil, actionPerformed: nil, dismissed: nil, getSourceRect: nil) (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 updated.", undoText: nil, customAction: nil), elevatedLayout: false, action: { _ in return false }), in: .current) + } }) } ) @@ -2645,7 +2653,7 @@ public final class StickerPackScreenImpl: ViewController, StickerPackScreen { private let initialSelectedStickerPackIndex: Int fileprivate weak var parentNavigationController: NavigationController? - private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? + fileprivate let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)? fileprivate var controllerNode: StickerPackScreenNode { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 0039683647..376bd60be9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -162,7 +162,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { self.activateBadgeAction?() } - public typealias AsyncLayout = (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ chatLocation: ChatLocation, _ title: String?, _ titleBadge: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: (Media, ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ constrainedSize: CGSize, _ animationCache: AnimationCache, _ animationRenderer: MultiAnimationRenderer) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) + public typealias AsyncLayout = (_ presentationData: ChatPresentationData, _ automaticDownloadSettings: MediaAutoDownloadSettings, _ associatedData: ChatMessageItemAssociatedData, _ attributes: ChatMessageEntryAttributes, _ context: AccountContext, _ controllerInteraction: ChatControllerInteraction, _ message: Message, _ messageRead: Bool, _ chatLocation: ChatLocation, _ title: String?, _ titleBadge: String?, _ subtitle: NSAttributedString?, _ text: String?, _ entities: [MessageTextEntity]?, _ media: ([Media], ChatMessageAttachedContentNodeMediaFlags)?, _ mediaBadge: String?, _ actionIcon: ChatMessageAttachedContentActionIcon?, _ actionTitle: String?, _ displayLine: Bool, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ constrainedSize: CGSize, _ animationCache: AnimationCache, _ animationRenderer: MultiAnimationRenderer) -> (CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) public func makeProgress() -> Promise { let progress = Promise() @@ -302,7 +302,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { var mediaAndFlags = mediaAndFlags if let mediaAndFlagsValue = mediaAndFlags { - if mediaAndFlagsValue.0 is TelegramMediaStory || mediaAndFlagsValue.0 is WallpaperPreviewMedia { + if mediaAndFlagsValue.0.first is TelegramMediaStory || mediaAndFlagsValue.0.first is WallpaperPreviewMedia { var flags = mediaAndFlagsValue.1 flags.remove(.preferMediaInline) mediaAndFlags = (mediaAndFlagsValue.0, flags) @@ -315,50 +315,52 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } var contentMediaInline = false - if let (media, flags) = mediaAndFlags { + if let (mediaArray, flags) = mediaAndFlags { contentMediaInline = flags.contains(.preferMediaInline) - if let file = media as? TelegramMediaFile { - if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 { - contentMediaValue = file - } else if file.isInstantVideo { - contentMediaValue = file - } else if file.isVideo { - contentMediaValue = file - } else if file.isSticker || file.isAnimatedSticker { - contentMediaValue = file - } else { - contentFileValue = file - } - - if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) { - contentMediaAutomaticDownload = .full - } else if shouldPredownloadMedia(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file) { - contentMediaAutomaticDownload = .prefetch - } - - if file.isAnimated { - contentMediaAutomaticPlayback = context.sharedContext.energyUsageSettings.autoplayGif - } else if file.isVideo && context.sharedContext.energyUsageSettings.autoplayVideo { - var willDownloadOrLocal = false - if case .full = contentMediaAutomaticDownload { - willDownloadOrLocal = true + if let media = mediaArray.first { + if let file = media as? TelegramMediaFile { + if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 { + contentMediaValue = file + } else if file.isInstantVideo { + contentMediaValue = file + } else if file.isVideo { + contentMediaValue = file + } else if file.isSticker || file.isAnimatedSticker { + contentMediaValue = file } else { - willDownloadOrLocal = context.account.postbox.mediaBox.completedResourcePath(file.resource) != nil + contentFileValue = file } - if willDownloadOrLocal { - contentMediaAutomaticPlayback = true - contentMediaAspectFilled = true + + if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) { + contentMediaAutomaticDownload = .full + } else if shouldPredownloadMedia(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, media: file) { + contentMediaAutomaticDownload = .prefetch } + + if file.isAnimated { + contentMediaAutomaticPlayback = context.sharedContext.energyUsageSettings.autoplayGif + } else if file.isVideo && context.sharedContext.energyUsageSettings.autoplayVideo { + var willDownloadOrLocal = false + if case .full = contentMediaAutomaticDownload { + willDownloadOrLocal = true + } else { + willDownloadOrLocal = context.account.postbox.mediaBox.completedResourcePath(file.resource) != nil + } + if willDownloadOrLocal { + contentMediaAutomaticPlayback = true + contentMediaAspectFilled = true + } + } + } else if let _ = media as? TelegramMediaImage { + contentMediaValue = media + } else if let _ = media as? TelegramMediaWebFile { + contentMediaValue = media + } else if let _ = media as? WallpaperPreviewMedia { + contentMediaValue = media + } else if let _ = media as? TelegramMediaStory { + contentMediaValue = media } - } else if let _ = media as? TelegramMediaImage { - contentMediaValue = media - } else if let _ = media as? TelegramMediaWebFile { - contentMediaValue = media - } else if let _ = media as? WallpaperPreviewMedia { - contentMediaValue = media - } else if let _ = media as? TelegramMediaStory { - contentMediaValue = media } } @@ -939,7 +941,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { self.context = context self.message = message - self.media = mediaAndFlags?.0 + self.media = mediaAndFlags?.0.first self.theme = presentationData.theme self.mainColor = mainColor diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift index 7da7c1c32c..d17580bb8f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift @@ -50,7 +50,7 @@ public final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessag } else { text = item.message.text } - let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil + let mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift index 7588af15ed..0b420f0c7f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift @@ -45,7 +45,7 @@ public final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubble let title: String = item.message.text.contains("\n") ? item.presentationData.strings.Channel_AdminLog_MessagePreviousLinks : item.presentationData.strings.Channel_AdminLog_MessagePreviousLink let text: String = item.message.text - let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil + let mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift index 79975870a3..d3ec9d1355 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift @@ -50,7 +50,7 @@ public final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBub } else { text = item.message.text } - let mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? = nil + let mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, messageEntities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift index b93e0ea81c..b4974c403a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift @@ -67,16 +67,16 @@ public final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNod var title: String? var text: String? - var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? + var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? if let game = game { title = game.title text = game.description if let file = game.file { - mediaAndFlags = (file, [.preferMediaBeforeText]) + mediaAndFlags = ([file], [.preferMediaBeforeText]) } else if let image = game.image { - mediaAndFlags = (image, [.preferMediaBeforeText]) + mediaAndFlags = ([image], [.preferMediaBeforeText]) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift index c78beea3bc..b46926dc47 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift @@ -52,7 +52,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent var title: String? var subtitle: NSAttributedString? = nil var text: String? - var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? + var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? var automaticDownloadSettings = item.controllerInteraction.automaticMediaDownloadSettings if let invoice = invoice { @@ -61,7 +61,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent if let image = invoice.photo { automaticDownloadSettings = MediaAutoDownloadSettings.defaultSettings - mediaAndFlags = (image, [.preferMediaBeforeText]) + mediaAndFlags = ([image], [.preferMediaBeforeText]) } else { let invoiceLabel = item.presentationData.strings.Message_InvoiceLabel var invoiceText = "\(formatCurrencyAmount(invoice.totalAmount, currency: invoice.currency)) " diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index b717441a94..d0c6cec2db 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -242,7 +242,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent var text: String? var entities: [MessageTextEntity]? var titleBadge: String? - var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? + var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? var badge: String? var actionIcon: ChatMessageAttachedContentActionIcon? @@ -307,9 +307,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent if let file = mainMedia as? TelegramMediaFile, webpage.type != "telegram_theme" { if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty { if automaticPlayback { - mediaAndFlags = (file, [.preferMediaBeforeText]) + mediaAndFlags = ([file], [.preferMediaBeforeText]) } else { - mediaAndFlags = (webpage.image ?? file, [.preferMediaBeforeText]) + mediaAndFlags = ([webpage.image ?? file], [.preferMediaBeforeText]) } } else if webpage.type == "telegram_background" { var colors: [UInt32] = [] @@ -321,12 +321,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent intensity = intensityValue } let media = WallpaperPreviewMedia(content: .file(file: file, colors: colors, rotation: rotation, intensity: intensity, false, false)) - mediaAndFlags = (media, [.preferMediaAspectFilled]) + mediaAndFlags = ([media], [.preferMediaAspectFilled]) if let fileSize = file.size { badge = dataSizeString(fileSize, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData)) } } else { - mediaAndFlags = (file, []) + mediaAndFlags = ([file], []) } } else if let image = mainMedia as? TelegramMediaImage { if let type = webpage.type, ["photo", "video", "embed", "gif", "document", "telegram_album"].contains(type) { @@ -338,13 +338,13 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } else if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty { flags.insert(.preferMediaBeforeText) } - mediaAndFlags = (image, flags) + mediaAndFlags = ([image], flags) } else if let _ = largestImageRepresentation(image.representations)?.dimensions { let flags = ChatMessageAttachedContentNodeMediaFlags() - mediaAndFlags = (image, flags) + mediaAndFlags = ([image], flags) } } else if let story = mainMedia as? TelegramMediaStory { - mediaAndFlags = (story, [.preferMediaBeforeText, .titleBeforeMedia]) + mediaAndFlags = ([story], [.preferMediaBeforeText, .titleBeforeMedia]) if let storyItem = item.message.associatedStories[story.storyId]?.get(Stories.StoredItem.self), case let .item(itemValue) = storyItem { text = itemValue.text entities = itemValue.entities @@ -372,7 +372,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } if let content = content { let media = WallpaperPreviewMedia(content: content) - mediaAndFlags = (media, []) + mediaAndFlags = ([media], []) } } else if type == "telegram_theme" { var file: TelegramMediaFile? @@ -397,10 +397,10 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } if let file = file { let media = WallpaperPreviewMedia(content: .file(file: file, colors: [], rotation: nil, intensity: nil, true, isSupported)) - mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) + mediaAndFlags = ([media], ChatMessageAttachedContentNodeMediaFlags()) } else if let settings = settings { let media = WallpaperPreviewMedia(content: .themeSettings(settings)) - mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) + mediaAndFlags = ([media], ChatMessageAttachedContentNodeMediaFlags()) } } } @@ -479,6 +479,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent break } } + for attribute in webpage.attributes { + if case let .stickerPack(stickerPack) = attribute, !stickerPack.files.isEmpty { + mediaAndFlags = (stickerPack.files, .preferMediaInline) + break + } + } if defaultWebpageImageSizeIsSmall(webpage: webpage) { mediaAndFlags?.1.insert(.preferMediaInline) @@ -514,7 +520,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent for media in item.message.media { switch media { case _ as TelegramMediaImage, _ as TelegramMediaFile, _ as TelegramMediaStory: - mediaAndFlags = (media, [.preferMediaInline]) + mediaAndFlags = ([media], [.preferMediaInline]) default: break } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift index 838538984c..cf84864576 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaCutoutScreen.swift @@ -258,7 +258,7 @@ private final class MediaCutoutScreenComponent: Component { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let result = super.hitTest(point, with: event) if let controller = self.environment?.controller() as? MediaCutoutScreen, [.erase, .restore].contains(controller.mode), result == self.previewContainerView { - return nil//controller.previewView.superview + return nil } return result } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 0b8a8b6069..d2c4f92f96 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -2418,7 +2418,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } - final class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate { + final class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate, UIScrollViewDelegate { private weak var controller: MediaEditorScreen? private let context: AccountContext fileprivate var interaction: DrawingToolsInteraction? @@ -2438,6 +2438,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate fileprivate let toolValue: ComponentView fileprivate let previewContainerView: UIView + fileprivate let previewScrollView: UIScrollView fileprivate let previewContentContainerView: PortalSourceView private var transitionInView: UIImageView? @@ -2514,7 +2515,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.componentHost = ComponentView() self.storyPreview = ComponentView() self.toolValue = ComponentView() - + self.previewContainerView = UIView() self.previewContainerView.alpha = 0.0 self.previewContainerView.clipsToBounds = true @@ -2523,6 +2524,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.previewContainerView.layer.cornerCurve = .continuous } + self.previewScrollView = UIScrollView() + self.previewScrollView.contentInsetAdjustmentBehavior = .never + self.previewScrollView.contentInset = .zero + self.previewScrollView.showsHorizontalScrollIndicator = false + self.previewScrollView.showsVerticalScrollIndicator = false + self.previewScrollView.panGestureRecognizer.minimumNumberOfTouches = 2 + self.previewScrollView.isScrollEnabled = false + self.previewContentContainerView = PortalSourceView() self.gradientView = UIImageView() @@ -2568,6 +2577,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.view.addSubview(self.backgroundDimView) self.view.addSubview(self.containerView) + + self.previewScrollView.delegate = self + self.containerView.addSubview(self.previewContainerView) if case .stickerEditor = controller.mode { @@ -2597,7 +2609,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.previewContainerView.addSubview(self.gradientView) } - self.previewContainerView.addSubview(self.previewContentContainerView) + self.previewContainerView.addSubview(self.previewScrollView) + self.previewScrollView.addSubview(self.previewContentContainerView) self.previewContentContainerView.addSubview(self.previewView) self.previewContentContainerView.addSubview(self.entitiesContainerView) @@ -3050,7 +3063,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate controller.stickerSelectedEmoji = emoji let stickerEntity = DrawingStickerEntity(content: .file(.standalone(media: sticker), .sticker)) stickerEntity.referenceDrawingSize = storyDimensions - stickerEntity.scale = 4.0 + stickerEntity.scale = 4.0 * 0.97 stickerEntity.position = CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0) self.entitiesView.add(stickerEntity, announce: false) } @@ -3262,6 +3275,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } ) + + Queue.mainQueue().after(0.1) { + self.previewScrollView.pinchGestureRecognizer?.isEnabled = false + } } @objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { @@ -4436,6 +4453,53 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } } + private func adjustPreviewZoom(updating: Bool = false) { + let minScale: CGFloat = 0.05 + let maxScale: CGFloat = 3.0 + + if self.previewScrollView.minimumZoomScale != minScale { + self.previewScrollView.minimumZoomScale = minScale + } + if self.previewScrollView.maximumZoomScale != maxScale { + self.previewScrollView.maximumZoomScale = maxScale + } + + let boundsSize = self.previewScrollView.frame.size + var contentFrame = self.previewContentContainerView.frame + if boundsSize.width > contentFrame.size.width { + contentFrame.origin.x = (boundsSize.width - contentFrame.size.width) / 2.0 + } else { + contentFrame.origin.x = 0.0 + } + + if boundsSize.height > contentFrame.size.height { + contentFrame.origin.y = (boundsSize.height - contentFrame.size.height) / 2.0 + } else { + contentFrame.origin.y = 0.0 + } + self.previewContentContainerView.frame = contentFrame + + if !updating { + self.stickerMaskDrawingView?.updateZoomScale(self.previewScrollView.zoomScale) + } + } + + func scrollViewDidZoom(_ scrollView: UIScrollView) { + self.adjustPreviewZoom() + } + + func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { + self.adjustPreviewZoom() + + if scrollView.zoomScale < 1.0 { + scrollView.setZoomScale(1.0, animated: true) + } + } + + func viewForZooming(in scrollView: UIScrollView) -> UIView? { + return self.previewContentContainerView + } + fileprivate var drawingScreen: DrawingScreen? fileprivate var stickerScreen: StickerPickerScreen? fileprivate weak var cutoutScreen: MediaCutoutScreen? @@ -4723,6 +4787,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate guard let mediaEditor = self.mediaEditor, let stickerMaskDrawingView = self.stickerMaskDrawingView, let stickerBackgroundView = self.stickerBackgroundView else { return } + + if [.cutoutErase, .cutoutRestore].contains(mode) { + self.previewScrollView.isScrollEnabled = true + self.previewScrollView.pinchGestureRecognizer?.isEnabled = true + } + let cutoutController = MediaCutoutScreen( context: self.context, mode: cutoutMode, @@ -4746,6 +4816,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } cutoutController.dismissed = { [weak self] in if let self { + self.previewScrollView.setZoomScale(1.0, animated: true) + self.previewScrollView.isScrollEnabled = false + self.previewScrollView.pinchGestureRecognizer?.isEnabled = false self.animateInFromTool(inPlace: true) } } @@ -4911,8 +4984,18 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let previewFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - previewSize.width) / 2.0), y: topInset - bottomInputOffset + self.dismissOffset), size: previewSize) transition.setFrame(view: self.previewContainerView, frame: previewFrame) + transition.setFrame(view: self.previewScrollView, frame: CGRect(origin: .zero, size: previewSize)) - transition.setFrame(view: self.previewContentContainerView, frame: CGRect(origin: .zero, size: previewSize)) + if self.previewScrollView.contentSize == .zero { + self.previewScrollView.zoomScale = 1.0 + self.previewScrollView.contentSize = previewSize + } + + if abs(self.previewContentContainerView.bounds.width - previewSize.width) > 1.0 { + transition.setFrame(view: self.previewContentContainerView, frame: CGRect(origin: .zero, size: previewSize)) + } + + self.adjustPreviewZoom(updating: true) transition.setFrame(view: self.previewView, frame: CGRect(origin: .zero, size: previewSize)) let entitiesViewScale = previewSize.width / storyDimensions.width @@ -5125,6 +5208,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate public var completion: (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _ in } public var dismissed: () -> Void = { } public var willDismiss: () -> Void = { } + public var sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? private var adminedChannels = Promise<[EnginePeer]>() private var closeFriends = Promise<[EnginePeer]>() @@ -6367,9 +6451,25 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate contextItems.append(.custom(StickerPackListContextItem(context: self.context, packs: self.myStickerPacks, packSelected: { [weak self] pack in guard let self else { - return + return true + } + if pack.count >= 120 { + let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.MediaEditor_StickersTooMuch, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { [weak self] action in + if case .info = action, let self { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories, forceDark: true, dismissed: { + + }) + self.push(controller) + } + return false + }) + self.hapticFeedback.error() + self.present(controller, in: .window(.root)) + return false + } else { + self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title)) + return true } - self.uploadSticker(file, action: .addToStickerPack(pack: .id(id: pack.id.id, accessHash: pack.accessHash), title: pack.title)) }), false)) let items = ContextController.Items( @@ -6552,7 +6652,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } self.present(controller, in: .window(.root)) } - + private let stickerUploadDisposable = MetaDisposable() private func uploadSticker(_ file: TelegramMediaFile, action: StickerAction) { let context = self.context diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift index 05f167e9f5..67da5b8fe1 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StickerPackListContextItem.swift @@ -13,9 +13,9 @@ import ContextUI final class StickerPackListContextItem: ContextMenuCustomItem { let context: AccountContext let packs: [(StickerPackCollectionInfo, StickerPackItem?)] - let packSelected: (StickerPackCollectionInfo) -> Void + let packSelected: (StickerPackCollectionInfo) -> Bool - init(context: AccountContext, packs: [(StickerPackCollectionInfo, StickerPackItem?)], packSelected: @escaping (StickerPackCollectionInfo) -> Void) { + init(context: AccountContext, packs: [(StickerPackCollectionInfo, StickerPackItem?)], packSelected: @escaping (StickerPackCollectionInfo) -> Bool) { self.context = context self.packs = packs self.packSelected = packSelected @@ -75,9 +75,9 @@ private final class StickerPackListContextItemNode: ASDisplayNode, ContextMenuCu } let action = ContextMenuActionItem(text: pack.title, textLayout: .singleLine, icon: { _ in nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { _, f in - f(.dismissWithoutContent) - - item.packSelected(pack) + if item.packSelected(pack) { + f(.dismissWithoutContent) + } }) let actionNode = ContextControllerActionsListActionItemNode(getController: getController, requestDismiss: actionSelected, requestUpdateAction: { _, _ in }, item: action) actionNodes.append(actionNode) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index b2304b60c9..34f0852b8a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -7642,6 +7642,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let editMessage = interfaceState.editMessage, let message = combinedInitialData.initialData?.associatedMessages[editMessage.messageId] { let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message) updated = updatedState + strongSelf.editingUrlPreviewQueryState?.1.dispose() strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState } updated = updated.updatedSlowmodeState(slowmodeState) @@ -8979,6 +8980,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message) updated = updatedState + strongSelf.editingUrlPreviewQueryState?.1.dispose() strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState updated = updated.updatedInputMode({ _ in diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 7f9aa96657..965451a3ec 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -1772,6 +1772,9 @@ extension ChatControllerImpl { } } as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void ) + editorController.sendSticker = { [weak self] file, sourceView, sourceRect in + return self?.interfaceInteraction?.sendSticker(file, true, sourceView, sourceRect, nil, []) ?? false + } self.push(editorController) }, dismissed: {} diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 80944b6120..78386b6264 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -761,7 +761,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { if file.isAnimatedSticker { thumbnailItem = .animated(EngineMediaResource(file.resource)) resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: file.resource) - } else if let dimensions = file.dimensions, let resource = chatMessageStickerResource(file: file, small: false) as? TelegramMediaResource { + } else if let dimensions = file.dimensions, let resource = chatMessageStickerResource(file: file, small: true) as? TelegramMediaResource { thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)) resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: resource) }