/* * This file is part of Stack Wallet. * * Copyright (c) 2023 Cypher Stack * All Rights Reserved. * The code is distributed under GPLv3 license, see LICENSE file for details. * Generated by Cypher Stack on 2023-05-26 * */ import 'package:flutter/cupertino.dart'; import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/services/event_bus/events/global/tor_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/utilities/amount/amount_unit.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/languages_enum.dart'; import 'package:stackwallet/utilities/enums/sync_type_enum.dart'; import 'package:stackwallet/wallets/wallet/mixins/cash_fusion.dart'; import 'package:uuid/uuid.dart'; class Prefs extends ChangeNotifier { Prefs._(); static final Prefs _instance = Prefs._(); static Prefs get instance => _instance; bool _initialized = false; bool get isInitialized => _initialized; Future init() async { if (!_initialized) { _currency = await _getPreferredCurrency(); // _exchangeRateType = await _getExchangeRateType(); _randomizePIN = await _getRandomizePIN(); _useBiometrics = await _getUseBiometrics(); _hasPin = await _getHasPin(); _language = await _getPreferredLanguage(); _showFavoriteWallets = await _getShowFavoriteWallets(); _wifiOnly = await _getUseWifiOnly(); _syncType = await _getSyncType(); _walletIdsSyncOnStartup = await _getWalletIdsSyncOnStartup(); _currentNotificationId = await _getCurrentNotificationIndex(); _lastUnlocked = await _getLastUnlocked(); _lastUnlockedTimeout = await _getLastUnlockedTimeout(); _showTestNetCoins = await _getShowTestNetCoins(); _torKillswitch = await _getTorKillswitch(); _isAutoBackupEnabled = await _getIsAutoBackupEnabled(); _autoBackupLocation = await _getAutoBackupLocation(); _backupFrequencyType = await _getBackupFrequencyType(); _lastAutoBackup = await _getLastAutoBackup(); _hideBlockExplorerWarning = await _getHideBlockExplorerWarning(); _gotoWalletOnStartup = await _getGotoWalletOnStartup(); _startupWalletId = await _getStartupWalletId(); _externalCalls = await _getHasExternalCalls(); _familiarity = await _getHasFamiliarity(); _userId = await _getUserId(); _signupEpoch = await _getSignupEpoch(); _enableCoinControl = await _getEnableCoinControl(); _enableSystemBrightness = await _getEnableSystemBrightness(); _themeId = await _getThemeId(); _systemBrightnessLightThemeId = await _getSystemBrightnessLightThemeId(); _systemBrightnessDarkThemeId = await _getSystemBrightnessDarkTheme(); await _setAmountUnits(); await _setMaxDecimals(); _useTor = await _getUseTor(); _fusionServerInfo = await _getFusionServerInfo(); _initialized = true; } } // last timestamp user unlocked wallet int _lastUnlockedTimeout = 60; int get lastUnlockedTimeout => _lastUnlockedTimeout; set lastUnlockedTimeout(int lastUnlockedTimeout) { if (_lastUnlockedTimeout != lastUnlockedTimeout) { DB.instance.put( boxName: DB.boxNamePrefs, key: "lastUnlockedTimeout", value: lastUnlockedTimeout); _lastUnlockedTimeout = lastUnlockedTimeout; notifyListeners(); } } Future _getLastUnlockedTimeout() async { return (DB.instance.get( boxName: DB.boxNamePrefs, key: "lastUnlockedTimeout")) as int? ?? 60; } // last timestamp user unlocked wallet int _lastUnlocked = 0; int get lastUnlocked => _lastUnlocked; set lastUnlocked(int lastUnlocked) { if (_lastUnlocked != lastUnlocked) { DB.instance.put( boxName: DB.boxNamePrefs, key: "lastUnlocked", value: lastUnlocked); _lastUnlocked = lastUnlocked; notifyListeners(); } } Future _getLastUnlocked() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "lastUnlocked") as int? ?? 0; } // notification index late int _currentNotificationId; int get currentNotificationId => _currentNotificationId; Future incrementCurrentNotificationIndex() async { if (_currentNotificationId <= Constants.notificationsMax) { _currentNotificationId++; } else { _currentNotificationId = 0; } await DB.instance.put( boxName: DB.boxNamePrefs, key: "currentNotificationId", value: _currentNotificationId); notifyListeners(); } Future _getCurrentNotificationIndex() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "currentNotificationId") as int? ?? 0; } // list of wallet ids to auto sync when auto sync only selected wallets is chosen List _walletIdsSyncOnStartup = []; List get walletIdsSyncOnStartup => _walletIdsSyncOnStartup; set walletIdsSyncOnStartup(List walletIdsSyncOnStartup) { if (_walletIdsSyncOnStartup != walletIdsSyncOnStartup) { DB.instance.put( boxName: DB.boxNamePrefs, key: "walletIdsSyncOnStartup", value: walletIdsSyncOnStartup); _walletIdsSyncOnStartup = walletIdsSyncOnStartup; notifyListeners(); } } Future> _getWalletIdsSyncOnStartup() async { final list = await DB.instance.get( boxName: DB.boxNamePrefs, key: "walletIdsSyncOnStartup") as List? ?? []; return List.from(list); } // sync type SyncingType _syncType = SyncingType.allWalletsOnStartup; SyncingType get syncType => _syncType; set syncType(SyncingType syncType) { if (_syncType != syncType) { DB.instance.put( boxName: DB.boxNamePrefs, key: "syncTypeIndex", value: syncType.index); _syncType = syncType; notifyListeners(); } } Future _getSyncType() async { final int index = await DB.instance.get( boxName: DB.boxNamePrefs, key: "syncTypeIndex") as int? ?? SyncingType.allWalletsOnStartup.index; return SyncingType.values[index]; } // wifi only bool _wifiOnly = false; bool get wifiOnly => _wifiOnly; set wifiOnly(bool wifiOnly) { if (_wifiOnly != wifiOnly) { DB.instance.put( boxName: DB.boxNamePrefs, key: "wifiOnly", value: wifiOnly); _wifiOnly = wifiOnly; notifyListeners(); } } Future _getUseWifiOnly() async { return await DB.instance .get(boxName: DB.boxNamePrefs, key: "wifiOnly") as bool? ?? false; } // show favorites bool _showFavoriteWallets = true; bool get showFavoriteWallets => _showFavoriteWallets; set showFavoriteWallets(bool showFavoriteWallets) { if (_showFavoriteWallets != showFavoriteWallets) { DB.instance.put( boxName: DB.boxNamePrefs, key: "showFavoriteWallets", value: showFavoriteWallets); _showFavoriteWallets = showFavoriteWallets; notifyListeners(); } } Future _getShowFavoriteWallets() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "showFavoriteWallets") as bool? ?? true; } // language String _language = Language.englishUS.description; String get language => _language; set language(String newLanguage) { if (_language != newLanguage) { DB.instance.put( boxName: DB.boxNamePrefs, key: "language", value: newLanguage); _language = newLanguage; notifyListeners(); } } Future _getPreferredLanguage() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "language") as String? ?? Language.englishUS.description; } // base currency String _currency = "USD"; String get currency => _currency; set currency(String newCurrency) { if (currency != newCurrency) { DB.instance.put( boxName: DB.boxNamePrefs, key: "currency", value: newCurrency); _currency = newCurrency; notifyListeners(); } } Future _getPreferredCurrency() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "currency") as String? ?? "USD"; } // exchange rate type // ExchangeRateType _exchangeRateType = ExchangeRateType.estimated; // // ExchangeRateType get exchangeRateType => _exchangeRateType; // // set exchangeRateType(ExchangeRateType exchangeRateType) { // if (_exchangeRateType != exchangeRateType) { // switch (exchangeRateType) { // case ExchangeRateType.estimated: // DB.instance.put( // boxName: DB.boxNamePrefs, // key: "exchangeRateType", // value: "estimated"); // break; // case ExchangeRateType.fixed: // DB.instance.put( // boxName: DB.boxNamePrefs, // key: "exchangeRateType", // value: "fixed"); // break; // } // _exchangeRateType = exchangeRateType; // notifyListeners(); // } // } // // Future _getExchangeRateType() async { // String? rate = await DB.instance.get( // boxName: DB.boxNamePrefs, key: "exchangeRateType") as String?; // rate ??= "estimated"; // switch (rate) { // case "estimated": // return ExchangeRateType.estimated; // case "fixed": // return ExchangeRateType.fixed; // default: // throw Exception("Invalid exchange rate type found in prefs!"); // } // } // randomize PIN bool _randomizePIN = false; bool get randomizePIN => _randomizePIN; set randomizePIN(bool randomizePIN) { if (_randomizePIN != randomizePIN) { DB.instance.put( boxName: DB.boxNamePrefs, key: "randomizePIN", value: randomizePIN); _randomizePIN = randomizePIN; notifyListeners(); } } Future _getRandomizePIN() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "randomizePIN") as bool? ?? false; } // use biometrics bool _useBiometrics = false; bool get useBiometrics => _useBiometrics; set useBiometrics(bool useBiometrics) { if (_useBiometrics != useBiometrics) { DB.instance.put( boxName: DB.boxNamePrefs, key: "useBiometrics", value: useBiometrics); _useBiometrics = useBiometrics; notifyListeners(); } } Future _getUseBiometrics() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "useBiometrics") as bool? ?? false; } // has set up pin bool _hasPin = false; bool get hasPin => _hasPin; set hasPin(bool hasPin) { if (_hasPin != hasPin) { DB.instance .put(boxName: DB.boxNamePrefs, key: "hasPin", value: hasPin); _hasPin = hasPin; notifyListeners(); } } Future _getHasPin() async { return await DB.instance .get(boxName: DB.boxNamePrefs, key: "hasPin") as bool? ?? false; } // familiarity int _familiarity = 0; int get familiarity => _familiarity; set familiarity(int familiarity) { if (_familiarity != familiarity) { DB.instance.put( boxName: DB.boxNamePrefs, key: "familiarity", value: familiarity); _familiarity = familiarity; notifyListeners(); } } Future _getHasFamiliarity() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "familiarity") as int? ?? 0; } // tor bool _torKillswitch = true; bool get torKillSwitch => _torKillswitch; set torKillSwitch(bool torKillswitch) { if (_torKillswitch != torKillswitch) { DB.instance.put( boxName: DB.boxNamePrefs, key: "torKillswitch", value: torKillswitch, ); _torKillswitch = torKillswitch; notifyListeners(); } } Future _getTorKillswitch() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "torKillswitch", ) as bool? ?? true; } // show testnet coins bool _showTestNetCoins = false; bool get showTestNetCoins => _showTestNetCoins; set showTestNetCoins(bool showTestNetCoins) { if (_showTestNetCoins != showTestNetCoins) { DB.instance.put( boxName: DB.boxNamePrefs, key: "showTestNetCoins", value: showTestNetCoins); _showTestNetCoins = showTestNetCoins; notifyListeners(); } } Future _getShowTestNetCoins() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "showTestNetCoins") as bool? ?? false; } // auto backup bool _isAutoBackupEnabled = false; bool get isAutoBackupEnabled => _isAutoBackupEnabled; set isAutoBackupEnabled(bool isAutoBackupEnabled) { if (_isAutoBackupEnabled != isAutoBackupEnabled) { DB.instance .put( boxName: DB.boxNamePrefs, key: "isAutoBackupEnabled", value: isAutoBackupEnabled) .then((_) { _isAutoBackupEnabled = isAutoBackupEnabled; notifyListeners(); }); } } Future _getIsAutoBackupEnabled() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "isAutoBackupEnabled") as bool? ?? false; } // auto backup file location uri String? _autoBackupLocation; String? get autoBackupLocation => _autoBackupLocation; set autoBackupLocation(String? autoBackupLocation) { if (this.autoBackupLocation != autoBackupLocation) { DB.instance.put( boxName: DB.boxNamePrefs, key: "autoBackupLocation", value: autoBackupLocation); _autoBackupLocation = autoBackupLocation; notifyListeners(); } } Future _getAutoBackupLocation() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "autoBackupLocation") as String?; } // auto backup frequency type BackupFrequencyType _backupFrequencyType = BackupFrequencyType.everyTenMinutes; BackupFrequencyType get backupFrequencyType => _backupFrequencyType; set backupFrequencyType(BackupFrequencyType backupFrequencyType) { if (_backupFrequencyType != backupFrequencyType) { switch (backupFrequencyType) { case BackupFrequencyType.everyTenMinutes: DB.instance.put( boxName: DB.boxNamePrefs, key: "backupFrequencyType", value: "10Min"); break; case BackupFrequencyType.everyAppStart: DB.instance.put( boxName: DB.boxNamePrefs, key: "backupFrequencyType", value: "onStart"); break; case BackupFrequencyType.afterClosingAWallet: DB.instance.put( boxName: DB.boxNamePrefs, key: "backupFrequencyType", value: "onWalletClose"); break; } _backupFrequencyType = backupFrequencyType; notifyListeners(); } } Future _getBackupFrequencyType() async { String? rate = await DB.instance.get( boxName: DB.boxNamePrefs, key: "backupFrequencyType") as String?; rate ??= "10Min"; switch (rate) { case "10Min": return BackupFrequencyType.everyTenMinutes; case "onStart": return BackupFrequencyType.everyAppStart; case "onWalletClose": return BackupFrequencyType.afterClosingAWallet; default: throw Exception("Invalid Backup Frequency type found in prefs!"); } } // auto backup last time stamp DateTime? _lastAutoBackup; DateTime? get lastAutoBackup => _lastAutoBackup; set lastAutoBackup(DateTime? lastAutoBackup) { if (this.lastAutoBackup != lastAutoBackup) { DB.instance.put( boxName: DB.boxNamePrefs, key: "lastAutoBackup", value: lastAutoBackup); _lastAutoBackup = lastAutoBackup; notifyListeners(); } } Future _getLastAutoBackup() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "autoBackupFileUri") as DateTime?; } // auto backup bool _hideBlockExplorerWarning = false; bool get hideBlockExplorerWarning => _hideBlockExplorerWarning; set hideBlockExplorerWarning(bool hideBlockExplorerWarning) { if (_hideBlockExplorerWarning != hideBlockExplorerWarning) { DB.instance .put( boxName: DB.boxNamePrefs, key: "hideBlockExplorerWarning", value: hideBlockExplorerWarning) .then((_) { _hideBlockExplorerWarning = hideBlockExplorerWarning; notifyListeners(); }); } } Future _getHideBlockExplorerWarning() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "hideBlockExplorerWarning") as bool? ?? false; } // auto backup bool _gotoWalletOnStartup = false; bool get gotoWalletOnStartup => _gotoWalletOnStartup; set gotoWalletOnStartup(bool gotoWalletOnStartup) { if (_gotoWalletOnStartup != gotoWalletOnStartup) { DB.instance .put( boxName: DB.boxNamePrefs, key: "gotoWalletOnStartup", value: gotoWalletOnStartup) .then((_) { _gotoWalletOnStartup = gotoWalletOnStartup; notifyListeners(); }); } } Future _getGotoWalletOnStartup() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "gotoWalletOnStartup") as bool? ?? false; } // startup wallet id String? _startupWalletId; String? get startupWalletId => _startupWalletId; set startupWalletId(String? startupWalletId) { if (this.startupWalletId != startupWalletId) { DB.instance.put( boxName: DB.boxNamePrefs, key: "startupWalletId", value: startupWalletId); _startupWalletId = startupWalletId; notifyListeners(); } } Future _getStartupWalletId() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "startupWalletId") as String?; } // incognito mode off by default // allow external network calls such as exchange data and price info bool _externalCalls = true; bool get externalCalls => _externalCalls; set externalCalls(bool externalCalls) { if (_externalCalls != externalCalls) { DB.instance .put( boxName: DB.boxNamePrefs, key: "externalCalls", value: externalCalls) .then((_) { _externalCalls = externalCalls; notifyListeners(); }); } } Future _getHasExternalCalls() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ?? true; } Future isExternalCallsSet() async { if (await DB.instance .get(boxName: DB.boxNamePrefs, key: "externalCalls") == null) { return false; } return true; } String? _userId; String? get userID => _userId; Future _getUserId() async { String? userID = await DB.instance .get(boxName: DB.boxNamePrefs, key: "userID") as String?; if (userID == null) { userID = const Uuid().v4(); await saveUserID(userID); } return userID; } Future saveUserID(String userId) async { _userId = userId; await DB.instance .put(boxName: DB.boxNamePrefs, key: "userID", value: _userId); // notifyListeners(); } int? _signupEpoch; int? get signupEpoch => _signupEpoch; Future _getSignupEpoch() async { int? signupEpoch = await DB.instance .get(boxName: DB.boxNamePrefs, key: "signupEpoch") as int?; if (signupEpoch == null) { signupEpoch = DateTime.now().millisecondsSinceEpoch ~/ Duration.millisecondsPerSecond; await saveSignupEpoch(signupEpoch); } return signupEpoch; } Future saveSignupEpoch(int signupEpoch) async { _signupEpoch = signupEpoch; await DB.instance.put( boxName: DB.boxNamePrefs, key: "signupEpoch", value: _signupEpoch); // notifyListeners(); } // show testnet coins bool _enableCoinControl = false; bool get enableCoinControl => _enableCoinControl; set enableCoinControl(bool enableCoinControl) { if (_enableCoinControl != enableCoinControl) { DB.instance.put( boxName: DB.boxNamePrefs, key: "enableCoinControl", value: enableCoinControl); _enableCoinControl = enableCoinControl; notifyListeners(); } } Future _getEnableCoinControl() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "enableCoinControl") as bool? ?? false; } // follow system brightness bool _enableSystemBrightness = false; bool get enableSystemBrightness => _enableSystemBrightness; set enableSystemBrightness(bool enableSystemBrightness) { if (_enableSystemBrightness != enableSystemBrightness) { DB.instance.put( boxName: DB.boxNamePrefs, key: "enableSystemBrightness", value: enableSystemBrightness); _enableSystemBrightness = enableSystemBrightness; notifyListeners(); } } Future _getEnableSystemBrightness() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "enableSystemBrightness") as bool? ?? false; } // current theme id String _themeId = "light"; String get themeId => _themeId; set themeId(String themeId) { if (this.themeId != themeId) { DB.instance.put( boxName: DB.boxNamePrefs, key: "themeId", value: themeId, ); _themeId = themeId; notifyListeners(); } } Future _getThemeId() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "themeId", ) as String? ?? "light"; } // current system brightness light theme id String _systemBrightnessLightThemeId = "light"; String get systemBrightnessLightThemeId => _systemBrightnessLightThemeId; set systemBrightnessLightThemeId(String systemBrightnessLightThemeId) { if (this.systemBrightnessLightThemeId != systemBrightnessLightThemeId) { DB.instance.put( boxName: DB.boxNamePrefs, key: "systemBrightnessLightThemeId", value: systemBrightnessLightThemeId, ); _systemBrightnessLightThemeId = systemBrightnessLightThemeId; notifyListeners(); } } Future _getSystemBrightnessLightThemeId() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "systemBrightnessLightThemeId", ) as String? ?? "light"; } // current system brightness dark theme id String _systemBrightnessDarkThemeId = "dark"; String get systemBrightnessDarkThemeId => _systemBrightnessDarkThemeId; set systemBrightnessDarkThemeId(String systemBrightnessDarkThemeId) { if (this.systemBrightnessDarkThemeId != systemBrightnessDarkThemeId) { DB.instance.put( boxName: DB.boxNamePrefs, key: "systemBrightnessDarkThemeId", value: systemBrightnessDarkThemeId, ); _systemBrightnessDarkThemeId = systemBrightnessDarkThemeId; notifyListeners(); } } Future _getSystemBrightnessDarkTheme() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "systemBrightnessDarkThemeId", ) as String? ?? "dark"; } // coin amount unit settings final Map _amountUnits = {}; AmountUnit amountUnit(Coin coin) => _amountUnits[coin] ?? AmountUnit.normal; void updateAmountUnit({required Coin coin, required AmountUnit amountUnit}) { if (this.amountUnit(coin) != amountUnit) { DB.instance.put( boxName: DB.boxNamePrefs, key: "amountUnitFor${coin.name}", value: amountUnit.index, ); _amountUnits[coin] = amountUnit; notifyListeners(); } } Future _setAmountUnits() async { for (final coin in Coin.values) { final unitIndex = await DB.instance.get( boxName: DB.boxNamePrefs, key: "amountUnitFor${coin.name}", ) as int? ?? 0; // 0 is "normal" _amountUnits[coin] = AmountUnit.values[unitIndex]; } } // coin precision setting (max decimal places to show) final Map _amountDecimals = {}; int maxDecimals(Coin coin) => _amountDecimals[coin] ?? coin.decimals; void updateMaxDecimals({required Coin coin, required int maxDecimals}) { if (this.maxDecimals(coin) != maxDecimals) { DB.instance.put( boxName: DB.boxNamePrefs, key: "maxDecimalsFor${coin.name}", value: maxDecimals, ); _amountDecimals[coin] = maxDecimals; notifyListeners(); } } Future _setMaxDecimals() async { for (final coin in Coin.values) { final decimals = await DB.instance.get( boxName: DB.boxNamePrefs, key: "maxDecimalsFor${coin.name}", ) as int? ?? (coin.decimals > 18 ? 18 : coin.decimals); // use some sane max rather than up to 30 that nano uses _amountDecimals[coin] = decimals; } } // enabled tor bool _useTor = false; bool get useTor => _useTor; set useTor(bool useTor) { if (_useTor != useTor) { DB.instance.put( boxName: DB.boxNamePrefs, key: "useTor", value: useTor, ); _useTor = useTor; notifyListeners(); GlobalEventBus.instance.fire( TorPreferenceChangedEvent( status: useTor ? TorStatus.enabled : TorStatus.disabled, message: "useTor updated in prefs", ), ); } } Future _getUseTor() async { return await DB.instance.get( boxName: DB.boxNamePrefs, key: "useTor", ) as bool? ?? false; } // fusion server info FusionInfo _fusionServerInfo = FusionInfo.DEFAULTS; FusionInfo get fusionServerInfo => _fusionServerInfo; set fusionServerInfo(FusionInfo fusionServerInfo) { if (this.fusionServerInfo != fusionServerInfo) { DB.instance.put( boxName: DB.boxNamePrefs, key: "fusionServerInfo", value: fusionServerInfo.toJsonString(), ); _fusionServerInfo = fusionServerInfo; notifyListeners(); } } Future _getFusionServerInfo() async { final saved = await DB.instance.get( boxName: DB.boxNamePrefs, key: "fusionServerInfo", ) as String?; try { return FusionInfo.fromJsonString(saved!); } catch (_) { return FusionInfo.DEFAULTS; } } }