diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 9df94346e4..d3eede5b34 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -3109,6 +3109,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if controller.isEmbeddedEditor == true { mediaEditor.onFirstDisplay = { [weak self] in if let self { + if let transitionInView = self.transitionInView { + self.transitionInView = nil + transitionInView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak transitionInView] _ in + transitionInView?.removeFromSuperview() + }) + } + if effectiveSubject.isPhoto { self.previewContainerView.layer.allowsGroupOpacity = true self.previewContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { _ in @@ -3765,6 +3772,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let transitionOut = controller.transitionOut(finished, isNew), let destinationView = transitionOut.destinationView { var destinationTransitionView: UIView? + var destinationTransitionRect: CGRect = .zero if !finished { if let transitionIn = controller.transitionIn, case let .gallery(galleryTransitionIn) = transitionIn, let sourceImage = galleryTransitionIn.sourceImage, isNew != true { let sourceSuperView = galleryTransitionIn.sourceView?.superview?.superview @@ -3774,6 +3782,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate destinationTransitionOutView.frame = self.previewContainerView.convert(self.previewContainerView.bounds, to: sourceSuperView) sourceSuperView?.addSubview(destinationTransitionOutView) destinationTransitionView = destinationTransitionOutView + destinationTransitionRect = galleryTransitionIn.sourceRect } if let view = self.componentHost.view as? MediaEditorScreenComponent.View { view.animateOut(to: .gallery) @@ -3853,7 +3862,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let destinationTransitionView { self.previewContainerView.layer.allowsGroupOpacity = true self.previewContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false) - destinationTransitionView.layer.animateFrame(from: destinationTransitionView.frame, to: destinationView.convert(destinationView.bounds, to: destinationTransitionView.superview), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak destinationTransitionView] _ in + destinationTransitionView.layer.animateFrame(from: destinationTransitionView.frame, to: destinationView.convert(destinationTransitionRect, to: destinationTransitionView.superview), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak destinationTransitionView] _ in destinationTransitionView?.removeFromSuperview() }) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD index f7b3a2de84..987dbc1b7b 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD @@ -43,6 +43,7 @@ swift_library( "//submodules/UndoUI", "//submodules/TelegramUI/Components/PlainButtonComponent", "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/MediaEditorScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index a0f7854d50..dd2cd7e7d9 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -33,6 +33,7 @@ import ShareController import UndoUI import PlainButtonComponent import ComponentDisplayAdapters +import MediaEditorScreen private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6) private let mediaBadgeTextColor = UIColor.white @@ -1266,6 +1267,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr private let listDisposable = MetaDisposable() private var hiddenMediaDisposable: Disposable? + private let updateDisposable = MetaDisposable() private var numberOfItemsToRequest: Int = 50 private var isRequestingView: Bool = false @@ -1765,6 +1767,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr self.hiddenMediaDisposable?.dispose() self.animationTimer?.invalidate() self.presentationDataDisposable?.dispose() + self.updateDisposable.dispose() } public func loadHole(anchor: SparseItemGrid.HoleAnchor, at location: SparseItemGrid.HoleLocation) -> Signal { @@ -1858,16 +1861,54 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr }))) } - /*items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryList_ItemAction_Edit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c.dismiss(completion: { - guard let self else { - return - } - let _ = self - - - }) - })))*/ + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.StoryList_ItemAction_Edit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c.dismiss(completion: { + guard let self else { + return + } + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let peer else { + return + } + + var foundItemLayer: SparseItemGridLayer? + var sourceImage: UIImage? + self.itemGrid.forEachVisibleItem { gridItem in + guard let itemLayer = gridItem.layer as? ItemLayer else { + return + } + if let listItem = itemLayer.item, listItem.story.id == item.id { + foundItemLayer = itemLayer + if let contents = itemLayer.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID { + sourceImage = UIImage(cgImage: contents as! CGImage) + } + } + } + + guard let controller = MediaEditorScreen.makeEditStoryController( + context: self.context, + peer: peer, + storyItem: item, + videoPlaybackPosition: nil, + repost: false, + transitionIn: .gallery(MediaEditorScreen.TransitionIn.GalleryTransitionIn(sourceView: self.itemGrid.view, sourceRect: foundItemLayer?.frame ?? .zero, sourceImage: sourceImage)), + transitionOut: MediaEditorScreen.TransitionOut(destinationView: self.itemGrid.view, destinationRect: foundItemLayer?.frame ?? .zero, destinationCornerRadius: 0.0), + update: { [weak self] disposable in + guard let self else { + return + } + self.updateDisposable.set(disposable) + } + ) else { + return + } + self.parentController?.push(controller) + }) + }) + }))) } if !item.isForwardingDisabled, case .everyone = item.privacy?.base { @@ -1880,7 +1921,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr let _ = (self.context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId) ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in guard let self else { return } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index a0219c6ba5..cd36427f1a 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -4308,7 +4308,7 @@ public final class StoryItemSetContainerComponent: Component { self.sendMessageContext.currentSpeechHolder = speechHolder } case .translate: - self.sendMessageContext.performTranslateTextAction(view: self, text: text.string) + self.sendMessageContext.performTranslateTextAction(view: self, text: text.string, entities: []) case .quote: break } @@ -5359,13 +5359,9 @@ public final class StoryItemSetContainerComponent: Component { private let updateDisposable = MetaDisposable() func openStoryEditing(repost: Bool = false) { - guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else { + guard let component = self.component else { return } - let context = component.context - let peerId = component.slice.peer.id - let item = component.slice.item.storyItem - let id = item.id self.isEditingStory = true self.updateIsProgressPaused() @@ -5376,277 +5372,39 @@ public final class StoryItemSetContainerComponent: Component { videoPlaybackPosition = view.videoPlaybackPosition } - let subject: Signal - subject = getStorySource(engine: component.context.engine, peerId: component.context.account.peerId, id: Int64(item.id)) - |> mapToSignal { source in - if !repost, let source { - return .single(.draft(source, Int64(item.id))) - } else { - let media = item.media._asMedia() - return fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .peer(peerReference.id), customUserContentType: .story, mediaReference: .story(peer: peerReference, id: item.id, media: media)) - |> mapToSignal { (value, isImage) -> Signal in - guard case let .data(data) = value, data.complete else { - return .complete() - } - if let image = UIImage(contentsOfFile: data.path) { - return .single(nil) - |> then( - .single(.image(image, PixelDimensions(image.size), nil, .bottomRight)) - |> delay(0.1, queue: Queue.mainQueue()) - ) - } else { - var duration: Double? - if let file = media as? TelegramMediaFile { - duration = file.duration - } - let symlinkPath = data.path + ".mp4" - if fileSize(symlinkPath) == nil { - let _ = try? FileManager.default.linkItem(atPath: data.path, toPath: symlinkPath) - } - return .single(nil) - |> then( - .single(.video(symlinkPath, nil, false, nil, nil, PixelDimensions(width: 720, height: 1280), duration ?? 0.0, [], .bottomRight)) - ) - } - } - } - } - - let initialCaption: NSAttributedString? - let initialPrivacy: EngineStoryPrivacy? - let initialMediaAreas: [MediaArea] - if repost { - initialCaption = nil - initialPrivacy = nil - initialMediaAreas = [] - } else { - initialCaption = chatInputStateStringWithAppliedEntities(item.text, entities: item.entities) - initialPrivacy = item.privacy - initialMediaAreas = item.mediaAreas - } - - let externalState = MediaEditorTransitionOutExternalState( - storyTarget: nil, - isForcedTarget: false, - isPeerArchived: false, - transitionOut: nil - ) - - var updateProgressImpl: ((Float) -> Void)? - let controller = MediaEditorScreen( - context: context, - mode: .storyEditor, - subject: subject, - isEditing: !repost, - forwardSource: repost ? (component.slice.peer, item) : nil, - initialCaption: initialCaption, - initialPrivacy: initialPrivacy, - initialMediaAreas: initialMediaAreas, - initialVideoPosition: videoPlaybackPosition, + guard let controller = MediaEditorScreen.makeEditStoryController( + context: component.context, + peer: component.slice.peer, + storyItem: component.slice.item.storyItem, + videoPlaybackPosition: videoPlaybackPosition, + repost: repost, transitionIn: .noAnimation, - transitionOut: { finished, isNew in - if repost && finished { - if let transitionOut = externalState.transitionOut?(externalState.storyTarget, externalState.isPeerArchived), let destinationView = transitionOut.destinationView { - return MediaEditorScreen.TransitionOut( - destinationView: destinationView, - destinationRect: transitionOut.destinationRect, - destinationCornerRadius: transitionOut.destinationCornerRadius - ) - } else { - return nil - } - } else { - return nil - } - }, - completion: { [weak self] result, commit in + transitionOut: nil, + completed: { [weak self] in guard let self else { return } - - let entities = generateChatInputTextEntities(result.caption) - - if repost { - let target: Stories.PendingTarget - let targetPeerId: EnginePeer.Id - if let sendAsPeerId = result.options.sendAsPeerId { - target = .peer(sendAsPeerId) - targetPeerId = sendAsPeerId - } else { - target = .myStories - targetPeerId = context.account.peerId - } - externalState.storyTarget = target - - self.component?.controller()?.dismiss(animated: false) - - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: targetPeerId)) - |> deliverOnMainQueue).startStandalone(next: { peer in - guard let peer else { - return - } - - if case let .user(user) = peer { - externalState.isPeerArchived = user.storiesHidden ?? false - - } else if case let .channel(channel) = peer { - externalState.isPeerArchived = channel.storiesHidden ?? false - } - - let forwardInfo = Stories.PendingForwardInfo(peerId: component.slice.peer.id, storyId: item.id, isModified: result.media != nil) - - if let rootController = context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface { - var existingMedia: EngineMedia? - if let _ = result.media { - } else { - existingMedia = item.media - } - rootController.proceedWithStoryUpload(target: target, result: result as! MediaEditorScreenResult, existingMedia: existingMedia, forwardInfo: forwardInfo, externalState: externalState, commit: commit) - } - }) - } else { - var updatedText: String? - var updatedEntities: [MessageTextEntity]? - if result.caption.string != item.text || entities != item.entities { - updatedText = result.caption.string - updatedEntities = entities - } - - if let mediaResult = result.media { - switch mediaResult { - case let .image(image, dimensions): - updateProgressImpl?(0.0) - - let tempFile = TempBox.shared.tempFile(fileName: "file") - defer { - TempBox.shared.dispose(tempFile) - } - if let imageData = compressImageToJPEG(image, quality: 0.7, tempFilePath: tempFile.path) { - self.updateDisposable.set((context.engine.messages.editStory(peerId: peerId, id: id, media: .image(dimensions: dimensions, data: imageData, stickers: result.stickers), mediaAreas: result.mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil) - |> deliverOnMainQueue).startStrict(next: { [weak self] result in - guard let self else { - return - } - switch result { - case let .progress(progress): - updateProgressImpl?(progress) - case .completed: - Queue.mainQueue().after(0.1) { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - - commit({}) - } - } - })) - } - case let .video(content, firstFrameImage, values, duration, dimensions): - updateProgressImpl?(0.0) - - if let valuesData = try? JSONEncoder().encode(values) { - let data = MemoryBuffer(data: valuesData) - let digest = MemoryBuffer(data: data.md5Digest()) - let adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true) - - let resource: TelegramMediaResource - switch content { - case let .imageFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .videoFile(path): - resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments) - case let .asset(localIdentifier): - resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments)) - } - - let tempFile = TempBox.shared.tempFile(fileName: "file") - defer { - TempBox.shared.dispose(tempFile) - } - let firstFrameImageData = firstFrameImage.flatMap { compressImageToJPEG($0, quality: 0.6, tempFilePath: tempFile.path) } - let firstFrameFile = firstFrameImageData.flatMap { data -> TempBoxFile? in - let file = TempBox.shared.tempFile(fileName: "image.jpg") - if let _ = try? data.write(to: URL(fileURLWithPath: file.path)) { - return file - } else { - return nil - } - } - - self.updateDisposable.set((context.engine.messages.editStory(peerId: peerId, id: id, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers), mediaAreas: result.mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil) - |> deliverOnMainQueue).startStrict(next: { [weak self] result in - guard let self else { - return - } - switch result { - case let .progress(progress): - updateProgressImpl?(progress) - case .completed: - Queue.mainQueue().after(0.1) { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - - commit({}) - } - } - })) - } - default: - break - } - } else if updatedText != nil { - let _ = (context.engine.messages.editStory(peerId: peerId, id: id, media: nil, mediaAreas: nil, text: updatedText, entities: updatedEntities, privacy: nil) - |> deliverOnMainQueue).startStandalone(next: { [weak self] result in - switch result { - case .completed: - Queue.mainQueue().after(0.1) { - if let self { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - } - commit({}) - } - default: - break - } - }) - } else { - self.isEditingStory = false - self.rewindCurrentItem() - self.updateIsProgressPaused() - self.state?.updated(transition: .easeInOut(duration: 0.2)) - - HapticFeedback().success() - - commit({}) - } + self.component?.controller()?.dismiss(animated: false) + }, + willDismiss: { [weak self] in + guard let self else { + return } + self.isEditingStory = false + self.rewindCurrentItem() + self.updateIsProgressPaused() + self.state?.updated(transition: .easeInOut(duration: 0.2)) + }, + update: { [weak self] disposable in + guard let self else { + return + } + self.updateDisposable.set(disposable) } - ) - controller.willDismiss = { [weak self] in - self?.isEditingStory = false - self?.rewindCurrentItem() - self?.updateIsProgressPaused() - self?.state?.updated(transition: .easeInOut(duration: 0.2)) + ) else { + return } - controller.navigationPresentation = .flatModal self.component?.controller()?.push(controller) - updateProgressImpl = { [weak controller, weak self] progress in - controller?.updateEditProgress(progress, cancel: { [weak self] in - self?.updateDisposable.set(nil) - }) - } } private func presentSaveUpgradeScreen() { @@ -7059,7 +6817,7 @@ public final class StoryItemSetContainerComponent: Component { guard let self, let component = self.component else { return } - self.sendMessageContext.performTranslateTextAction(view: self, text: component.slice.item.storyItem.text) + self.sendMessageContext.performTranslateTextAction(view: self, text: component.slice.item.storyItem.text, entities: component.slice.item.storyItem.entities) }))) } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 3b495e571a..4f6685fa82 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -1159,7 +1159,7 @@ final class StoryItemSetContainerSendMessage { controller.present(shareController, in: .window(.root)) } - func performTranslateTextAction(view: StoryItemSetContainerComponent.View, text: String) { + func performTranslateTextAction(view: StoryItemSetContainerComponent.View, text: String, entities: [MessageTextEntity]) { guard let component = view.component else { return } @@ -1190,7 +1190,7 @@ final class StoryItemSetContainerSendMessage { let _ = ApplicationSpecificNotice.incrementTranslationSuggestion(accountManager: component.context.sharedContext.accountManager, timestamp: Int32(Date().timeIntervalSince1970)).start() - let translateController = TranslateScreen(context: component.context, forceTheme: defaultDarkPresentationTheme, text: text, canCopy: true, fromLanguage: language, ignoredLanguages: translationSettings.ignoredLanguages) + let translateController = TranslateScreen(context: component.context, forceTheme: defaultDarkPresentationTheme, text: text, entities: entities, canCopy: true, fromLanguage: language, ignoredLanguages: translationSettings.ignoredLanguages) translateController.pushController = { [weak view] c in guard let view, let component = view.component else { return @@ -1762,7 +1762,7 @@ final class StoryItemSetContainerSendMessage { self.sendMessages(view: view, peer: targetPeer, messages: enqueueMessages, silentPosting: silent, scheduleTime: scheduleTime) } else { - let contactController = component.context.sharedContext.makeDeviceContactInfoController(context: component.context, subject: .filter(peer: peerAndContactData.0?._asPeer(), contactId: nil, contactData: contactData, completion: { [weak self, weak view] peer, contactData in + let contactController = component.context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: component.context), environment: ShareControllerAppEnvironment(sharedContext: component.context.sharedContext), subject: .filter(peer: peerAndContactData.0?._asPeer(), contactId: nil, contactData: contactData, completion: { [weak self, weak view] peer, contactData in guard let self, let view else { return } diff --git a/submodules/TranslateUI/BUILD b/submodules/TranslateUI/BUILD index e96f5e190c..6de2b55b35 100644 --- a/submodules/TranslateUI/BUILD +++ b/submodules/TranslateUI/BUILD @@ -27,6 +27,7 @@ swift_library( "//submodules/ComponentFlow:ComponentFlow", "//submodules/Components/ViewControllerComponent:ViewControllerComponent", "//submodules/Components/MultilineTextComponent:MultilineTextComponent", + "//submodules/Components/MultilineTextWithEntitiesComponent:MultilineTextWithEntitiesComponent", "//submodules/Components/BundleIconComponent:BundleIconComponent", "//submodules/UndoUI:UndoUI", "//submodules/ActivityIndicator:ActivityIndicator", diff --git a/submodules/TranslateUI/Sources/TranslateScreen.swift b/submodules/TranslateUI/Sources/TranslateScreen.swift index 560e0f06ba..1cc8c69c9d 100644 --- a/submodules/TranslateUI/Sources/TranslateScreen.swift +++ b/submodules/TranslateUI/Sources/TranslateScreen.swift @@ -11,6 +11,7 @@ import Speak import ComponentFlow import ViewControllerComponent import MultilineTextComponent +import MultilineTextWithEntitiesComponent import BundleIconComponent import UndoUI @@ -35,15 +36,17 @@ private final class TranslateScreenComponent: CombinedComponent { let context: AccountContext let text: String + let entities: [MessageTextEntity] let fromLanguage: String? let toLanguage: String let copyTranslation: ((String) -> Void)? let changeLanguage: (String, String, @escaping (String, String) -> Void) -> Void let expand: () -> Void - init(context: AccountContext, text: String, fromLanguage: String?, toLanguage: String, copyTranslation: ((String) -> Void)?, changeLanguage: @escaping (String, String, @escaping (String, String) -> Void) -> Void, expand: @escaping () -> Void) { + init(context: AccountContext, text: String, entities: [MessageTextEntity], fromLanguage: String?, toLanguage: String, copyTranslation: ((String) -> Void)?, changeLanguage: @escaping (String, String, @escaping (String, String) -> Void) -> Void, expand: @escaping () -> Void) { self.context = context self.text = text + self.entities = entities self.fromLanguage = fromLanguage self.toLanguage = toLanguage self.copyTranslation = copyTranslation @@ -58,6 +61,9 @@ private final class TranslateScreenComponent: CombinedComponent { if lhs.text != rhs.text { return false } + if lhs.entities != rhs.entities { + return false + } if lhs.fromLanguage != rhs.fromLanguage { return false } @@ -995,7 +1001,7 @@ public class TranslateScreen: ViewController { public var wasDismissed: (() -> Void)? - public convenience init(context: AccountContext, forceTheme: PresentationTheme? = nil, text: String, canCopy: Bool, fromLanguage: String?, toLanguage: String? = nil, isExpanded: Bool = false, ignoredLanguages: [String]? = nil) { + public convenience init(context: AccountContext, forceTheme: PresentationTheme? = nil, text: String, entities: [MessageTextEntity] = [], canCopy: Bool, fromLanguage: String?, toLanguage: String? = nil, isExpanded: Bool = false, ignoredLanguages: [String]? = nil) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } var baseLanguageCode = presentationData.strings.baseLanguageCode @@ -1024,7 +1030,7 @@ public class TranslateScreen: ViewController { var copyTranslationImpl: ((String) -> Void)? var changeLanguageImpl: ((String, String, @escaping (String, String) -> Void) -> Void)? var expandImpl: (() -> Void)? - self.init(context: context, component: TranslateScreenComponent(context: context, text: text, fromLanguage: fromLanguage, toLanguage: toLanguage, copyTranslation: !canCopy ? nil : { text in + self.init(context: context, component: TranslateScreenComponent(context: context, text: text, entities: entities, fromLanguage: fromLanguage, toLanguage: toLanguage, copyTranslation: !canCopy ? nil : { text in copyTranslationImpl?(text) }, changeLanguage: { fromLang, toLang, completion in changeLanguageImpl?(fromLang, toLang, completion)