mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
374 lines
15 KiB
Swift
374 lines
15 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import SwiftSignalKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import TelegramCore
|
|
import SyncCore
|
|
import TelegramPresentationData
|
|
import ActivityIndicator
|
|
import AccountContext
|
|
import AppBundle
|
|
|
|
public struct LanguageSuggestionControllerStrings {
|
|
let ChooseLanguage: String
|
|
let Other: String
|
|
let English: String
|
|
|
|
public init(localization: SuggestedLocalizationInfo) {
|
|
var chooseLanguage = "Choose Your Language"
|
|
var other = "Other"
|
|
var english = "English"
|
|
|
|
for entry in localization.extractedEntries {
|
|
switch entry {
|
|
case let .string(key, value):
|
|
switch key {
|
|
case "Localization.ChooseLanguage":
|
|
chooseLanguage = value
|
|
case "Localization.LanguageOther":
|
|
other = value
|
|
case "Localization.EnglishLanguageName":
|
|
english = value
|
|
default:
|
|
break
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
self.ChooseLanguage = chooseLanguage
|
|
self.Other = other
|
|
self.English = english
|
|
}
|
|
|
|
public init(bundle: Bundle?) {
|
|
var chooseLanguage = "Choose Your Language"
|
|
var other = "Other"
|
|
var english = "English"
|
|
|
|
if let bundle = bundle {
|
|
for key in LanguageSuggestionControllerStrings.keys {
|
|
let value = bundle.localizedString(forKey: key, value: nil, table: nil)
|
|
if value != key {
|
|
switch key {
|
|
case "Localization.ChooseLanguage":
|
|
chooseLanguage = value
|
|
case "Localization.LanguageOther":
|
|
other = value
|
|
case "Localization.EnglishLanguageName":
|
|
english = value
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.ChooseLanguage = chooseLanguage
|
|
self.Other = other
|
|
self.English = english
|
|
}
|
|
|
|
public static let keys: [String] = [
|
|
"Localization.ChooseLanguage",
|
|
"Localization.LanguageOther",
|
|
"Localization.EnglishLanguageName"
|
|
]
|
|
}
|
|
|
|
private enum LanguageSuggestionItemType {
|
|
case localization(String)
|
|
case disclosure
|
|
case action
|
|
}
|
|
|
|
private struct LanguageSuggestionItem {
|
|
public let type: LanguageSuggestionItemType
|
|
public let title: String
|
|
public let subtitle: String?
|
|
public let action: () -> Void
|
|
|
|
public init(type: LanguageSuggestionItemType, title: String, subtitle: String?, action: @escaping () -> Void) {
|
|
self.type = type
|
|
self.title = title
|
|
self.subtitle = subtitle
|
|
self.action = action
|
|
}
|
|
}
|
|
|
|
private final class LanguageSuggestionItemNode: HighlightableButtonNode {
|
|
private let backgroundNode: ASDisplayNode
|
|
private let separatorNode: ASDisplayNode
|
|
private let subtitleNode: ASTextNode
|
|
private let iconNode: ASImageNode
|
|
|
|
let item: LanguageSuggestionItem
|
|
|
|
override var isSelected: Bool {
|
|
didSet {
|
|
if case .localization = self.item.type {
|
|
self.iconNode.isHidden = !self.isSelected
|
|
}
|
|
}
|
|
}
|
|
|
|
init(theme: PresentationTheme, item: LanguageSuggestionItem) {
|
|
self.item = item
|
|
|
|
self.backgroundNode = ASDisplayNode()
|
|
self.backgroundNode.isLayerBacked = true
|
|
self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
|
|
self.backgroundNode.alpha = 0.0
|
|
|
|
self.separatorNode = ASDisplayNode()
|
|
self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
|
|
|
|
self.subtitleNode = ASTextNode()
|
|
|
|
self.iconNode = ASImageNode()
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.subtitleNode)
|
|
self.addSubnode(self.separatorNode)
|
|
self.addSubnode(self.iconNode)
|
|
|
|
var color: UIColor = theme.actionSheet.primaryTextColor
|
|
var alignment: ASHorizontalAlignment = .left
|
|
var inset: CGFloat = 19.0
|
|
var icon: UIImage?
|
|
switch item.type {
|
|
case .action:
|
|
alignment = .middle
|
|
color = theme.actionSheet.controlAccentColor
|
|
inset = 0.0
|
|
case .disclosure:
|
|
icon = PresentationResourcesItemList.disclosureArrowImage(theme)
|
|
case .localization:
|
|
icon = PresentationResourcesItemList.checkIconImage(theme)
|
|
}
|
|
|
|
self.iconNode.image = icon
|
|
self.contentHorizontalAlignment = alignment
|
|
self.setTitle(item.title, with: Font.regular(17.0), with: color, for: [])
|
|
|
|
var titleVerticalOffset: CGFloat = 0.0
|
|
if let subtitle = item.subtitle {
|
|
self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor)
|
|
titleVerticalOffset = 20.0
|
|
}
|
|
self.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: inset, bottom: titleVerticalOffset, right: 0.0)
|
|
|
|
self.highligthedChanged = { [weak self] value in
|
|
if let strongSelf = self {
|
|
if value {
|
|
if strongSelf.backgroundNode.supernode == nil {
|
|
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
|
}
|
|
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
|
|
strongSelf.backgroundNode.alpha = 1.0
|
|
} else if !strongSelf.backgroundNode.alpha.isZero {
|
|
strongSelf.backgroundNode.alpha = 0.0
|
|
strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override func didLoad() {
|
|
super.didLoad()
|
|
|
|
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
|
}
|
|
|
|
@objc func pressed() {
|
|
self.item.action()
|
|
}
|
|
|
|
public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
|
|
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: self.item.subtitle != nil ? 58.0 : 44.0))
|
|
self.backgroundNode.frame = bounds
|
|
|
|
let subtitleSize = self.subtitleNode.measure(bounds.size)
|
|
self.subtitleNode.frame = CGRect(origin: CGPoint(x: 19.0, y: 31.0), size: subtitleSize)
|
|
self.separatorNode.frame = CGRect(x: 0.0, y: bounds.height - UIScreenPixel, width: bounds.width, height: UIScreenPixel)
|
|
if let icon = self.iconNode.image {
|
|
self.iconNode.frame = CGRect(origin: CGPoint(x: bounds.width - icon.size.width - 19.0, y: floorToScreenPixels((bounds.height - icon.size.height) / 2.0)), size: icon.size)
|
|
}
|
|
return bounds.size
|
|
}
|
|
}
|
|
|
|
private final class LanguageSuggestionAlertContentNode: AlertContentNode {
|
|
private var validLayout: CGSize?
|
|
|
|
private let titleNode: ASTextNode
|
|
private let subtitleNode: ASTextNode
|
|
private let titleSeparatorNode: ASDisplayNode
|
|
private let activityIndicator: ActivityIndicator
|
|
|
|
private var nodes: [LanguageSuggestionItemNode]
|
|
|
|
private let disposable = MetaDisposable()
|
|
|
|
override var dismissOnOutsideTap: Bool {
|
|
return self.isUserInteractionEnabled
|
|
}
|
|
|
|
init(presentationData: PresentationData, strings: LanguageSuggestionControllerStrings, englishStrings: LanguageSuggestionControllerStrings, suggestedLocalization: LocalizationInfo, openSelection: @escaping () -> Void, applyLocalization: @escaping (String, () -> Void) -> Void, dismiss: @escaping () -> Void) {
|
|
let selectedLocalization = ValuePromise(suggestedLocalization.languageCode, ignoreRepeated: true)
|
|
|
|
self.titleNode = ASTextNode()
|
|
self.titleNode.attributedText = NSAttributedString(string: strings.ChooseLanguage, font: Font.bold(presentationData.listsFontSize.baseDisplaySize), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
|
|
self.titleNode.maximumNumberOfLines = 2
|
|
|
|
self.subtitleNode = ASTextNode()
|
|
self.subtitleNode.attributedText = NSAttributedString(string: englishStrings.ChooseLanguage, font: Font.regular(floor(presentationData.listsFontSize.baseDisplaySize * 14.0 / 17.0)), textColor: presentationData.theme.actionSheet.secondaryTextColor, paragraphAlignment: .center)
|
|
self.subtitleNode.maximumNumberOfLines = 2
|
|
|
|
self.titleSeparatorNode = ASDisplayNode()
|
|
self.titleSeparatorNode.backgroundColor = presentationData.theme.actionSheet.opaqueItemSeparatorColor
|
|
|
|
self.activityIndicator = ActivityIndicator(type: .custom(presentationData.theme.actionSheet.controlAccentColor, 22.0, 1.0, false))
|
|
self.activityIndicator.isHidden = true
|
|
|
|
var items: [LanguageSuggestionItem] = []
|
|
items.append(LanguageSuggestionItem(type: .localization(suggestedLocalization.languageCode), title: suggestedLocalization.localizedTitle, subtitle: suggestedLocalization.title, action: {
|
|
selectedLocalization.set(suggestedLocalization.languageCode)
|
|
}))
|
|
items.append(LanguageSuggestionItem(type: .localization("en"), title: strings.English, subtitle: englishStrings.English, action: {
|
|
selectedLocalization.set("en")
|
|
}))
|
|
items.append(LanguageSuggestionItem(type: .disclosure, title: strings.Other, subtitle: englishStrings.Other != strings.Other ? englishStrings.Other : nil, action: {
|
|
openSelection()
|
|
}))
|
|
|
|
var applyImpl: (() -> Void)?
|
|
items.append(LanguageSuggestionItem(type: .action, title: "OK", subtitle: nil, action: {
|
|
applyImpl?()
|
|
}))
|
|
|
|
self.nodes = items.map { LanguageSuggestionItemNode(theme: presentationData.theme, item: $0) }
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.titleNode)
|
|
self.addSubnode(self.subtitleNode)
|
|
self.addSubnode(self.titleSeparatorNode)
|
|
self.addSubnode(self.activityIndicator)
|
|
for node in self.nodes {
|
|
self.addSubnode(node)
|
|
}
|
|
|
|
self.disposable.set(selectedLocalization.get().start(next: { [weak self] selectedCode in
|
|
if let strongSelf = self {
|
|
for node in strongSelf.nodes {
|
|
if case let .localization(code) = node.item.type {
|
|
node.isSelected = code == selectedCode
|
|
}
|
|
}
|
|
}
|
|
}))
|
|
|
|
applyImpl = { [weak self] in
|
|
if let strongSelf = self {
|
|
strongSelf.isUserInteractionEnabled = false
|
|
|
|
_ = (selectedLocalization.get()
|
|
|> take(1)).start(next: { selectedCode in
|
|
applyLocalization(selectedCode, { [weak self] in
|
|
if let strongSelf = self {
|
|
strongSelf.activityIndicator.isHidden = false
|
|
if let lastNode = strongSelf.nodes.last {
|
|
lastNode.isHidden = true
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
self.disposable.dispose()
|
|
}
|
|
|
|
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
|
var size = size
|
|
size.width = min(size.width, 270.0)
|
|
|
|
self.validLayout = size
|
|
|
|
var origin: CGPoint = CGPoint(x: 0.0, y: 17.0)
|
|
|
|
let titleSize = self.titleNode.measure(size)
|
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
|
origin.y += titleSize.height + 3.0
|
|
|
|
let subtitleSize = self.subtitleNode.measure(size)
|
|
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - subtitleSize.width) / 2.0), y: origin.y), size: subtitleSize))
|
|
origin.y += subtitleSize.height + 17.0
|
|
transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(x: 0.0, y: origin.y - UIScreenPixel, width: size.width, height: UIScreenPixel))
|
|
|
|
var lastNodeSize: CGSize?
|
|
for node in self.nodes {
|
|
let size = node.updateLayout(size)
|
|
transition.updateFrame(node: node, frame: CGRect(origin: origin, size: size))
|
|
origin.y += size.height
|
|
lastNodeSize = size
|
|
}
|
|
|
|
if let lastSize = lastNodeSize {
|
|
let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0))
|
|
transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - indicatorSize.width) / 2.0), y: origin.y - lastSize.height + floorToScreenPixels((lastSize.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
|
}
|
|
|
|
return CGSize(width: size.width, height: origin.y - UIScreenPixel)
|
|
}
|
|
}
|
|
|
|
public func languageSuggestionController(context: AccountContext, suggestedLocalization: SuggestedLocalizationInfo, currentLanguageCode: String, openSelection: @escaping () -> Void) -> AlertController? {
|
|
guard let localization = suggestedLocalization.availableLocalizations.filter({ $0.languageCode == suggestedLocalization.languageCode }).first else {
|
|
return nil
|
|
}
|
|
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let theme = context.sharedContext.presentationData
|
|
let strings = LanguageSuggestionControllerStrings(localization: suggestedLocalization)
|
|
guard let mainPath = getAppBundle().path(forResource: "en", ofType: "lproj") else {
|
|
return nil
|
|
}
|
|
let englishStrings = LanguageSuggestionControllerStrings(bundle: Bundle(path: mainPath))
|
|
|
|
let disposable = MetaDisposable()
|
|
|
|
var dismissImpl: ((Bool) -> Void)?
|
|
let contentNode = LanguageSuggestionAlertContentNode(presentationData: presentationData, strings: strings, englishStrings: englishStrings, suggestedLocalization: localization, openSelection: {
|
|
dismissImpl?(true)
|
|
openSelection()
|
|
}, applyLocalization: { languageCode, startActivity in
|
|
if languageCode == currentLanguageCode {
|
|
dismissImpl?(true)
|
|
} else {
|
|
startActivity()
|
|
disposable.set((downloadAndApplyLocalization(accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, languageCode: languageCode)
|
|
|> deliverOnMainQueue).start(completed: {
|
|
dismissImpl?(true)
|
|
}))
|
|
}
|
|
}, dismiss: {
|
|
dismissImpl?(true)
|
|
})
|
|
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
|
dismissImpl = { [weak controller] animated in
|
|
if animated {
|
|
controller?.dismissAnimated()
|
|
} else {
|
|
controller?.dismiss()
|
|
}
|
|
}
|
|
return controller
|
|
}
|