Add chat translation suggestion

This commit is contained in:
Ilya Laktyushin 2023-02-10 19:32:47 +04:00
parent a02ece5c0e
commit a235246c49
3 changed files with 133 additions and 10 deletions

View File

@ -167,6 +167,8 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case audioTranscriptionSuggestion = 33
case clearStorageDismissedTipSize = 34
case dismissedTrendingEmojiPacks = 35
case audioRateOptionsTip = 36
case translationSuggestion = 37
var key: ValueBoxKey {
let v = ValueBoxKey(length: 4)
@ -359,6 +361,14 @@ private struct ApplicationSpecificNoticeKeys {
static func dismissedTrendingEmojiPacks() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedTrendingEmojiPacks.key)
}
static func translationSuggestionNotice() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.translationSuggestion.key)
}
static func audioRateOptionsTip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.audioRateOptionsTip.key)
}
}
public struct ApplicationSpecificNotice {
@ -1207,22 +1217,84 @@ public struct ApplicationSpecificNotice {
}
}
public static func incrementAudioTranscriptionSuggestion(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
public static func incrementAudioTranscriptionSuggestion(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.audioTranscriptionSuggestion())?.get(ApplicationSpecificCounterNotice.self) {
currentValue = value.value
}
let previousValue = currentValue
currentValue += Int32(count)
currentValue += count
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.audioTranscriptionSuggestion(), entry)
}
return Int(previousValue)
return previousValue
}
}
public static func translationSuggestion(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<(Int32, Int32), NoError> {
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.translationSuggestionNotice())
|> map { view -> (Int32, Int32) in
if let value = view.value?.get(ApplicationSpecificTimestampAndCounterNotice.self) {
return (value.counter, value.timestamp)
} else {
return (0, 0)
}
}
}
public static func incrementTranslationSuggestion(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1, timestamp: Int32) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
var currentValue: Int32 = 0
var currentTimestamp: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.translationSuggestionNotice())?.get(ApplicationSpecificTimestampAndCounterNotice.self) {
currentValue = value.counter
currentTimestamp = value.timestamp
}
if currentTimestamp > timestamp {
return Int32(currentValue)
} else {
let previousValue = currentValue
currentValue = max(0, Int32(currentValue + count))
if let entry = CodableEntry(ApplicationSpecificTimestampAndCounterNotice(counter: currentValue, timestamp: timestamp)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.translationSuggestionNotice(), entry)
}
return Int32(previousValue)
}
}
}
public static func getAudioRateOptionsTip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.audioRateOptionsTip())?.get(ApplicationSpecificCounterNotice.self) {
return value.value
} else {
return 0
}
}
}
public static func incrementAudioRateOptionsTip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.audioRateOptionsTip())?.get(ApplicationSpecificCounterNotice.self) {
currentValue = value.value
}
let previousValue = currentValue
currentValue += count
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.audioRateOptionsTip(), entry)
}
return previousValue
}
}
public static func reset(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
}

View File

@ -3550,7 +3550,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let f = {
let _ = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|> take(1)
|> deliverOnMainQueue).start(next: { sharedData in
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
guard let strongSelf = self else {
return
}
let translationSettings: TranslationSettings
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
translationSettings = current
@ -3565,6 +3568,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let (_, language) = canTranslateText(context: context, text: text.string, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: showTranslateIfTopical, ignoredLanguages: translationSettings.ignoredLanguages)
let _ = ApplicationSpecificNotice.incrementTranslationSuggestion(accountManager: context.sharedContext.accountManager, timestamp: Int32(Date().timeIntervalSince1970)).start()
let controller = TranslateScreen(context: context, text: text.string, canCopy: canCopy, fromLanguage: language)
controller.pushController = { [weak self] c in
self?.effectiveNavigationController?._keepModalDismissProgress = true
@ -6717,9 +6722,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.translationStateDisposable = (combineLatest(
queue: .concurrentDefaultQueue(),
isPremium,
isHidden
) |> mapToSignal { isPremium, isHidden -> Signal<ChatPresentationTranslationState?, NoError> in
if isPremium && !isHidden {
isHidden,
ApplicationSpecificNotice.translationSuggestion(accountManager: self.context.sharedContext.accountManager)
) |> mapToSignal { isPremium, isHidden, counterAndTimestamp -> Signal<ChatPresentationTranslationState?, NoError> in
var maybeSuggestPremium = false
if counterAndTimestamp.0 >= 3 {
maybeSuggestPremium = true
}
if (isPremium || maybeSuggestPremium) && !isHidden {
return chatTranslationState(context: context, peerId: peerId)
|> map { translationState -> ChatPresentationTranslationState? in
if let translationState, !translationState.fromLang.isEmpty {

View File

@ -16,6 +16,8 @@ import MoreButtonNode
import ContextUI
import TranslateUI
import TelegramUIPreferences
import TelegramNotices
import PremiumUI
final class ChatTranslationPanelNode: ASDisplayNode {
private let context: AccountContext
@ -26,7 +28,8 @@ final class ChatTranslationPanelNode: ASDisplayNode {
private let buttonIconNode: ASImageNode
private let buttonTextNode: ImmediateTextNode
private let moreButton: MoreButtonNode
private let closeButton: HighlightableButtonNode
private var theme: PresentationTheme?
private var chatInterfaceState: ChatPresentationInterfaceState?
@ -47,6 +50,11 @@ final class ChatTranslationPanelNode: ASDisplayNode {
self.moreButton = MoreButtonNode(theme: context.sharedContext.currentPresentationData.with { $0 }.theme)
self.moreButton.iconNode.enqueueState(.more, animated: false)
self.moreButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
self.closeButton = HighlightableButtonNode()
self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
self.closeButton.displaysAsynchronously = false
super.init()
@ -65,6 +73,9 @@ final class ChatTranslationPanelNode: ASDisplayNode {
strongSelf.morePressed(node: strongSelf.moreButton.contextSourceNode, gesture: gesture)
}
}
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
self.addSubnode(self.closeButton)
}
func animateOut() {
@ -90,6 +101,7 @@ final class ChatTranslationPanelNode: ASDisplayNode {
self.buttonIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/Translate"), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor)
self.moreButton.theme = interfaceState.theme
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelEncircledCloseIconImage(interfaceState.theme), for: [])
}
if themeUpdated || isEnabledUpdated {
@ -139,6 +151,17 @@ final class ChatTranslationPanelNode: ASDisplayNode {
let moreButtonSize = self.moreButton.measure(CGSize(width: 100.0, height: panelHeight))
self.moreButton.frame = CGRect(origin: CGPoint(x: width - contentRightInset - moreButtonSize.width, y: floorToScreenPixels((panelHeight - moreButtonSize.height) / 2.0)), size: moreButtonSize)
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
self.closeButton.frame: CGRect(origin: CGPoint(x: width - contentRightInset - closeButtonSize.width, y: floorToScreenPixels((panelHeight - closeButtonSize.height) / 2.0)), size: closeButtonSize)
if interfaceState.isPremium {
self.moreButton.isHidden = false
self.closeButton.isHidden = true
} else {
self.moreButton.isHidden = true
self.closeButton.isHidden = false
}
let buttonPadding: CGFloat = 10.0
let buttonSpacing: CGFloat = 10.0
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight))
@ -154,12 +177,30 @@ final class ChatTranslationPanelNode: ASDisplayNode {
return panelHeight
}
@objc private func closePressed() {
let _ = ApplicationSpecificNotice.incrementTranslationSuggestion(accountManager: self.context.sharedContext.accountManager, count: -100, timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 7).start()
}
@objc private func buttonPressed() {
guard let translationState = self.chatInterfaceState?.translationState else {
return
}
self.interfaceInteraction?.toggleTranslation(translationState.isEnabled ? .original : .translated)
let isPremium = self.chatInterfaceState?.isPremium ?? false
if isPremium {
self.interfaceInteraction?.toggleTranslation(translationState.isEnabled ? .original : .translated)
} else if !translationState.isEnabled {
let context = self.context
var replaceImpl: ((ViewController) -> Void)?
let controller = PremiumDemoScreen(context: context, subject: .translation, action: {
let controller = PremiumIntroScreen(context: context, source: .translation)
replaceImpl?(controller)
})
replaceImpl = { [weak controller] c in
controller?.replace(with: c)
}
self.interfaceInteraction?.chatController()?.push(controller)
}
}
@objc private func morePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) {