[WIP] Stickers editor

This commit is contained in:
Ilya Laktyushin 2024-04-09 00:50:37 +04:00
parent ff5307a254
commit 70eef8ecac
16 changed files with 204 additions and 81 deletions

View File

@ -11904,3 +11904,5 @@ Sorry for the inconvenience.";
"Conversation.ViewStickers" = "VIEW STICKERS"; "Conversation.ViewStickers" = "VIEW STICKERS";
"Conversation.ViewEmojis" = "VIEW EMOJIS"; "Conversation.ViewEmojis" = "VIEW EMOJIS";
"MediaEditor.StickersTooMuch" = "Sorry, you've reached the maximum number of stickers in this set. Try a different one.";

View File

@ -314,7 +314,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker { if stickerItem.file.isAnimatedSticker || stickerItem.file.isVideoSticker {
let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) let dimensions = stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
if stickerItem.file.isVideoSticker { 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 { } 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)))) 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))))
} }

View File

@ -1250,6 +1250,7 @@ private final class StickerPackContainer: ASDisplayNode {
let presentationData = self.presentationData let presentationData = self.presentationData
let updatedPresentationData = self.controller?.updatedPresentationData let updatedPresentationData = self.controller?.updatedPresentationData
let navigationController = self.controller?.parentNavigationController as? NavigationController let navigationController = self.controller?.parentNavigationController as? NavigationController
let sendSticker = self.controller?.sendSticker
var dismissImpl: (() -> Void)? var dismissImpl: (() -> Void)?
let mainController = context.sharedContext.makeStickerMediaPickerScreen( let mainController = context.sharedContext.makeStickerMediaPickerScreen(
@ -1275,7 +1276,7 @@ private final class StickerPackContainer: ASDisplayNode {
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
commit() 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)) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
@ -1301,6 +1302,7 @@ private final class StickerPackContainer: ASDisplayNode {
let presentationData = self.presentationData let presentationData = self.presentationData
let updatedPresentationData = self.controller?.updatedPresentationData let updatedPresentationData = self.controller?.updatedPresentationData
let navigationController = self.controller?.parentNavigationController as? NavigationController let navigationController = self.controller?.parentNavigationController as? NavigationController
let sendSticker = self.controller?.sendSticker
let context = self.context let context = self.context
let controller = self.context.sharedContext.makeStickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData, completion: { file in 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 packReference: StickerPackReference = .id(id: info.id.id, accessHash: info.accessHash)
let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker) let _ = (context.engine.stickers.addStickerToStickerSet(packReference: packReference, sticker: sticker)
|> deliverOnMainQueue).start(completed: { |> 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)) (navigationController?.viewControllers.last as? ViewController)?.present(packController, in: .window(.root))
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
@ -1345,8 +1347,10 @@ private final class StickerPackContainer: ASDisplayNode {
} }
let context = self.context let context = self.context
let presentationData = self.presentationData
let updatedPresentationData = self.controller?.updatedPresentationData let updatedPresentationData = self.controller?.updatedPresentationData
let navigationController = self.controller?.parentNavigationController as? NavigationController let navigationController = self.controller?.parentNavigationController as? NavigationController
let sendSticker = self.controller?.sendSticker
self.controller?.dismiss() self.controller?.dismiss()
@ -1369,8 +1373,12 @@ private final class StickerPackContainer: ASDisplayNode {
|> deliverOnMainQueue).start(completed: { |> deliverOnMainQueue).start(completed: {
commit() 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)) (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 private let initialSelectedStickerPackIndex: Int
fileprivate weak var parentNavigationController: NavigationController? 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)? private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
fileprivate var controllerNode: StickerPackScreenNode { fileprivate var controllerNode: StickerPackScreenNode {

View File

@ -162,7 +162,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
self.activateBadgeAction?() 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<Bool> { public func makeProgress() -> Promise<Bool> {
let progress = Promise<Bool>() let progress = Promise<Bool>()
@ -302,7 +302,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
var mediaAndFlags = mediaAndFlags var mediaAndFlags = mediaAndFlags
if let mediaAndFlagsValue = 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 var flags = mediaAndFlagsValue.1
flags.remove(.preferMediaInline) flags.remove(.preferMediaInline)
mediaAndFlags = (mediaAndFlagsValue.0, flags) mediaAndFlags = (mediaAndFlagsValue.0, flags)
@ -315,50 +315,52 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
} }
var contentMediaInline = false var contentMediaInline = false
if let (media, flags) = mediaAndFlags { if let (mediaArray, flags) = mediaAndFlags {
contentMediaInline = flags.contains(.preferMediaInline) contentMediaInline = flags.contains(.preferMediaInline)
if let file = media as? TelegramMediaFile { if let media = mediaArray.first {
if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 { if let file = media as? TelegramMediaFile {
contentMediaValue = file if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 {
} else if file.isInstantVideo { contentMediaValue = file
contentMediaValue = file } else if file.isInstantVideo {
} else if file.isVideo { contentMediaValue = file
contentMediaValue = file } else if file.isVideo {
} else if file.isSticker || file.isAnimatedSticker { contentMediaValue = file
contentMediaValue = file } else if file.isSticker || file.isAnimatedSticker {
} else { contentMediaValue = file
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
} else { } else {
willDownloadOrLocal = context.account.postbox.mediaBox.completedResourcePath(file.resource) != nil contentFileValue = file
} }
if willDownloadOrLocal {
contentMediaAutomaticPlayback = true if shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file) {
contentMediaAspectFilled = true 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.context = context
self.message = message self.message = message
self.media = mediaAndFlags?.0 self.media = mediaAndFlags?.0.first
self.theme = presentationData.theme self.theme = presentationData.theme
self.mainColor = mainColor self.mainColor = mainColor

View File

@ -50,7 +50,7 @@ public final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessag
} else { } else {
text = item.message.text 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) 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)

View File

@ -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 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 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) 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)

View File

@ -50,7 +50,7 @@ public final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBub
} else { } else {
text = item.message.text 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) 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)

View File

@ -67,16 +67,16 @@ public final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNod
var title: String? var title: String?
var text: String? var text: String?
var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)?
if let game = game { if let game = game {
title = game.title title = game.title
text = game.description text = game.description
if let file = game.file { if let file = game.file {
mediaAndFlags = (file, [.preferMediaBeforeText]) mediaAndFlags = ([file], [.preferMediaBeforeText])
} else if let image = game.image { } else if let image = game.image {
mediaAndFlags = (image, [.preferMediaBeforeText]) mediaAndFlags = ([image], [.preferMediaBeforeText])
} }
} }

View File

@ -52,7 +52,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent
var title: String? var title: String?
var subtitle: NSAttributedString? = nil var subtitle: NSAttributedString? = nil
var text: String? var text: String?
var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)?
var automaticDownloadSettings = item.controllerInteraction.automaticMediaDownloadSettings var automaticDownloadSettings = item.controllerInteraction.automaticMediaDownloadSettings
if let invoice = invoice { if let invoice = invoice {
@ -61,7 +61,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent
if let image = invoice.photo { if let image = invoice.photo {
automaticDownloadSettings = MediaAutoDownloadSettings.defaultSettings automaticDownloadSettings = MediaAutoDownloadSettings.defaultSettings
mediaAndFlags = (image, [.preferMediaBeforeText]) mediaAndFlags = ([image], [.preferMediaBeforeText])
} else { } else {
let invoiceLabel = item.presentationData.strings.Message_InvoiceLabel let invoiceLabel = item.presentationData.strings.Message_InvoiceLabel
var invoiceText = "\(formatCurrencyAmount(invoice.totalAmount, currency: invoice.currency)) " var invoiceText = "\(formatCurrencyAmount(invoice.totalAmount, currency: invoice.currency)) "

View File

@ -242,7 +242,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
var text: String? var text: String?
var entities: [MessageTextEntity]? var entities: [MessageTextEntity]?
var titleBadge: String? var titleBadge: String?
var mediaAndFlags: (Media, ChatMessageAttachedContentNodeMediaFlags)? var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)?
var badge: String? var badge: String?
var actionIcon: ChatMessageAttachedContentActionIcon? var actionIcon: ChatMessageAttachedContentActionIcon?
@ -307,9 +307,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
if let file = mainMedia as? TelegramMediaFile, webpage.type != "telegram_theme" { if let file = mainMedia as? TelegramMediaFile, webpage.type != "telegram_theme" {
if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty { if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty {
if automaticPlayback { if automaticPlayback {
mediaAndFlags = (file, [.preferMediaBeforeText]) mediaAndFlags = ([file], [.preferMediaBeforeText])
} else { } else {
mediaAndFlags = (webpage.image ?? file, [.preferMediaBeforeText]) mediaAndFlags = ([webpage.image ?? file], [.preferMediaBeforeText])
} }
} else if webpage.type == "telegram_background" { } else if webpage.type == "telegram_background" {
var colors: [UInt32] = [] var colors: [UInt32] = []
@ -321,12 +321,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
intensity = intensityValue intensity = intensityValue
} }
let media = WallpaperPreviewMedia(content: .file(file: file, colors: colors, rotation: rotation, intensity: intensity, false, false)) 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 { if let fileSize = file.size {
badge = dataSizeString(fileSize, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData)) badge = dataSizeString(fileSize, formatting: DataSizeStringFormatting(chatPresentationData: item.presentationData))
} }
} else { } else {
mediaAndFlags = (file, []) mediaAndFlags = ([file], [])
} }
} else if let image = mainMedia as? TelegramMediaImage { } else if let image = mainMedia as? TelegramMediaImage {
if let type = webpage.type, ["photo", "video", "embed", "gif", "document", "telegram_album"].contains(type) { 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 { } else if let embedUrl = webpage.embedUrl, !embedUrl.isEmpty {
flags.insert(.preferMediaBeforeText) flags.insert(.preferMediaBeforeText)
} }
mediaAndFlags = (image, flags) mediaAndFlags = ([image], flags)
} else if let _ = largestImageRepresentation(image.representations)?.dimensions { } else if let _ = largestImageRepresentation(image.representations)?.dimensions {
let flags = ChatMessageAttachedContentNodeMediaFlags() let flags = ChatMessageAttachedContentNodeMediaFlags()
mediaAndFlags = (image, flags) mediaAndFlags = ([image], flags)
} }
} else if let story = mainMedia as? TelegramMediaStory { } 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 { if let storyItem = item.message.associatedStories[story.storyId]?.get(Stories.StoredItem.self), case let .item(itemValue) = storyItem {
text = itemValue.text text = itemValue.text
entities = itemValue.entities entities = itemValue.entities
@ -372,7 +372,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
} }
if let content = content { if let content = content {
let media = WallpaperPreviewMedia(content: content) let media = WallpaperPreviewMedia(content: content)
mediaAndFlags = (media, []) mediaAndFlags = ([media], [])
} }
} else if type == "telegram_theme" { } else if type == "telegram_theme" {
var file: TelegramMediaFile? var file: TelegramMediaFile?
@ -397,10 +397,10 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
} }
if let file = file { if let file = file {
let media = WallpaperPreviewMedia(content: .file(file: file, colors: [], rotation: nil, intensity: nil, true, isSupported)) 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 { } else if let settings = settings {
let media = WallpaperPreviewMedia(content: .themeSettings(settings)) let media = WallpaperPreviewMedia(content: .themeSettings(settings))
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags()) mediaAndFlags = ([media], ChatMessageAttachedContentNodeMediaFlags())
} }
} }
} }
@ -479,6 +479,12 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
break break
} }
} }
for attribute in webpage.attributes {
if case let .stickerPack(stickerPack) = attribute, !stickerPack.files.isEmpty {
mediaAndFlags = (stickerPack.files, .preferMediaInline)
break
}
}
if defaultWebpageImageSizeIsSmall(webpage: webpage) { if defaultWebpageImageSizeIsSmall(webpage: webpage) {
mediaAndFlags?.1.insert(.preferMediaInline) mediaAndFlags?.1.insert(.preferMediaInline)
@ -514,7 +520,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
for media in item.message.media { for media in item.message.media {
switch media { switch media {
case _ as TelegramMediaImage, _ as TelegramMediaFile, _ as TelegramMediaStory: case _ as TelegramMediaImage, _ as TelegramMediaFile, _ as TelegramMediaStory:
mediaAndFlags = (media, [.preferMediaInline]) mediaAndFlags = ([media], [.preferMediaInline])
default: default:
break break
} }

View File

@ -258,7 +258,7 @@ private final class MediaCutoutScreenComponent: Component {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event) let result = super.hitTest(point, with: event)
if let controller = self.environment?.controller() as? MediaCutoutScreen, [.erase, .restore].contains(controller.mode), result == self.previewContainerView { 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 return result
} }

View File

@ -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 weak var controller: MediaEditorScreen?
private let context: AccountContext private let context: AccountContext
fileprivate var interaction: DrawingToolsInteraction? fileprivate var interaction: DrawingToolsInteraction?
@ -2438,6 +2438,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
fileprivate let toolValue: ComponentView<Empty> fileprivate let toolValue: ComponentView<Empty>
fileprivate let previewContainerView: UIView fileprivate let previewContainerView: UIView
fileprivate let previewScrollView: UIScrollView
fileprivate let previewContentContainerView: PortalSourceView fileprivate let previewContentContainerView: PortalSourceView
private var transitionInView: UIImageView? private var transitionInView: UIImageView?
@ -2514,7 +2515,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.componentHost = ComponentView<ViewControllerComponentContainer.Environment>() self.componentHost = ComponentView<ViewControllerComponentContainer.Environment>()
self.storyPreview = ComponentView<Empty>() self.storyPreview = ComponentView<Empty>()
self.toolValue = ComponentView<Empty>() self.toolValue = ComponentView<Empty>()
self.previewContainerView = UIView() self.previewContainerView = UIView()
self.previewContainerView.alpha = 0.0 self.previewContainerView.alpha = 0.0
self.previewContainerView.clipsToBounds = true self.previewContainerView.clipsToBounds = true
@ -2523,6 +2524,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.previewContainerView.layer.cornerCurve = .continuous 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.previewContentContainerView = PortalSourceView()
self.gradientView = UIImageView() self.gradientView = UIImageView()
@ -2568,6 +2577,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.view.addSubview(self.backgroundDimView) self.view.addSubview(self.backgroundDimView)
self.view.addSubview(self.containerView) self.view.addSubview(self.containerView)
self.previewScrollView.delegate = self
self.containerView.addSubview(self.previewContainerView) self.containerView.addSubview(self.previewContainerView)
if case .stickerEditor = controller.mode { if case .stickerEditor = controller.mode {
@ -2597,7 +2609,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.previewContainerView.addSubview(self.gradientView) 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.previewView)
self.previewContentContainerView.addSubview(self.entitiesContainerView) self.previewContentContainerView.addSubview(self.entitiesContainerView)
@ -3050,7 +3063,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
controller.stickerSelectedEmoji = emoji controller.stickerSelectedEmoji = emoji
let stickerEntity = DrawingStickerEntity(content: .file(.standalone(media: sticker), .sticker)) let stickerEntity = DrawingStickerEntity(content: .file(.standalone(media: sticker), .sticker))
stickerEntity.referenceDrawingSize = storyDimensions 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) stickerEntity.position = CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0)
self.entitiesView.add(stickerEntity, announce: false) 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 { @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 drawingScreen: DrawingScreen?
fileprivate var stickerScreen: StickerPickerScreen? fileprivate var stickerScreen: StickerPickerScreen?
fileprivate weak var cutoutScreen: MediaCutoutScreen? 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 { guard let mediaEditor = self.mediaEditor, let stickerMaskDrawingView = self.stickerMaskDrawingView, let stickerBackgroundView = self.stickerBackgroundView else {
return return
} }
if [.cutoutErase, .cutoutRestore].contains(mode) {
self.previewScrollView.isScrollEnabled = true
self.previewScrollView.pinchGestureRecognizer?.isEnabled = true
}
let cutoutController = MediaCutoutScreen( let cutoutController = MediaCutoutScreen(
context: self.context, context: self.context,
mode: cutoutMode, mode: cutoutMode,
@ -4746,6 +4816,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
} }
cutoutController.dismissed = { [weak self] in cutoutController.dismissed = { [weak self] in
if let self { if let self {
self.previewScrollView.setZoomScale(1.0, animated: true)
self.previewScrollView.isScrollEnabled = false
self.previewScrollView.pinchGestureRecognizer?.isEnabled = false
self.animateInFromTool(inPlace: true) 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) 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.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)) transition.setFrame(view: self.previewView, frame: CGRect(origin: .zero, size: previewSize))
let entitiesViewScale = previewSize.width / storyDimensions.width 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 completion: (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _ in }
public var dismissed: () -> Void = { } public var dismissed: () -> Void = { }
public var willDismiss: () -> Void = { } public var willDismiss: () -> Void = { }
public var sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
private var adminedChannels = Promise<[EnginePeer]>() private var adminedChannels = Promise<[EnginePeer]>()
private var closeFriends = 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 contextItems.append(.custom(StickerPackListContextItem(context: self.context, packs: self.myStickerPacks, packSelected: { [weak self] pack in
guard let self else { 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)) }), false))
let items = ContextController.Items( let items = ContextController.Items(
@ -6552,7 +6652,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
} }
self.present(controller, in: .window(.root)) self.present(controller, in: .window(.root))
} }
private let stickerUploadDisposable = MetaDisposable() private let stickerUploadDisposable = MetaDisposable()
private func uploadSticker(_ file: TelegramMediaFile, action: StickerAction) { private func uploadSticker(_ file: TelegramMediaFile, action: StickerAction) {
let context = self.context let context = self.context

View File

@ -13,9 +13,9 @@ import ContextUI
final class StickerPackListContextItem: ContextMenuCustomItem { final class StickerPackListContextItem: ContextMenuCustomItem {
let context: AccountContext let context: AccountContext
let packs: [(StickerPackCollectionInfo, StickerPackItem?)] 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.context = context
self.packs = packs self.packs = packs
self.packSelected = packSelected 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 let action = ContextMenuActionItem(text: pack.title, textLayout: .singleLine, icon: { _ in nil }, iconSource: thumbnailIconSource, iconPosition: .left, action: { _, f in
f(.dismissWithoutContent) if item.packSelected(pack) {
f(.dismissWithoutContent)
item.packSelected(pack) }
}) })
let actionNode = ContextControllerActionsListActionItemNode(getController: getController, requestDismiss: actionSelected, requestUpdateAction: { _, _ in }, item: action) let actionNode = ContextControllerActionsListActionItemNode(getController: getController, requestDismiss: actionSelected, requestUpdateAction: { _, _ in }, item: action)
actionNodes.append(actionNode) actionNodes.append(actionNode)

View File

@ -7642,6 +7642,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let editMessage = interfaceState.editMessage, let message = combinedInitialData.initialData?.associatedMessages[editMessage.messageId] { if let editMessage = interfaceState.editMessage, let message = combinedInitialData.initialData?.associatedMessages[editMessage.messageId] {
let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message) let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message)
updated = updatedState updated = updatedState
strongSelf.editingUrlPreviewQueryState?.1.dispose()
strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState
} }
updated = updated.updatedSlowmodeState(slowmodeState) 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) let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message)
updated = updatedState updated = updatedState
strongSelf.editingUrlPreviewQueryState?.1.dispose()
strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState
updated = updated.updatedInputMode({ _ in updated = updated.updatedInputMode({ _ in

View File

@ -1772,6 +1772,9 @@ extension ChatControllerImpl {
} }
} as (MediaEditorScreen.Result, @escaping (@escaping () -> Void) -> Void) -> Void } 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) self.push(editorController)
}, },
dismissed: {} dismissed: {}

View File

@ -761,7 +761,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
if file.isAnimatedSticker { if file.isAnimatedSticker {
thumbnailItem = .animated(EngineMediaResource(file.resource)) thumbnailItem = .animated(EngineMediaResource(file.resource))
resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: 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)) thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: resource) resourceReference = MediaResourceReference.media(media: .standalone(media: file), resource: resource)
} }