Swiftgram/submodules/TelegramUI/Sources/WalletContextImpl.swift

284 lines
13 KiB
Swift

#if ENABLE_WALLET
import Foundation
import UIKit
import Display
import WalletUI
import Postbox
import TelegramCore
import AccountContext
import SwiftSignalKit
import TelegramPresentationData
import ShareController
import DeviceAccess
import PresentationDataUtils
import WalletCore
extension WalletConfiguration {
static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
if let data = appConfiguration.data, let config = data["wallet_config"] as? String, let blockchainName = data["wallet_blockchain_name"] as? String {
var disableProxy = false
if let value = data["wallet_disable_proxy"] as? String {
disableProxy = value != "0"
} else if let value = data["wallet_disable_proxy"] as? Int {
disableProxy = value != 0
}
return WalletConfiguration(config: config, blockchainName: blockchainName, disableProxy: disableProxy)
} else {
return .defaultValue
}
}
}
final class WalletStorageInterfaceImpl: WalletStorageInterface {
private let postbox: Postbox
init(postbox: Postbox) {
self.postbox = postbox
}
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError> {
return self.postbox.preferencesView(keys: [PreferencesKeys.walletCollection])
|> map { view -> [WalletStateRecord] in
guard let walletCollection = view.values[PreferencesKeys.walletCollection] as? WalletCollection else {
return []
}
return walletCollection.wallets.flatMap { item -> WalletStateRecord? in
do {
return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
} catch {
return nil
}
}
}
}
func getWalletRecords() -> Signal<[WalletStateRecord], NoError> {
return self.postbox.transaction { transaction -> [WalletStateRecord] in
guard let walletCollection = transaction.getPreferencesEntry(key: PreferencesKeys.walletCollection) as? WalletCollection else {
return []
}
return walletCollection.wallets.flatMap { item -> WalletStateRecord? in
do {
return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
} catch {
return nil
}
}
}
}
func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError> {
return self.postbox.transaction { transaction -> [WalletStateRecord] in
var updatedRecords: [WalletStateRecord] = []
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
let updatedItems = f(walletCollection.wallets.flatMap { item -> WalletStateRecord? in
do {
return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
} catch {
return nil
}
})
walletCollection.wallets = updatedItems.flatMap { item in
do {
return WalletCollectionItem(info: try JSONEncoder().encode(item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap {
try? JSONEncoder().encode($0)
})
} catch {
return nil
}
}
return walletCollection
})
return updatedRecords
}
}
func localWalletConfiguration() -> Signal<LocalWalletConfiguration, NoError> {
return .single(LocalWalletConfiguration(source: .string(""), blockchainName: ""))
}
func updateLocalWalletConfiguration(_ f: @escaping (LocalWalletConfiguration) -> LocalWalletConfiguration) -> Signal<Never, NoError> {
return .complete()
}
}
final class WalletContextImpl: WalletContext {
private let context: AccountContext
let storage: WalletStorageInterface
let tonInstance: TonInstance
let keychain: TonKeychain
let strings: PresentationStrings
let presentationData: WalletPresentationData
let supportsCustomConfigurations: Bool = false
var termsUrl: String? {
return self.strings.TelegramWallet_Intro_TermsUrl
}
var feeInfoUrl: String? {
return self.strings.AppWallet_TransactionInfo_FeeInfoURL
}
var inForeground: Signal<Bool, NoError> {
return self.context.sharedContext.applicationBindings.applicationInForeground
}
func downloadFile(url: URL) -> Signal<Data, WalletDownloadFileError> {
return .fail(.generic)
}
func updateResolvedWalletConfiguration(source: LocalWalletConfigurationSource, blockchainName: String, resolvedValue: String) -> Signal<Never, NoError> {
return .complete()
}
init(context: AccountContext, tonContext: TonContext) {
self.context = context
self.storage = WalletStorageInterfaceImpl(postbox: self.context.account.postbox)
self.tonInstance = tonContext.instance
self.keychain = tonContext.keychain
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.strings = presentationData.strings
let theme = presentationData.theme
let strings = presentationData.strings
let timeFormat: WalletTimeFormat
switch presentationData.dateTimeFormat.timeFormat {
case .military:
timeFormat = .military
case .regular:
timeFormat = .regular
}
let dateFormat: WalletDateFormat
switch presentationData.dateTimeFormat.dateFormat {
case .dayFirst:
dateFormat = .dayFirst
case .monthFirst:
dateFormat = .monthFirst
}
let navigationBarData = NavigationBarPresentationData(presentationData: presentationData)
self.presentationData = WalletPresentationData(
theme: WalletTheme(
info: WalletInfoTheme(
buttonBackgroundColor: UIColor(rgb: 0x32aafe),
buttonTextColor: .white,
incomingFundsTitleColor: theme.chatList.secretTitleColor,
outgoingFundsTitleColor: theme.list.itemDestructiveColor
), transaction: WalletTransactionTheme(
descriptionBackgroundColor: theme.chat.message.incoming.bubble.withoutWallpaper.fill,
descriptionTextColor: theme.chat.message.incoming.primaryTextColor
), setup: WalletSetupTheme(
buttonFillColor: theme.list.itemCheckColors.fillColor,
buttonForegroundColor: theme.list.itemCheckColors.foregroundColor,
inputBackgroundColor: theme.actionSheet.inputBackgroundColor,
inputPlaceholderColor: theme.actionSheet.inputPlaceholderColor,
inputTextColor: theme.actionSheet.inputTextColor,
inputClearButtonColor: theme.actionSheet.inputClearButtonColor.withAlphaComponent(0.8)
),
list: WalletListTheme(
itemPrimaryTextColor: theme.list.itemPrimaryTextColor,
itemSecondaryTextColor: theme.list.itemSecondaryTextColor,
itemPlaceholderTextColor: theme.list.itemPlaceholderTextColor,
itemDestructiveColor: theme.list.itemDestructiveColor,
itemAccentColor: theme.list.itemAccentColor,
itemDisabledTextColor: theme.list.itemDisabledTextColor,
plainBackgroundColor: theme.list.plainBackgroundColor,
blocksBackgroundColor: theme.list.blocksBackgroundColor,
itemPlainSeparatorColor: theme.list.itemPlainSeparatorColor,
itemBlocksBackgroundColor: theme.list.itemBlocksBackgroundColor,
itemBlocksSeparatorColor: theme.list.itemBlocksSeparatorColor,
itemHighlightedBackgroundColor: theme.list.itemHighlightedBackgroundColor,
sectionHeaderTextColor: theme.list.sectionHeaderTextColor,
freeTextColor: theme.list.freeTextColor,
freeTextErrorColor: theme.list.freeTextErrorColor,
inputClearButtonColor: theme.list.inputClearButtonColor
),
statusBarStyle: theme.rootController.statusBarStyle.style,
navigationBar: navigationBarData.theme,
keyboardAppearance: theme.rootController.keyboardColor.keyboardAppearance,
alert: AlertControllerTheme(presentationData: presentationData),
actionSheet: ActionSheetControllerTheme(presentationData: presentationData)
), strings: WalletStrings(
primaryComponent: WalletStringsComponent(
languageCode: strings.primaryComponent.languageCode,
localizedName: strings.primaryComponent.localizedName,
pluralizationRulesCode: strings.primaryComponent.pluralizationRulesCode,
dict: strings.primaryComponent.dict
),
secondaryComponent: strings.secondaryComponent.flatMap { component in
return WalletStringsComponent(
languageCode: component.languageCode,
localizedName: component.localizedName,
pluralizationRulesCode: component.pluralizationRulesCode,
dict: component.dict
)
},
groupingSeparator: strings.groupingSeparator
), dateTimeFormat: WalletPresentationDateTimeFormat(
timeFormat: timeFormat,
dateFormat: dateFormat,
dateSeparator: presentationData.dateTimeFormat.dateSeparator,
decimalSeparator: presentationData.dateTimeFormat.decimalSeparator,
groupingSeparator: presentationData.dateTimeFormat.groupingSeparator
)
)
}
func getServerSalt() -> Signal<Data, WalletContextGetServerSaltError> {
return getServerWalletSalt(network: self.context.account.network)
|> mapError { _ -> WalletContextGetServerSaltError in
return .generic
}
}
func presentNativeController(_ controller: UIViewController) {
self.context.sharedContext.mainWindow?.presentNative(controller)
}
func idleTimerExtension() -> Disposable {
return self.context.sharedContext.applicationBindings.pushIdleTimerExtension()
}
func openUrl(_ url: String) {
return self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {})
}
func shareUrl(_ url: String) {
let controller = ShareController(context: self.context, subject: .url(url))
self.context.sharedContext.mainWindow?.present(controller, on: .root)
}
func openPlatformSettings() {
self.context.sharedContext.applicationBindings.openSettings()
}
func authorizeAccessToCamera(completion: @escaping () -> Void) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: presentationData, present: { c, a in
c.presentationArguments = a
self.context.sharedContext.mainWindow?.present(c, on: .root)
}, openSettings: { [weak self] in
self?.openPlatformSettings()
}, { granted in
guard granted else {
return
}
completion()
})
}
func pickImage(present: @escaping (ViewController) -> Void, completion: @escaping (UIImage) -> Void) {
self.context.sharedContext.openImagePicker(context: self.context, completion: { image in
completion(image)
}, present: { [weak self] controller in
present(controller)
})
}
}
#endif