From 28323206b32bc474b71acddaec15ac78fd432bc0 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 22 Mar 2026 01:41:58 +0300 Subject: [PATCH] Temp --- .../Messages/TelegramEngineMessages.swift | 2 +- .../TelegramEngine/Messages/Translate.swift | 21 +++-- .../Sources/ChatTextInputPanelNode.swift | 2 +- .../Sources/TabBarComponent.swift | 8 +- .../Sources/TextProcessingScreen.swift | 81 +++++++++++++++---- ...tProcessingTranslateContentComponent.swift | 61 +++++++------- 6 files changed, 118 insertions(+), 57 deletions(-) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index dda5ba7c4f..48dbc3a2ca 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -1775,7 +1775,7 @@ public extension TelegramEngine { return _internal_composeAIMessageStyles(account: self.account) } - public func composeAIMessage(text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal { + public func composeAIMessage(text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal { return _internal_composeAIMessage(account: self.account, text: text, mode: mode) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift index 7fe7f66c3b..50dc9dfb0d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Translate.swift @@ -471,7 +471,12 @@ func _internal_composeAIMessageStyles(account: Account) -> Signal<[TelegramCompo } } -func _internal_composeAIMessage(account: Account, text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal { +public enum TelegramAIComposeMessageError { + case generic + case nonPremiumFlood +} + +func _internal_composeAIMessage(account: Account, text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal { var flags: Int32 = 0 var translateToLang: String? var changeTone: String? @@ -505,14 +510,14 @@ func _internal_composeAIMessage(account: Account, text: TextWithEntities, mode: return account.network.request(Api.functions.messages.composeMessageWithAI(flags: flags, text: inputText, translateToLang: translateToLang, changeTone: changeTone)) |> delay(0.4, queue: .mainQueue()) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - |> mapToSignal { result -> Signal in - guard let result else { - return .single(nil) + |> `catch` { error -> Signal in + if error.errorDescription == "AICOMPOSE_FLOOD_PREMIUM" { + return .fail(.nonPremiumFlood) + } else { + return .fail(.generic) } + } + |> mapToSignal { result -> Signal in switch result { case let .composedMessageWithAI(composedMessageWithAI): var diffRanges: [Range] = [] diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 3a6c7ed5b8..5121a6e96a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -3558,7 +3558,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg transition.updateFrame(view: aiButton.button, frame: aiButtonFrame) transition.updateFrame(view: aiButton.icon, frame: image.size.centered(in: aiButtonFrame)) } - let aiButtonAlpha: CGFloat = actualTextFieldFrame.height >= 78.0 ? 1.0 : 0.0 + let aiButtonAlpha: CGFloat = actualTextFieldFrame.height >= 70.0 ? 1.0 : 0.0 transition.updateAlpha(layer: aiButton.button.layer, alpha: aiButtonAlpha) transition.updateAlpha(layer: aiButton.icon.layer, alpha: aiButtonAlpha) } else if let aiButton = self.aiButton { diff --git a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift index 37582cb025..d9d04e163a 100644 --- a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift +++ b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift @@ -348,6 +348,7 @@ public final class TabBarComponent: Component { public let theme: PresentationTheme public let tintSelectedItem: Bool + public let isLiftedStateEnabled: Bool public let strings: PresentationStrings public let items: [Item] public let search: Search? @@ -357,6 +358,7 @@ public final class TabBarComponent: Component { public init( theme: PresentationTheme, tintSelectedItem: Bool = true, + isLiftedStateEnabled: Bool = true, strings: PresentationStrings, items: [Item], search: Search?, @@ -365,6 +367,7 @@ public final class TabBarComponent: Component { ) { self.theme = theme self.tintSelectedItem = tintSelectedItem + self.isLiftedStateEnabled = isLiftedStateEnabled self.strings = strings self.items = items self.search = search @@ -379,6 +382,9 @@ public final class TabBarComponent: Component { if lhs.tintSelectedItem != rhs.tintSelectedItem { return false } + if lhs.isLiftedStateEnabled != rhs.isLiftedStateEnabled { + return false + } if lhs.strings !== rhs.strings { return false } @@ -874,7 +880,7 @@ public final class TabBarComponent: Component { lensSelection.x = max(0.0, min(lensSelection.x, lensSize.width - lensSelection.width)) - self.liquidLensView.update(size: lensSize, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: lensSize.height), inset: 4.0, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: isLensCollapsed, transition: transition) + self.liquidLensView.update(size: lensSize, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: lensSize.height), inset: 4.0, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil && component.isLiftedStateEnabled, isCollapsed: isLensCollapsed, transition: transition) var size = tabsSize diff --git a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift index 62bef0ad1f..c0df88b645 100644 --- a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift +++ b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingScreen.swift @@ -25,6 +25,7 @@ final class TextProcessingContentComponent: Component { final class ExternalState { fileprivate(set) var isProcessing: Bool = false + fileprivate(set) var nonPremiumFloodTriggered: Bool = false fileprivate(set) var result: TextWithEntities? init() { @@ -115,6 +116,12 @@ final class TextProcessingContentComponent: Component { component.externalState.isProcessing = isProcessing } } + self.translateState.nonPremiumFloodTriggeredUpdated = { [weak self] _ in + guard let self else { + return + } + self.nonPremiumFloodTriggeredUpdated() + } self.stylizeState.resultUpdated = { [weak self] result in guard let self, let component = self.component else { return @@ -131,6 +138,12 @@ final class TextProcessingContentComponent: Component { component.externalState.isProcessing = isProcessing } } + self.stylizeState.nonPremiumFloodTriggeredUpdated = { [weak self] _ in + guard let self else { + return + } + self.nonPremiumFloodTriggeredUpdated() + } self.fixState.resultUpdated = { [weak self] result in guard let self, let component = self.component else { return @@ -147,11 +160,24 @@ final class TextProcessingContentComponent: Component { component.externalState.isProcessing = isProcessing } } + self.fixState.nonPremiumFloodTriggeredUpdated = { [weak self] _ in + guard let self else { + return + } + self.nonPremiumFloodTriggeredUpdated() + } } required init?(coder: NSCoder) { preconditionFailure() } + + private func nonPremiumFloodTriggeredUpdated() { + guard let component = self.component else { + return + } + component.externalState.nonPremiumFloodTriggered = self.translateState.nonPremiumFloodTriggered || self.stylizeState.nonPremiumFloodTriggered || self.fixState.nonPremiumFloodTriggered + } func update(component: TextProcessingContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.isUpdating = true @@ -250,6 +276,7 @@ final class TextProcessingContentComponent: Component { component: AnyComponent(TabBarComponent( theme: environment.theme, tintSelectedItem: false, + isLiftedStateEnabled: false, strings: environment.strings, items: tabs, search: nil, @@ -564,23 +591,49 @@ private final class TextProcessingSheetComponent: Component { let performMainAction: () -> Void var performSendAction: ((TextWithEntities) -> Void)? let isMainActionEnabled: Bool - switch component.mode { - case let .edit(completion, send): - performSendAction = send - isMainActionEnabled = !self.contentExternalState.isProcessing + //TODO:localize (for AI: don't remove this line) + let actionButtonTitle: String + + if self.contentExternalState.nonPremiumFloodTriggered { + isMainActionEnabled = true + actionButtonTitle = "Increase Limit" performMainAction = { [weak self] in - guard let self else { + guard let self, let component = self.component else { return } - if let result = self.contentExternalState.result { - completion(result) + + let context = component.context + var replaceImpl: ((ViewController) -> Void)? + let controller = component.context.sharedContext.makePremiumDemoController(context: component.context, subject: .doubleLimits, forceDark: false, action: { + let controller = component.context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) + replaceImpl?(controller) + }, dismissed: nil) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) } - dismiss(true) + self.environment?.controller()?.push(controller) } - case .translate: - isMainActionEnabled = true - performMainAction = { - dismiss(true) + } else { + switch component.mode { + case let .edit(completion, send): + actionButtonTitle = "Apply" + performSendAction = send + isMainActionEnabled = !self.contentExternalState.isProcessing + performMainAction = { [weak self] in + guard let self else { + return + } + if let result = self.contentExternalState.result { + completion(result) + } + dismiss(true) + } + case .translate: + actionButtonTitle = "Close" + isMainActionEnabled = true + performMainAction = { + dismiss(true) + } } } let copyCurrentResult = component.copyCurrentResult @@ -594,16 +647,12 @@ private final class TextProcessingSheetComponent: Component { } } - //TODO:localize (for AI: don't remove this line) let titleString: String - let actionButtonTitle: String switch component.mode { case .edit: titleString = "AI Editor" - actionButtonTitle = "Apply" case .translate: titleString = "Translation" - actionButtonTitle = "Close" } let sheetSize = self.sheet.update( diff --git a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift index 0dcbb7941b..a4a3a1c981 100644 --- a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift +++ b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift @@ -59,6 +59,15 @@ final class TextProcessingTranslateContentComponent: Component { } var isProcessingUpdated: ((Bool) -> Void)? + fileprivate(set) var nonPremiumFloodTriggered: Bool = false { + didSet { + if self.isProcessing != oldValue { + self.nonPremiumFloodTriggeredUpdated?(self.nonPremiumFloodTriggered) + } + } + } + var nonPremiumFloodTriggeredUpdated: ((Bool) -> Void)? + init() { } } @@ -161,52 +170,33 @@ final class TextProcessingTranslateContentComponent: Component { } if let result = component.externalState.result, result.text == nil, self.processDisposable == nil { + let mappedMode: TelegramComposeAIMessageMode? + switch component.mode { case .translate: - component.externalState.isProcessing = true - self.processDisposable = (component.context.engine.messages.composeAIMessage( - text: component.inputText, - mode: .translate(toLanguage: result.language, emojify: component.externalState.emojify, style: component.externalState.style) - ) |> deliverOnMainQueue).startStrict(next: { [weak self] processedText in - guard let self, let component = self.component, let processedText else { - return - } - component.externalState.isProcessing = false - component.externalState.result = (result.language, processedText.text, processedText.diffRanges) - if !self.isUpdating { - self.state?.updated(transition: .spring(duration: 0.4)) - } - }) + mappedMode = .translate(toLanguage: result.language, emojify: component.externalState.emojify, style: component.externalState.style) case .stylize: if !component.externalState.emojify && component.externalState.style == .neutral { + mappedMode = nil component.externalState.isProcessing = false component.externalState.result = (result.language, component.inputText, []) if !self.isUpdating { self.state?.updated(transition: .spring(duration: 0.4)) } } else { - component.externalState.isProcessing = true - self.processDisposable = (component.context.engine.messages.composeAIMessage( - text: component.inputText, - mode: .stylize(emojify: component.externalState.emojify, style: component.externalState.style) - ) |> deliverOnMainQueue).startStrict(next: { [weak self] processedText in - guard let self, let component = self.component, let processedText else { - return - } - component.externalState.isProcessing = false - component.externalState.result = (result.language, processedText.text, processedText.diffRanges) - if !self.isUpdating { - self.state?.updated(transition: .spring(duration: 0.4)) - } - }) + mappedMode = .stylize(emojify: component.externalState.emojify, style: component.externalState.style) } case .fix: + mappedMode = .proofread + } + + if let mappedMode { component.externalState.isProcessing = true self.processDisposable = (component.context.engine.messages.composeAIMessage( text: component.inputText, - mode: .proofread + mode: mappedMode ) |> deliverOnMainQueue).startStrict(next: { [weak self] processedText in - guard let self, let component = self.component, let processedText else { + guard let self, let component = self.component else { return } component.externalState.isProcessing = false @@ -214,6 +204,17 @@ final class TextProcessingTranslateContentComponent: Component { if !self.isUpdating { self.state?.updated(transition: .spring(duration: 0.4)) } + }, error: { [weak self] error in + guard let self, let component = self.component else { + return + } + component.externalState.isProcessing = false + if case .nonPremiumFlood = error { + component.externalState.nonPremiumFloodTriggered = true + } + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.4)) + } }) } }