#if ENABLE_WALLET import Foundation import UIKit import Display import WalletUI import Postbox import TelegramCore import SyncCore 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