mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Temp
This commit is contained in:
parent
40e1e6a116
commit
28323206b3
6 changed files with 118 additions and 57 deletions
|
|
@ -1775,7 +1775,7 @@ public extension TelegramEngine {
|
|||
return _internal_composeAIMessageStyles(account: self.account)
|
||||
}
|
||||
|
||||
public func composeAIMessage(text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal<TelegramAIComposeMessageResult?, NoError> {
|
||||
public func composeAIMessage(text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal<TelegramAIComposeMessageResult, TelegramAIComposeMessageError> {
|
||||
return _internal_composeAIMessage(account: self.account, text: text, mode: mode)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,7 +471,12 @@ func _internal_composeAIMessageStyles(account: Account) -> Signal<[TelegramCompo
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_composeAIMessage(account: Account, text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal<TelegramAIComposeMessageResult?, NoError> {
|
||||
public enum TelegramAIComposeMessageError {
|
||||
case generic
|
||||
case nonPremiumFlood
|
||||
}
|
||||
|
||||
func _internal_composeAIMessage(account: Account, text: TextWithEntities, mode: TelegramComposeAIMessageMode) -> Signal<TelegramAIComposeMessageResult, TelegramAIComposeMessageError> {
|
||||
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<Api.messages.ComposedMessageWithAI?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<TelegramAIComposeMessageResult?, NoError> in
|
||||
guard let result else {
|
||||
return .single(nil)
|
||||
|> `catch` { error -> Signal<Api.messages.ComposedMessageWithAI, TelegramAIComposeMessageError> in
|
||||
if error.errorDescription == "AICOMPOSE_FLOOD_PREMIUM" {
|
||||
return .fail(.nonPremiumFlood)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { result -> Signal<TelegramAIComposeMessageResult, TelegramAIComposeMessageError> in
|
||||
switch result {
|
||||
case let .composedMessageWithAI(composedMessageWithAI):
|
||||
var diffRanges: [Range<Int>] = []
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ViewControllerComponentContainer.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(
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue