#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 { return .single(LocalWalletConfiguration(source: .string(""), blockchainName: "")) } func updateLocalWalletConfiguration(_ f: @escaping (LocalWalletConfiguration) -> LocalWalletConfiguration) -> Signal { 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 { return self.context.sharedContext.applicationBindings.applicationInForeground } func downloadFile(url: URL) -> Signal { return .fail(.generic) } func updateResolvedWalletConfiguration(source: LocalWalletConfigurationSource, blockchainName: String, resolvedValue: String) -> Signal { 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 { 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