mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 11:50:56 +00:00
Various Improvements
This commit is contained in:
parent
55e914abae
commit
fc8787d732
@ -7149,3 +7149,14 @@ Sorry for the inconvenience.";
|
|||||||
"Conversation.ContextMenuTranslate" = "Translate";
|
"Conversation.ContextMenuTranslate" = "Translate";
|
||||||
|
|
||||||
"ClearCache.ClearDescription" = "All media will stay in the Telegram cloud and can be re-downloaded if you need it again.";
|
"ClearCache.ClearDescription" = "All media will stay in the Telegram cloud and can be re-downloaded if you need it again.";
|
||||||
|
|
||||||
|
"Localization.TranslateMessages" = "Translate Messages";
|
||||||
|
"Localization.ShowTranslate" = "Show Translate Button";
|
||||||
|
"Localization.ShowTranslateInfo" = "Show 'Translate' button in the message action menu.";
|
||||||
|
"Localization.DoNotTranslate" = "Do Not Translate";
|
||||||
|
"Localization.DoNotTranslateInfo" = "Do not show 'Translate' button in the message action menu for this language";
|
||||||
|
"Localization.DoNotTranslateManyInfo" = "Do not show 'Translate' button in the message action menu for this languages";
|
||||||
|
|
||||||
|
"Localization.InterfaceLanguage" = "Interface Language";
|
||||||
|
|
||||||
|
"DoNotTranslate.Title" = "Do Not Translate";
|
||||||
|
|||||||
@ -92,7 +92,6 @@ public class LocalizationListController: ViewController {
|
|||||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||||
self.controllerNode.updatePresentationData(self.presentationData)
|
self.controllerNode.updatePresentationData(self.presentationData)
|
||||||
|
|
||||||
|
|
||||||
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.editPressed))
|
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.editPressed))
|
||||||
let doneItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
|
let doneItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
|
||||||
if self.navigationItem.rightBarButtonItem === self.editItem {
|
if self.navigationItem.rightBarButtonItem === self.editItem {
|
||||||
@ -124,6 +123,8 @@ public class LocalizationListController: ViewController {
|
|||||||
}
|
}
|
||||||
}, present: { [weak self] c, a in
|
}, present: { [weak self] c, a in
|
||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
|
}, push: { [weak self] c in
|
||||||
|
self?.push(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.controllerNode.listNode.visibleContentOffsetChanged = { [weak self] offset in
|
self.controllerNode.listNode.visibleContentOffsetChanged = { [weak self] offset in
|
||||||
|
|||||||
@ -14,14 +14,18 @@ import ShareController
|
|||||||
import SearchBarNode
|
import SearchBarNode
|
||||||
import SearchUI
|
import SearchUI
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
private enum LanguageListSection: ItemListSectionId {
|
private enum LanguageListSection: ItemListSectionId {
|
||||||
|
case translate
|
||||||
case official
|
case official
|
||||||
case unofficial
|
case unofficial
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum LanguageListEntryId: Hashable {
|
private enum LanguageListEntryId: Hashable {
|
||||||
case search
|
case search
|
||||||
|
case translate(Int)
|
||||||
|
case localizationTitle
|
||||||
case localization(String)
|
case localization(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,10 +35,26 @@ private enum LanguageListEntryType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum LanguageListEntry: Comparable, Identifiable {
|
private enum LanguageListEntry: Comparable, Identifiable {
|
||||||
|
case translateTitle(text: String)
|
||||||
|
case translate(text: String, value: Bool)
|
||||||
|
case doNotTranslate(text: String, value: String)
|
||||||
|
case translateInfo(text: String)
|
||||||
|
|
||||||
|
case localizationTitle(text: String, section: ItemListSectionId)
|
||||||
case localization(index: Int, info: LocalizationInfo?, type: LanguageListEntryType, selected: Bool, activity: Bool, revealed: Bool, editing: Bool)
|
case localization(index: Int, info: LocalizationInfo?, type: LanguageListEntryType, selected: Bool, activity: Bool, revealed: Bool, editing: Bool)
|
||||||
|
|
||||||
var stableId: LanguageListEntryId {
|
var stableId: LanguageListEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .translateTitle:
|
||||||
|
return .translate(0)
|
||||||
|
case .translate:
|
||||||
|
return .translate(1)
|
||||||
|
case .doNotTranslate:
|
||||||
|
return .translate(2)
|
||||||
|
case .translateInfo:
|
||||||
|
return .translate(3)
|
||||||
|
case .localizationTitle:
|
||||||
|
return .localizationTitle
|
||||||
case let .localization(index, info, _, _, _, _, _):
|
case let .localization(index, info, _, _, _, _, _):
|
||||||
return .localization(info?.languageCode ?? "\(index)")
|
return .localization(info?.languageCode ?? "\(index)")
|
||||||
}
|
}
|
||||||
@ -42,8 +62,18 @@ private enum LanguageListEntry: Comparable, Identifiable {
|
|||||||
|
|
||||||
private func index() -> Int {
|
private func index() -> Int {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .translateTitle:
|
||||||
|
return 0
|
||||||
|
case .translate:
|
||||||
|
return 1
|
||||||
|
case .doNotTranslate:
|
||||||
|
return 2
|
||||||
|
case .translateInfo:
|
||||||
|
return 3
|
||||||
|
case .localizationTitle:
|
||||||
|
return 1000
|
||||||
case let .localization(index, _, _, _, _, _, _):
|
case let .localization(index, _, _, _, _, _, _):
|
||||||
return index
|
return 1001 + index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +81,22 @@ private enum LanguageListEntry: Comparable, Identifiable {
|
|||||||
return lhs.index() < rhs.index()
|
return lhs.index() < rhs.index()
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(presentationData: PresentationData, searchMode: Bool, openSearch: @escaping () -> Void, selectLocalization: @escaping (LocalizationInfo) -> Void, setItemWithRevealedOptions: @escaping (String?, String?) -> Void, removeItem: @escaping (String) -> Void) -> ListViewItem {
|
func item(presentationData: PresentationData, searchMode: Bool, openSearch: @escaping () -> Void, toggleShowTranslate: @escaping (Bool) -> Void, openDoNotTranslate: @escaping () -> Void, selectLocalization: @escaping (LocalizationInfo) -> Void, setItemWithRevealedOptions: @escaping (String?, String?) -> Void, removeItem: @escaping (String) -> Void) -> ListViewItem {
|
||||||
switch self {
|
switch self {
|
||||||
|
case let .translateTitle(text):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), text: text, sectionId: LanguageListSection.translate.rawValue)
|
||||||
|
case let .translate(text, value):
|
||||||
|
return ItemListSwitchItem(presentationData: ItemListPresentationData(presentationData), title: text, value: value, sectionId: LanguageListSection.translate.rawValue, style: .blocks, updated: { value in
|
||||||
|
toggleShowTranslate(value)
|
||||||
|
})
|
||||||
|
case let .doNotTranslate(text, value):
|
||||||
|
return ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), title: text, label: value, sectionId: LanguageListSection.translate.rawValue, style: .blocks, action: {
|
||||||
|
openDoNotTranslate()
|
||||||
|
})
|
||||||
|
case let .translateInfo(text):
|
||||||
|
return ItemListTextItem(presentationData: ItemListPresentationData(presentationData), text: .plain(text), sectionId: LanguageListSection.translate.rawValue)
|
||||||
|
case let .localizationTitle(text, section):
|
||||||
|
return ItemListSectionHeaderItem(presentationData: ItemListPresentationData(presentationData), text: text, sectionId: section)
|
||||||
case let .localization(_, info, type, selected, activity, revealed, editing):
|
case let .localization(_, info, type, selected, activity, revealed, editing):
|
||||||
return LocalizationListItem(presentationData: ItemListPresentationData(presentationData), id: info?.languageCode ?? "", title: info?.title ?? " ", subtitle: info?.localizedTitle ?? " ", checked: selected, activity: activity, loading: info == nil, editing: LocalizationListItemEditing(editable: !selected && !searchMode && !(info?.isOfficial ?? true), editing: editing, revealed: !selected && revealed, reorderable: false), sectionId: type == .official ? LanguageListSection.official.rawValue : LanguageListSection.unofficial.rawValue, alwaysPlain: searchMode, action: {
|
return LocalizationListItem(presentationData: ItemListPresentationData(presentationData), id: info?.languageCode ?? "", title: info?.title ?? " ", subtitle: info?.localizedTitle ?? " ", checked: selected, activity: activity, loading: info == nil, editing: LocalizationListItemEditing(editable: !selected && !searchMode && !(info?.isOfficial ?? true), editing: editing, revealed: !selected && revealed, reorderable: false), sectionId: type == .official ? LanguageListSection.official.rawValue : LanguageListSection.unofficial.rawValue, alwaysPlain: searchMode, action: {
|
||||||
if let info = info {
|
if let info = info {
|
||||||
@ -74,8 +118,8 @@ private func preparedLanguageListSearchContainerTransition(presentationData: Pre
|
|||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdate)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdate)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: true, openSearch: {}, selectLocalization: selectLocalization, setItemWithRevealedOptions: { _, _ in }, removeItem: { _ in }), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: true, openSearch: {}, toggleShowTranslate: { _ in }, openDoNotTranslate: {}, selectLocalization: selectLocalization, setItemWithRevealedOptions: { _, _ in }, removeItem: { _ in }), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: true, openSearch: {}, selectLocalization: selectLocalization, setItemWithRevealedOptions: { _, _ in }, removeItem: { _ in }), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: true, openSearch: {}, toggleShowTranslate: { _ in }, openDoNotTranslate: {}, selectLocalization: selectLocalization, setItemWithRevealedOptions: { _, _ in }, removeItem: { _ in }), directionHint: nil) }
|
||||||
|
|
||||||
return LocalizationListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
|
return LocalizationListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
|
||||||
}
|
}
|
||||||
@ -262,12 +306,12 @@ private struct LanguageListNodeTransition {
|
|||||||
let crossfade: Bool
|
let crossfade: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedLanguageListNodeTransition(presentationData: PresentationData, from fromEntries: [LanguageListEntry], to toEntries: [LanguageListEntry], openSearch: @escaping () -> Void, selectLocalization: @escaping (LocalizationInfo) -> Void, setItemWithRevealedOptions: @escaping (String?, String?) -> Void, removeItem: @escaping (String) -> Void, firstTime: Bool, isLoading: Bool, forceUpdate: Bool, animated: Bool, crossfade: Bool) -> LanguageListNodeTransition {
|
private func preparedLanguageListNodeTransition(presentationData: PresentationData, from fromEntries: [LanguageListEntry], to toEntries: [LanguageListEntry], openSearch: @escaping () -> Void, toggleShowTranslate: @escaping (Bool) -> Void, openDoNotTranslate: @escaping () -> Void, selectLocalization: @escaping (LocalizationInfo) -> Void, setItemWithRevealedOptions: @escaping (String?, String?) -> Void, removeItem: @escaping (String) -> Void, firstTime: Bool, isLoading: Bool, forceUpdate: Bool, animated: Bool, crossfade: Bool) -> LanguageListNodeTransition {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdate)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries, allUpdated: forceUpdate)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: false, openSearch: openSearch, selectLocalization: selectLocalization, setItemWithRevealedOptions: setItemWithRevealedOptions, removeItem: removeItem), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: false, openSearch: openSearch, toggleShowTranslate: toggleShowTranslate, openDoNotTranslate: openDoNotTranslate, selectLocalization: selectLocalization, setItemWithRevealedOptions: setItemWithRevealedOptions, removeItem: removeItem), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: false, openSearch: openSearch, selectLocalization: selectLocalization, setItemWithRevealedOptions: setItemWithRevealedOptions, removeItem: removeItem), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(presentationData: presentationData, searchMode: false, openSearch: openSearch, toggleShowTranslate: toggleShowTranslate, openDoNotTranslate: openDoNotTranslate, selectLocalization: selectLocalization, setItemWithRevealedOptions: setItemWithRevealedOptions, removeItem: removeItem), directionHint: nil) }
|
||||||
|
|
||||||
return LanguageListNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, isLoading: isLoading, animated: animated, crossfade: crossfade)
|
return LanguageListNodeTransition(deletions: deletions, insertions: insertions, updates: updates, firstTime: firstTime, isLoading: isLoading, animated: animated, crossfade: crossfade)
|
||||||
}
|
}
|
||||||
@ -279,6 +323,7 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
private let requestActivateSearch: () -> Void
|
private let requestActivateSearch: () -> Void
|
||||||
private let requestDeactivateSearch: () -> Void
|
private let requestDeactivateSearch: () -> Void
|
||||||
private let present: (ViewController, Any?) -> Void
|
private let present: (ViewController, Any?) -> Void
|
||||||
|
private let push: (ViewController) -> Void
|
||||||
|
|
||||||
private var didSetReady = false
|
private var didSetReady = false
|
||||||
let _ready = ValuePromise<Bool>()
|
let _ready = ValuePromise<Bool>()
|
||||||
@ -304,7 +349,7 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, navigationBar: NavigationBar, requestActivateSearch: @escaping () -> Void, requestDeactivateSearch: @escaping () -> Void, updateCanStartEditing: @escaping (Bool?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
init(context: AccountContext, presentationData: PresentationData, navigationBar: NavigationBar, requestActivateSearch: @escaping () -> Void, requestDeactivateSearch: @escaping () -> Void, updateCanStartEditing: @escaping (Bool?) -> Void, present: @escaping (ViewController, Any?) -> Void, push: @escaping (ViewController) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.presentationDataValue.set(.single(presentationData))
|
self.presentationDataValue.set(.single(presentationData))
|
||||||
@ -312,6 +357,7 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
self.requestActivateSearch = requestActivateSearch
|
self.requestActivateSearch = requestActivateSearch
|
||||||
self.requestDeactivateSearch = requestDeactivateSearch
|
self.requestDeactivateSearch = requestDeactivateSearch
|
||||||
self.present = present
|
self.present = present
|
||||||
|
self.push = push
|
||||||
|
|
||||||
self.listNode = ListView()
|
self.listNode = ListView()
|
||||||
self.listNode.keepTopItemOverscrollBackground = ListViewKeepTopItemOverscrollBackground(color: presentationData.theme.list.blocksBackgroundColor, direction: true)
|
self.listNode.keepTopItemOverscrollBackground = ListViewKeepTopItemOverscrollBackground(color: presentationData.theme.list.blocksBackgroundColor, direction: true)
|
||||||
@ -373,7 +419,7 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
let preferencesKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.localizationListState]))
|
let preferencesKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.localizationListState]))
|
||||||
let previousState = Atomic<LocalizationListState?>(value: nil)
|
let previousState = Atomic<LocalizationListState?>(value: nil)
|
||||||
let previousEntriesHolder = Atomic<([LanguageListEntry], PresentationTheme, PresentationStrings)?>(value: nil)
|
let previousEntriesHolder = Atomic<([LanguageListEntry], PresentationTheme, PresentationStrings)?>(value: nil)
|
||||||
self.listDisposable = combineLatest(queue: .mainQueue(), context.account.postbox.combinedView(keys: [preferencesKey]), context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.localizationSettings]), self.presentationDataValue.get(), self.applyingCode.get(), revealedCode.get(), self.isEditing.get()).start(next: { [weak self] view, sharedData, presentationData, applyingCode, revealedCode, isEditing in
|
self.listDisposable = combineLatest(queue: .mainQueue(), context.account.postbox.combinedView(keys: [preferencesKey]), context.sharedContext.accountManager.sharedData(keys: [SharedDataKeys.localizationSettings, ApplicationSpecificSharedDataKeys.translationSettings]), self.presentationDataValue.get(), self.applyingCode.get(), revealedCode.get(), self.isEditing.get()).start(next: { [weak self] view, sharedData, presentationData, applyingCode, revealedCode, isEditing in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -385,10 +431,26 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
var existingIds = Set<String>()
|
var existingIds = Set<String>()
|
||||||
|
|
||||||
|
var showTranslate = true
|
||||||
|
if let translationSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
|
||||||
|
showTranslate = translationSettings.showTranslate
|
||||||
|
}
|
||||||
|
|
||||||
let localizationListState = (view.views[preferencesKey] as? PreferencesView)?.values[PreferencesKeys.localizationListState]?.get(LocalizationListState.self)
|
let localizationListState = (view.views[preferencesKey] as? PreferencesView)?.values[PreferencesKeys.localizationListState]?.get(LocalizationListState.self)
|
||||||
if let localizationListState = localizationListState, !localizationListState.availableOfficialLocalizations.isEmpty {
|
if let localizationListState = localizationListState, !localizationListState.availableOfficialLocalizations.isEmpty {
|
||||||
strongSelf.currentListState = localizationListState
|
strongSelf.currentListState = localizationListState
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
entries.append(.translateTitle(text: presentationData.strings.Localization_TranslateMessages.uppercased()))
|
||||||
|
entries.append(.translate(text: presentationData.strings.Localization_ShowTranslate, value: showTranslate))
|
||||||
|
if showTranslate {
|
||||||
|
entries.append(.doNotTranslate(text: presentationData.strings.Localization_DoNotTranslate, value: ""))
|
||||||
|
entries.append(.translateInfo(text: presentationData.strings.Localization_DoNotTranslateInfo))
|
||||||
|
} else {
|
||||||
|
entries.append(.translateInfo(text: presentationData.strings.Localization_ShowTranslateInfo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let availableSavedLocalizations = localizationListState.availableSavedLocalizations.filter({ info in !localizationListState.availableOfficialLocalizations.contains(where: { $0.languageCode == info.languageCode }) })
|
let availableSavedLocalizations = localizationListState.availableSavedLocalizations.filter({ info in !localizationListState.availableOfficialLocalizations.contains(where: { $0.languageCode == info.languageCode }) })
|
||||||
if availableSavedLocalizations.isEmpty {
|
if availableSavedLocalizations.isEmpty {
|
||||||
updateCanStartEditing(nil)
|
updateCanStartEditing(nil)
|
||||||
@ -396,6 +458,7 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
updateCanStartEditing(isEditing)
|
updateCanStartEditing(isEditing)
|
||||||
}
|
}
|
||||||
if !availableSavedLocalizations.isEmpty {
|
if !availableSavedLocalizations.isEmpty {
|
||||||
|
entries.append(.localizationTitle(text: presentationData.strings.Localization_InterfaceLanguage.uppercased(), section: LanguageListSection.unofficial.rawValue))
|
||||||
for info in availableSavedLocalizations {
|
for info in availableSavedLocalizations {
|
||||||
if existingIds.contains(info.languageCode) {
|
if existingIds.contains(info.languageCode) {
|
||||||
continue
|
continue
|
||||||
@ -403,6 +466,8 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
existingIds.insert(info.languageCode)
|
existingIds.insert(info.languageCode)
|
||||||
entries.append(.localization(index: entries.count, info: info, type: .unofficial, selected: info.languageCode == activeLanguageCode, activity: applyingCode == info.languageCode, revealed: revealedCode == info.languageCode, editing: isEditing))
|
entries.append(.localization(index: entries.count, info: info, type: .unofficial, selected: info.languageCode == activeLanguageCode, activity: applyingCode == info.languageCode, revealed: revealedCode == info.languageCode, editing: isEditing))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
entries.append(.localizationTitle(text: presentationData.strings.Localization_InterfaceLanguage.uppercased(), section: LanguageListSection.official.rawValue))
|
||||||
}
|
}
|
||||||
for info in localizationListState.availableOfficialLocalizations {
|
for info in localizationListState.availableOfficialLocalizations {
|
||||||
if existingIds.contains(info.languageCode) {
|
if existingIds.contains(info.languageCode) {
|
||||||
@ -420,7 +485,19 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
|
|||||||
let previousState = previousState.swap(localizationListState)
|
let previousState = previousState.swap(localizationListState)
|
||||||
|
|
||||||
let previousEntriesAndPresentationData = previousEntriesHolder.swap((entries, presentationData.theme, presentationData.strings))
|
let previousEntriesAndPresentationData = previousEntriesHolder.swap((entries, presentationData.theme, presentationData.strings))
|
||||||
let transition = preparedLanguageListNodeTransition(presentationData: presentationData, from: previousEntriesAndPresentationData?.0 ?? [], to: entries, openSearch: openSearch, selectLocalization: { [weak self] info in self?.selectLocalization(info) }, setItemWithRevealedOptions: setItemWithRevealedOptions, removeItem: removeItem, firstTime: previousEntriesAndPresentationData == nil, isLoading: entries.isEmpty, forceUpdate: previousEntriesAndPresentationData?.1 !== presentationData.theme || previousEntriesAndPresentationData?.2 !== presentationData.strings, animated: (previousEntriesAndPresentationData?.0.count ?? 0) >= entries.count, crossfade: (previousState == nil) != (localizationListState == nil))
|
let transition = preparedLanguageListNodeTransition(presentationData: presentationData, from: previousEntriesAndPresentationData?.0 ?? [], to: entries, openSearch: openSearch, toggleShowTranslate: { value in
|
||||||
|
let _ = updateTranslationSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
|
var updated = current.withUpdatedShowTranslate(value)
|
||||||
|
if !value {
|
||||||
|
updated = updated.withUpdatedIgnoredLanguages(nil)
|
||||||
|
}
|
||||||
|
return updated
|
||||||
|
}).start()
|
||||||
|
}, openDoNotTranslate: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.push(translationSettingsController(context: strongSelf.context))
|
||||||
|
}
|
||||||
|
}, selectLocalization: { [weak self] info in self?.selectLocalization(info) }, setItemWithRevealedOptions: setItemWithRevealedOptions, removeItem: removeItem, firstTime: previousEntriesAndPresentationData == nil, isLoading: entries.isEmpty, forceUpdate: previousEntriesAndPresentationData?.1 !== presentationData.theme || previousEntriesAndPresentationData?.2 !== presentationData.strings, animated: (previousEntriesAndPresentationData?.0.count ?? 0) >= entries.count, crossfade: (previousState == nil) != (localizationListState == nil))
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
})
|
})
|
||||||
self.updatedDisposable = context.engine.localization.synchronizedLocalizationListState().start()
|
self.updatedDisposable = context.engine.localization.synchronizedLocalizationListState().start()
|
||||||
|
|||||||
@ -0,0 +1,147 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import ItemListUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
import TelegramStringFormatting
|
||||||
|
import AccountContext
|
||||||
|
import Translate
|
||||||
|
|
||||||
|
private final class TranslationSettingsControllerArguments {
|
||||||
|
let context: AccountContext
|
||||||
|
let updateLanguageSelected: (String, Bool) -> Void
|
||||||
|
|
||||||
|
init(context: AccountContext, updateLanguageSelected: @escaping (String, Bool) -> Void) {
|
||||||
|
self.context = context
|
||||||
|
self.updateLanguageSelected = updateLanguageSelected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TranslationSettingsControllerSection: Int32 {
|
||||||
|
case languages
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TranslationSettingsControllerEntry: ItemListNodeEntry {
|
||||||
|
case language(Int32, PresentationTheme, String, String, Bool, String)
|
||||||
|
|
||||||
|
var section: ItemListSectionId {
|
||||||
|
switch self {
|
||||||
|
case .language:
|
||||||
|
return TranslationSettingsControllerSection.languages.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stableId: Int32 {
|
||||||
|
switch self {
|
||||||
|
case let .language(index, _, _, _, _, _):
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: TranslationSettingsControllerEntry, rhs: TranslationSettingsControllerEntry) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case let .language(lhsIndex, lhsTheme, lhsTitle, lhsSubtitle, lhsValue, lhsCode):
|
||||||
|
if case let .language(rhsIndex, rhsTheme, rhsTitle, rhsSubtitle, rhsValue, rhsCode) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsSubtitle == rhsSubtitle, lhsValue == rhsValue, lhsCode == rhsCode {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func <(lhs: TranslationSettingsControllerEntry, rhs: TranslationSettingsControllerEntry) -> Bool {
|
||||||
|
return lhs.stableId < rhs.stableId
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
|
let arguments = arguments as! TranslationSettingsControllerArguments
|
||||||
|
switch self {
|
||||||
|
case let .language(_, _, title, subtitle, value, code):
|
||||||
|
return LocalizationListItem(presentationData: presentationData, id: code, title: title, subtitle: subtitle, checked: value, activity: false, loading: false, editing: LocalizationListItemEditing(editable: false, editing: false, revealed: false, reorderable: false), sectionId: self.section, alwaysPlain: false, action: {
|
||||||
|
arguments.updateLanguageSelected(code, !value)
|
||||||
|
}, setItemWithRevealedOptions: { _, _ in }, removeItem: { _ in })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func translationSettingsControllerEntries(theme: PresentationTheme, strings: PresentationStrings, settings: TranslationSettings, languages: [(String, String, String)]) -> [TranslationSettingsControllerEntry] {
|
||||||
|
var entries: [TranslationSettingsControllerEntry] = []
|
||||||
|
|
||||||
|
var index: Int32 = 0
|
||||||
|
var selectedLanguages: Set<String>
|
||||||
|
if let ignoredLanguages = settings.ignoredLanguages {
|
||||||
|
selectedLanguages = Set(ignoredLanguages)
|
||||||
|
} else {
|
||||||
|
selectedLanguages = Set([strings.baseLanguageCode])
|
||||||
|
}
|
||||||
|
for (code, title, subtitle) in languages {
|
||||||
|
entries.append(.language(index, theme, title, subtitle, selectedLanguages.contains(code), code))
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
public func translationSettingsController(context: AccountContext) -> ViewController {
|
||||||
|
let actionsDisposable = DisposableSet()
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let interfaceLanguageCode = presentationData.strings.baseLanguageCode
|
||||||
|
|
||||||
|
let arguments = TranslationSettingsControllerArguments(context: context, updateLanguageSelected: { code, value in
|
||||||
|
let _ = updateTranslationSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
|
var updated = current
|
||||||
|
var updatedIgnoredLanguages = updated.ignoredLanguages ?? []
|
||||||
|
if value {
|
||||||
|
if current.ignoredLanguages == nil {
|
||||||
|
updatedIgnoredLanguages.append(interfaceLanguageCode)
|
||||||
|
}
|
||||||
|
if !updatedIgnoredLanguages.contains(code) {
|
||||||
|
updatedIgnoredLanguages.append(code)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updatedIgnoredLanguages.removeAll(where: { $0 == code })
|
||||||
|
}
|
||||||
|
updated = updated.withUpdatedIgnoredLanguages(updatedIgnoredLanguages)
|
||||||
|
return updated
|
||||||
|
}).start()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
let enLocale = Locale(identifier: "en")
|
||||||
|
var languages: [(String, String, String)] = []
|
||||||
|
for code in supportedTranslationLanguages {
|
||||||
|
if let title = enLocale.localizedString(forLanguageCode: code) {
|
||||||
|
let languageLocale = Locale(identifier: code)
|
||||||
|
let subtitle = languageLocale.localizedString(forLanguageCode: code) ?? title
|
||||||
|
let value = (code, title.capitalized, subtitle.capitalized)
|
||||||
|
if code == interfaceLanguageCode {
|
||||||
|
languages.insert(value, at: 0)
|
||||||
|
} else {
|
||||||
|
languages.append(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sharedData = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
||||||
|
let signal = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, sharedData)
|
||||||
|
|> map { presentationData, sharedData -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
|
let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) ?? TranslationSettings.defaultSettings
|
||||||
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.DoNotTranslate_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: translationSettingsControllerEntries(theme: presentationData.theme, strings: presentationData.strings, settings: settings, languages: languages), style: .blocks, animateChanges: false)
|
||||||
|
|
||||||
|
return (controllerState, (listState, arguments))
|
||||||
|
}
|
||||||
|
|> afterDisposed {
|
||||||
|
actionsDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = ItemListController(context: context, state: signal)
|
||||||
|
controller.alwaysSynchronous = true
|
||||||
|
return controller
|
||||||
|
}
|
||||||
@ -24,6 +24,8 @@ import AvatarNode
|
|||||||
import AdUI
|
import AdUI
|
||||||
import TelegramNotices
|
import TelegramNotices
|
||||||
import ReactionListContextMenuContent
|
import ReactionListContextMenuContent
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import Translate
|
||||||
|
|
||||||
private struct MessageContextMenuData {
|
private struct MessageContextMenuData {
|
||||||
let starStatus: Bool?
|
let starStatus: Bool?
|
||||||
@ -543,7 +545,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
|
return transaction.getCombinedPeerReadState(messages[0].id.peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?), NoError> = combineLatest(
|
let dataSignal: Signal<(MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?, TranslationSettings), NoError> = combineLatest(
|
||||||
loadLimits,
|
loadLimits,
|
||||||
loadStickerSaveStatusSignal,
|
loadStickerSaveStatusSignal,
|
||||||
loadResourceStatusSignal,
|
loadResourceStatusSignal,
|
||||||
@ -553,9 +555,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
cachedData,
|
cachedData,
|
||||||
readState,
|
readState,
|
||||||
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager),
|
ApplicationSpecificNotice.getMessageViewsPrivacyTips(accountManager: context.sharedContext.accountManager),
|
||||||
context.engine.stickers.availableReactions()
|
context.engine.stickers.availableReactions(),
|
||||||
|
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings])
|
||||||
)
|
)
|
||||||
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState, messageViewsPrivacyTips, availableReactions -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?) in
|
|> map { limitsAndAppConfig, stickerSaveStatus, resourceStatus, messageActions, updatingMessageMedia, cachedData, readState, messageViewsPrivacyTips, availableReactions, sharedData -> (MessageContextMenuData, [MessageId: ChatUpdatingMessageMedia], CachedPeerData?, AppConfiguration, Bool, Int32, AvailableReactions?, TranslationSettings) in
|
||||||
let (limitsConfiguration, appConfig) = limitsAndAppConfig
|
let (limitsConfiguration, appConfig) = limitsAndAppConfig
|
||||||
var canEdit = false
|
var canEdit = false
|
||||||
if !isAction {
|
if !isAction {
|
||||||
@ -568,12 +571,19 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
isMessageRead = readState.isOutgoingMessageIndexRead(message.index)
|
isMessageRead = readState.isOutgoingMessageIndexRead(message.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions)
|
let translationSettings: TranslationSettings
|
||||||
|
if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) {
|
||||||
|
translationSettings = current
|
||||||
|
} else {
|
||||||
|
translationSettings = TranslationSettings.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
return (MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, resourceStatus: resourceStatus, messageActions: messageActions), updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataSignal
|
return dataSignal
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions -> ContextController.Items in
|
|> map { data, updatingMessageMedia, cachedData, appConfig, isMessageRead, messageViewsPrivacyTips, availableReactions, translationSettings -> ContextController.Items in
|
||||||
var actions: [ContextMenuItem] = []
|
var actions: [ContextMenuItem] = []
|
||||||
|
|
||||||
var isPinnedMessages = false
|
var isPinnedMessages = false
|
||||||
@ -760,7 +770,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
f(.default)
|
f(.default)
|
||||||
})))
|
})))
|
||||||
|
|
||||||
if #available(iOS 15.0, *), !message.text.isEmpty {
|
if canTranslateText(context: context, text: message.text, showTranslate: translationSettings.showTranslate, ignoredLanguages: translationSettings.ignoredLanguages) {
|
||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuTranslate, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuTranslate, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
|
|||||||
@ -1248,14 +1248,20 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
let animationProgress: CGFloat = (currentValue - initialHeight) / (targetHeight - initialHeight)
|
let animationProgress: CGFloat = (currentValue - initialHeight) / (targetHeight - initialHeight)
|
||||||
let scaleProgress: CGFloat
|
let scaleProgress: CGFloat
|
||||||
var effectiveAvatarInset = avatarInset
|
var effectiveAvatarInset = avatarInset
|
||||||
if currentValue < targetHeight {
|
if abs(targetHeight - initialHeight) > 100.0 {
|
||||||
initialSize = displaySize
|
if currentValue < targetHeight {
|
||||||
targetSize = maximumDisplaySize
|
initialSize = displaySize
|
||||||
scaleProgress = animationProgress
|
targetSize = maximumDisplaySize
|
||||||
} else if currentValue > targetHeight {
|
scaleProgress = animationProgress
|
||||||
initialSize = maximumDisplaySize
|
} else if currentValue > targetHeight {
|
||||||
targetSize = displaySize
|
initialSize = maximumDisplaySize
|
||||||
scaleProgress = 1.0 - animationProgress
|
targetSize = displaySize
|
||||||
|
scaleProgress = 1.0 - animationProgress
|
||||||
|
} else {
|
||||||
|
initialSize = isPlaying ? maximumDisplaySize : displaySize
|
||||||
|
targetSize = initialSize
|
||||||
|
scaleProgress = isPlaying ? 1.0 : 0.0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
initialSize = isPlaying ? maximumDisplaySize : displaySize
|
initialSize = isPlaying ? maximumDisplaySize : displaySize
|
||||||
targetSize = initialSize
|
targetSize = initialSize
|
||||||
@ -1324,6 +1330,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
|||||||
let actionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY), size: actionButtonsSize)
|
let actionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY), size: actionButtonsSize)
|
||||||
actionButtonsNode.frame = actionButtonsFrame
|
actionButtonsNode.frame = actionButtonsFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let reactionButtonsNode = self.reactionButtonsNode {
|
||||||
|
let reactionButtonsSize = reactionButtonsNode.frame.size
|
||||||
|
let reactionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY + 6.0), size: reactionButtonsSize)
|
||||||
|
reactionButtonsNode.frame = reactionButtonsFrame
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func openMessageContextMenu() {
|
override func openMessageContextMenu() {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 {
|
|||||||
case contactSynchronizationSettings = 15
|
case contactSynchronizationSettings = 15
|
||||||
case webBrowserSettings = 16
|
case webBrowserSettings = 16
|
||||||
case intentsSettings = 17
|
case intentsSettings = 17
|
||||||
|
case translationSettings = 18
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificSharedDataKeys {
|
public struct ApplicationSpecificSharedDataKeys {
|
||||||
@ -56,6 +57,7 @@ public struct ApplicationSpecificSharedDataKeys {
|
|||||||
public static let contactSynchronizationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.contactSynchronizationSettings.rawValue)
|
public static let contactSynchronizationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.contactSynchronizationSettings.rawValue)
|
||||||
public static let webBrowserSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.webBrowserSettings.rawValue)
|
public static let webBrowserSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.webBrowserSettings.rawValue)
|
||||||
public static let intentsSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.intentsSettings.rawValue)
|
public static let intentsSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.intentsSettings.rawValue)
|
||||||
|
public static let translationSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.translationSettings.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
import Foundation
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
public struct TranslationSettings: Codable, Equatable {
|
||||||
|
public var showTranslate: Bool
|
||||||
|
public var ignoredLanguages: [String]?
|
||||||
|
|
||||||
|
public static var defaultSettings: TranslationSettings {
|
||||||
|
return TranslationSettings(showTranslate: true, ignoredLanguages: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(showTranslate: Bool, ignoredLanguages: [String]?) {
|
||||||
|
self.showTranslate = showTranslate
|
||||||
|
self.ignoredLanguages = ignoredLanguages
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||||
|
|
||||||
|
self.showTranslate = try container.decodeIfPresent(Bool.self, forKey: "showTranslate") ?? true
|
||||||
|
self.ignoredLanguages = try container.decodeIfPresent([String].self, forKey: "ignoredLanguages")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||||
|
|
||||||
|
try container.encode(self.showTranslate, forKey: "showTranslate")
|
||||||
|
try container.encodeIfPresent(self.ignoredLanguages, forKey: "ignoredLanguages")
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: TranslationSettings, rhs: TranslationSettings) -> Bool {
|
||||||
|
return lhs.showTranslate == rhs.showTranslate && lhs.ignoredLanguages == rhs.ignoredLanguages
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedShowTranslate(_ showTranslate: Bool) -> TranslationSettings {
|
||||||
|
return TranslationSettings(showTranslate: showTranslate, ignoredLanguages: self.ignoredLanguages)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedIgnoredLanguages(_ ignoredLanguages: [String]?) -> TranslationSettings {
|
||||||
|
return TranslationSettings(showTranslate: self.showTranslate, ignoredLanguages: ignoredLanguages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateTranslationSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (TranslationSettings) -> TranslationSettings) -> Signal<Void, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Void in
|
||||||
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.translationSettings, { entry in
|
||||||
|
let currentSettings: TranslationSettings
|
||||||
|
if let entry = entry?.get(TranslationSettings.self) {
|
||||||
|
currentSettings = entry
|
||||||
|
} else {
|
||||||
|
currentSettings = TranslationSettings.defaultSettings
|
||||||
|
}
|
||||||
|
return PreferencesEntry(f(currentSettings))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,11 +2,57 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import NaturalLanguage
|
||||||
|
|
||||||
// Incuding at least one Objective-C class in a swift file ensures that it doesn't get stripped by the linker
|
// Incuding at least one Objective-C class in a swift file ensures that it doesn't get stripped by the linker
|
||||||
private final class LinkHelperClass: NSObject {
|
private final class LinkHelperClass: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var supportedTranslationLanguages = [
|
||||||
|
"en",
|
||||||
|
"ar",
|
||||||
|
"zh",
|
||||||
|
"fr",
|
||||||
|
"de",
|
||||||
|
"it",
|
||||||
|
"jp",
|
||||||
|
"ko",
|
||||||
|
"pt",
|
||||||
|
"ru",
|
||||||
|
"es"
|
||||||
|
]
|
||||||
|
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
private let languageRecognizer = NLLanguageRecognizer()
|
||||||
|
|
||||||
|
public func canTranslateText(context: AccountContext, text: String, showTranslate: Bool, ignoredLanguages: [String]?) -> Bool {
|
||||||
|
guard showTranslate, text.count > 0 else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
var dontTranslateLanguages: [String] = []
|
||||||
|
if let ignoredLanguages = ignoredLanguages {
|
||||||
|
dontTranslateLanguages = ignoredLanguages
|
||||||
|
} else {
|
||||||
|
dontTranslateLanguages = [context.sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode]
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = String(text.prefix(64))
|
||||||
|
languageRecognizer.processString(text)
|
||||||
|
let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 2)
|
||||||
|
languageRecognizer.reset()
|
||||||
|
|
||||||
|
if let language = hypotheses.first(where: { supportedTranslationLanguages.contains($0.key.rawValue) }) {
|
||||||
|
return !dontTranslateLanguages.contains(language.key.rawValue)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func translateText(context: AccountContext, text: String) {
|
public func translateText(context: AccountContext, text: String) {
|
||||||
guard !text.isEmpty else {
|
guard !text.isEmpty else {
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user