diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml new file mode 100644 index 000000000..9d49ed3d8 --- /dev/null +++ b/.github/workflows/cache_dependencies.yml @@ -0,0 +1,56 @@ +name: Cache Dependencies + +on: + push: + branches: [ main ] + +jobs: + test: + + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: Flutter action + uses: subosito/flutter-action@v1 + with: + flutter-version: '3.3.x' + channel: stable + + - name: Install package dependencies + run: sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang + + - name: Execute Build and Setup Commands + run: | + sudo mkdir -p /opt/android + sudo chown $USER /opt/android + cd /opt/android + git clone https://github.com/cake-tech/cake_wallet.git --branch main + cd cake_wallet/scripts/android/ + ./install_ndk.sh + source ./app_env.sh cakewallet + ./app_config.sh + + - name: Cache Externals + id: cache-externals + uses: actions/cache@v3 + with: + path: | + /opt/android/cake_wallet/cw_haven/android/.cxx + /opt/android/cake_wallet/cw_haven/ios/External + /opt/android/cake_wallet/cw_monero/android/.cxx + /opt/android/cake_wallet/cw_monero/ios/External + /opt/android/cake_wallet/cw_shared_external/ios/External + key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }} + + - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} + name: Generate Externals + run: | + cd /opt/android/cake_wallet/scripts/android/ + source ./app_env.sh cakewallet + ./build_all.sh + ./copy_monero_deps.sh diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml new file mode 100644 index 000000000..3a439c556 --- /dev/null +++ b/.github/workflows/pr_test_build.yml @@ -0,0 +1,151 @@ +name: PR Test Build + +on: + pull_request: + branches: [ main ] + +jobs: + test: + + runs-on: ubuntu-18.04 + env: + STORE_PASS: test@cake_wallet + KEY_PASS: test@cake_wallet + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '8.x' + + - name: Flutter action + uses: subosito/flutter-action@v1 + with: + flutter-version: '3.3.x' + channel: stable + + - name: Install package dependencies + run: sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang + + - name: Execute Build and Setup Commands + run: | + sudo mkdir -p /opt/android + sudo chown $USER /opt/android + cd /opt/android + git clone https://github.com/cake-tech/cake_wallet.git --branch $GITHUB_HEAD_REF + cd cake_wallet/scripts/android/ + ./install_ndk.sh + source ./app_env.sh cakewallet + ./app_config.sh + + - name: Cache Externals + id: cache-externals + uses: actions/cache@v3 + with: + path: | + /opt/android/cake_wallet/cw_haven/android/.cxx + /opt/android/cake_wallet/cw_haven/ios/External + /opt/android/cake_wallet/cw_monero/android/.cxx + /opt/android/cake_wallet/cw_monero/ios/External + /opt/android/cake_wallet/cw_shared_external/ios/External + key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }} + + - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} + name: Generate Externals + run: | + cd /opt/android/cake_wallet/scripts/android/ + source ./app_env.sh cakewallet + ./build_all.sh + ./copy_monero_deps.sh + + - name: Install Flutter dependencies + run: | + cd /opt/android/cake_wallet + flutter pub get + + - name: Generate KeyStore + run: | + cd /opt/android/cake_wallet/android/app + keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS + + - name: Generate key properties + run: | + cd /opt/android/cake_wallet + flutter packages pub run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS + + - name: Generate localization + run: | + cd /opt/android/cake_wallet + flutter packages pub run tool/generate_localization.dart + + - name: Build generated code + run: | + cd /opt/android/cake_wallet + cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. + cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. + cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. + cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. + flutter packages pub run build_runner build --delete-conflicting-outputs + + - name: Add secrets + run: | + cd /opt/android/cake_wallet + touch lib/.secrets.g.dart + echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart + echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart + echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart + echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart + echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart + echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart + echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart + echo "const wyreApiKey = '${{ secrets.WYRE_API_KEY }}';" >> lib/.secrets.g.dart + echo "const wyreAccountId = '${{ secrets.WYRE_ACCOUNT_ID }}';" >> lib/.secrets.g.dart + echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart + echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart + echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart + echo "const sideShiftApiKey = '${{ secrets.SIDE_SHIFT_API_KEY }}';" >> lib/.secrets.g.dart + echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart + echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart + echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart + echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart + + - name: Rename app + run: sed -i -e "s/\${APP_NAME}/$GITHUB_HEAD_REF/g" /opt/android/cake_wallet/android/app/src/main/AndroidManifest.xml + + - name: Build + run: | + cd /opt/android/cake_wallet + flutter build apk --release + +# - name: Push to App Center +# run: | +# echo 'Installing App Center CLI tools' +# npm install -g appcenter-cli +# echo "Publishing test to App Center" +# appcenter distribute release \ +# --group "Testers" \ +# --file "/opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk" \ +# --release-notes ${GITHUB_HEAD_REF} \ +# --app Cake-Labs/Cake-Wallet \ +# --token ${{ secrets.APP_CENTER_TOKEN }} \ +# --quiet + + - name: Rename apk file + run: | + cd /opt/android/cake_wallet/build/app/outputs/apk/release + mkdir test-apk + cp app-release.apk test-apk/$GITHUB_HEAD_REF.apk + + - name: Upload Artifact + uses: kittaakos/upload-artifact-as-is@v0 + with: + path: /opt/android/cake_wallet/build/app/outputs/apk/release/test-apk/ + + - name: Send Test APK + continue-on-error: true + run: | + cd /opt/android/cake_wallet + var=$(curl --upload-file build/app/outputs/apk/release/app-release.apk https://transfer.sh/$GITHUB_HEAD_REF.apk -H "Max-Days: 10") + curl ${{ secrets.SLACK_WEB_HOOK }} -H "Content-Type: application/json" -d '{"apk_link": "'"$var"'","ticket": "'"$GITHUB_HEAD_REF"'"}' diff --git a/README.md b/README.md index bd74ccfbc..f485ca244 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,55 @@ We have 24/7 free support. Please contact support@cakewallet.com More instructions to follow For instructions on how to build for Android: please view file `howto-build-android.md` + +# Contributing + +## Improving translations + +Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull request with the changes. + +## Current list of language files: + +- English +- Spanish +- French +- German +- Italian +- Portugese +- Dutch +- Polish +- Croatian +- Russian +- Ukranian +- Hindi +- Japanese +- Chinese +- Korean + +## Add a new language + +1. Create a new `strings_XX.arb` file in `res/values/`, replacing XX with the language's [ISO 639-1 code](https://en.wikipedia.org/wiki/ISO_639-1). + +2. Edit the strings in this file, replacing XXX below with the translation for each string. + +`"welcome" : "Welcome to",` -> `"welcome" : "XXX",` + +3. For strings where there is a variable, denoted by a $ symbol and braces, such as ${status}, the string in braces should not be translated. For example, when editing line 106: + +"time" : "${minutes}m ${seconds}s" + +The only parts to be translated, if needed, are the values m and s after the variables. + +4. Add the language to `lib/entities/language_service.dart` under both `supportedLocales` and `localeCountryCode`. Use the name of the language in the local language and in English in parentheses after for `supportedLocales`. Use the [ISO 3166-1 alpha-3 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) for `localeCountryCode`. You must choose one country, so choose the country with the most native speakers of this language or is otherwise best associated with this language. + +5. Add a relevant flag to `assets/images/flags/XXXX.png`, replacing XXXX with the 3 digit localeCountryCode. The image must be 42x36 pizxels with a 3 pixels of transparent margin on all 4 sides. + +## Add a new fiat currency + +1. Check with [Cake Wallet support](https://guides.cakewallet.com) to see if the desired new fiat currency is available through our fiat API. Not all fiat currencies are. + +2. If the currency is associated strongly with a specific issuing country, map the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code with the applicable [ISO 3166-1 alpha-3 code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) in `lib/entities/fiat_currency.dart`. If the currency is used in a whole region or organization, then map with a reasonable interpretation of this (eg: eur countryCode for EUR symbol). + +3. Add the raw mapping underneath in `lib/entities/fiat_currency.dart` following the same format as the others. + +4. Add a flag of the issuing country or organization to `assets/images/flags/XXXX.png`, replacing XXXX with the ISO 3166-1 alpha-3 code used above (eg: `usa.png`, `eur.png`). Do not add this if the flag with the same name already exists. The image must be 42x36 pizxels with a 3 pixels of transparent margin on all 4 sides. \ No newline at end of file diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index 22278d5f1..9f6b65785 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -16,7 +16,7 @@ android:requestLegacyExternalStorage="true"> + + + + + + + + + > feeRates() async { try { final topDoubleString = await estimatefee(p: 1); - final middleDoubleString = await estimatefee(p: 20); + final middleDoubleString = await estimatefee(p: 5); final bottomDoubleString = await estimatefee(p: 100); final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000) diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index f8662eb95..9174fb3f8 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -72,7 +72,7 @@ abstract class ElectrumTransactionHistoryBase txs.entries.forEach((entry) { final val = entry.value; - if (val is Map) { + if (val is Map) { final tx = ElectrumTransactionInfo.fromJson(val, walletInfo.type); _updateOrInsert(tx); } @@ -85,9 +85,6 @@ abstract class ElectrumTransactionHistoryBase } void _updateOrInsert(ElectrumTransactionInfo transaction) { - if (transaction.id == null) { - return; - } if (transactions[transaction.id] == null) { transactions[transaction.id] = transaction; @@ -98,6 +95,7 @@ abstract class ElectrumTransactionHistoryBase originalTx?.height = transaction.height; originalTx?.date ??= transaction.date; originalTx?.isPending = transaction.isPending; + originalTx?.direction = transaction.direction; } } } diff --git a/cw_bitcoin/lib/electrum_transaction_info.dart b/cw_bitcoin/lib/electrum_transaction_info.dart index 6e85a2f88..8d6ef0fea 100644 --- a/cw_bitcoin/lib/electrum_transaction_info.dart +++ b/cw_bitcoin/lib/electrum_transaction_info.dart @@ -228,9 +228,7 @@ class ElectrumTransactionInfo extends TransactionInfo { m['id'] = id; m['height'] = height; m['amount'] = amount; - // FIX-ME: Hardcoded value - // m['direction'] = direction.index; - m['direction'] = 0; + m['direction'] = direction.index; m['date'] = date.millisecondsSinceEpoch; m['isPending'] = isPending; m['confirmations'] = confirmations; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 7abed8c07..f31db7bb8 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -129,7 +129,7 @@ abstract class ElectrumWalletBase extends WalletBase startSync() async { try { - syncStatus = StartingSyncStatus(); + syncStatus = AttemptingSyncStatus(); await walletAddresses.discoverAddresses(); await updateTransactions(); _subscribeForUpdates(); @@ -191,8 +191,10 @@ abstract class ElectrumWalletBase extends WalletBase with Serializable { const CryptoCurrency({ String title = '', int raw = -1, - this.name, + required this.name, + this.fullName, this.iconPath, - this.tag,}) + this.tag}) : super(title: title, raw: raw); + final String name; final String? tag; - final String? name; + final String? fullName; final String? iconPath; static const all = [ @@ -38,7 +36,7 @@ class CryptoCurrency extends EnumerableItem with Serializable { CryptoCurrency.ape, CryptoCurrency.avaxc, CryptoCurrency.btt, - CryptoCurrency.bttbsc, + CryptoCurrency.bttc, CryptoCurrency.doge, CryptoCurrency.firo, CryptoCurrency.usdttrc20, @@ -53,7 +51,6 @@ class CryptoCurrency extends EnumerableItem with Serializable { CryptoCurrency.xvg, CryptoCurrency.usdcpoly, CryptoCurrency.dcr, - CryptoCurrency.husd, CryptoCurrency.kmd, CryptoCurrency.mana, CryptoCurrency.maticpoly, @@ -70,339 +67,116 @@ class CryptoCurrency extends EnumerableItem with Serializable { CryptoCurrency.stx, ]; - static const xmr = CryptoCurrency(title: 'XMR', iconPath: 'assets/images/monero_icon.png', name: 'Monero', raw: 0); - static const ada = CryptoCurrency(title: 'ADA', iconPath: 'assets/images/ada_icon.png', name: 'Cardano', raw: 1); - static const bch = CryptoCurrency(title: 'BCH', iconPath: 'assets/images/bch_icon.png',name: 'Bitcoin Cash', raw: 2); - static const bnb = CryptoCurrency(title: 'BNB', iconPath: 'assets/images/bnb_icon.png', tag: 'BSC', name: 'Binance Coin', raw: 3); - static const btc = CryptoCurrency(title: 'BTC', iconPath: 'assets/images/btc.png', name: 'Bitcoin', raw: 4); - static const dai = CryptoCurrency(title: 'DAI', iconPath: 'assets/images/dai_icon.png', tag: 'ETH', name: 'Dai', raw: 5); - static const dash = CryptoCurrency(title: 'DASH', iconPath: 'assets/images/dash_icon.png', name: 'Dash', raw: 6); - static const eos = CryptoCurrency(title: 'EOS', iconPath: 'assets/images/eos_icon.png', name: 'EOS', raw: 7); - static const eth = CryptoCurrency(title: 'ETH', iconPath: 'assets/images/eth_icon.png', name: 'Ethereum', raw: 8); - static const ltc = CryptoCurrency(title: 'LTC', iconPath: 'assets/images/litecoin-ltc_icon.png', name: 'Litecoin', raw: 9); - static const nano = CryptoCurrency(title: 'NANO', raw: 10); - static const trx = CryptoCurrency(title: 'TRX', iconPath: 'assets/images/trx_icon.png', name: 'TRON', raw: 11); - static const usdt = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdt_icon.png', tag: 'OMNI', name: 'USDT', raw: 12); - static const usdterc20 = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdterc20_icon.png', tag: 'ETH', name: 'USDT', raw: 13); - static const xlm = CryptoCurrency(title: 'XLM', iconPath: 'assets/images/xlm_icon.png', name: 'Stellar', raw: 14); - static const xrp = CryptoCurrency(title: 'XRP', iconPath: 'assets/images/xrp_icon.png', name: 'Ripple', raw: 15); - static const xhv = CryptoCurrency(title: 'XHV', iconPath: 'assets/images/xhv_logo.png', name: 'Haven Protocol', raw: 16); + static const havenCurrencies = [ + xag, + xau, + xaud, + xbtc, + xcad, + xchf, + xcny, + xeur, + xgbp, + xjpy, + xnok, + xnzd, + xusd, + ]; - static const xag = CryptoCurrency(title: 'XAG', tag: 'XHV', raw: 17); - static const xau = CryptoCurrency(title: 'XAU', tag: 'XHV', raw: 18); - static const xaud = CryptoCurrency(title: 'XAUD', tag: 'XHV', raw: 19); - static const xbtc = CryptoCurrency(title: 'XBTC', tag: 'XHV', raw: 20); - static const xcad = CryptoCurrency(title: 'XCAD', tag: 'XHV', raw: 21); - static const xchf = CryptoCurrency(title: 'XCHF', tag: 'XHV', raw: 22); - static const xcny = CryptoCurrency(title: 'XCNY', tag: 'XHV', raw: 23); - static const xeur = CryptoCurrency(title: 'XEUR', tag: 'XHV', raw: 24); - static const xgbp = CryptoCurrency(title: 'XGBP', tag: 'XHV', raw: 25); - static const xjpy = CryptoCurrency(title: 'XJPY', tag: 'XHV', raw: 26); - static const xnok = CryptoCurrency(title: 'XNOK', tag: 'XHV', raw: 27); - static const xnzd = CryptoCurrency(title: 'XNZD', tag: 'XHV', raw: 28); - static const xusd = CryptoCurrency(title: 'XUSD', tag: 'XHV', raw: 29); + static const xmr = CryptoCurrency(title: 'XMR', iconPath: 'assets/images/monero_icon.png', fullName: 'Monero', raw: 0, name: 'xmr'); + static const ada = CryptoCurrency(title: 'ADA', iconPath: 'assets/images/ada_icon.png', fullName: 'Cardano', raw: 1, name: 'ada'); + static const bch = CryptoCurrency(title: 'BCH', iconPath: 'assets/images/bch_icon.png',fullName: 'Bitcoin Cash', raw: 2, name: 'bch'); + static const bnb = CryptoCurrency(title: 'BNB', iconPath: 'assets/images/bnb_icon.png', tag: 'BSC', fullName: 'Binance Coin', raw: 3, name: 'bnb'); + static const btc = CryptoCurrency(title: 'BTC', iconPath: 'assets/images/btc.png', fullName: 'Bitcoin', raw: 4, name: 'btc'); + static const dai = CryptoCurrency(title: 'DAI', iconPath: 'assets/images/dai_icon.png', tag: 'ETH', fullName: 'Dai', raw: 5, name: 'dai'); + static const dash = CryptoCurrency(title: 'DASH', iconPath: 'assets/images/dash_icon.png', fullName: 'Dash', raw: 6, name: 'dash'); + static const eos = CryptoCurrency(title: 'EOS', iconPath: 'assets/images/eos_icon.png', fullName: 'EOS', raw: 7, name: 'eos'); + static const eth = CryptoCurrency(title: 'ETH', iconPath: 'assets/images/eth_icon.png', fullName: 'Ethereum', raw: 8, name: 'eth'); + static const ltc = CryptoCurrency(title: 'LTC', iconPath: 'assets/images/litecoin-ltc_icon.png', fullName: 'Litecoin', raw: 9, name: 'ltc'); + static const nano = CryptoCurrency(title: 'NANO', raw: 10, name: 'nano'); + static const trx = CryptoCurrency(title: 'TRX', iconPath: 'assets/images/trx_icon.png', fullName: 'TRON', raw: 11, name: 'trx'); + static const usdt = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdt_icon.png', tag: 'OMNI', fullName: 'USDT', raw: 12, name: 'usdt'); + static const usdterc20 = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdterc20_icon.png', tag: 'ETH', fullName: 'USDT', raw: 13, name: 'usdterc20'); + static const xlm = CryptoCurrency(title: 'XLM', iconPath: 'assets/images/xlm_icon.png', fullName: 'Stellar', raw: 14, name: 'xlm'); + static const xrp = CryptoCurrency(title: 'XRP', iconPath: 'assets/images/xrp_icon.png', fullName: 'Ripple', raw: 15, name: 'xrp'); + static const xhv = CryptoCurrency(title: 'XHV', iconPath: 'assets/images/xhv_logo.png', fullName: 'Haven Protocol', raw: 16, name: 'xhv'); - static const ape = CryptoCurrency(title: 'APE', iconPath: 'assets/images/ape_icon.png', tag: 'ETH', raw: 30); - static const avaxc = CryptoCurrency(title: 'AVAX', iconPath: 'assets/images/avaxc_icon.png', tag: 'C-CHAIN', raw: 31); - static const btt = CryptoCurrency(title: 'BTT', iconPath: 'assets/images/btt_icon.png', raw: 32); - static const bttbsc = CryptoCurrency(title: 'BTT', iconPath: 'assets/images/bttbsc_icon.png', tag: 'BSC', raw: 33); - static const doge = CryptoCurrency(title: 'DOGE', iconPath: 'assets/images/doge_icon.png', raw: 34); - static const firo = CryptoCurrency(title: 'FIRO', iconPath: 'assets/images/firo_icon.png', raw: 35); - static const usdttrc20 = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdttrc20_icon.png', tag: 'TRX', raw: 36); - static const hbar = CryptoCurrency(title: 'HBAR', iconPath: 'assets/images/hbar_icon.png', raw: 37); - static const sc = CryptoCurrency(title: 'SC', iconPath: 'assets/images/sc_icon.png', raw: 38); - static const sol = CryptoCurrency(title: 'SOL', iconPath: 'assets/images/sol_icon.png', raw: 39); - static const usdc = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdc_icon.png', tag: 'ETH', raw: 40); - static const usdcsol = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdcsol_icon.png', tag: 'SOL', raw: 41); - static const zaddr = CryptoCurrency(title: 'ZZEC', tag: 'ZEC', name: 'Shielded Zcash', iconPath: 'assets/images/zaddr_icon.png', raw: 42); - static const zec = CryptoCurrency(title: 'TZEC', tag: 'ZEC', name: 'Transparent Zcash', iconPath: 'assets/images/zec_icon.png', raw: 43); - static const zen = CryptoCurrency(title: 'ZEN', iconPath: 'assets/images/zen_icon.png', raw: 44); - static const xvg = CryptoCurrency(title: 'XVG', name: 'Verge', iconPath: 'assets/images/xvg_icon.png', raw: 45); + static const xag = CryptoCurrency(title: 'XAG', tag: 'XHV', raw: 17, name: 'xag'); + static const xau = CryptoCurrency(title: 'XAU', tag: 'XHV', raw: 18, name: 'xau'); + static const xaud = CryptoCurrency(title: 'XAUD', tag: 'XHV', raw: 19, name: 'xaud'); + static const xbtc = CryptoCurrency(title: 'XBTC', tag: 'XHV', raw: 20, name: 'xbtc'); + static const xcad = CryptoCurrency(title: 'XCAD', tag: 'XHV', raw: 21, name: 'xcad'); + static const xchf = CryptoCurrency(title: 'XCHF', tag: 'XHV', raw: 22, name: 'xchf'); + static const xcny = CryptoCurrency(title: 'XCNY', tag: 'XHV', raw: 23, name: 'xcny'); + static const xeur = CryptoCurrency(title: 'XEUR', tag: 'XHV', raw: 24, name: 'xeur'); + static const xgbp = CryptoCurrency(title: 'XGBP', tag: 'XHV', raw: 25, name: 'xgbp'); + static const xjpy = CryptoCurrency(title: 'XJPY', tag: 'XHV', raw: 26, name: 'xjpy'); + static const xnok = CryptoCurrency(title: 'XNOK', tag: 'XHV', raw: 27, name: 'xnok'); + static const xnzd = CryptoCurrency(title: 'XNZD', tag: 'XHV', raw: 28, name: 'xnzd'); + static const xusd = CryptoCurrency(title: 'XUSD', tag: 'XHV', raw: 29, name: 'xusd'); - static const usdcpoly = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdc_icon.png', tag: 'POLY', raw: 46); - static const dcr = CryptoCurrency(title: 'DCR', iconPath: 'assets/images/dcr_icon.png', raw: 47); - static const husd = CryptoCurrency(title: 'HUSD', iconPath: 'assets/images/husd_icon.png', tag: 'ETH', raw: 48); - static const kmd = CryptoCurrency(title: 'KMD', iconPath: 'assets/images/kmd_icon.png', raw: 49); - static const mana = CryptoCurrency(title: 'MANA', iconPath: 'assets/images/mana_icon.png', tag: 'ETH', raw: 50); - static const maticpoly = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'POLY', raw: 51); - static const matic = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'ETH', raw: 52); - static const mkr = CryptoCurrency(title: 'MKR', iconPath: 'assets/images/mkr_icon.png', tag: 'ETH', raw: 53); - static const near = CryptoCurrency(title: 'NEAR', iconPath: 'assets/images/near_icon.png', raw: 54); - static const oxt = CryptoCurrency(title: 'OXT', iconPath: 'assets/images/oxt_icon.png', tag: 'ETH', raw: 55); - static const paxg = CryptoCurrency(title: 'PAXG', iconPath: 'assets/images/paxg_icon.png', tag: 'ETH', raw: 56); - static const pivx = CryptoCurrency(title: 'PIVX', iconPath: 'assets/images/pivx_icon.png', raw: 57); - static const rune = CryptoCurrency(title: 'RUNE', iconPath: 'assets/images/rune_icon.png', raw: 58); - static const rvn = CryptoCurrency(title: 'RVN', iconPath: 'assets/images/rvn_icon.png', raw: 59); - static const scrt = CryptoCurrency(title: 'SCRT', iconPath: 'assets/images/scrt_icon.png', raw: 60); - static const uni = CryptoCurrency(title: 'UNI', iconPath: 'assets/images/uni_icon.png', tag: 'ETH', raw: 61); - static const stx = CryptoCurrency(title: 'STX', iconPath: 'assets/images/stx_icon.png', raw: 62); + static const ape = CryptoCurrency(title: 'APE', iconPath: 'assets/images/ape_icon.png', tag: 'ETH', raw: 30, name: 'ape'); + static const avaxc = CryptoCurrency(title: 'AVAX', iconPath: 'assets/images/avaxc_icon.png', tag: 'C-CHAIN', raw: 31, name: 'avaxc'); + static const btt = CryptoCurrency(title: 'BTT', iconPath: 'assets/images/btt_icon.png', raw: 32, name: 'btt'); + static const bttc = CryptoCurrency(title: 'BTTC', iconPath: 'assets/images/bttbsc_icon.png',fullName: 'BitTorrent-NEW (Binance Smart Chain)', tag: 'BSC', raw: 33, name: 'bttc'); + static const doge = CryptoCurrency(title: 'DOGE', iconPath: 'assets/images/doge_icon.png', raw: 34, name: 'doge'); + static const firo = CryptoCurrency(title: 'FIRO', iconPath: 'assets/images/firo_icon.png', raw: 35, name: 'firo'); + static const usdttrc20 = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdttrc20_icon.png', tag: 'TRX', raw: 36, name: 'usdttrc20'); + static const hbar = CryptoCurrency(title: 'HBAR', iconPath: 'assets/images/hbar_icon.png', raw: 37, name: 'hbar'); + static const sc = CryptoCurrency(title: 'SC', iconPath: 'assets/images/sc_icon.png', raw: 38, name: 'sc'); + static const sol = CryptoCurrency(title: 'SOL', iconPath: 'assets/images/sol_icon.png', raw: 39, name: 'sol'); + static const usdc = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdc_icon.png', tag: 'ETH', raw: 40, name: 'usdc'); + static const usdcsol = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdcsol_icon.png', tag: 'SOL', raw: 41, name: 'usdcsol'); + static const zaddr = CryptoCurrency(title: 'ZZEC', tag: 'ZEC', fullName: 'Shielded Zcash', iconPath: 'assets/images/zaddr_icon.png', raw: 42, name: 'zaddr'); + static const zec = CryptoCurrency(title: 'TZEC', tag: 'ZEC', fullName: 'Transparent Zcash', iconPath: 'assets/images/zec_icon.png', raw: 43, name: 'zec'); + static const zen = CryptoCurrency(title: 'ZEN', iconPath: 'assets/images/zen_icon.png', raw: 44, name: 'zen'); + static const xvg = CryptoCurrency(title: 'XVG', fullName: 'Verge', iconPath: 'assets/images/xvg_icon.png', raw: 45, name: 'xvg'); + static const usdcpoly = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdc_icon.png', tag: 'POLY', raw: 46, name: 'usdcpoly'); + static const dcr = CryptoCurrency(title: 'DCR', iconPath: 'assets/images/dcr_icon.png', raw: 47, name: 'dcr'); + static const kmd = CryptoCurrency(title: 'KMD', iconPath: 'assets/images/kmd_icon.png', raw: 48, name: 'kmd'); + static const mana = CryptoCurrency(title: 'MANA', iconPath: 'assets/images/mana_icon.png', tag: 'ETH', raw: 49, name: 'mana'); + static const maticpoly = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'POLY', raw: 50, name: 'maticpoly'); + static const matic = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'ETH', raw: 51, name: 'matic'); + static const mkr = CryptoCurrency(title: 'MKR', iconPath: 'assets/images/mkr_icon.png', tag: 'ETH', raw: 52, name: 'mkr'); + static const near = CryptoCurrency(title: 'NEAR', iconPath: 'assets/images/near_icon.png', raw: 53, name: 'near'); + static const oxt = CryptoCurrency(title: 'OXT', iconPath: 'assets/images/oxt_icon.png', tag: 'ETH', raw: 54, name: 'oxt'); + static const paxg = CryptoCurrency(title: 'PAXG', iconPath: 'assets/images/paxg_icon.png', tag: 'ETH', raw: 55, name: 'paxg'); + static const pivx = CryptoCurrency(title: 'PIVX', iconPath: 'assets/images/pivx_icon.png', raw: 56, name: 'pivx'); + static const rune = CryptoCurrency(title: 'RUNE', iconPath: 'assets/images/rune_icon.png', raw: 57, name: 'rune'); + static const rvn = CryptoCurrency(title: 'RVN', iconPath: 'assets/images/rvn_icon.png', raw: 58, name: 'rvn'); + static const scrt = CryptoCurrency(title: 'SCRT', iconPath: 'assets/images/scrt_icon.png', raw: 59, name: 'scrt'); + static const uni = CryptoCurrency(title: 'UNI', iconPath: 'assets/images/uni_icon.png', tag: 'ETH', raw: 60, name: 'uni'); + static const stx = CryptoCurrency(title: 'STX', iconPath: 'assets/images/stx_icon.png', raw: 61, name: 'stx'); + static final Map _rawCurrencyMap = + [...all, ...havenCurrencies].fold>({}, (acc, item) { + acc.addAll({item.raw: item}); + return acc; + }); + + static final Map _nameCurrencyMap = + [...all, ...havenCurrencies].fold>({}, (acc, item) { + acc.addAll({item.name: item}); + return acc; + }); static CryptoCurrency deserialize({required int raw}) { - switch (raw) { - case 0: - return CryptoCurrency.xmr; - case 1: - return CryptoCurrency.ada; - case 2: - return CryptoCurrency.bch; - case 3: - return CryptoCurrency.bnb; - case 4: - return CryptoCurrency.btc; - case 5: - return CryptoCurrency.dai; - case 6: - return CryptoCurrency.dash; - case 7: - return CryptoCurrency.eos; - case 8: - return CryptoCurrency.eth; - case 9: - return CryptoCurrency.ltc; - case 10: - return CryptoCurrency.nano; - case 11: - return CryptoCurrency.trx; - case 12: - return CryptoCurrency.usdt; - case 13: - return CryptoCurrency.usdterc20; - case 14: - return CryptoCurrency.xlm; - case 15: - return CryptoCurrency.xrp; - case 16: - return CryptoCurrency.xhv; - case 17: - return CryptoCurrency.xag; - case 18: - return CryptoCurrency.xau; - case 19: - return CryptoCurrency.xaud; - case 20: - return CryptoCurrency.xbtc; - case 21: - return CryptoCurrency.xcad; - case 22: - return CryptoCurrency.xchf; - case 23: - return CryptoCurrency.xcny; - case 24: - return CryptoCurrency.xeur; - case 25: - return CryptoCurrency.xgbp; - case 26: - return CryptoCurrency.xjpy; - case 27: - return CryptoCurrency.xnok; - case 28: - return CryptoCurrency.xnzd; - case 29: - return CryptoCurrency.xusd; - case 30: - return CryptoCurrency.ape; - case 31: - return CryptoCurrency.avaxc; - case 32: - return CryptoCurrency.btt; - case 33: - return CryptoCurrency.bttbsc; - case 34: - return CryptoCurrency.doge; - case 35: - return CryptoCurrency.firo; - case 36: - return CryptoCurrency.usdttrc20; - case 37: - return CryptoCurrency.hbar; - case 38: - return CryptoCurrency.sc; - case 39: - return CryptoCurrency.sol; - case 40: - return CryptoCurrency.usdc; - case 41: - return CryptoCurrency.usdcsol; - case 42: - return CryptoCurrency.zaddr; - case 43: - return CryptoCurrency.zec; - case 44: - return CryptoCurrency.zen; - case 45: - return CryptoCurrency.xvg; - case 46: - return CryptoCurrency.usdcpoly; - case 47: - return CryptoCurrency.dcr; - case 48: - return CryptoCurrency.husd; - case 49: - return CryptoCurrency.kmd; - case 50: - return CryptoCurrency.mana; - case 51: - return CryptoCurrency.maticpoly; - case 52: - return CryptoCurrency.matic; - case 53: - return CryptoCurrency.mkr; - case 54: - return CryptoCurrency.near; - case 55: - return CryptoCurrency.oxt; - case 56: - return CryptoCurrency.paxg; - case 57: - return CryptoCurrency.pivx; - case 58: - return CryptoCurrency.rune; - case 59: - return CryptoCurrency.rvn; - case 60: - return CryptoCurrency.scrt; - case 61: - return CryptoCurrency.uni; - case 62: - return CryptoCurrency.stx; - default: - throw Exception('Unexpected token: $raw for CryptoCurrency deserialize'); + + if (CryptoCurrency._rawCurrencyMap[raw] == null) { + final s = 'Unexpected token: $raw for CryptoCurrency deserialize'; + throw ArgumentError.value(raw, 'raw', s); } + return CryptoCurrency._rawCurrencyMap[raw]!; } - static CryptoCurrency fromString(String raw) { - switch (raw.toLowerCase()) { - case 'xmr': - return CryptoCurrency.xmr; - case 'ada': - return CryptoCurrency.ada; - case 'bch': - return CryptoCurrency.bch; - case 'bnbmainnet': - return CryptoCurrency.bnb; - case 'btc': - return CryptoCurrency.btc; - case 'dai': - return CryptoCurrency.dai; - case 'dash': - return CryptoCurrency.dash; - case 'eos': - return CryptoCurrency.eos; - case 'eth': - return CryptoCurrency.eth; - case 'ltc': - return CryptoCurrency.ltc; - case 'nano': - return CryptoCurrency.nano; - case 'trx': - return CryptoCurrency.trx; - case 'usdc': - return CryptoCurrency.usdc; - case 'usdterc20': - return CryptoCurrency.usdterc20; - case 'xlm': - return CryptoCurrency.xlm; - case 'xrp': - return CryptoCurrency.xrp; - case 'xhv': - return CryptoCurrency.xhv; - case 'xag': - return CryptoCurrency.xag; - case 'xau': - return CryptoCurrency.xau; - case 'xaud': - return CryptoCurrency.xaud; - case 'xbtc': - return CryptoCurrency.xbtc; - case 'xcad': - return CryptoCurrency.xcad; - case 'xchf': - return CryptoCurrency.xchf; - case 'xcny': - return CryptoCurrency.xcny; - case 'xeur': - return CryptoCurrency.xeur; - case 'xgbp': - return CryptoCurrency.xgbp; - case 'xjpy': - return CryptoCurrency.xjpy; - case 'xnok': - return CryptoCurrency.xnok; - case 'xnzd': - return CryptoCurrency.xnzd; - case 'xusd': - return CryptoCurrency.xusd; - case 'ape': - return CryptoCurrency.ape; - case 'avax': - return CryptoCurrency.avaxc; - case 'btt': - return CryptoCurrency.btt; - case 'bttbsc': - return CryptoCurrency.bttbsc; - case 'doge': - return CryptoCurrency.doge; - case 'firo': - return CryptoCurrency.firo; - case 'usdttrc20': - return CryptoCurrency.usdttrc20; - case 'hbar': - return CryptoCurrency.hbar; - case 'sc': - return CryptoCurrency.sc; - case 'sol': - return CryptoCurrency.sol; - case 'usdt': - return CryptoCurrency.usdt; - case 'usdcsol': - return CryptoCurrency.usdcsol; - case 'zaddr': - return CryptoCurrency.zaddr; - case 'zec': - return CryptoCurrency.zec; - case 'zen': - return CryptoCurrency.zen; - case 'xvg': - return CryptoCurrency.xvg; - case 'usdcpoly': - return CryptoCurrency.usdcpoly; - case 'dcr': - return CryptoCurrency.dcr; - case 'husd': - return CryptoCurrency.husd; - case 'kmd': - return CryptoCurrency.kmd; - case 'mana': - return CryptoCurrency.mana; - case 'maticpoly': - return CryptoCurrency.maticpoly; - case 'matic': - return CryptoCurrency.matic; - case 'mkr': - return CryptoCurrency.mkr; - case 'near': - return CryptoCurrency.near; - case 'oxt': - return CryptoCurrency.oxt; - case 'paxg': - return CryptoCurrency.paxg; - case 'pivx': - return CryptoCurrency.pivx; - case 'rune': - return CryptoCurrency.rune; - case 'rvn': - return CryptoCurrency.rvn; - case 'scrt': - return CryptoCurrency.scrt; - case 'uni': - return CryptoCurrency.uni; - case 'stx': - return CryptoCurrency.stx; - default: - throw Exception('Unexpected token: $raw for CryptoCurrency fromString'); + static CryptoCurrency fromString(String name) { + + if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) { + final s = 'Unexpected token: $name for CryptoCurrency fromString'; + throw ArgumentError.value(name, 'name', s); } + return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!; } @override diff --git a/cw_core/lib/monero_transaction_priority.dart b/cw_core/lib/monero_transaction_priority.dart index cca887398..f5c00ecc7 100644 --- a/cw_core/lib/monero_transaction_priority.dart +++ b/cw_core/lib/monero_transaction_priority.dart @@ -21,22 +21,6 @@ class MoneroTransactionPriority extends TransactionPriority { static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4); static const standard = slow; - - static List forWalletType(WalletType type) { - switch (type) { - case WalletType.monero: - return MoneroTransactionPriority.all; - case WalletType.bitcoin: - return [ - MoneroTransactionPriority.slow, - MoneroTransactionPriority.automatic, - MoneroTransactionPriority.fast - ]; - default: - return []; - } - } - static MoneroTransactionPriority deserialize({required int raw}) { switch (raw) { case 0: diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 886add3cd..1322c5b78 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -17,6 +17,7 @@ class Node extends HiveObject with Keyable { {this.login, this.password, this.useSSL, + this.trusted = false, String? uri, WalletType? type,}) { if (uri != null) { @@ -31,7 +32,8 @@ class Node extends HiveObject with Keyable { : uriRaw = map['uri'] as String? ?? '', login = map['login'] as String?, password = map['password'] as String?, - useSSL = map['useSSL'] as bool?; + useSSL = map['useSSL'] as bool?, + trusted = map['trusted'] as bool? ?? false; static const typeId = 1; static const boxName = 'Nodes'; @@ -51,6 +53,9 @@ class Node extends HiveObject with Keyable { @HiveField(4) bool? useSSL; + @HiveField(5, defaultValue: false) + bool trusted; + bool get isSSL => useSSL ?? false; Uri get uri { @@ -104,28 +109,28 @@ class Node extends HiveObject with Keyable { final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path); final realm = 'monero-rpc'; final body = { - 'jsonrpc': '2.0', - 'id': '0', + 'jsonrpc': '2.0', + 'id': '0', 'method': 'get_info' }; try { final authenticatingClient = HttpClient(); - + authenticatingClient.addCredentials( rpcUri, - realm, + realm, HttpClientDigestCredentials(login ?? '', password ?? ''), ); - + final http.Client client = ioc.IOClient(authenticatingClient); - + final response = await client.post( rpcUri, headers: {'Content-Type': 'application/json'}, body: json.encode(body), ); - + client.close(); final resBody = json.decode(response.body) as Map; diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index fabec67c8..4983967d0 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -28,7 +28,7 @@ class NotConnectedSyncStatus extends SyncStatus { double progress() => 0.0; } -class StartingSyncStatus extends SyncStatus { +class AttemptingSyncStatus extends SyncStatus { @override double progress() => 0.0; } diff --git a/cw_haven/ios/Classes/haven_api.cpp b/cw_haven/ios/Classes/haven_api.cpp index c1013bf87..aecaf0016 100644 --- a/cw_haven/ios/Classes/haven_api.cpp +++ b/cw_haven/ios/Classes/haven_api.cpp @@ -927,6 +927,16 @@ extern "C" return static_cast(rates.size()); } + void set_trusted_daemon(bool arg) + { + m_wallet->setTrustedDaemon(arg); + } + + bool trusted_daemon() + { + return m_wallet->trustedDaemon(); + } + #ifdef __cplusplus } #endif diff --git a/cw_haven/lib/api/signatures.dart b/cw_haven/lib/api/signatures.dart index 774a303d7..31c911edc 100644 --- a/cw_haven/lib/api/signatures.dart +++ b/cw_haven/lib/api/signatures.dart @@ -137,4 +137,8 @@ typedef get_rate = Pointer Function(); typedef size_of_rate = Int32 Function(); -typedef update_rate = Void Function(); \ No newline at end of file +typedef update_rate = Void Function(); + +typedef set_trusted_daemon = Void Function(Int8 trusted); + +typedef trusted_daemon = Int8 Function(); \ No newline at end of file diff --git a/cw_haven/lib/api/types.dart b/cw_haven/lib/api/types.dart index f1dc0e26b..de9ff74a0 100644 --- a/cw_haven/lib/api/types.dart +++ b/cw_haven/lib/api/types.dart @@ -135,4 +135,8 @@ typedef GetRate = Pointer Function(); typedef SizeOfRate = int Function(); -typedef UpdateRate = void Function(); \ No newline at end of file +typedef UpdateRate = void Function(); + +typedef SetTrustedDaemon = void Function(int); + +typedef TrustedDaemon = int Function(); \ No newline at end of file diff --git a/cw_haven/lib/api/wallet.dart b/cw_haven/lib/api/wallet.dart index 2b85f60e2..bdf6a1af7 100644 --- a/cw_haven/lib/api/wallet.dart +++ b/cw_haven/lib/api/wallet.dart @@ -116,6 +116,14 @@ final rescanBlockchainAsyncNative = havenApi .lookup>('rescan_blockchain') .asFunction(); +final setTrustedDaemonNative = havenApi + .lookup>('set_trusted_daemon') + .asFunction(); + +final trustedDaemonNative = havenApi + .lookup>('trusted_daemon') + .asFunction(); + int getSyncingHeight() => getSyncingHeightNative(); bool isNeededToRefresh() => isNeededToRefreshNative() != 0; @@ -351,3 +359,7 @@ Future isConnected() => compute(_isConnected, 0); Future getNodeHeight() => compute(_getNodeHeight, 0); void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); + +Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted)); + +Future trustedDaemon() async => trustedDaemonNative() != 0; diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart index 4f360bdff..a4b949d8f 100644 --- a/cw_haven/lib/haven_wallet.dart +++ b/cw_haven/lib/haven_wallet.dart @@ -121,6 +121,8 @@ abstract class HavenWalletBase extends WalletBasegetSubaddressLabel(accountIndex, addressIndex).c_str()); } + void set_trusted_daemon(bool arg) + { + m_wallet->setTrustedDaemon(arg); + } + + bool trusted_daemon() + { + return m_wallet->trustedDaemon(); + } + #ifdef __cplusplus } #endif diff --git a/cw_monero/ios/Classes/monero_api.h b/cw_monero/ios/Classes/monero_api.h index 3e2ebcff2..74258ba4c 100644 --- a/cw_monero/ios/Classes/monero_api.h +++ b/cw_monero/ios/Classes/monero_api.h @@ -30,6 +30,9 @@ void set_refresh_from_block_height(uint64_t height); void set_recovering_from_seed(bool is_recovery); void store(char *path); +void set_trusted_daemon(bool arg); +bool trusted_daemon(); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/cw_monero/lib/api/signatures.dart b/cw_monero/lib/api/signatures.dart index f9e88153e..0b14fd557 100644 --- a/cw_monero/lib/api/signatures.dart +++ b/cw_monero/lib/api/signatures.dart @@ -125,4 +125,8 @@ typedef rescan_blockchain = Void Function(); typedef get_subaddress_label = Pointer Function( Int32 accountIndex, - Int32 addressIndex); \ No newline at end of file + Int32 addressIndex); + +typedef set_trusted_daemon = Void Function(Int8 trusted); + +typedef trusted_daemon = Int8 Function(); \ No newline at end of file diff --git a/cw_monero/lib/api/types.dart b/cw_monero/lib/api/types.dart index 468ce93e2..c5918c12a 100644 --- a/cw_monero/lib/api/types.dart +++ b/cw_monero/lib/api/types.dart @@ -123,4 +123,8 @@ typedef RescanBlockchainAsync = void Function(); typedef GetSubaddressLabel = Pointer Function( int accountIndex, - int addressIndex); \ No newline at end of file + int addressIndex); + +typedef SetTrustedDaemon = void Function(int); + +typedef TrustedDaemon = int Function(); \ No newline at end of file diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 5c2f1bd27..7ddbf29dc 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -120,6 +120,14 @@ final getSubaddressLabelNative = moneroApi .lookup>('get_subaddress_label') .asFunction(); +final setTrustedDaemonNative = moneroApi + .lookup>('set_trusted_daemon') + .asFunction(); + +final trustedDaemonNative = moneroApi + .lookup>('trusted_daemon') + .asFunction(); + int getSyncingHeight() => getSyncingHeightNative(); bool isNeededToRefresh() => isNeededToRefreshNative() != 0; @@ -359,4 +367,8 @@ void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); String getSubaddressLabel(int accountIndex, int addressIndex) { return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex)); -} \ No newline at end of file +} + +Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted)); + +Future trustedDaemon() async => trustedDaemonNative() != 0; \ No newline at end of file diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 404d8bad0..a9d708c05 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -136,6 +136,8 @@ abstract class MoneroWalletBase extends WalletBase` and checkout version 2.0.4 by `git checkout 2.0.4`. +Need to install flutter with version `3.x.x`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually). ### 4. Verify Installations @@ -66,7 +66,7 @@ Verify that the Android toolchain, Flutter, and Android Studio have been correct The output of this command will appear like this, indicating successful installations. If there are problems with your installation, they **must** be corrected before proceeding. ``` Doctor summary (to see all details, run flutter doctor -v): -[✓] Flutter (Channel stable, 2.0.4, on Linux, locale en_US.UTF-8) +[✓] Flutter (Channel stable, 3.x.x, on Linux, locale en_US.UTF-8) [✓] Android toolchain - develop for Android devices (Android SDK version 28) [✓] Android Studio (version 4.0) ``` @@ -156,6 +156,10 @@ Generate mobx models for `cw_bitcoin`: `cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..` +Generate mobx models for `cw_haven`: + +`cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..` + Finally build mobx models for the app: `$ flutter packages pub run build_runner build --delete-conflicting-outputs` diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist index ad8816ca3..794391665 100644 --- a/ios/Runner/InfoBase.plist +++ b/ios/Runner/InfoBase.plist @@ -21,18 +21,48 @@ CFBundleSignature ???? CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - y.at - CFBundleURLSchemes - - cakewallet - - - + + + CFBundleTypeRole + Editor + CFBundleURLName + y.at + CFBundleURLSchemes + + cakewallet + + + + CFBundleTypeRole + Editor + CFBundleURLName + bitcoin + CFBundleURLSchemes + + bitcoin + + + + CFBundleTypeRole + Editor + CFBundleURLName + monero + CFBundleURLSchemes + + monero + + + + CFBundleTypeRole + Editor + CFBundleURLName + litecoin + CFBundleURLSchemes + + litecoin + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index f224efde6..630ecf27f 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -51,6 +51,10 @@ class CWBitcoin extends Bitcoin { TransactionPriority deserializeBitcoinTransactionPriority(int raw) => BitcoinTransactionPriority.deserialize(raw: raw); + @override + TransactionPriority deserializeLitecoinTransactionPriority(int raw) + => LitecoinTransactionPriority.deserialize(raw: raw); + @override int getFeeRate(Object wallet, TransactionPriority priority) { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 9d7b0e3b5..519cd92a3 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -24,7 +24,6 @@ class AddressValidator extends TextValidator { return '[0-9a-zA-Z_]'; case CryptoCurrency.usdc: case CryptoCurrency.usdcpoly: - case CryptoCurrency.husd: case CryptoCurrency.ape: case CryptoCurrency.avaxc: case CryptoCurrency.eth: @@ -156,7 +155,7 @@ class AddressValidator extends TextValidator { return [98, 99, 106]; case CryptoCurrency.btt: return [34]; - case CryptoCurrency.bttbsc: + case CryptoCurrency.bttc: return [34]; case CryptoCurrency.doge: return [34]; @@ -181,7 +180,6 @@ class AddressValidator extends TextValidator { case CryptoCurrency.stx: return [40, 41, 42]; case CryptoCurrency.usdcpoly: - case CryptoCurrency.husd: case CryptoCurrency.mana: case CryptoCurrency.matic: case CryptoCurrency.maticpoly: diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart index 2ae37e2b0..54f89437a 100644 --- a/lib/core/auth_service.dart +++ b/lib/core/auth_service.dart @@ -4,12 +4,19 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/encrypt.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/store/settings_store.dart'; class AuthService with Store { - AuthService({required this.secureStorage, required this.sharedPreferences}); + AuthService({ + required this.secureStorage, + required this.sharedPreferences, + required this.settingsStore, + }); final FlutterSecureStorage secureStorage; final SharedPreferences sharedPreferences; + final SettingsStore settingsStore; Future setPassword(String password) async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); @@ -19,8 +26,7 @@ class AuthService with Store { Future canAuthenticate() async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); - final walletName = - sharedPreferences.getString(PreferencesKey.currentWalletName) ?? ''; + final walletName = sharedPreferences.getString(PreferencesKey.currentWalletName) ?? ''; var password = ''; try { @@ -39,4 +45,25 @@ class AuthService with Store { return decodedPin == pin; } + + void saveLastAuthTime() { + int timestamp = DateTime.now().millisecondsSinceEpoch; + sharedPreferences.setInt(PreferencesKey.lastAuthTimeMilliseconds, timestamp); + } + + bool requireAuth() { + final timestamp = sharedPreferences.getInt(PreferencesKey.lastAuthTimeMilliseconds); + final duration = _durationToRequireAuth(timestamp ?? 0); + final requiredPinInterval = settingsStore.pinTimeOutDuration; + + return duration >= requiredPinInterval.value; + } + + int _durationToRequireAuth(int timestamp) { + DateTime before = DateTime.fromMillisecondsSinceEpoch(timestamp); + DateTime now = DateTime.now(); + Duration timeDifference = now.difference(before); + + return timeDifference.inMinutes; + } } diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 999b67120..ffcb9eb4c 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -214,8 +214,10 @@ class BackupService { final currentBitcoinElectrumSererId = data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?; final currentLanguageCode = data[PreferencesKey.currentLanguageCode] as String?; final displayActionListMode = data[PreferencesKey.displayActionListModeKey] as int?; + final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?; final currentPinLength = data[PreferencesKey.currentPinLength] as int?; final currentTheme = data[PreferencesKey.currentTheme] as int?; + final disableExchange = data[PreferencesKey.disableExchangeKey] as bool?; final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?; final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?; final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?; @@ -266,6 +268,10 @@ class BackupService { await _sharedPreferences.setInt(PreferencesKey.displayActionListModeKey, displayActionListMode); + if (fiatApiMode != null) + await _sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, + fiatApiMode); + if (currentPinLength != null) await _sharedPreferences.setInt(PreferencesKey.currentPinLength, currentPinLength); @@ -274,6 +280,10 @@ class BackupService { await _sharedPreferences.setInt( PreferencesKey.currentTheme, currentTheme); + if (disableExchange != null) + await _sharedPreferences.setBool( + PreferencesKey.disableExchangeKey, disableExchange); + if (currentDefaultSettingsMigrationVersion != null) await _sharedPreferences.setInt( PreferencesKey.currentDefaultSettingsMigrationVersion, @@ -421,12 +431,16 @@ class BackupService { _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey), PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme), + PreferencesKey.disableExchangeKey: + _sharedPreferences.getBool(PreferencesKey.disableExchangeKey), PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion), PreferencesKey.bitcoinTransactionPriority: _sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority), PreferencesKey.moneroTransactionPriority: _sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority), + PreferencesKey.currentFiatApiModeKey: + _sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey), }; return json.encode(preferences); diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index e46ec2490..66094de2b 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -14,8 +14,8 @@ String syncStatusTitle(SyncStatus syncStatus) { return S.current.sync_status_not_connected; } - if (syncStatus is StartingSyncStatus) { - return S.current.sync_status_starting_sync; + if (syncStatus is AttemptingSyncStatus) { + return S.current.sync_status_attempting_sync; } if (syncStatus is FailedSyncStatus) { diff --git a/lib/di.dart b/lib/di.dart index 90549cf6f..1acdad2dd 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -5,9 +5,15 @@ import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; +import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; +import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; +import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; +import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; +import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; +import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart'; @@ -26,6 +32,11 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; +import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; +import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cake_wallet/core/backup_service.dart'; import 'package:cw_core/wallet_service.dart'; @@ -49,7 +60,6 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart' import 'package:cake_wallet/src/screens/faq/faq_page.dart'; import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; -import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/rescan/rescan_page.dart'; @@ -59,7 +69,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/send/send_template_page.dart'; -import 'package:cake_wallet/src/screens/settings/settings.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; @@ -115,7 +124,6 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_v import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; import 'package:cake_wallet/view_model/send/send_view_model.dart'; -import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:cake_wallet/view_model/wallet_keys_view_model.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; @@ -153,6 +161,7 @@ import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; +import 'package:cw_core/crypto_currency.dart'; final getIt = GetIt.instance; @@ -300,7 +309,10 @@ Future setup( getIt.registerFactory(() => AuthService( secureStorage: getIt.get(), - sharedPreferences: getIt.get())); + sharedPreferences: getIt.get(), + settingsStore: getIt.get(), + ), + ); getIt.registerFactory(() => AuthViewModel( getIt.get(), @@ -343,7 +355,7 @@ Future setup( onAuthenticationFinished: onAuthFinished, closable: closable ?? false)); - getIt.registerFactory(() => + getIt.registerFactory(() => BalancePage(dashboardViewModel: getIt.get(), settingsStore: getIt.get())); getIt.registerFactory(() => DashboardPage( balancePage: getIt.get(), walletViewModel: getIt.get(), addressListViewModel: getIt.get())); @@ -376,9 +388,11 @@ Future setup( getIt.get(), _transactionDescriptionBox)); - getIt.registerFactory( - () => SendPage(sendViewModel: getIt.get(), - settingsViewModel: getIt.get())); + getIt.registerFactoryParam( + (PaymentRequest? initialPaymentRequest, _) => SendPage( + sendViewModel: getIt.get(), + initialPaymentRequest: initialPaymentRequest, + )); getIt.registerFactory(() => SendTemplatePage( sendTemplateViewModel: getIt.get())); @@ -386,7 +400,10 @@ Future setup( getIt.registerFactory(() => WalletListViewModel( _walletInfoSource, getIt.get(), - getIt.get())); + getIt.get(), + getIt.get(), + ), + ); getIt.registerFactory(() => WalletListPage(walletListViewModel: getIt.get())); @@ -434,12 +451,20 @@ Future setup( getIt.get(param1: account))); getIt.registerFactory(() { - final appStore = getIt.get(); - final yatStore = getIt.get(); - return SettingsViewModel(appStore.settingsStore, yatStore, appStore.wallet!); + return DisplaySettingsViewModel(getIt.get()); }); - getIt.registerFactory(() => SettingsPage(getIt.get())); + getIt.registerFactory(() { + return PrivacySettingsViewModel(getIt.get()); + }); + + getIt.registerFactory(() { + return OtherSettingsViewModel(getIt.get(), getIt.get().wallet!); + }); + + getIt.registerFactory(() { + return SecuritySettingsViewModel(getIt.get(), getIt.get()); + }); getIt .registerFactory(() => WalletSeedViewModel(getIt.get().wallet!)); @@ -458,12 +483,11 @@ Future setup( (ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact)); - getIt.registerFactory( - () => ContactListViewModel(_contactSource, _walletInfoSource)); + getIt.registerFactoryParam( + (CryptoCurrency? cur, _) => ContactListViewModel(_contactSource, _walletInfoSource, cur)); - getIt.registerFactoryParam( - (bool isEditable, _) => ContactListPage(getIt.get(), - isEditable: isEditable)); + getIt.registerFactoryParam((CryptoCurrency? cur, _) + => ContactListPage(getIt.get(param1: cur))); getIt.registerFactoryParam( (ContactRecord? contact, _) => @@ -475,10 +499,22 @@ Future setup( _nodeSource, appStore.wallet!, appStore.settingsStore); }); - getIt.registerFactory(() => NodeListPage(getIt.get())); + getIt.registerFactory(() => ConnectionSyncPage(getIt.get(), getIt.get())); - getIt.registerFactory(() => - NodeCreateOrEditViewModel(_nodeSource, getIt.get().wallet!)); + getIt.registerFactory(() => SecurityBackupPage(getIt.get())); + + getIt.registerFactory(() => PrivacyPage(getIt.get())); + + getIt.registerFactory(() => DisplaySettingsPage(getIt.get())); + + getIt.registerFactory(() => OtherSettingsPage(getIt.get())); + + getIt.registerFactoryParam( + (WalletType? type, _) => NodeCreateOrEditViewModel( + _nodeSource, + type ?? getIt.get().wallet!.type, + getIt.get(), + )); getIt.registerFactory( () => NodeCreateOrEditPage(getIt.get())); @@ -494,7 +530,6 @@ Future setup( getIt.get(), getIt.get().settingsStore, getIt.get(), - getIt.get(), )); getIt.registerFactory(() => ExchangeTradeViewModel( @@ -678,7 +713,7 @@ Future setup( getIt.registerFactoryParam( (String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,)); - + getIt.registerFactory(() => IoniaApi()); getIt.registerFactory(() => AnyPayApi()); @@ -698,7 +733,7 @@ Future setup( getIt.registerFactoryParam((double amount, merchant) { return IoniaMerchPurchaseViewModel( - ioniaAnyPayService: getIt.get(), + ioniaAnyPayService: getIt.get(), amount: amount, ioniaMerchant: merchant, sendViewModel: getIt.get() @@ -741,31 +776,32 @@ Future setup( ioniaService: getIt.get(), giftCard: giftCard); }); - + getIt.registerFactoryParam((List args, _) { final amount = args[0] as double; final merchant = args[1] as IoniaMerchant; final tip = args[2] as IoniaTip; - + return IoniaCustomTipViewModel(amount: amount, tip: tip, ioniaMerchant: merchant); }); - + getIt.registerFactoryParam((IoniaGiftCard giftCard, _) { return IoniaGiftCardDetailPage(getIt.get(param1: giftCard)); }); getIt.registerFactoryParam((List args, _){ final giftCard = args.first as IoniaGiftCard; - - return IoniaMoreOptionsPage(giftCard); + + return IoniaMoreOptionsPage(giftCard); }); - getIt.registerFactoryParam((IoniaGiftCard giftCard, _) => IoniaCustomRedeemViewModel(giftCard)); + getIt.registerFactoryParam((IoniaGiftCard giftCard, _) + => IoniaCustomRedeemViewModel(giftCard: giftCard, ioniaService: getIt.get())); getIt.registerFactoryParam((List args, _){ final giftCard = args.first as IoniaGiftCard; - - return IoniaCustomRedeemPage(getIt.get(param1: giftCard) ); + + return IoniaCustomRedeemPage(getIt.get(param1: giftCard) ); }); @@ -794,5 +830,8 @@ Future setup( (IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo) => IoniaPaymentStatusPage(getIt.get(param1: paymentInfo, param2: committedInfo))); + getIt.registerFactoryParam((type, _) => + AdvancedPrivacySettingsViewModel(type, getIt.get())); + _isSetupFinished = true; -} +} \ No newline at end of file diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index b2036c3f6..3c8d9fbbe 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -69,6 +69,8 @@ Future defaultSettingsMigration( sharedPreferences: sharedPreferences, nodes: nodes); await changeLitecoinCurrentElectrumServerToDefault( sharedPreferences: sharedPreferences, nodes: nodes); + await changeHavenCurrentNodeToDefault( + sharedPreferences: sharedPreferences, nodes: nodes); break; case 2: @@ -133,19 +135,48 @@ Future defaultSettingsMigration( await changeDefaultHavenNode(nodes); break; + case 18: + await addOnionNode(nodes); + break; + + case 19: + await validateBitcoinSavedTransactionPriority(sharedPreferences); + break; + default: break; } await sharedPreferences.setInt( - 'current_default_settings_migration_version', version); + PreferencesKey.currentDefaultSettingsMigrationVersion, version); } catch (e) { print('Migration error: ${e.toString()}'); } }); await sharedPreferences.setInt( - 'current_default_settings_migration_version', version); + PreferencesKey.currentDefaultSettingsMigrationVersion, version); +} + +Future validateBitcoinSavedTransactionPriority(SharedPreferences sharedPreferences) async { + if (bitcoin == null) { + return; + } + final int? savedBitcoinPriority = + sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority); + if (!bitcoin!.getTransactionPriorities().any((element) => element.raw == savedBitcoinPriority)) { + await sharedPreferences.setInt( + PreferencesKey.bitcoinTransactionPriority, bitcoin!.getMediumTransactionPriority().serialize()); + } +} + +Future addOnionNode(Box nodes) async { + final onionNodeUri = "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081"; + + // check if the user has this node before (added it manually) + if (nodes.values.firstWhereOrNull((element) => element.uriRaw == onionNodeUri) == null) { + await nodes.add(Node(uri: onionNodeUri, type: WalletType.monero)); + } } Future replaceNodesMigration({required Box nodes}) async { @@ -176,7 +207,7 @@ Future changeMoneroCurrentNodeToDefault( final node = getMoneroDefaultNode(nodes: nodes); final nodeId = node?.key as int ?? 0; // 0 - England - await sharedPreferences.setInt('current_node_id', nodeId); + await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, nodeId); } Node? getBitcoinDefaultElectrumServer({required Box nodes}) { @@ -223,7 +254,7 @@ Future changeBitcoinCurrentElectrumServerToDefault( final server = getBitcoinDefaultElectrumServer(nodes: nodes); final serverId = server?.key as int ?? 0; - await sharedPreferences.setInt('current_node_id_btc', serverId); + await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId); } Future changeLitecoinCurrentElectrumServerToDefault( @@ -232,7 +263,7 @@ Future changeLitecoinCurrentElectrumServerToDefault( final server = getLitecoinDefaultElectrumServer(nodes: nodes); final serverId = server?.key as int ?? 0; - await sharedPreferences.setInt('current_node_id_ltc', serverId); + await sharedPreferences.setInt(PreferencesKey.currentLitecoinElectrumSererIdKey, serverId); } Future changeHavenCurrentNodeToDefault( @@ -252,7 +283,7 @@ Future replaceDefaultNode( 'eu-node.cakewallet.io:18081', 'node.cakewallet.io:18081' ]; - final currentNodeId = sharedPreferences.getInt('current_node_id'); + final currentNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); final currentNode = nodes.values.firstWhereOrNull((Node node) => node.key == currentNodeId); final needToReplace = @@ -277,17 +308,29 @@ Future updateNodeTypes({required Box nodes}) async { Future addBitcoinElectrumServerList({required Box nodes}) async { final serverList = await loadBitcoinElectrumServerList(); - await nodes.addAll(serverList); + for (var node in serverList) { + if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) { + await nodes.add(node); + } + } } Future addLitecoinElectrumServerList({required Box nodes}) async { final serverList = await loadLitecoinElectrumServerList(); - await nodes.addAll(serverList); + for (var node in serverList) { + if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) { + await nodes.add(node); + } + } } Future addHavenNodeList({required Box nodes}) async { final nodeList = await loadDefaultHavenNodes(); - await nodes.addAll(nodeList); + for (var node in nodeList) { + if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) { + await nodes.add(node); + } + } } Future addAddressesForMoneroWallets( @@ -431,7 +474,7 @@ Future resetBitcoinElectrumServer( final oldElectrumServer = nodeSource.values.firstWhereOrNull( (node) => node.uri.toString().contains('electrumx.cakewallet.com')); var cakeWalletNode = nodeSource.values.firstWhereOrNull( - (node) => node.uri.toString() == cakeWalletBitcoinElectrumUri); + (node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri); if (cakeWalletNode == null) { cakeWalletNode = diff --git a/lib/entities/fiat_api_mode.dart b/lib/entities/fiat_api_mode.dart new file mode 100644 index 000000000..bb16ca5a4 --- /dev/null +++ b/lib/entities/fiat_api_mode.dart @@ -0,0 +1,39 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cw_core/enumerable_item.dart'; + +class FiatApiMode extends EnumerableItem with Serializable { + const FiatApiMode({required String title, required int raw}) : super(title: title, raw: raw); + + static const all = [FiatApiMode.enabled, FiatApiMode.torOnly, FiatApiMode.disabled]; + + static const enabled = FiatApiMode(raw: 0, title: 'Enabled'); + static const torOnly = FiatApiMode(raw: 1, title: 'Tor only'); + static const disabled = FiatApiMode(raw: 2, title: 'Disabled'); + + static FiatApiMode deserialize({required int raw}) { + switch (raw) { + case 0: + return enabled; + case 1: + return torOnly; + case 2: + return disabled; + default: + throw Exception('Unexpected token: $raw for FiatApiMode deserialize'); + } + } + + @override + String toString() { + switch (this) { + case FiatApiMode.enabled: + return S.current.enabled; + case FiatApiMode.torOnly: + return S.current.tor_only; + case FiatApiMode.disabled: + return S.current.disabled; + default: + return ''; + } + } +} diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index 7dccc59c0..3b5f68eb5 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -18,7 +18,8 @@ class LanguageService { 'uk': 'Українська (Ukrainian)', 'zh': '中文 (Chinese)', 'hr': 'Hrvatski (Croatian)', - 'it': 'Italiano (Italian)' + 'it': 'Italiano (Italian)', + 'th': 'ภาษาไทย (Thai)' }; static const Map localeCountryCode = { @@ -36,7 +37,8 @@ class LanguageService { 'uk': 'ukr', 'zh': 'chn', 'hr': 'hrv', - 'it': 'ita' + 'it': 'ita', + 'th': 'tha' }; static final list = {}; diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 7fcaeeaad..775f6e229 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -4,7 +4,6 @@ import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/entities/emoji_string_extension.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:flutter/foundation.dart'; import 'package:cake_wallet/entities/fio_address_provider.dart'; class AddressResolver { @@ -51,7 +50,7 @@ class AddressResolver { return ParsedAddress(addresses: [text]); } - if (unstoppableDomains.any((domain) => name.contains(domain))) { + if (unstoppableDomains.any((domain) => name.trim() == domain)) { final address = await fetchUnstoppableDomainAddress(text, ticker); return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text); } diff --git a/lib/entities/pin_code_required_duration.dart b/lib/entities/pin_code_required_duration.dart new file mode 100644 index 000000000..fef5715b5 --- /dev/null +++ b/lib/entities/pin_code_required_duration.dart @@ -0,0 +1,32 @@ +import 'package:cake_wallet/generated/i18n.dart'; + +enum PinCodeRequiredDuration { + always(0), + tenminutes(10), + onehour(60); + + const PinCodeRequiredDuration(this.value); + final int value; + + static PinCodeRequiredDuration deserialize({required int raw}) => + PinCodeRequiredDuration.values.firstWhere((e) => e.value == raw); + + @override + String toString(){ + String label = ''; + switch (this) { + case PinCodeRequiredDuration.always: + label = S.current.always; + break; + case PinCodeRequiredDuration.tenminutes: + label = S.current.minutes_to_pin_code('10'); + break; + case PinCodeRequiredDuration.onehour: + label = S.current.minutes_to_pin_code('60'); + break; + } + return label; + + } + +} \ No newline at end of file diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index a28410192..90d4dcad7 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -9,6 +9,7 @@ class PreferencesKey { static const currentTransactionPriorityKeyLegacy = 'current_fee_priority'; static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; static const shouldSaveRecipientAddressKey = 'save_recipient_address'; + static const currentFiatApiModeKey = 'current_fiat_api_mode'; static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication'; static const disableExchangeKey = 'disable_exchange'; @@ -21,9 +22,14 @@ class PreferencesKey { 'current_default_settings_migration_version'; static const moneroTransactionPriority = 'current_fee_priority_monero'; static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin'; + static const havenTransactionPriority = 'current_fee_priority_haven'; + static const litecoinTransactionPriority = 'current_fee_priority_litecoin'; static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowYatPopup = 'should_show_yat_popup'; static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1'; + static const pinTimeOutDuration = 'pin_timeout_duration'; + static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds'; + static String moneroWalletUpdateV1Key(String name) => '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}'; diff --git a/lib/entities/priority_for_wallet_type.dart b/lib/entities/priority_for_wallet_type.dart new file mode 100644 index 000000000..927ab8803 --- /dev/null +++ b/lib/entities/priority_for_wallet_type.dart @@ -0,0 +1,21 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/monero/monero.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/wallet_type.dart'; + +List priorityForWalletType(WalletType type) { + switch (type) { + case WalletType.monero: + return monero!.getTransactionPriorities(); + case WalletType.bitcoin: + return bitcoin!.getTransactionPriorities(); + case WalletType.litecoin: + return bitcoin!.getLitecoinTransactionPriorities(); + case WalletType.haven: + return haven!.getTransactionPriorities(); + default: + return []; + } +} + diff --git a/lib/exchange/changenow/changenow_exchange_provider.dart b/lib/exchange/changenow/changenow_exchange_provider.dart index 20f529733..a9fb7c9ca 100644 --- a/lib/exchange/changenow/changenow_exchange_provider.dart +++ b/lib/exchange/changenow/changenow_exchange_provider.dart @@ -11,7 +11,6 @@ import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/changenow/changenow_request.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; -import 'package:cake_wallet/exchange/trade_not_created_exeption.dart'; class ChangeNowExchangeProvider extends ExchangeProvider { ChangeNowExchangeProvider() @@ -21,8 +20,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { .where((i) => i != CryptoCurrency.xhv) .map((i) => CryptoCurrency.all .where((i) => i != CryptoCurrency.xhv) - .map((k) => ExchangePair(from: i, to: k, reverse: true)) - .where((c) => c != null)) + .map((k) => ExchangePair(from: i, to: k, reverse: true))) .expand((i) => i) .toList()); @@ -43,6 +41,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider { @override bool get isEnabled => true; + @override + bool get supportsFixedRate => true; + @override ExchangeProviderDescription get description => ExchangeProviderDescription.changeNow; @@ -96,25 +97,36 @@ class ChangeNowExchangeProvider extends ExchangeProvider { apiHeaderKey: apiKey, 'Content-Type': 'application/json'}; final flow = getFlow(isFixedRateMode); + final type = isFixedRateMode ? 'reverse' : 'direct'; final body = { 'fromCurrency': normalizeCryptoCurrency(_request.from), 'toCurrency': normalizeCryptoCurrency(_request.to), 'fromNetwork': networkFor(_request.from), 'toNetwork': networkFor(_request.to), - 'fromAmount': _request.fromAmount, - 'toAmount': _request.toAmount, + if (!isFixedRateMode) 'fromAmount': _request.fromAmount, + if (isFixedRateMode) 'toAmount': _request.toAmount, 'address': _request.address, 'flow': flow, + 'type': type, 'refundAddress': _request.refundAddress }; if (isFixedRateMode) { + // since we schedule to calculate the rate every 5 seconds we need to ensure that + // we have the latest rate id with the given inputs before creating the trade + await fetchRate( + from: _request.from, + to: _request.to, + amount: double.tryParse(_request.toAmount) ?? 0, + isFixedRateMode: true, + isReceiveAmount: true, + ); body['rateId'] = _lastUsedRateId; } final uri = Uri.https(apiAuthority, createTradePath); final response = await post(uri, headers: headers, body: json.encode(body)); - + if (response.statusCode == 400) { final responseJSON = json.decode(response.body) as Map; final error = responseJSON['error'] as String; @@ -130,7 +142,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { final id = responseJSON['id'] as String; final inputAddress = responseJSON['payinAddress'] as String; final refundAddress = responseJSON['refundAddress'] as String; - final extraId = responseJSON['payinExtraId'] as String; + final extraId = responseJSON['payinExtraId'] as String?; return Trade( id: id, @@ -141,7 +153,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { refundAddress: refundAddress, extraId: extraId, createdAt: DateTime.now(), - amount: _request.fromAmount, + amount: responseJSON['fromAmount']?.toString() ?? _request.fromAmount, state: TradeState.created); } @@ -180,9 +192,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { final extraId = responseJSON['payinExtraId'] as String; final outputTransaction = responseJSON['payoutHash'] as String; final expiredAtRaw = responseJSON['validUntil'] as String; - final expiredAt = expiredAtRaw != null - ? DateTime.parse(expiredAtRaw).toLocal() - : null; + final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal(); return Trade( id: id, @@ -198,7 +208,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { } @override - Future calculateAmount( + Future fetchRate( {required CryptoCurrency from, required CryptoCurrency to, required double amount, @@ -214,10 +224,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider { final type = isReverse ? 'reverse' : 'direct'; final flow = getFlow(isFixedRateMode); final params = { - 'fromCurrency': isReverse ? normalizeCryptoCurrency(to) : normalizeCryptoCurrency(from), - 'toCurrency': isReverse ? normalizeCryptoCurrency(from) : normalizeCryptoCurrency(to), - 'fromNetwork': isReverse ? networkFor(to) : networkFor(from), - 'toNetwork': isReverse ? networkFor(from) : networkFor(to), + 'fromCurrency': normalizeCryptoCurrency(from), + 'toCurrency': normalizeCryptoCurrency(to), + 'fromNetwork': networkFor(from), + 'toNetwork': networkFor(to), 'type': type, 'flow': flow}; @@ -238,7 +248,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider { _lastUsedRateId = rateId; } - return isReverse ? fromAmount : toAmount; + return isReverse ? (amount / fromAmount) : (toAmount / amount); } catch(e) { print(e.toString()); return 0.0; diff --git a/lib/exchange/exchange_provider.dart b/lib/exchange/exchange_provider.dart index 14fba803f..763350c94 100644 --- a/lib/exchange/exchange_provider.dart +++ b/lib/exchange/exchange_provider.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/exchange_pair.dart'; @@ -14,6 +13,7 @@ abstract class ExchangeProvider { ExchangeProviderDescription get description; bool get isAvailable; bool get isEnabled; + bool get supportsFixedRate; @override String toString() => title; @@ -26,7 +26,7 @@ abstract class ExchangeProvider { required TradeRequest request, required bool isFixedRateMode}); Future findTradeById({required String id}); - Future calculateAmount({ + Future fetchRate({ required CryptoCurrency from, required CryptoCurrency to, required double amount, diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index f9e359454..2fd231085 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -24,6 +24,9 @@ class ExchangeProviderDescription extends EnumerableItem static const simpleSwap = ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png'); + static const all = + ExchangeProviderDescription(title: 'All trades', raw: 5, image:''); + static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { case 0: @@ -36,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem return sideShift; case 4: return simpleSwap; + case 5: + return all; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); } diff --git a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart index 1df4fef87..a2a72b24f 100644 --- a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart +++ b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart @@ -66,6 +66,9 @@ class MorphTokenExchangeProvider extends ExchangeProvider { @override bool get isEnabled => true; + @override + bool get supportsFixedRate => false; + @override ExchangeProviderDescription get description => ExchangeProviderDescription.morphToken; @@ -200,7 +203,7 @@ class MorphTokenExchangeProvider extends ExchangeProvider { } @override - Future calculateAmount( + Future fetchRate( {required CryptoCurrency from, required CryptoCurrency to, required double amount, diff --git a/lib/exchange/sideshift/sideshift_exchange_provider.dart b/lib/exchange/sideshift/sideshift_exchange_provider.dart index a732f7ef2..59d7964cb 100644 --- a/lib/exchange/sideshift/sideshift_exchange_provider.dart +++ b/lib/exchange/sideshift/sideshift_exchange_provider.dart @@ -12,7 +12,6 @@ import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/exchange/trade_request.dart'; import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/limits.dart'; -import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; class SideShiftExchangeProvider extends ExchangeProvider { @@ -28,7 +27,6 @@ class SideShiftExchangeProvider extends ExchangeProvider { static const List _notSupported = [ CryptoCurrency.xhv, CryptoCurrency.dcr, - CryptoCurrency.husd, CryptoCurrency.kmd, CryptoCurrency.mkr, CryptoCurrency.near, @@ -39,6 +37,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { CryptoCurrency.rvn, CryptoCurrency.scrt, CryptoCurrency.stx, + CryptoCurrency.bttc, ]; static List _supportedPairs() { @@ -48,8 +47,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { return supportedCurrencies .map((i) => supportedCurrencies - .map((k) => ExchangePair(from: i, to: k, reverse: true)) - .where((c) => c != null)) + .map((k) => ExchangePair(from: i, to: k, reverse: true))) .expand((i) => i) .toList(); } @@ -59,7 +57,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { ExchangeProviderDescription.sideShift; @override - Future calculateAmount( + Future fetchRate( {required CryptoCurrency from, required CryptoCurrency to, required double amount, @@ -81,9 +79,7 @@ class SideShiftExchangeProvider extends ExchangeProvider { if (amount > max) return 0.00; - final estimatedAmount = rate * amount; - - return estimatedAmount; + return rate; } catch (_) { return 0.00; } @@ -249,15 +245,15 @@ class SideShiftExchangeProvider extends ExchangeProvider { final expectedSendAmount = responseJSON['depositAmount'].toString(); final deposits = responseJSON['deposits'] as List?; TradeState? state; + String? status; - if (deposits != null && deposits.isNotEmpty) { - final status = deposits[0]['status'] as String; - state = TradeState.deserialize(raw: status); + if (deposits?.isNotEmpty ?? false) { + status = deposits![0]['status'] as String?; } + state = TradeState.deserialize(raw: status ?? 'created'); final expiredAtRaw = responseJSON['expiresAtISO'] as String; - final expiredAt = - expiredAtRaw != null ? DateTime.parse(expiredAtRaw).toLocal() : null; + final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal(); return Trade( id: id, @@ -277,6 +273,9 @@ class SideShiftExchangeProvider extends ExchangeProvider { @override bool get isEnabled => true; + @override + bool get supportsFixedRate => true; + @override String get title => 'SideShift'; diff --git a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart index c6e1baeea..2521c1486 100644 --- a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart +++ b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart @@ -20,8 +20,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { .where((i) => i != CryptoCurrency.zaddr) .map((i) => CryptoCurrency.all .where((i) => i != CryptoCurrency.zaddr) - .map((k) => ExchangePair(from: i, to: k, reverse: true)) - .where((c) => c != null)) + .map((k) => ExchangePair(from: i, to: k, reverse: true))) .expand((i) => i) .toList()); @@ -37,7 +36,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { ExchangeProviderDescription.simpleSwap; @override - Future calculateAmount( + Future fetchRate( {required CryptoCurrency from, required CryptoCurrency to, required double amount, @@ -59,9 +58,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { final uri = Uri.https(apiAuthority, getEstimatePath, params); final response = await get(uri); - if (response.body == null || response.body == "null") return 0.00; + if (response.body == "null") return 0.00; final data = json.decode(response.body) as String; - return double.parse(data); + return double.parse(data) / amount; } catch (_) { return 0.00; } @@ -210,6 +209,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { @override bool get isEnabled => true; + @override + bool get supportsFixedRate => false; + @override String get title => 'SimpleSwap'; diff --git a/lib/exchange/xmrto/xmrto_exchange_provider.dart b/lib/exchange/xmrto/xmrto_exchange_provider.dart index d5f79361e..536754e18 100644 --- a/lib/exchange/xmrto/xmrto_exchange_provider.dart +++ b/lib/exchange/xmrto/xmrto_exchange_provider.dart @@ -48,6 +48,9 @@ class XMRTOExchangeProvider extends ExchangeProvider { @override bool get isEnabled => true; + @override + bool get supportsFixedRate => false; + @override ExchangeProviderDescription get description => ExchangeProviderDescription.xmrto; @@ -191,7 +194,7 @@ class XMRTOExchangeProvider extends ExchangeProvider { } @override - Future calculateAmount( + Future fetchRate( {required CryptoCurrency from, required CryptoCurrency to, required double amount, diff --git a/lib/ionia/ionia_gift_card.dart b/lib/ionia/ionia_gift_card.dart index b729e261f..2ba0fd136 100644 --- a/lib/ionia/ionia_gift_card.dart +++ b/lib/ionia/ionia_gift_card.dart @@ -37,7 +37,7 @@ class IoniaGiftCard { purchaseAmount: element['PurchaseAmount'] as double, actualAmount: element['ActualAmount'] as double, totalTransactionAmount: element['TotalTransactionAmount'] as double, - totalDashTransactionAmount: element['TotalDashTransactionAmount'] as double, + totalDashTransactionAmount: (element['TotalDashTransactionAmount'] as double?) ?? 0.0, remainingAmount: element['RemainingAmount'] as double, isActive: element['IsActive'] as bool, isEmpty: element['IsEmpty'] as bool, diff --git a/lib/ionia/ionia_service.dart b/lib/ionia/ionia_service.dart index 942bc25b5..51e23ad28 100644 --- a/lib/ionia/ionia_service.dart +++ b/lib/ionia/ionia_service.dart @@ -148,8 +148,8 @@ class IoniaService { // Redeem - Future redeem(IoniaGiftCard giftCard) async { - await chargeGiftCard(giftCardId: giftCard.id, amount: giftCard.remainingAmount); + Future redeem({required int giftCardId, required double amount}) async { + await chargeGiftCard(giftCardId: giftCardId, amount: amount); } // Get Gift Card diff --git a/lib/main.dart b/lib/main.dart index afa5804a0..d9f35805a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; @@ -48,6 +49,7 @@ import 'package:cake_wallet/wallet_type_utils.dart'; final navigatorKey = GlobalKey(); final rootKey = GlobalKey(); +final RouteObserver routeObserver = RouteObserver(); Future main() async { @@ -166,7 +168,7 @@ Future main() async { exchangeTemplates: exchangeTemplates, transactionDescriptions: transactionDescriptions, secureStorage: secureStorage, - initialMigrationVersion: 17); + initialMigrationVersion: 19); runApp(App()); }, (error, stackTrace) async { print("@@@@@@@@@@@@@@@@ in run zone guard"); @@ -276,12 +278,6 @@ class AppState extends State with SingleTickerProviderStateMixin { //_handleInitialUri(); } - @override - void dispose() { - stream?.cancel(); - super.dispose(); - } - Future _handleInitialUri() async { try { final uri = await getInitialUri(); @@ -329,11 +325,12 @@ class AppState extends State with SingleTickerProviderStateMixin { Widget build(BuildContext context) { return Observer(builder: (BuildContext context) { final appStore = getIt.get(); + final authService = getIt.get(); final settingsStore = appStore.settingsStore; final statusBarColor = Colors.transparent; final authenticationStore = getIt.get(); final initialRoute = - authenticationStore.state == AuthenticationState.denied + authenticationStore.state == AuthenticationState.uninitialized ? Routes.disclaimer : Routes.login; final currentTheme = settingsStore.currentTheme; @@ -353,7 +350,9 @@ class AppState extends State with SingleTickerProviderStateMixin { appStore: appStore, authenticationStore: authenticationStore, navigatorKey: navigatorKey, + authService: authService, child: MaterialApp( + navigatorObservers: [routeObserver], navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, theme: settingsStore.theme, diff --git a/lib/reactions/bootstrap.dart b/lib/reactions/bootstrap.dart index 8d9950e64..4b65ed9d2 100644 --- a/lib/reactions/bootstrap.dart +++ b/lib/reactions/bootstrap.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/reactions/fiat_rate_update.dart'; import 'package:cake_wallet/reactions/on_current_node_change.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; @@ -22,13 +23,14 @@ Future bootstrap(GlobalKey navigatorKey) async { final currentWalletName = getIt .get() .getString(PreferencesKey.currentWalletName); - authenticationStore.state = currentWalletName == null - ? AuthenticationState.denied - : AuthenticationState.installed; + if (currentWalletName != null) { + authenticationStore.state = AuthenticationState.installed; + } startAuthenticationStateChange(authenticationStore, navigatorKey); startCurrentWalletChangeReaction( appStore, settingsStore, fiatConversionStore); startCurrentFiatChangeReaction(appStore, settingsStore, fiatConversionStore); startOnCurrentNodeChangeReaction(appStore); + startFiatRateUpdate(appStore, settingsStore, fiatConversionStore); } diff --git a/lib/reactions/fiat_rate_update.dart b/lib/reactions/fiat_rate_update.dart index 784a14e82..48c61d8ca 100644 --- a/lib/reactions/fiat_rate_update.dart +++ b/lib/reactions/fiat_rate_update.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:cake_wallet/core/fiat_conversion_service.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; @@ -8,30 +9,27 @@ import 'package:cw_core/wallet_type.dart'; Timer? _timer; -Future startFiatRateUpdate(AppStore appStore, SettingsStore settingsStore, - FiatConversionStore fiatConversionStore) async { +Future startFiatRateUpdate( + AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) async { if (_timer != null) { return; } - if (appStore.wallet != null) { - fiatConversionStore.prices[appStore.wallet!.currency] = - await FiatConversionService.fetchPrice( - appStore.wallet!.currency, settingsStore.fiatCurrency); - } + _timer = Timer.periodic(Duration(seconds: 30), (_) async { + try { + if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) { + return; + } - _timer = Timer.periodic( - Duration(seconds: 30), - (_) async { - try { - if (appStore.wallet!.type == WalletType.haven) { - await updateHavenRate(fiatConversionStore); - } else { - fiatConversionStore.prices[appStore.wallet!.currency] = await FiatConversionService.fetchPrice( - appStore.wallet!.currency, settingsStore.fiatCurrency); - } - } catch(e) { - print(e); - } - }); + if (appStore.wallet!.type == WalletType.haven) { + await updateHavenRate(fiatConversionStore); + } else { + fiatConversionStore.prices[appStore.wallet!.currency] = + await FiatConversionService.fetchPrice( + appStore.wallet!.currency, settingsStore.fiatCurrency); + } + } catch (e) { + print(e); + } + }); } diff --git a/lib/reactions/on_authentication_state_change.dart b/lib/reactions/on_authentication_state_change.dart index c91f5277e..edadf33b0 100644 --- a/lib/reactions/on_authentication_state_change.dart +++ b/lib/reactions/on_authentication_state_change.dart @@ -26,10 +26,5 @@ void startAuthenticationStateChange(AuthenticationStore authenticationStore, await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false); return; } - - if (state == AuthenticationState.denied) { - await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.welcome, (_) => false); - return; - } }); } diff --git a/lib/reactions/on_current_fiat_change.dart b/lib/reactions/on_current_fiat_change.dart index 86a5ee881..5170c4576 100644 --- a/lib/reactions/on_current_fiat_change.dart +++ b/lib/reactions/on_current_fiat_change.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/fiat_conversion_service.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; @@ -12,7 +13,7 @@ void startCurrentFiatChangeReaction(AppStore appStore, _onCurrentFiatCurrencyChangeDisposer?.reaction.dispose(); _onCurrentFiatCurrencyChangeDisposer = reaction( (_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async { - if (appStore.wallet == null) { + if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) { return; } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index cce972368..270415820 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; import 'package:cw_core/transaction_history.dart'; @@ -87,7 +88,7 @@ void startCurrentWalletChangeReaction(AppStore appStore, TransactionHistoryBase, TransactionInfo>? wallet) async { try { - if (wallet == null) { + if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) { return; } diff --git a/lib/router.dart b/lib/router.dart index b255ce8b9..896ddd99b 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -5,21 +5,30 @@ import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; +import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; +import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; +import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; +import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; +import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; +import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; +import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; +import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; +import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -36,7 +45,6 @@ import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; -import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart'; import 'package:cake_wallet/src/screens/receive/receive_page.dart'; import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; @@ -56,7 +64,6 @@ import 'package:cake_wallet/src/screens/contact/contact_page.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_wallet_from_seed_details.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; -import 'package:cake_wallet/src/screens/settings/settings.dart'; import 'package:cake_wallet/src/screens/rescan/rescan_page.dart'; import 'package:cake_wallet/src/screens/faq/faq_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; @@ -74,6 +81,7 @@ import 'package:cake_wallet/src/screens/ionia/ionia.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart'; import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; +import 'package:cw_core/crypto_currency.dart'; late RouteSettings currentRouteSettings; @@ -209,8 +217,12 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get()); case Routes.send: + final initialPaymentRequest = settings.arguments as PaymentRequest?; + return CupertinoPageRoute( - fullscreenDialog: true, builder: (_) => getIt.get()); + fullscreenDialog: true, builder: (_) => getIt.get( + param1: initialPaymentRequest, + )); case Routes.sendTemplate: return CupertinoPageRoute( @@ -274,10 +286,26 @@ Route createRoute(RouteSettings settings) { param2: false), onWillPop: () async => false)); - case Routes.nodeList: + case Routes.connectionSync: return CupertinoPageRoute( - builder: (_) => getIt.get()); + builder: (_) => getIt.get()); + case Routes.securityBackupPage: + return CupertinoPageRoute( + builder: (_) => getIt.get()); + + case Routes.privacyPage: + return CupertinoPageRoute( + builder: (_) => getIt.get()); + + case Routes.displaySettingsPage: + return CupertinoPageRoute( + builder: (_) => getIt.get()); + + case Routes.otherSettingsPage: + return CupertinoPageRoute( + builder: (_) => getIt.get()); + case Routes.newNode: return CupertinoPageRoute( builder: (_) => getIt.get()); @@ -298,11 +326,13 @@ Route createRoute(RouteSettings settings) { case Routes.addressBook: return MaterialPageRoute( - builder: (_) => getIt.get(param1: true)); + builder: (_) => + getIt.get()); case Routes.pickerAddressBook: + final selectedCurrency = settings.arguments as CryptoCurrency; return MaterialPageRoute( - builder: (_) => getIt.get(param1: false)); + builder: (_) => getIt.get(param1: selectedCurrency)); case Routes.addressBookAddContact: return CupertinoPageRoute( @@ -360,9 +390,6 @@ Route createRoute(RouteSettings settings) { return CupertinoPageRoute( builder: (_) => getIt.get()); - case Routes.settings: - return MaterialPageRoute(builder: (_) => getIt.get()); - case Routes.rescan: return MaterialPageRoute(builder: (_) => getIt.get()); @@ -475,6 +502,15 @@ Route createRoute(RouteSettings settings) { case Routes.onramperPage: return CupertinoPageRoute(builder: (_) => getIt.get()); + case Routes.advancedPrivacySettings: + final type = settings.arguments as WalletType; + + return CupertinoPageRoute( + builder: (_) => AdvancedPrivacySettingsPage( + getIt.get(param1: type), + getIt.get(param1: type), + )); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 3a781ac3a..5ea250310 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -21,7 +21,6 @@ class Routes { static const seedLanguage = '/seed_language'; static const walletList = '/view_model.wallet_list'; static const auth = '/auth'; - static const nodeList = '/node_list'; static const newNode = '/new_node_list'; static const login = '/login'; static const splash = '/splash'; @@ -77,4 +76,10 @@ class Routes { static const ioniaMoreOptionsPage = '/ionia_more_options_page'; static const ioniaCustomRedeemPage = '/ionia_custom_redeem_page'; static const onramperPage = '/onramper'; + static const connectionSync = '/connection_sync_page'; + static const securityBackupPage = '/security_and_backup_page'; + static const privacyPage = '/privacy_page'; + static const displaySettingsPage = '/display_settings_page'; + static const otherSettingsPage = '/other_settings_page'; + static const advancedPrivacySettings = '/advanced_privacy_settings'; } diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index 7912472d8..0065b9281 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -9,24 +9,22 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/src/widgets/collapsible_standart_list.dart'; class ContactListPage extends BasePage { - ContactListPage(this.contactListViewModel, {this.isEditable = true}); + ContactListPage(this.contactListViewModel); final ContactListViewModel contactListViewModel; - final bool isEditable; @override String get title => S.current.address_book; @override Widget? trailing(BuildContext context) { - if (!isEditable) { + if (!contactListViewModel.isEditable) { return null; } @@ -60,21 +58,24 @@ class ContactListPage extends BasePage { @override Widget body(BuildContext context) { + return Container( padding: EdgeInsets.only(top: 20.0, bottom: 20.0), child: Observer( - builder: (_) { - return CollapsibleSectionList( + builder: (_) { + final contacts = contactListViewModel.contactsToShow; + final walletContacts = contactListViewModel.walletContactsToShow; + return CollapsibleSectionList( context: context, sectionCount: 2, themeColor: Theme.of(context).primaryTextTheme.headline6!.color!, dividerThemeColor: Theme.of(context).primaryTextTheme.caption!.decorationColor!, sectionTitleBuilder: (_, int sectionIndex) { - var title = 'Contacts'; + var title = S.current.contact_list_contacts; if (sectionIndex == 0) { - title = 'My wallets'; + title = S.current.contact_list_wallets; } return Container( @@ -82,35 +83,37 @@ class ContactListPage extends BasePage { child: Text(title, style: TextStyle(fontSize: 36))); }, itemCounter: (int sectionIndex) => sectionIndex == 0 - ? contactListViewModel.walletContacts.length - : contactListViewModel.contacts.length, + ? walletContacts.length + : contacts.length, itemBuilder: (_, sectionIndex, index) { if (sectionIndex == 0) { - final walletInfo = contactListViewModel.walletContacts[index]; + final walletInfo = walletContacts[index]; return generateRaw(context, walletInfo); } - final contact = contactListViewModel.contacts[index]; + final contact = contacts[index]; final content = generateRaw(context, contact); - return !isEditable - ? content - : Slidable( + return contactListViewModel.isEditable + ? Slidable( key: Key('${contact.key}'), endActionPane: _actionPane(context, contact), child: content, - ); + ) + : content; }, - ); - }, - )); + );}) + ); } Widget generateRaw(BuildContext context, ContactBase contact) { - final image = _getCurrencyImage(contact.type); + final image = contact.type.iconPath; + final currencyIcon = image != null ? Image.asset(image, height: 24, width: 24) + : const SizedBox(height: 24, width: 24); + return GestureDetector( onTap: () async { - if (!isEditable) { + if (!contactListViewModel.isEditable) { Navigator.of(context).pop(contact); return; } @@ -131,12 +134,10 @@ class ContactListPage extends BasePage { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ - image ?? Offstage(), + currencyIcon, Expanded( child: Padding( - padding: image != null - ? EdgeInsets.only(left: 12) - : EdgeInsets.only(left: 0), + padding: EdgeInsets.only(left: 12), child: Text( contact.name, style: TextStyle( @@ -152,69 +153,6 @@ class ContactListPage extends BasePage { ); } - Image? _getCurrencyImage(CryptoCurrency currency) { - Image? image; - - switch (currency) { - case CryptoCurrency.xmr: - image = - Image.asset('assets/images/monero_logo.png', height: 24, width: 24); - break; - case CryptoCurrency.ada: - image = Image.asset('assets/images/ada.png', height: 24, width: 24); - break; - case CryptoCurrency.bch: - image = Image.asset('assets/images/bch.png', height: 24, width: 24); - break; - case CryptoCurrency.bnb: - image = Image.asset('assets/images/bnb.png', height: 24, width: 24); - break; - case CryptoCurrency.btc: - image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); - break; - case CryptoCurrency.dai: - image = Image.asset('assets/images/dai.png', height: 24, width: 24); - break; - case CryptoCurrency.dash: - image = Image.asset('assets/images/dash.png', height: 24, width: 24); - break; - case CryptoCurrency.eos: - image = Image.asset('assets/images/eos.png', height: 24, width: 24); - break; - case CryptoCurrency.eth: - image = Image.asset('assets/images/eth.png', height: 24, width: 24); - break; - case CryptoCurrency.ltc: - image = - Image.asset('assets/images/litecoin.png', height: 24, width: 24); - break; - case CryptoCurrency.nano: - image = Image.asset('assets/images/nano.png', height: 24, width: 24); - break; - case CryptoCurrency.trx: - image = Image.asset('assets/images/trx.png', height: 24, width: 24); - break; - case CryptoCurrency.usdt: - image = Image.asset('assets/images/usdt.png', height: 24, width: 24); - break; - case CryptoCurrency.usdterc20: - image = Image.asset('assets/images/usdterc.png', height: 24, width: 24); - break; - case CryptoCurrency.xlm: - image = Image.asset('assets/images/xlm.png', height: 24, width: 24); - break; - case CryptoCurrency.xrp: - image = Image.asset('assets/images/xrp.png', height: 24, width: 24); - break; - case CryptoCurrency.xhv: - image = Image.asset('assets/images/haven_logo.png', height: 24, width: 24); - break; - default: - image = null; - } - return image; - } - Future showAlertDialog(BuildContext context) async { return await showPopUp( context: context, diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index fe8414134..c21365aa4 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -60,7 +60,7 @@ class DashboardPage extends BasePage { Widget middle(BuildContext context) { return SyncIndicator(dashboardViewModel: walletViewModel, onTap: () => Navigator.of(context, rootNavigator: true) - .pushNamed(Routes.nodeList)); + .pushNamed(Routes.connectionSync)); } @override diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index 68e7cc76d..1638c0bc4 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -1,81 +1,64 @@ import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/screens/dashboard/wallet_menu_item.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/screens/auth/auth_page.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; // FIXME: terrible design class WalletMenu { WalletMenu(this.context, this.reconnect, this.hasRescan) : items = [] { items.addAll([ - WalletMenuItem( - title: S.current.reconnect, - image: Image.asset('assets/images/reconnect_menu.png', - height: 16, width: 16), - handler: () => _presentReconnectAlert(context)), - if (hasRescan) - WalletMenuItem( - title: S.current.rescan, - image: Image.asset('assets/images/filter_icon.png', - height: 16, width: 16, color: Palette.darkBlue), - handler: () => Navigator.of(context).pushNamed(Routes.rescan)), - WalletMenuItem( - title: S.current.wallets, - image: Image.asset('assets/images/wallet_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.walletList)), - WalletMenuItem( - title: S.current.nodes, - image: Image.asset('assets/images/nodes_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.nodeList)), - WalletMenuItem( - title: S.current.show_keys, - image: - Image.asset('assets/images/key_menu.png', height: 16, width: 16), - handler: () { - Navigator.of(context).pushNamed(Routes.auth, - arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { - if (isAuthenticatedSuccessfully) { - auth.close(route: Routes.showKeys); - } - }); - }), - WalletMenuItem( - title: S.current.address_book_menu, - image: Image.asset('assets/images/open_book_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.addressBook)), - WalletMenuItem( - title: S.current.backup, - image: Image.asset('assets/images/restore_wallet.png', - height: 16, - width: 16, - color: Palette.darkBlue), - handler: () { - Navigator.of(context).pushNamed( - Routes.auth, - arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { - if (isAuthenticatedSuccessfully) { - auth.close(route:Routes.backup); - } - }); - }), - WalletMenuItem( - title: S.current.settings_title, - image: Image.asset('assets/images/settings_menu.png', - height: 16, width: 16), - handler: () => Navigator.of(context).pushNamed(Routes.settings)), - WalletMenuItem( - title: S.current.settings_support, - image: Image.asset('assets/images/question_mark.png', - height: 16, width: 16, color: Palette.darkBlue), - handler: () => Navigator.of(context).pushNamed(Routes.support)), + WalletMenuItem( + title: S.current.connection_sync, + image: Image.asset('assets/images/nodes_menu.png', + height: 16, width: 16), + handler: () => Navigator.of(context).pushNamed(Routes.connectionSync), + ), + WalletMenuItem( + title: S.current.wallets, + image: Image.asset('assets/images/wallet_menu.png', + height: 16, width: 16), + handler: () => Navigator.of(context).pushNamed(Routes.walletList), + ), + WalletMenuItem( + title: S.current.address_book_menu, + image: Image.asset('assets/images/open_book_menu.png', + height: 16, width: 16), + handler: () => Navigator.of(context).pushNamed(Routes.addressBook), + ), + WalletMenuItem( + title: S.current.security_and_backup, + image: + Image.asset('assets/images/key_menu.png', height: 16, width: 16), + handler: () { + Navigator.of(context).pushNamed(Routes.securityBackupPage); + }), + WalletMenuItem( + title: S.current.privacy_settings, + image: + Image.asset('assets/images/privacy_menu.png', height: 16, width: 16), + handler: () { + Navigator.of(context).pushNamed(Routes.privacyPage); + }), + WalletMenuItem( + title: S.current.display_settings, + image: Image.asset('assets/images/eye_menu.png', + height: 16, width: 16), + handler: () => Navigator.of(context).pushNamed(Routes.displaySettingsPage), + ), + WalletMenuItem( + title: S.current.other_settings, + image: Image.asset('assets/images/settings_menu.png', + height: 16, width: 16), + handler: () => Navigator.of(context).pushNamed(Routes.otherSettingsPage), + ), + WalletMenuItem( + title: S.current.settings_support, + image: Image.asset('assets/images/question_mark.png', + height: 16, width: 16, color: Palette.darkBlue), + handler: () => Navigator.of(context).pushNamed(Routes.support), + ), ]); } @@ -86,23 +69,6 @@ class WalletMenu { void action(int index) { final item = items[index]; - item?.handler(); - } - - Future _presentReconnectAlert(BuildContext context) async { - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).reconnection, - alertContent: S.of(context).reconnect_alert_text, - rightButtonText: S.of(context).ok, - leftButtonText: S.of(context).cancel, - actionRightButton: () async { - Navigator.of(context).pop(); - await reconnect?.call(); - }, - actionLeftButton: () => Navigator.of(context).pop()); - }); + item.handler(); } } diff --git a/lib/src/screens/dashboard/widgets/filter_tile.dart b/lib/src/screens/dashboard/widgets/filter_tile.dart index 9a5646ac9..3be96073a 100644 --- a/lib/src/screens/dashboard/widgets/filter_tile.dart +++ b/lib/src/screens/dashboard/widgets/filter_tile.dart @@ -9,12 +9,7 @@ class FilterTile extends StatelessWidget { Widget build(BuildContext context) { return Container( width: double.infinity, - padding: EdgeInsets.only( - top: 18, - bottom: 18, - left: 24, - right: 24 - ), + padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0), child: child, ); } diff --git a/lib/src/screens/dashboard/widgets/filter_widget.dart b/lib/src/screens/dashboard/widgets/filter_widget.dart index 98d5add81..17df0bc5e 100644 --- a/lib/src/screens/dashboard/widgets/filter_widget.dart +++ b/lib/src/screens/dashboard/widgets/filter_widget.dart @@ -1,25 +1,26 @@ import 'dart:ui'; import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; +import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; -import 'package:cake_wallet/src/widgets/checkbox_widget.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; //import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker; class FilterWidget extends StatelessWidget { FilterWidget({required this.dashboardViewModel}); final DashboardViewModel dashboardViewModel; - final backVector = Image.asset('assets/images/back_vector.png', - color: Palette.darkBlueCraiola - ); + final closeIcon = Image.asset('assets/images/close.png', color: Palette.darkBlueCraiola); @override Widget build(BuildContext context) { + const sectionDivider = const SectionDivider(); return AlertBackground( child: Stack( alignment: Alignment.center, @@ -27,129 +28,82 @@ class FilterWidget extends StatelessWidget { Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - S.of(context).filters, - style: TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.bold, - fontFamily: 'Lato', - decoration: TextDecoration.none, - ), - ), Padding( - padding: EdgeInsets.only( - left: 24, - right: 24, - top: 24 - ), + padding: EdgeInsets.only(left: 24, right: 24, top: 24), child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(14)), + borderRadius: BorderRadius.all(Radius.circular(24)), child: Container( color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, - child: ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: dashboardViewModel.filterItems.length, - separatorBuilder: (context, _) => Container( - height: 1, - color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: EdgeInsets.all(24.0), + child: Text( + S.of(context).filter_by, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.overline!.color!, + fontSize: 16, + fontFamily: 'Lato', + decoration: TextDecoration.none, + ), + ), ), - itemBuilder: (_, index1) { - final title = dashboardViewModel.filterItems.keys.elementAt(index1); - final section = dashboardViewModel.filterItems.values.elementAt(index1); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only( - top: 20, - left: 24, - right: 24 - ), - child: Text( - title, - style: TextStyle( - color: Theme.of(context).accentTextTheme!.subtitle1!.color!, - fontSize: 16, - fontWeight: FontWeight.w500, - fontFamily: 'Lato', - decoration: TextDecoration.none + sectionDivider, + ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: dashboardViewModel.filterItems.length, + separatorBuilder: (context, _) => sectionDivider, + itemBuilder: (_, index1) { + final title = dashboardViewModel.filterItems.keys.elementAt(index1); + final section = dashboardViewModel.filterItems.values.elementAt(index1); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(top: 20, left: 24, right: 24), + child: Text( + title, + style: TextStyle( + color: Theme.of(context).primaryTextTheme!.headline6!.color!, + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none), ), ), - ), - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: section.length, - separatorBuilder: (context, _) => Container( - height: 1, - padding: EdgeInsets.only(left: 24), - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, - child: Container( - height: 1, - color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!, - ), - ), - itemBuilder: (_, index2) { - - final item = section[index2]; - final content = item.onChanged != null - ? CheckboxWidget( - value: item.value(), - caption: item.caption, - onChanged: item.onChanged - ) - : GestureDetector( - onTap: () async { - //final List picked = - //await date_rage_picker.showDatePicker( - // context: context, - // initialFirstDate: DateTime.now() - // .subtract(Duration(days: 1)), - // initialLastDate: (DateTime.now()), - // firstDate: DateTime(2015), - // lastDate: DateTime.now() - // .add(Duration(days: 1))); - - //if (picked != null && picked.length == 2) { - // dashboardViewModel.transactionFilterStore - // .changeStartDate(picked.first); - // dashboardViewModel.transactionFilterStore - // .changeEndDate(picked.last); - //} - }, - child: Padding( - padding: EdgeInsets.only(left: 32), - child: Text( - item.caption, - style: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline6!.color!, - fontSize: 18, - fontFamily: 'Lato', - fontWeight: FontWeight.w500, - decoration: TextDecoration.none - ), - ), - ), - ); - - return FilterTile(child: content); - }, - ) - ], - ); - }, - ), + ListView.builder( + padding: EdgeInsets.symmetric(vertical: 8.0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: section.length, + itemBuilder: (_, index2) { + final item = section[index2]; + final content = Observer( + builder: (_) => StandardCheckbox( + value: item.value(), + caption: item.caption, + gradientBackground: true, + borderColor: Theme.of(context).dividerColor, + iconColor: Colors.white, + onChanged: (value) => item.onChanged(), + )); + return FilterTile(child: content); + }, + ) + ], + ); + }, + ), + ]), ), ), ), ], ), - AlertCloseButton(image: backVector) + AlertCloseButton(image: closeIcon) ], ), ); } -} \ No newline at end of file +} diff --git a/lib/src/screens/dashboard/widgets/market_place_page.dart b/lib/src/screens/dashboard/widgets/market_place_page.dart index a64a132ad..9ac225634 100644 --- a/lib/src/screens/dashboard/widgets/market_place_page.dart +++ b/lib/src/screens/dashboard/widgets/market_place_page.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:url_launcher/url_launcher.dart'; class MarketPlacePage extends StatelessWidget { @@ -48,6 +49,15 @@ class MarketPlacePage extends StatelessWidget { title: S.of(context).cake_pay_title, subTitle: S.of(context).cake_pay_subtitle, ), + SizedBox(height: 20), + MarketPlaceItem( + onTap: () => launchUrl( + Uri.https("buy.cakepay.com"), + mode: LaunchMode.externalApplication, + ), + title: S.of(context).cake_pay_web_cards_title, + subTitle: S.of(context).cake_pay_web_cards_subtitle, + ), ], ), ), @@ -72,7 +82,7 @@ class MarketPlacePage extends StatelessWidget { buttonAction: () => Navigator.of(context).pop()); }); break; - default: + default: Navigator.of(context).pushNamed(Routes.ioniaWelcomePage); } } diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/widgets/transactions_page.dart index 8ab7c0efb..64f02c73c 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/widgets/transactions_page.dart @@ -57,7 +57,9 @@ class TransactionsPage extends StatelessWidget { formattedDate: DateFormat('HH:mm') .format(transaction.date), formattedAmount: item.formattedCryptoAmount, - formattedFiatAmount: item.formattedFiatAmount, + formattedFiatAmount: + dashboardViewModel.balanceViewModel.isFiatDisabled + ? '' : item.formattedFiatAmount, isPending: transaction.isPending)); } diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 744848156..7b73a134b 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -40,7 +40,6 @@ class ExchangePage extends BasePage { final ExchangeViewModel exchangeViewModel; final depositKey = GlobalKey(); final receiveKey = GlobalKey(); - final checkBoxKey = GlobalKey(); final _formKey = GlobalKey(); final _depositAmountFocus = FocusNode(); final _depositAddressFocus = FocusNode(); @@ -339,7 +338,6 @@ class ExchangePage extends BasePage { mainAxisAlignment: MainAxisAlignment.start, children: [ StandardCheckbox( - key: checkBoxKey, value: exchangeViewModel.isFixedRateMode, caption: S.of(context).fixed_rate, onChanged: (value) => @@ -682,12 +680,6 @@ class ExchangePage extends BasePage { } }); - reaction((_) => exchangeViewModel.isFixedRateMode, (bool value) { - if (checkBoxKey.currentState!.value != exchangeViewModel.isFixedRateMode) { - checkBoxKey.currentState!.value = exchangeViewModel.isFixedRateMode; - } - }); - depositAddressController.addListener( () => exchangeViewModel.depositAddress = depositAddressController.text); diff --git a/lib/src/screens/exchange/widgets/currency_picker.dart b/lib/src/screens/exchange/widgets/currency_picker.dart index 9f8b9e493..b785378c5 100644 --- a/lib/src/screens/exchange/widgets/currency_picker.dart +++ b/lib/src/screens/exchange/widgets/currency_picker.dart @@ -56,7 +56,7 @@ class CurrencyPickerState extends State { .where((element) => (element.title != null ? element.title.toLowerCase().contains(subString.toLowerCase()) : false) || (element.tag != null ? element.tag!.toLowerCase().contains(subString.toLowerCase()) : false) || - (element.name != null ? element.name!.toLowerCase().contains(subString.toLowerCase()) : false)) + (element.fullName != null ? element.fullName!.toLowerCase().contains(subString.toLowerCase()) : false)) .toList(); return; } diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 98843b1fb..40aa679a4 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -395,7 +395,8 @@ class ExchangeCardState extends State { buttonColor: widget.addressButtonsColor, validator: widget.addressTextFieldValidator, onPushPasteButton: widget.onPushPasteButton, - onPushAddressBookButton: widget.onPushAddressBookButton + onPushAddressBookButton: widget.onPushAddressBookButton, + selectedCurrency: _selectedCurrency ), ) diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index aa3079197..5aacf3f1f 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -8,7 +8,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'; -import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/widgets/list_row.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart'; @@ -194,7 +194,7 @@ class ExchangeTradeState extends State { final item = widget.exchangeTradeViewModel.items[index]; final value = item.data ?? fetchingLabel; - final content = StandartListRow( + final content = ListRow( title: item.title, value: value, valueFontSize: 14, @@ -378,12 +378,10 @@ class ExchangeTradeState extends State { }); }, actionLeftButton: () => Navigator.of(context).pop(), - feeFiatAmount: widget.exchangeTradeViewModel.sendViewModel.pendingTransactionFeeFiatAmount - + ' ' + widget.exchangeTradeViewModel.sendViewModel.fiat.title, - fiatAmountValue: widget.exchangeTradeViewModel.sendViewModel - .pendingTransactionFiatAmount + - ' ' + - widget.exchangeTradeViewModel.sendViewModel.fiat.title, + feeFiatAmount: widget.exchangeTradeViewModel + .pendingTransactionFeeFiatAmountFormatted, + fiatAmountValue: widget.exchangeTradeViewModel + .pendingTransactionFiatAmountValueFormatted, outputs: widget.exchangeTradeViewModel.sendViewModel .outputs); }); diff --git a/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart b/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart index dae5413a4..f2702d791 100644 --- a/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart +++ b/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; @@ -18,12 +19,11 @@ class IoniaCustomRedeemPage extends BasePage { ) : _amountFieldFocus = FocusNode(), _amountController = TextEditingController() { _amountController.addListener(() { - ioniaCustomRedeemViewModel.updateAmount(_amountController.text); + ioniaCustomRedeemViewModel.updateAmount(_amountController.text); }); } final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel; - @override String get title => S.current.custom_redeem_amount; @@ -50,7 +50,7 @@ class IoniaCustomRedeemPage extends BasePage { disableScroll: true, config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!, + keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!, nextFocus: false, actions: [ KeyboardActionsItem( @@ -67,10 +67,11 @@ class IoniaCustomRedeemPage extends BasePage { Container( padding: EdgeInsets.symmetric(horizontal: 25), decoration: BoxDecoration( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), gradient: LinearGradient(colors: [ - Theme.of(context).primaryTextTheme!.subtitle1!.color!, - Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!, + Theme.of(context).primaryTextTheme.subtitle1!.color!, + Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!, ], begin: Alignment.topLeft, end: Alignment.bottomRight), ), child: Column( @@ -85,11 +86,11 @@ class IoniaCustomRedeemPage extends BasePage { inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))], hintText: '1000', placeholderTextStyle: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline5!.color!, + color: Theme.of(context).primaryTextTheme.headline5!.color!, fontWeight: FontWeight.w500, fontSize: 36, ), - borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!, + borderColor: Theme.of(context).primaryTextTheme.headline5!.color!, textColor: Colors.white, textStyle: TextStyle( color: Colors.white, @@ -114,14 +115,17 @@ class IoniaCustomRedeemPage extends BasePage { ), ), SizedBox(height: 8), - Observer(builder: (_)=> - !ioniaCustomRedeemViewModel.disableRedeem ? - Center( - child: Text('\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}', - style: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline5!.color!, - ),), - ) : SizedBox.shrink(), + Observer( + builder: (_) => !ioniaCustomRedeemViewModel.disableRedeem + ? Center( + child: Text( + '\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}', + style: TextStyle( + color: Theme.of(context).primaryTextTheme.headline5!.color!, + ), + ), + ) + : SizedBox.shrink(), ), SizedBox(height: 24), ], @@ -131,30 +135,37 @@ class IoniaCustomRedeemPage extends BasePage { padding: const EdgeInsets.all(24.0), child: CardItem( title: giftCard.legalName, - backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1), + backgroundColor: Theme.of(context) + .accentTextTheme + .headline1! + .backgroundColor! + .withOpacity(0.1), discount: giftCard.remainingAmount, isAmount: true, discountBackground: AssetImage('assets/images/red_badge_discount.png'), - titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!, + titleColor: Theme.of(context).accentTextTheme.headline1!.backgroundColor!, subtitleColor: Theme.of(context).hintColor, subTitle: S.of(context).online, logoUrl: giftCard.logoUrl, ), - ), + ), ], ), bottomSection: Column( children: [ - Padding( - padding: EdgeInsets.only(bottom: 12), - child: PrimaryButton( - onPressed: () { - Navigator.of(context).pop(_amountController.text); - }, - isDisabled: ioniaCustomRedeemViewModel.disableRedeem, - text: S.of(context).add_custom_redemption, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, - textColor: Colors.white, + Observer( + builder: (_) => Padding( + padding: EdgeInsets.only(bottom: 12), + child: LoadingPrimaryButton( + isLoading: ioniaCustomRedeemViewModel.redeemState is IsExecutingState, + isDisabled: ioniaCustomRedeemViewModel.disableRedeem, + text: S.of(context).add_custom_redemption, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, + textColor: Colors.white, + onPressed: () => ioniaCustomRedeemViewModel.addCustomRedeem().then((value) { + Navigator.of(context).pop(ioniaCustomRedeemViewModel.remaining.toString()); + }), + ), ), ), SizedBox(height: 30), diff --git a/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart b/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart index 1c768fa17..0b7a479e0 100644 --- a/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart +++ b/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart @@ -11,6 +11,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/typography.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/utils/route_aware.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart'; import 'package:device_display_brightness/device_display_brightness.dart'; import 'package:flutter/material.dart'; @@ -32,7 +33,7 @@ class IoniaGiftCardDetailPage extends BasePage { final _backButton = Icon( Icons.arrow_back_ios, - color: Theme.of(context).primaryTextTheme!.headline6!.color!, + color: Theme.of(context).primaryTextTheme.headline6!.color!, size: 16, ); return Padding( @@ -43,14 +44,11 @@ class IoniaGiftCardDetailPage extends BasePage { child: ButtonTheme( minWidth: double.minPositive, child: TextButton( - // FIX-ME: Style + // FIX-ME: Style //highlightColor: Colors.transparent, //splashColor: Colors.transparent, //padding: EdgeInsets.all(0), - onPressed: () { - onClose(context); - DeviceDisplayBrightness.setBrightness(viewModel.brightness); - }, + onPressed: ()=> onClose(context), child: _backButton), ), ), @@ -61,13 +59,13 @@ class IoniaGiftCardDetailPage extends BasePage { Widget middle(BuildContext context) { return Text( viewModel.giftCard.legalName, - style: textMediumSemiBold(color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!), + style: + textMediumSemiBold(color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!), ); } @override Widget body(BuildContext context) { - viewModel.increaseBrightness(); reaction((_) => viewModel.redeemState, (ExecutionState state) { if (state is FailureState) { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -84,7 +82,12 @@ class IoniaGiftCardDetailPage extends BasePage { } }); - return ScrollableWithBottomSection( + return RouteAwareWidget( + pushToWidget: ()=> viewModel.increaseBrightness(), + pushToNextWidget: ()=> DeviceDisplayBrightness.setBrightness(viewModel.brightness), + popNextWidget: ()=> viewModel.increaseBrightness(), + popWidget: ()=> DeviceDisplayBrightness.setBrightness(viewModel.brightness), + child: ScrollableWithBottomSection( contentPadding: EdgeInsets.all(24), content: Column( children: [ @@ -102,20 +105,21 @@ class IoniaGiftCardDetailPage extends BasePage { title: S.of(context).gift_card_number, subTitle: viewModel.giftCard.cardNumber, ), - if (viewModel.giftCard.cardPin?.isNotEmpty ?? false) - ...[Divider(height: 30), + if (viewModel.giftCard.cardPin.isNotEmpty) ...[ + Divider(height: 30), buildIoniaTile( context, title: S.of(context).pin_number, subTitle: viewModel.giftCard.cardPin, - )], + ) + ], Divider(height: 30), - Observer(builder: (_) => - buildIoniaTile( - context, - title: S.of(context).amount, - subTitle: viewModel.remainingAmount.toStringAsFixed(2) ?? '0.00', - )), + Observer( + builder: (_) => buildIoniaTile( + context, + title: S.of(context).amount, + subTitle: viewModel.remainingAmount.toStringAsFixed(2), + )), Divider(height: 50), TextIconButton( label: S.of(context).how_to_use_card, @@ -130,29 +134,28 @@ class IoniaGiftCardDetailPage extends BasePage { if (!viewModel.giftCard.isEmpty) { return Column( children: [ - //PrimaryButton( - // onPressed: () async { - // final amount = await Navigator.of(context) - // .pushNamed(Routes.ioniaMoreOptionsPage, arguments: [viewModel.giftCard]) as String; - // if (amount != null) { - // viewModel.updateRemaining(double.parse(amount)); - // } - // }, - // text: S.of(context).more_options, - // color: Theme.of(context).accentTextTheme!.caption!.color!, - // textColor: Theme.of(context).primaryTextTheme!.headline6!.color!, - //), - //SizedBox(height: 12), + PrimaryButton( + onPressed: () async { + await Navigator.of(context).pushNamed( + Routes.ioniaMoreOptionsPage, + arguments: [viewModel.giftCard]) as String?; + viewModel.refeshCard(); + }, + text: S.of(context).more_options, + color: Theme.of(context).accentTextTheme.caption!.color!, + textColor: Theme.of(context).primaryTextTheme.headline6!.color!, + ), + SizedBox(height: 12), LoadingPrimaryButton( isLoading: viewModel.redeemState is IsExecutingState, onPressed: () => viewModel.redeem().then( (_) { - Navigator.of(context) - .pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst); + Navigator.of(context).pushNamedAndRemoveUntil( + Routes.ioniaManageCardsPage, (route) => route.isFirst); }, ), text: S.of(context).mark_as_redeemed, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, textColor: Colors.white, ), ], @@ -163,17 +166,16 @@ class IoniaGiftCardDetailPage extends BasePage { }, ), ), - ); + )); } Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) { return IoniaTile( - title: title, - subTitle: subTitle, - onTap: () { - Clipboard.setData(ClipboardData(text: subTitle)); - showBar(context, - S.of(context).transaction_details_copied(title)); + title: title, + subTitle: subTitle, + onTap: () { + Clipboard.setData(ClipboardData(text: subTitle)); + showBar(context, S.of(context).transaction_details_copied(title)); }); } @@ -184,10 +186,10 @@ class IoniaGiftCardDetailPage extends BasePage { showPopUp( context: context, builder: (BuildContext context) { - return IoniaAlertModal( + return IoniaAlertModal( title: S.of(context).how_to_use_card, content: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: viewModel.giftCard.instructions .map((instruction) { return [ @@ -196,13 +198,13 @@ class IoniaGiftCardDetailPage extends BasePage { child: Text( instruction.header, style: textLargeSemiBold( - color: Theme.of(context).textTheme!.headline3!.color!, + color: Theme.of(context).textTheme.headline3!.color!, ), )), Text( instruction.body, style: textMedium( - color: Theme.of(context).textTheme!.headline3!.color!, + color: Theme.of(context).textTheme.headline3!.color!, ), ) ]; @@ -210,7 +212,7 @@ class IoniaGiftCardDetailPage extends BasePage { .expand((e) => e) .toList()), actionTitle: S.of(context).send_got_it, - ); + ); }); } } diff --git a/lib/src/screens/ionia/cards/ionia_manage_cards_page.dart b/lib/src/screens/ionia/cards/ionia_manage_cards_page.dart index 2fbb1d57d..1c13f7a88 100644 --- a/lib/src/screens/ionia/cards/ionia_manage_cards_page.dart +++ b/lib/src/screens/ionia/cards/ionia_manage_cards_page.dart @@ -118,7 +118,7 @@ class IoniaManageCardsPage extends BasePage { width: 32, padding: EdgeInsets.all(8), decoration: BoxDecoration( - color: Colors.white.withOpacity(0.15), + color: Theme.of(context).textTheme!.headline6!.backgroundColor!, border: Border.all( color: Colors.white.withOpacity(0.2), ), diff --git a/lib/src/screens/ionia/cards/ionia_more_options_page.dart b/lib/src/screens/ionia/cards/ionia_more_options_page.dart index 84f0bed30..7d38c5e1e 100644 --- a/lib/src/screens/ionia/cards/ionia_more_options_page.dart +++ b/lib/src/screens/ionia/cards/ionia_more_options_page.dart @@ -5,7 +5,6 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/typography.dart'; import 'package:flutter/material.dart'; - class IoniaMoreOptionsPage extends BasePage { IoniaMoreOptionsPage(this.giftCard); @@ -16,7 +15,7 @@ class IoniaMoreOptionsPage extends BasePage { return Text( S.current.more_options, style: textMediumSemiBold( - color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!, + color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!, ), ); } @@ -27,40 +26,45 @@ class IoniaMoreOptionsPage extends BasePage { padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox(height: 10,), - Center(child: Text(S.of(context).choose_from_available_options, style: textMedium( - color: Theme.of(context).primaryTextTheme!.headline6!.color!, - ),)), - SizedBox(height: 40,), - InkWell( - onTap: () async { - final amount = await Navigator.of(context).pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String; - if(amount.isNotEmpty){ - Navigator.pop(context, amount); - } - }, - child: _GradiantContainer( - content: Padding( - padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50), - child: Text( - S.of(context).custom_redeem_amount, - style: textXLargeSemiBold(), - ), - ), - ), - ) - ], - ), + children: [ + SizedBox( + height: 10, + ), + Center( + child: Text( + S.of(context).choose_from_available_options, + style: textMedium( + color: Theme.of(context).primaryTextTheme.headline6!.color!, + ), + ), + ), + SizedBox(height: 40), + InkWell( + onTap: () async { + final amount = await Navigator.of(context) + .pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String?; + if (amount != null && amount.isNotEmpty) { + Navigator.pop(context); + } + }, + child: _GradiantContainer( + content: Padding( + padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50), + child: Text( + S.of(context).custom_redeem_amount, + style: textXLargeSemiBold(), + ), + ), + ), + ) + ], + ), ); } } class _GradiantContainer extends StatelessWidget { - const _GradiantContainer({ - Key? key, - required this.content - }) : super(key: key); + const _GradiantContainer({Key? key, required this.content}) : super(key: key); final Widget content; diff --git a/lib/src/screens/ionia/widgets/ionia_filter_modal.dart b/lib/src/screens/ionia/widgets/ionia_filter_modal.dart index ec6e84cc0..2b885f736 100644 --- a/lib/src/screens/ionia/widgets/ionia_filter_modal.dart +++ b/lib/src/screens/ionia/widgets/ionia_filter_modal.dart @@ -20,7 +20,7 @@ class IoniaFilterModal extends StatelessWidget { padding: EdgeInsets.all(10), child: Image.asset( 'assets/images/mini_search_icon.png', - color: Theme.of(context).accentColor, + color: Theme.of(context).textTheme.subtitle2!.color!, ), ); return Scaffold( @@ -53,7 +53,7 @@ class IoniaFilterModal extends StatelessWidget { prefixIcon: searchIcon, hintText: S.of(context).search_category, contentPadding: EdgeInsets.only(bottom: 5), - fillColor: Theme.of(context).textTheme!.subtitle1!.backgroundColor!, + fillColor: Theme.of(context).primaryTextTheme!.caption!.decorationColor!.withOpacity(0.5), border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(8), diff --git a/lib/src/screens/monero_accounts/monero_account_list_page.dart b/lib/src/screens/monero_accounts/monero_account_list_page.dart index 99134369e..7fe15948f 100644 --- a/lib/src/screens/monero_accounts/monero_account_list_page.dart +++ b/lib/src/screens/monero_accounts/monero_account_list_page.dart @@ -1,4 +1,5 @@ import 'dart:ui'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -84,10 +85,7 @@ class MoneroAccountListPage extends StatelessWidget { padding: EdgeInsets.zero, controller: controller, separatorBuilder: (context, index) => - Container( - height: 1, - color: Theme.of(context).dividerColor, - ), + const SectionDivider(), itemCount: accounts.length ?? 0, itemBuilder: (context, index) { final account = accounts[index]; diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart new file mode 100644 index 000000000..6c2f996df --- /dev/null +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -0,0 +1,111 @@ +import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; +import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; + +class AdvancedPrivacySettingsPage extends BasePage { + AdvancedPrivacySettingsPage(this.advancedPrivacySettingsViewModel, this.nodeViewModel); + + final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel; + final NodeCreateOrEditViewModel nodeViewModel; + + @override + String get title => S.current.privacy_settings; + + @override + Widget body(BuildContext context) => + AdvancedPrivacySettingsBody(advancedPrivacySettingsViewModel, nodeViewModel); +} + +class AdvancedPrivacySettingsBody extends StatefulWidget { + const AdvancedPrivacySettingsBody(this.privacySettingsViewModel, this.nodeViewModel, {Key? key}) + : super(key: key); + + final AdvancedPrivacySettingsViewModel privacySettingsViewModel; + final NodeCreateOrEditViewModel nodeViewModel; + + @override + _AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState(); +} + +class _AdvancedPrivacySettingsBodyState extends State { + _AdvancedPrivacySettingsBodyState(); + + final _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: 24), + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ...widget.privacySettingsViewModel.settings.map( + (item) => Observer( + builder: (_) => SettingsSwitcherCell( + title: item.title, + value: item.value(), + onValueChange: item.onValueChange, + ), + ), + ), + Observer( + builder: (_) { + if (widget.privacySettingsViewModel.addCustomNode) { + return Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: NodeForm( + formKey: _formKey, + nodeViewModel: widget.nodeViewModel, + ), + ); + } + return const SizedBox(); + }, + ), + ], + ), + bottomSectionPadding: EdgeInsets.all(24), + bottomSection: Column( + children: [ + LoadingPrimaryButton( + onPressed: () { + if (widget.privacySettingsViewModel.addCustomNode) { + if (_formKey.currentState != null && !_formKey.currentState!.validate()) { + return; + } + + widget.nodeViewModel.save(saveAsCurrent: true); + } + + Navigator.pop(context); + }, + text: S.of(context).continue_text, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, + textColor: Colors.white, + ), + const SizedBox(height: 25), + Padding( + padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15), + child: Text( + S.of(context).settings_can_be_changed_later, + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).accentTextTheme.headline2?.color, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index cd08cfb61..9e0cd898c 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -200,18 +200,30 @@ class _WalletNameFormState extends State { ] ]), bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Observer( - builder: (context) { - return LoadingPrimaryButton( - onPressed: _confirmForm, - text: S.of(context).seed_language_next, - color: Colors.green, - textColor: Colors.white, - isLoading: _walletNewVM.state is IsExecutingState, - isDisabled: _walletNewVM.name.isEmpty, - ); - }, + EdgeInsets.all(24), + bottomSection: Column( + children: [ + Observer( + builder: (context) { + return LoadingPrimaryButton( + onPressed: _confirmForm, + text: S.of(context).seed_language_next, + color: Colors.green, + textColor: Colors.white, + isLoading: _walletNewVM.state is IsExecutingState, + isDisabled: _walletNewVM.name.isEmpty, + ); + }, + ), + const SizedBox(height: 25), + GestureDetector( + onTap: () { + Navigator.of(context) + .pushNamed(Routes.advancedPrivacySettings, arguments: _walletNewVM.type); + }, + child: Text(S.of(context).advanced_privacy_settings), + ), + ], )), ); } diff --git a/lib/src/screens/nodes/node_create_or_edit_page.dart b/lib/src/screens/nodes/node_create_or_edit_page.dart index bd8153226..8173af2f0 100644 --- a/lib/src/screens/nodes/node_create_or_edit_page.dart +++ b/lib/src/screens/nodes/node_create_or_edit_page.dart @@ -1,16 +1,13 @@ import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/core/node_address_validator.dart'; -import 'package:cake_wallet/core/node_port_validator.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; @@ -108,76 +105,10 @@ class NodeCreateOrEditPage extends BasePage { padding: EdgeInsets.only(left: 24, right: 24), child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(bottom: 24.0), - content: Form( - key: _formKey, - child: Column( - children: [ - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _addressController, - hintText: S.of(context).node_address, - validator: NodeAddressValidator(), - ) - ) - ], - ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _portController, - hintText: S.of(context).node_port, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: false), - validator: NodePortValidator(), - ) - ) - ], - ), - SizedBox(height: 10.0), - if (nodeCreateOrEditViewModel.hasAuthCredentials) ...[ - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _loginController, - hintText: S.of(context).login, - ) - ) - ], - ), - SizedBox(height: 10.0), - Row( - children: [ - Expanded( - child: BaseTextFormField( - controller: _passwordController, - hintText: S.of(context).password, - ) - ) - ], - ), - Padding( - padding: EdgeInsets.only(top: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Observer( - builder: (_) => StandardCheckbox( - value: nodeCreateOrEditViewModel.useSSL, - onChanged: (value) => - nodeCreateOrEditViewModel.useSSL = value, - caption: S.of(context).use_ssl, - )) - ], - )) - ] - ], - )), + content: NodeForm( + formKey: _formKey, + nodeViewModel: nodeCreateOrEditViewModel, + ), bottomSectionPadding: EdgeInsets.only(bottom: 24), bottomSection: Observer( builder: (_) => Row( diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart deleted file mode 100644 index 556a0fd06..000000000 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cw_core/node.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/palette.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart'; -import 'package:cake_wallet/src/widgets/standard_list.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; -import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; - -class NodeListPage extends BasePage { - NodeListPage(this.nodeListViewModel); - - @override - String get title => S.current.nodes; - - final NodeListViewModel nodeListViewModel; - - @override - Widget trailing(context) { - return Container( - height: 32, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(16)), - color: Theme.of(context).accentTextTheme.caption!.color!), - child: ButtonTheme( - minWidth: double.minPositive, - child: TextButton( - onPressed: () async { - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).node_reset_settings_title, - alertContent: - S.of(context).nodes_list_reset_to_default_message, - rightButtonText: S.of(context).reset, - leftButtonText: S.of(context).cancel, - actionRightButton: () async { - Navigator.of(context).pop(); - await nodeListViewModel.reset(); - }, - actionLeftButton: () => Navigator.of(context).pop()); - }); - }, - child: Text( - S.of(context).reset, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w600, - color: Palette.blueCraiola), - )), - ), - ); - } - - @override - Widget body(BuildContext context) { - return Container( - padding: EdgeInsets.only(top: 10), - child: Observer( - builder: (BuildContext context) { - return SectionStandardList( - sectionCount: 2, - context: context, - itemCounter: (int sectionIndex) { - if (sectionIndex == 0) { - return 1; - } - - return nodeListViewModel.nodes.length; - }, - itemBuilder: (_, sectionIndex, index) { - if (sectionIndex == 0) { - return NodeHeaderListRow( - title: S.of(context).add_new_node, - onTap: (_) async => await Navigator.of(context) - .pushNamed(Routes.newNode)); - } - - final node = nodeListViewModel.nodes[index]; - final isSelected = - node.keyIndex == nodeListViewModel.currentNode.keyIndex; - final nodeListRow = NodeListRow( - title: node.uriRaw, - isSelected: isSelected, - isAlive: node.requestNode(), - onTap: (_) async { - if (isSelected) { - return; - } - - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: - S.of(context).change_current_node_title, - alertContent: S - .of(context) - .change_current_node(node.uriRaw), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).change, - actionLeftButton: () => - Navigator.of(context).pop(), - actionRightButton: () async { - await nodeListViewModel.setAsCurrent(node); - Navigator.of(context).pop(); - }); - }); - }); - - final dismissibleRow = Slidable( - key: Key('${node.keyIndex}'), - startActionPane: _actionPane(context, node), - endActionPane: _actionPane(context, node), - child: nodeListRow, - ); - - return isSelected ? nodeListRow : dismissibleRow; - }); - }, - ), - ); - } - - ActionPane _actionPane(BuildContext context, Node node) => ActionPane( - motion: const ScrollMotion(), - extentRatio: 0.3, - children: [ - SlidableAction( - onPressed: (context) async { - final confirmed = await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).remove_node, - alertContent: - S.of(context).remove_node_message, - rightButtonText: S.of(context).remove, - leftButtonText: S.of(context).cancel, - actionRightButton: () => - Navigator.pop(context, true), - actionLeftButton: () => - Navigator.pop(context, false)); - }) ?? - false; - - if (confirmed) { - await nodeListViewModel.delete(node); - } - }, - backgroundColor: Colors.red, - foregroundColor: Colors.white, - icon: CupertinoIcons.delete, - label: S.of(context).delete, - ), - ], - ); -} diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart new file mode 100644 index 000000000..52851e063 --- /dev/null +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -0,0 +1,151 @@ +import 'package:cake_wallet/core/node_address_validator.dart'; +import 'package:cake_wallet/core/node_port_validator.dart'; +import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; +import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:mobx/mobx.dart'; + +class NodeForm extends StatelessWidget { + NodeForm({ + required this.nodeViewModel, + required this.formKey, + }) : _addressController = TextEditingController(), + _portController = TextEditingController(), + _loginController = TextEditingController(), + _passwordController = TextEditingController() { + reaction((_) => nodeViewModel.address, (String address) { + if (address != _addressController.text) { + _addressController.text = address; + } + }); + + reaction((_) => nodeViewModel.port, (String port) { + if (port != _portController.text) { + _portController.text = port; + } + }); + + if (nodeViewModel.hasAuthCredentials) { + reaction((_) => nodeViewModel.login, (String login) { + if (login != _loginController.text) { + _loginController.text = login; + } + }); + + reaction((_) => nodeViewModel.password, (String password) { + if (password != _passwordController.text) { + _passwordController.text = password; + } + }); + } + + _addressController + .addListener(() => nodeViewModel.address = _addressController.text); + _portController + .addListener(() => nodeViewModel.port = _portController.text); + _loginController + .addListener(() => nodeViewModel.login = _loginController.text); + _passwordController + .addListener(() => nodeViewModel.password = _passwordController.text); + } + + final NodeCreateOrEditViewModel nodeViewModel; + final GlobalKey formKey; + + final TextEditingController _addressController; + final TextEditingController _portController; + final TextEditingController _loginController; + final TextEditingController _passwordController; + + @override + Widget build(BuildContext context) { + return Form( + key: formKey, + child: Column( + children: [ + Row( + children: [ + Expanded( + child: BaseTextFormField( + controller: _addressController, + hintText: S.of(context).node_address, + validator: NodeAddressValidator(), + ), + ) + ], + ), + SizedBox(height: 10.0), + Row( + children: [ + Expanded( + child: BaseTextFormField( + controller: _portController, + hintText: S.of(context).node_port, + keyboardType: TextInputType.numberWithOptions( + signed: false, decimal: false), + validator: NodePortValidator(), + )) + ], + ), + SizedBox(height: 10.0), + if (nodeViewModel.hasAuthCredentials) ...[ + Row( + children: [ + Expanded( + child: BaseTextFormField( + controller: _loginController, + hintText: S.of(context).login, + )) + ], + ), + SizedBox(height: 10.0), + Row( + children: [ + Expanded( + child: BaseTextFormField( + controller: _passwordController, + hintText: S.of(context).password, + )) + ], + ), + Padding( + padding: EdgeInsets.only(top: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Observer( + builder: (_) => StandardCheckbox( + value: nodeViewModel.useSSL, + onChanged: (value) => nodeViewModel.useSSL = value, + caption: S.of(context).use_ssl, + ), + ) + ], + ), + ), + Padding( + padding: EdgeInsets.only(top: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Observer( + builder: (_) => StandardCheckbox( + value: nodeViewModel.trusted, + onChanged: (value) => nodeViewModel.trusted = value, + caption: S.of(context).trusted, + ), + ), + ], + ), + ), + ] + ], + ), + ); + } +} diff --git a/lib/src/screens/nodes/widgets/node_list_row.dart b/lib/src/screens/nodes/widgets/node_list_row.dart index 3008d450c..580aba170 100644 --- a/lib/src/screens/nodes/widgets/node_list_row.dart +++ b/lib/src/screens/nodes/widgets/node_list_row.dart @@ -37,7 +37,10 @@ class NodeHeaderListRow extends StandardListRow { @override Widget buildTrailing(BuildContext context) { - return Icon(Icons.add, - color: Theme.of(context).accentTextTheme!.subtitle1!.color!, size: 24.0); + return SizedBox( + width: 20, + child: Icon(Icons.add, + color: Theme.of(context).accentTextTheme!.subtitle1!.color!, size: 24.0), + ); } } diff --git a/lib/src/screens/order_details/order_details_page.dart b/lib/src/screens/order_details/order_details_page.dart index 2e6ece509..0784c7008 100644 --- a/lib/src/screens/order_details/order_details_page.dart +++ b/lib/src/screens/order_details/order_details_page.dart @@ -7,7 +7,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/widgets/list_row.dart'; import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart'; class OrderDetailsPage extends BasePage { @@ -57,7 +57,7 @@ class OrderDetailsPageBodyState extends State { if (item is TrackTradeListItem) { return GestureDetector( onTap: item.onTap, - child: StandartListRow( + child: ListRow( title: '${item.title}', value: '${item.value}')); } else { return GestureDetector( @@ -65,7 +65,7 @@ class OrderDetailsPageBodyState extends State { Clipboard.setData(ClipboardData(text: '${item.value}')); showBar(context, S.of(context).copied_to_clipboard); }, - child: StandartListRow( + child: ListRow( title: '${item.title}', value: '${item.value}')); } }); diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 6f6b4e31c..71ee578a0 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -135,8 +136,7 @@ class ReceivePage extends BasePage { Observer( builder: (_) => ListView.separated( padding: EdgeInsets.all(0), - separatorBuilder: (context, _) => Container( - height: 1, color: Theme.of(context).dividerColor), + separatorBuilder: (context, _) => const SectionDivider(), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: addressListViewModel.items.length, diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index c507f6e1f..2526d15f3 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -1,23 +1,28 @@ import 'dart:async'; +import 'package:cake_wallet/core/auth_service.dart'; +import 'package:cake_wallet/utils/payment_request.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/authentication_store.dart'; import 'package:cake_wallet/entities/qr_scanner.dart'; +import 'package:uni_links/uni_links.dart'; class Root extends StatefulWidget { - Root( - {required Key key, - required this.authenticationStore, - required this.appStore, - required this.child, - required this.navigatorKey}) - : super(key: key); + Root({ + required Key key, + required this.authenticationStore, + required this.appStore, + required this.child, + required this.navigatorKey, + required this.authService, + }) : super(key: key); final AuthenticationStore authenticationStore; final AppStore appStore; final GlobalKey navigatorKey; + final AuthService authService; final Widget child; @override @@ -26,22 +31,56 @@ class Root extends StatefulWidget { class RootState extends State with WidgetsBindingObserver { RootState() - : _isInactiveController = StreamController.broadcast(), - _isInactive = false, - _postFrameCallback = false; + : _isInactiveController = StreamController.broadcast(), + _isInactive = false, + _requestAuth = true, + _postFrameCallback = false; Stream get isInactive => _isInactiveController.stream; StreamController _isInactiveController; bool _isInactive; bool _postFrameCallback; + bool _requestAuth; + + StreamSubscription? stream; + Uri? launchUri; @override void initState() { + _requestAuth = widget.authService.requireAuth(); _isInactiveController = StreamController.broadcast(); _isInactive = false; _postFrameCallback = false; WidgetsBinding.instance.addObserver(this); super.initState(); + + initUniLinks(); + } + + @override + void dispose() { + stream?.cancel(); + super.dispose(); + } + + /// handle app links while the app is already started + /// whether its in the foreground or in the background. + Future initUniLinks() async { + try { + stream = uriLinkStream.listen((Uri? uri) { + handleDeepLinking(uri); + }); + + handleDeepLinking(await getInitialUri()); + } catch (e) { + print(e); + } + } + + void handleDeepLinking(Uri? uri) { + if (uri == null || !mounted) return; + + launchUri = uri; } @override @@ -52,11 +91,15 @@ class RootState extends State with WidgetsBindingObserver { return; } - if (!_isInactive && - widget.authenticationStore.state == AuthenticationState.allowed) { + if (!_isInactive && widget.authenticationStore.state == AuthenticationState.allowed) { setState(() => _setInactive(true)); } + break; + case AppLifecycleState.resumed: + setState(() { + _requestAuth = widget.authService.requireAuth(); + }); break; default: break; @@ -65,7 +108,7 @@ class RootState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - if (_isInactive && !_postFrameCallback) { + if (_isInactive && !_postFrameCallback && _requestAuth) { _postFrameCallback = true; WidgetsBinding.instance.addPostFrameCallback((_) { widget.navigatorKey.currentState?.pushNamed(Routes.unlock, @@ -75,9 +118,19 @@ class RootState extends State with WidgetsBindingObserver { } _reset(); - auth.close(); + auth.close( + route: launchUri != null ? Routes.send : null, + arguments: PaymentRequest.fromUri(launchUri), + ); + launchUri = null; }); }); + } else if (launchUri != null) { + widget.navigatorKey.currentState?.pushNamed( + Routes.send, + arguments: PaymentRequest.fromUri(launchUri), + ); + launchUri = null; } return WillPopScope(onWillPop: () async => false, child: widget.child); diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index e0cfff517..2cd849f34 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,13 +1,11 @@ -import 'dart:ui'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart'; import 'package:cake_wallet/src/screens/send/widgets/send_card.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart'; +import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/send/output.dart'; -import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; @@ -28,13 +26,15 @@ import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:cw_core/crypto_currency.dart'; class SendPage extends BasePage { - SendPage({required this.sendViewModel,required this.settingsViewModel }) : _formKey = GlobalKey(),fiatFromSettings = settingsViewModel.fiatCurrency; + SendPage({ + required this.sendViewModel, + this.initialPaymentRequest, + }) : _formKey = GlobalKey(); final SendViewModel sendViewModel; - final SettingsViewModel settingsViewModel; final GlobalKey _formKey; final controller = PageController(initialPage: 0); - final FiatCurrency fiatFromSettings ; + final PaymentRequest? initialPaymentRequest; bool _effectsInstalled = false; @@ -55,7 +55,7 @@ class SendPage extends BasePage { @override void onClose(BuildContext context) { - settingsViewModel.setFiatCurrency(fiatFromSettings); + sendViewModel.onClose(); Navigator.of(context).pop(); } @@ -121,6 +121,7 @@ class SendPage extends BasePage { key: output.key, output: output, sendViewModel: sendViewModel, + initialPaymentRequest: initialPaymentRequest, ); }); }, @@ -236,7 +237,7 @@ class SendPage extends BasePage { if(template.isCurrencySelected){ output.setCryptoAmount(template.amount); }else{ - settingsViewModel.setFiatCurrency(fiatFromTemplate); + sendViewModel.setFiatCurrency(fiatFromTemplate); output.setFiatAmount(template.amountFiat); } output.resetParsedAddress(); @@ -386,16 +387,10 @@ class SendPage extends BasePage { amount: S.of(context).send_amount, amountValue: sendViewModel.pendingTransaction!.amountFormatted, - fiatAmountValue: - sendViewModel.pendingTransactionFiatAmount + - ' ' + - sendViewModel.fiat.title, + fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted, fee: S.of(context).send_fee, feeValue: sendViewModel.pendingTransaction!.feeFormatted, - feeFiatAmount: - sendViewModel.pendingTransactionFeeFiatAmount + - ' ' + - sendViewModel.fiat.title, + feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted, outputs: sendViewModel.outputs, rightButtonText: S.of(context).ok, leftButtonText: S.of(context).cancel, diff --git a/lib/src/screens/send/widgets/choose_yat_address_alert.dart b/lib/src/screens/send/widgets/choose_yat_address_alert.dart index 93785743d..5d937a571 100644 --- a/lib/src/screens/send/widgets/choose_yat_address_alert.dart +++ b/lib/src/screens/send/widgets/choose_yat_address_alert.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/widgets/cake_scrollbar.dart'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/base_alert_dialog.dart'; @@ -70,10 +71,7 @@ class ChooseYatAddressButtonsState extends State { controller: controller, padding: EdgeInsets.all(0), itemCount: itemCount, - separatorBuilder: (_, __) => Container( - height: 1, - color: Theme.of(context).dividerColor, - ), + separatorBuilder: (_, __) => const SectionDivider(), itemBuilder: (context, index) { final address = addresses[index]; diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index dc589923f..082067d95 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -1,11 +1,11 @@ -import 'dart:ui'; +import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/view_model/send/output.dart'; -import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -21,21 +21,28 @@ class SendCard extends StatefulWidget { SendCard({ Key? key, required this.output, - required this.sendViewModel}) : super(key: key); + required this.sendViewModel, + this.initialPaymentRequest, + }) : super(key: key); final Output output; final SendViewModel sendViewModel; + final PaymentRequest? initialPaymentRequest; @override SendCardState createState() => SendCardState( output: output, - sendViewModel: sendViewModel + sendViewModel: sendViewModel, + initialPaymentRequest: initialPaymentRequest, ); } class SendCardState extends State with AutomaticKeepAliveClientMixin { - SendCardState({required this.output, required this.sendViewModel}) + SendCardState({ + required this.output, + required this.sendViewModel, + this.initialPaymentRequest}) : addressController = TextEditingController(), cryptoAmountController = TextEditingController(), fiatAmountController = TextEditingController(), @@ -50,6 +57,7 @@ class SendCardState extends State final Output output; final SendViewModel sendViewModel; + final PaymentRequest? initialPaymentRequest; final TextEditingController addressController; final TextEditingController cryptoAmountController; @@ -62,6 +70,27 @@ class SendCardState extends State bool _effectsInstalled = false; + @override + void initState() { + super.initState(); + + /// if the current wallet doesn't match the one in the qr code + if (initialPaymentRequest != null && + sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: S.of(context).unmatched_currencies, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + }); + } + } + @override Widget build(BuildContext context) { super.build(context); @@ -155,6 +184,7 @@ class SendCardState extends State await output.fetchParsedAddress(context); }, validator: validator, + selectedCurrency: sendViewModel.currency, ); }), if (output.isParsedAddress) Padding( @@ -332,7 +362,8 @@ class SendCardState extends State ], ), )), - Padding( + if (!sendViewModel.isFiatDisabled) + Padding( padding: const EdgeInsets.only(top: 20), child: BaseTextFormField( focusNode: fiatAmountFocus, @@ -438,8 +469,9 @@ class SendCardState extends State Padding( padding: EdgeInsets.only(top: 5), - child: Text( - output + child: sendViewModel.isFiatDisabled + ? const SizedBox(height: 14) + : Text(output .estimatedFeeFiatAmount + ' ' + sendViewModel @@ -510,8 +542,12 @@ class SendCardState extends State } void _setEffects(BuildContext context) { - addressController.text = output.address; - cryptoAmountController.text = output.cryptoAmount; + if (output.address.isNotEmpty) { + addressController.text = output.address; + } + if (output.cryptoAmount.isNotEmpty) { + cryptoAmountController.text = output.cryptoAmount; + } fiatAmountController.text = output.fiatAmount; noteController.text = output.note; extractedAddressController.text = output.extractedAddress; @@ -603,6 +639,13 @@ class SendCardState extends State extractedAddressController.text = extractedAddress; }); + if (initialPaymentRequest != null && + sendViewModel.walletCurrencyName == initialPaymentRequest!.scheme.toLowerCase()) { + addressController.text = initialPaymentRequest!.address; + cryptoAmountController.text = initialPaymentRequest!.amount; + noteController.text = initialPaymentRequest!.note; + } + _effectsInstalled = true; } diff --git a/lib/src/screens/settings/connection_sync_page.dart b/lib/src/screens/settings/connection_sync_page.dart new file mode 100644 index 000000000..0e56d6e58 --- /dev/null +++ b/lib/src/screens/settings/connection_sync_page.dart @@ -0,0 +1,155 @@ +import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cw_core/node.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart'; +import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +class ConnectionSyncPage extends BasePage { + ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel); + + @override + String get title => S.current.connection_sync; + + final NodeListViewModel nodeListViewModel; + final DashboardViewModel dashboardViewModel; + + @override + Widget body(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: 10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SettingsCellWithArrow( + title: S.current.reconnect, + handler: (context) => _presentReconnectAlert(context), + ), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + SettingsCellWithArrow( + title: S.current.rescan, + handler: (context) => Navigator.of(context).pushNamed(Routes.rescan), + ), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + NodeHeaderListRow( + title: S.of(context).add_new_node, + onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode), + ), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + SizedBox(height: 100), + Observer( + builder: (BuildContext context) { + return Flexible( + child: SectionStandardList( + sectionCount: 1, + context: context, + dividerPadding: EdgeInsets.symmetric(horizontal: 24), + itemCounter: (int sectionIndex) { + return nodeListViewModel.nodes.length; + }, + itemBuilder: (_, sectionIndex, index) { + final node = nodeListViewModel.nodes[index]; + final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; + final nodeListRow = NodeListRow( + title: node.uriRaw, + isSelected: isSelected, + isAlive: node.requestNode(), + onTap: (_) async { + if (isSelected) { + return; + } + + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).change_current_node_title, + alertContent: S.of(context).change_current_node(node.uriRaw), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => Navigator.of(context).pop(), + actionRightButton: () async { + await nodeListViewModel.setAsCurrent(node); + Navigator.of(context).pop(); + }, + ); + }); + }, + ); + + final dismissibleRow = Slidable( + key: Key('${node.keyIndex}'), + startActionPane: _actionPane(context, node), + endActionPane: _actionPane(context, node), + child: nodeListRow, + ); + + return isSelected ? nodeListRow : dismissibleRow; + }, + ), + ); + }, + ), + ], + ), + ); + } + + Future _presentReconnectAlert(BuildContext context) async { + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).reconnection, + alertContent: S.of(context).reconnect_alert_text, + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () async { + Navigator.of(context).pop(); + await dashboardViewModel.reconnect(); + }, + actionLeftButton: () => Navigator.of(context).pop()); + }, + ); + } + + ActionPane _actionPane(BuildContext context, Node node) => ActionPane( + motion: const ScrollMotion(), + extentRatio: 0.3, + children: [ + SlidableAction( + onPressed: (context) async { + final confirmed = await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).remove_node, + alertContent: S.of(context).remove_node_message, + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, + actionRightButton: () => Navigator.pop(context, true), + actionLeftButton: () => Navigator.pop(context, false)); + }) ?? + false; + + if (confirmed) { + await nodeListViewModel.delete(node); + } + }, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: CupertinoIcons.delete, + label: S.of(context).delete, + ), + ], + ); +} diff --git a/lib/src/screens/settings/display_settings_page.dart b/lib/src/screens/settings/display_settings_page.dart new file mode 100644 index 000000000..39123a7eb --- /dev/null +++ b/lib/src/screens/settings/display_settings_page.dart @@ -0,0 +1,81 @@ +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/entities/language_service.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class DisplaySettingsPage extends BasePage { + DisplaySettingsPage(this._displaySettingsViewModel); + + @override + String get title => S.current.display_settings; + + final DisplaySettingsViewModel _displaySettingsViewModel; + + @override + Widget body(BuildContext context) { + return Observer(builder: (_) { + return Container( + padding: EdgeInsets.only(top: 10), + child: Column( + children: [ + SettingsSwitcherCell( + title: S.current.settings_display_balance, + value: _displaySettingsViewModel.shouldDisplayBalance, + onValueChange: (_, bool value) { + _displaySettingsViewModel.setShouldDisplayBalance(value); + }), + //if (!isHaven) it does not work correctly + if(!_displaySettingsViewModel.disabledFiatApiMode) + SettingsPickerCell( + title: S.current.settings_currency, + searchHintText: S.current.search_currency, + items: FiatCurrency.all, + selectedItem: _displaySettingsViewModel.fiatCurrency, + onItemSelected: (FiatCurrency currency) => _displaySettingsViewModel.setFiatCurrency(currency), + images: FiatCurrency.all.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")).toList(), + isGridView: true, + matchingCriteria: (FiatCurrency currency, String searchText) { + return currency.title.toLowerCase().contains(searchText) || + currency.fullName.toLowerCase().contains(searchText); + }, + ), + SettingsPickerCell( + title: S.current.settings_change_language, + searchHintText: S.current.search_language, + items: LanguageService.list.keys.toList(), + displayItem: (dynamic code) { + return LanguageService.list[code] ?? ''; + }, + selectedItem: _displaySettingsViewModel.languageCode, + onItemSelected: _displaySettingsViewModel.onLanguageSelected, + images: LanguageService.list.keys + .map((e) => Image.asset("assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) + .toList(), + matchingCriteria: (String code, String searchText) { + return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; + }, + ), + SettingsChoicesCell( + ChoicesListItem( + title: S.current.color_theme, + items: ThemeList.all, + selectedItem: _displaySettingsViewModel.theme, + onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme), + ), + ), + ], + ), + ); + }); + } +} diff --git a/lib/src/screens/settings/other_settings_page.dart b/lib/src/screens/settings/other_settings_page.dart new file mode 100644 index 000000000..e055e2e8e --- /dev/null +++ b/lib/src/screens/settings/other_settings_page.dart @@ -0,0 +1,45 @@ +import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart'; +import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class OtherSettingsPage extends BasePage { + OtherSettingsPage(this._otherSettingsViewModel); + + @override + String get title => S.current.other_settings; + + final OtherSettingsViewModel _otherSettingsViewModel; + + @override + Widget body(BuildContext context) { + return Observer(builder: (_) { + return Container( + padding: EdgeInsets.only(top: 10), + child: Column(children: [ + SettingsPickerCell( + title: S.current.settings_fee_priority, + items: priorityForWalletType(_otherSettingsViewModel.walletType), + displayItem: _otherSettingsViewModel.getDisplayPriority, + selectedItem: _otherSettingsViewModel.transactionPriority, + onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected, + ), + SettingsCellWithArrow( + title: S.current.settings_terms_and_conditions, + handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.readDisclaimer), + ), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + Spacer(), + SettingsVersionCell(title: S.of(context).version(_otherSettingsViewModel.currentVersion)) + ]), + ); + }); + } +} diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart new file mode 100644 index 000000000..2f15cc225 --- /dev/null +++ b/lib/src/screens/settings/privacy_page.dart @@ -0,0 +1,47 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class PrivacyPage extends BasePage { + PrivacyPage(this._privacySettingsViewModel); + + @override + String get title => S.current.privacy_settings; + + final PrivacySettingsViewModel _privacySettingsViewModel; + + @override + Widget body(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: 10), + child: Observer(builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SettingsSwitcherCell( + title: S.current.disable_fiat, + value: _privacySettingsViewModel.isFiatDisabled, + onValueChange: (BuildContext context, bool value) { + _privacySettingsViewModel.setFiatMode(value); + }), + SettingsSwitcherCell( + title: S.current.disable_exchange, + value: _privacySettingsViewModel.disableExchange, + onValueChange: (BuildContext context, bool value) { + _privacySettingsViewModel.setEnableExchange(value); + }), + SettingsSwitcherCell( + title: S.current.settings_save_recipient_address, + value: _privacySettingsViewModel.shouldSaveRecipientAddress, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setShouldSaveRecipientAddress(value); + }) + ], + ); + }), + ); + } +} diff --git a/lib/src/screens/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart new file mode 100644 index 000000000..102a94b36 --- /dev/null +++ b/lib/src/screens/settings/security_backup_page.dart @@ -0,0 +1,102 @@ +import 'package:cake_wallet/entities/pin_code_required_duration.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/auth/auth_page.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class SecurityBackupPage extends BasePage { + SecurityBackupPage(this._securitySettingsViewModel); + + @override + String get title => S.current.security_and_backup; + + final SecuritySettingsViewModel _securitySettingsViewModel; + + @override + Widget body(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: 10), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + SettingsCellWithArrow( + title: S.current.show_keys, + handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired() + ? Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { + if (isAuthenticatedSuccessfully) { + auth.close(route: Routes.showKeys); + } + }) + : Navigator.of(context).pushNamed(Routes.showKeys), + ), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + SettingsCellWithArrow( + title: S.current.create_backup, + handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired() + ? Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { + if (isAuthenticatedSuccessfully) { + auth.close(route: Routes.backup); + } + }) + : Navigator.of(context).pushNamed(Routes.backup), + ), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + SettingsCellWithArrow( + title: S.current.settings_change_pin, + handler: (_) => Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { + auth.close( + route: isAuthenticatedSuccessfully ? Routes.setupPin : null, + arguments: (PinCodeState setupPinContext, String _) { + setupPinContext.close(); + }, + ); + })), + StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), + Observer(builder: (_) { + return SettingsSwitcherCell( + title: S.current.settings_allow_biometrical_authentication, + value: _securitySettingsViewModel.allowBiometricalAuthentication, + onValueChange: (BuildContext context, bool value) { + if (value) { + Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { + if (isAuthenticatedSuccessfully) { + if (await _securitySettingsViewModel.biometricAuthenticated()) { + _securitySettingsViewModel + .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); + } + } else { + _securitySettingsViewModel + .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); + } + + auth.close(); + }); + } else { + _securitySettingsViewModel.setAllowBiometricalAuthentication(value); + } + }); + }), + Observer(builder: (_) { + return SettingsPickerCell( + title: S.current.require_pin_after, + items: PinCodeRequiredDuration.values, + selectedItem: _securitySettingsViewModel.pinCodeRequiredDuration, + onItemSelected: (PinCodeRequiredDuration code) { + _securitySettingsViewModel.setPinCodeRequiredDuration(code); + }, + ); + }), + ]), + ); + } +} diff --git a/lib/src/screens/settings/settings.dart b/lib/src/screens/settings/settings.dart deleted file mode 100644 index 3f6f41e7b..000000000 --- a/lib/src/screens/settings/settings.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart'; -import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cake_wallet/view_model/settings/version_list_item.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; -import 'package:cake_wallet/view_model/settings/link_list_item.dart'; -import 'package:cake_wallet/view_model/settings/picker_list_item.dart'; -import 'package:cake_wallet/view_model/settings/regular_list_item.dart'; -import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; -import 'package:cake_wallet/src/widgets/standard_list.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; - -class SettingsPage extends BasePage { - SettingsPage(this.settingsViewModel); - - final SettingsViewModel settingsViewModel; - - @override - String get title => S.current.settings_title; - - @override - Widget body(BuildContext context) { - // FIX-ME: Added `context` it was not used here before, maby bug ? - return SectionStandardList( - context: context, - sectionCount: settingsViewModel.sections.length, - itemCounter: (int sectionIndex) { - if (sectionIndex < settingsViewModel.sections.length) { - return settingsViewModel.sections[sectionIndex].length; - } - - return 0; - }, - itemBuilder: (_, sectionIndex, itemIndex) { - final item = settingsViewModel.sections[sectionIndex][itemIndex]; - - if (item is PickerListItem) { - return Observer(builder: (_) { - return SettingsPickerCell( - displayItem: item.displayItem, - title: item.title, - selectedItem: item.selectedItem(), - items: item.items, - onItemSelected: (dynamic value) => item.onItemSelected(value), - images: item.images, - searchHintText: item.searchHintText, - isGridView: item.isGridView, - matchingCriteria: (dynamic value, String searchText) => item.matchingCriteria(value, searchText), - ); - }); - } - - if (item is SwitcherListItem) { - return Observer(builder: (_) { - return SettingsSwitcherCell( - title: item.title, - value: item.value(), - onValueChange: item.onValueChange); - }); - } - - if (item is RegularListItem) { - return SettingsCellWithArrow( - title: item.title, handler: item.handler); - } - - if (item is LinkListItem) { - return SettingsLinkProviderCell( - title: item.title, - icon: item.icon, - link: item.link, - linkTitle: item.linkTitle); - } - - if (item is VersionListItem) { - return Observer(builder: (_) { - return SettingsVersionCell( - title: - S.of(context).version(settingsViewModel.currentVersion)); - }); - } - - if (item is ChoicesListItem) { - return SettingsChoicesCell(item); - } - - return Container(); - }); - } -} diff --git a/lib/src/screens/settings/widgets/settings_picker_cell.dart b/lib/src/screens/settings/widgets/settings_picker_cell.dart index 1da2d6fc1..8dd5c8c2c 100644 --- a/lib/src/screens/settings/widgets/settings_picker_cell.dart +++ b/lib/src/screens/settings/widgets/settings_picker_cell.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; -class SettingsPickerCell extends StandardListRow { +class SettingsPickerCell extends StandardListRow { SettingsPickerCell( {required String title, required this.selectedItem, diff --git a/lib/src/screens/settings/widgets/settings_switcher_cell.dart b/lib/src/screens/settings/widgets/settings_switcher_cell.dart index fc99f3297..c1d7fa150 100644 --- a/lib/src/screens/settings/widgets/settings_switcher_cell.dart +++ b/lib/src/screens/settings/widgets/settings_switcher_cell.dart @@ -1,6 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; -import 'package:cake_wallet/src/widgets/standart_switch.dart'; +import 'package:cake_wallet/src/widgets/standard_switch.dart'; class SettingsSwitcherCell extends StandardListRow { SettingsSwitcherCell( @@ -11,6 +11,6 @@ class SettingsSwitcherCell extends StandardListRow { final void Function(BuildContext context, bool value)? onValueChange; @override - Widget buildTrailing(BuildContext context) => StandartSwitch( + Widget buildTrailing(BuildContext context) => StandardSwitch( value: value, onTaped: () => onValueChange?.call(context, !value)); } diff --git a/lib/src/screens/trade_details/trade_details_page.dart b/lib/src/screens/trade_details/trade_details_page.dart index 500d16beb..1958a7d58 100644 --- a/lib/src/screens/trade_details/trade_details_page.dart +++ b/lib/src/screens/trade_details/trade_details_page.dart @@ -1,6 +1,6 @@ import 'package:cake_wallet/src/widgets/standard_list.dart'; -import 'package:cake_wallet/src/widgets/standart_list_card.dart'; -import 'package:cake_wallet/src/widgets/standart_list_status_row.dart'; +import 'package:cake_wallet/src/widgets/standard_list_card.dart'; +import 'package:cake_wallet/src/widgets/standard_list_status_row.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/view_model/trade_details_view_model.dart'; import 'package:flutter/material.dart'; @@ -9,7 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/widgets/list_row.dart'; import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart'; @@ -62,18 +62,18 @@ class TradeDetailsPageBodyState extends State { if (item is TrackTradeListItem) { return GestureDetector( onTap: item.onTap, - child: StandartListRow( + child: ListRow( title: '${item.title}', value: '${item.value}')); } if (item is DetailsListStatusItem) { - return StandartListStatusRow( + return StandardListStatusRow( title: item.title, value: item.value); } if (item is TradeDetailsListCardItem) { - return TradeDatailsStandartListCard( + return TradeDetailsStandardListCard( id: item.id, create: item.createdAt, pair: item.pair, @@ -86,7 +86,7 @@ class TradeDetailsPageBodyState extends State { Clipboard.setData(ClipboardData(text: '${item.value}')); showBar(context, S.of(context).copied_to_clipboard); }, - child: StandartListRow( + child: ListRow( title: '${item.title}', value: '${item.value}')); }); }); diff --git a/lib/src/screens/transaction_details/transaction_details_page.dart b/lib/src/screens/transaction_details/transaction_details_page.dart index 098d65c23..1b79ceeb0 100644 --- a/lib/src/screens/transaction_details/transaction_details_page.dart +++ b/lib/src/screens/transaction_details/transaction_details_page.dart @@ -6,7 +6,7 @@ import 'package:cake_wallet/view_model/transaction_details_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/widgets/list_row.dart'; import 'package:cake_wallet/src/screens/transaction_details/blockexplorer_list_item.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; @@ -42,7 +42,7 @@ class TransactionDetailsPage extends BasePage { S.of(context).transaction_details_copied(item.title)); }, child: - StandartListRow(title: '${item.title}:', value: item.value), + ListRow(title: '${item.title}:', value: item.value), ); } @@ -50,7 +50,7 @@ class TransactionDetailsPage extends BasePage { return GestureDetector( onTap: item.onTap, child: - StandartListRow(title: '${item.title}:', value: item.value), + ListRow(title: '${item.title}:', value: item.value), ); } diff --git a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart index b1c4ff01b..d8ce24d88 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_details_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_details_page.dart @@ -5,7 +5,7 @@ import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/widgets/list_row.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -30,7 +30,7 @@ class UnspentCoinsDetailsPage extends BasePage { final item = unspentCoinsDetailsViewModel.items[index]; if (item is StandartListItem) { - return StandartListRow( + return ListRow( title: '${item.title}:', value: item.value); } diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart index 6272469df..734aeb7ee 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart @@ -1,4 +1,4 @@ -import 'package:cake_wallet/src/widgets/standart_switch.dart'; +import 'package:cake_wallet/src/widgets/standard_switch.dart'; import 'package:flutter/material.dart'; class UnspentCoinsSwitchRow extends StatelessWidget { @@ -33,7 +33,7 @@ class UnspentCoinsSwitchRow extends StatelessWidget { textAlign: TextAlign.left), Padding( padding: EdgeInsets.only(top: 12), - child: StandartSwitch( + child: StandardSwitch( value: switchValue, onTaped: () => onSwitchValueChange(!switchValue)) ) diff --git a/lib/src/screens/wallet_keys/wallet_keys_page.dart b/lib/src/screens/wallet_keys/wallet_keys_page.dart index 942809ac8..e81ec7d2a 100644 --- a/lib/src/screens/wallet_keys/wallet_keys_page.dart +++ b/lib/src/screens/wallet_keys/wallet_keys_page.dart @@ -1,4 +1,5 @@ import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -6,7 +7,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/src/widgets/list_row.dart'; import 'package:cake_wallet/view_model/wallet_keys_view_model.dart'; class WalletKeysPage extends BasePage { @@ -57,10 +58,7 @@ class WalletKeysPage extends BasePage { height: 1, padding: EdgeInsets.only(left: 24), color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!, - child: Container( - height: 1, - color: Theme.of(context).dividerColor, - ), + child: const SectionDivider(), ), itemCount: walletKeysViewModel.items.length, itemBuilder: (BuildContext context, int index) { @@ -71,7 +69,7 @@ class WalletKeysPage extends BasePage { Clipboard.setData(ClipboardData(text: item.value)); showBar(context, S.of(context).copied_key_to_clipboard(item.title)); }, - child: StandartListRow( + child: ListRow( title: item.title + ':', value: item.value, ), diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index c1a7ea953..d76631b3f 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -220,68 +220,88 @@ class WalletListBodyState extends State { } Future _loadWallet(WalletListItem wallet) async { - await Navigator.of(context).pushNamed(Routes.auth, arguments: - (bool isAuthenticatedSuccessfully, AuthPageState auth) async { - if (!isAuthenticatedSuccessfully) { - return; - } + if (await widget.walletListViewModel.checkIfAuthRequired()) { + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { + if (!isAuthenticatedSuccessfully) { + return; + } + try { + auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name)); + await widget.walletListViewModel.loadWallet(wallet); + auth.hideProgressText(); + auth.close(); + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context).pop(); + }); + } catch (e) { + auth.changeProcessText( + S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + } + }); + } else { try { - auth.changeProcessText( - S.of(context).wallet_list_loading_wallet(wallet.name)); + changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name)); await widget.walletListViewModel.loadWallet(wallet); - auth.hideProgressText(); - auth.close(); - WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.of(context).pop(); - }); + hideProgressText(); + Navigator.of(context).pop(); } catch (e) { - auth.changeProcessText(S - .of(context) - .wallet_list_failed_to_load(wallet.name, e.toString())); + changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); } - }); + } } Future _removeWallet(WalletListItem wallet) async { - await Navigator.of(context).pushNamed(Routes.auth, arguments: - (bool isAuthenticatedSuccessfully, AuthPageState auth) async { - if (!isAuthenticatedSuccessfully) { - return; - } - - bool confirmed = false; - - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).delete_wallet, - alertContent: S.of(context).delete_wallet_confirm_message(wallet.name), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).delete, - actionLeftButton: () => Navigator.of(context).pop(), - actionRightButton: () { - confirmed = true; - Navigator.of(context).pop(); - }, - ); - }); - - if (confirmed) { - try { - auth.changeProcessText( - S.of(context).wallet_list_removing_wallet(wallet.name)); - await widget.walletListViewModel.remove(wallet); - } catch (e) { - auth.changeProcessText(S - .of(context) - .wallet_list_failed_to_remove(wallet.name, e.toString())); + if (widget.walletListViewModel.checkIfAuthRequired()) { + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async { + if (!isAuthenticatedSuccessfully) { + return; } - } + _onSuccessfulAuth(wallet, auth); + }); + } else { + _onSuccessfulAuth(wallet, null); + } + } - auth.close(); - }); + void _onSuccessfulAuth(WalletListItem wallet, AuthPageState? auth) async { + bool confirmed = false; + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).delete_wallet, + alertContent: S.of(context).delete_wallet_confirm_message(wallet.name), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).delete, + actionLeftButton: () => Navigator.of(context).pop(), + actionRightButton: () { + confirmed = true; + Navigator.of(context).pop(); + }, + ); + }); + + if (confirmed) { + try { + auth != null + ? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name)) + : changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name)); + await widget.walletListViewModel.remove(wallet); + } catch (e) { + auth != null + ? auth.changeProcessText( + S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()), + ) + : changeProcessText( + S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()), + ); + } + } + + auth?.close(); } void changeProcessText(String text) { @@ -294,16 +314,16 @@ class WalletListBodyState extends State { } ActionPane _actionPane(WalletListItem wallet) => ActionPane( - motion: const ScrollMotion(), - extentRatio: 0.3, - children: [ - SlidableAction( - onPressed: (_) => _removeWallet(wallet), - backgroundColor: Colors.red, - foregroundColor: Colors.white, - icon: CupertinoIcons.delete, - label: S.of(context).delete, - ), - ], - ); + motion: const ScrollMotion(), + extentRatio: 0.3, + children: [ + SlidableAction( + onPressed: (_) => _removeWallet(wallet), + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: CupertinoIcons.delete, + label: S.of(context).delete, + ), + ], + ); } diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 35bf3ed85..7e0727312 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/entities/qr_scanner.dart'; import 'package:cake_wallet/entities/contact_base.dart'; +import 'package:cw_core/crypto_currency.dart'; enum AddressTextFieldOption { paste, qrCode, addressBook } @@ -26,7 +27,8 @@ class AddressTextField extends StatelessWidget { this.hintStyle, this.validator, this.onPushPasteButton, - this.onPushAddressBookButton}); + this.onPushAddressBookButton, + this.selectedCurrency}); static const prefixIconWidth = 34.0; static const prefixIconHeight = 34.0; @@ -47,6 +49,7 @@ class AddressTextField extends StatelessWidget { final FocusNode? focusNode; final Function(BuildContext context)? onPushPasteButton; final Function(BuildContext context)? onPushAddressBookButton; + final CryptoCurrency? selectedCurrency; @override Widget build(BuildContext context) { @@ -207,7 +210,7 @@ class AddressTextField extends StatelessWidget { Future _presetAddressBookPicker(BuildContext context) async { final contact = await Navigator.of(context, rootNavigator: true) - .pushNamed(Routes.pickerAddressBook); + .pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency); if (contact is ContactBase && contact.address != null) { controller?.text = contact.address; diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index 70370e227..effbbc562 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -1,4 +1,5 @@ import 'dart:ui'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/palette.dart'; @@ -76,10 +77,7 @@ class BaseAlertDialog extends StatelessWidget { ), )), ), - Container( - width: 1, - color: Theme.of(context).dividerColor, - ), + const SectionDivider(), Expanded( child: TextButton( onPressed: actionRight, @@ -140,10 +138,7 @@ class BaseAlertDialog extends StatelessWidget { isDividerExists ? Padding( padding: EdgeInsets.only(top: 16, bottom: 8), - child: Container( - height: 1, - color: Theme.of(context).dividerColor, - ), + child: const SectionDivider(), ) : Offstage(), Padding( @@ -152,10 +147,7 @@ class BaseAlertDialog extends StatelessWidget { ) ], ), - Container( - height: 1, - color: Theme.of(context).dividerColor, - ), + const SectionDivider(), actionButtons(context) ], ), diff --git a/lib/src/widgets/standart_list_row.dart b/lib/src/widgets/list_row.dart similarity index 96% rename from lib/src/widgets/standart_list_row.dart rename to lib/src/widgets/list_row.dart index 94a4c4c76..40824fe59 100644 --- a/lib/src/widgets/standart_list_row.dart +++ b/lib/src/widgets/list_row.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class StandartListRow extends StatelessWidget { - StandartListRow( +class ListRow extends StatelessWidget { + ListRow( {required this.title, required this.value, this.titleFontSize = 14, diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index 36aafe5aa..f26ff3ee2 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; -class Picker extends StatefulWidget { +class Picker extends StatefulWidget { Picker({ required this.selectedAtIndex, required this.items, @@ -39,7 +39,7 @@ class Picker extends StatefulWidget { _PickerState createState() => _PickerState(items, images, onItemSelected); } -class _PickerState extends State { +class _PickerState extends State> { _PickerState(this.items, this.images, this.onItemSelected); final Function(Item) onItemSelected; @@ -59,7 +59,7 @@ class _PickerState extends State { images = []; for (int i=0;i extends State { child: Padding( padding: EdgeInsets.only(left: image != null ? 12 : 0), child: Text( - // What a hack (item as) ? - widget.displayItem?.call(item as Object) ?? item.toString(), + widget.displayItem?.call(item) ?? item.toString(), style: TextStyle( fontSize: 14, fontFamily: 'Lato', diff --git a/lib/src/widgets/section_divider.dart b/lib/src/widgets/section_divider.dart new file mode 100644 index 000000000..bef85f975 --- /dev/null +++ b/lib/src/widgets/section_divider.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class SectionDivider extends StatelessWidget { + const SectionDivider(); + + @override + Widget build(BuildContext context) { + return Container( + height: 1, + color: Theme.of(context).dividerColor, + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/standard_checkbox.dart b/lib/src/widgets/standard_checkbox.dart index a59364fe1..d2cd6463d 100644 --- a/lib/src/widgets/standard_checkbox.dart +++ b/lib/src/widgets/standard_checkbox.dart @@ -2,42 +2,42 @@ import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class StandardCheckbox extends StatefulWidget { - StandardCheckbox({ - Key? key, - required this.value, - this.caption = '', - required this.onChanged}) - : super(key: key); +class StandardCheckbox extends StatelessWidget { + StandardCheckbox( + {required this.value, + this.caption = '', + this.gradientBackground = false, + this.borderColor, + this.iconColor, + required this.onChanged}); final bool value; final String caption; + final bool gradientBackground; + final Color? borderColor; + final Color? iconColor; final Function(bool) onChanged; - @override - StandardCheckboxState createState() => - StandardCheckboxState(value, caption, onChanged); -} - -class StandardCheckboxState extends State { - StandardCheckboxState(this.value, this.caption, this.onChanged); - - bool value; - String caption; - Function(bool) onChanged; - - void changeValue(bool newValue) { - setState(() => value = newValue); - } - @override Widget build(BuildContext context) { + final baseGradient = LinearGradient(colors: [ + Theme.of(context).primaryTextTheme.subtitle1!.color!, + Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!, + ], begin: Alignment.centerLeft, end: Alignment.centerRight); + + final boxBorder = Border.all( + color: borderColor ?? Theme.of(context).primaryTextTheme.caption!.color!, width: 1.0); + + final checkedBoxDecoration = BoxDecoration( + gradient: gradientBackground ? baseGradient : null, + border: gradientBackground ? null : boxBorder, + borderRadius: BorderRadius.all(Radius.circular(8.0))); + + final uncheckedBoxDecoration = + BoxDecoration(border: boxBorder, borderRadius: BorderRadius.all(Radius.circular(8.0))); + return GestureDetector( - onTap: () { - value = !value; - onChanged(value); - setState(() {}); - }, + onTap: () => onChanged(!value), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, @@ -45,38 +45,25 @@ class StandardCheckboxState extends State { Container( height: 24.0, width: 24.0, - decoration: BoxDecoration( - border: Border.all( - color: Theme.of(context) - .primaryTextTheme! - .caption! - .color!, - width: 1.0), - borderRadius: BorderRadius.all( - Radius.circular(8.0)), - color: Theme.of(context).backgroundColor), + decoration: value ? checkedBoxDecoration : uncheckedBoxDecoration, child: value - ? Icon( - Icons.check, - color: Colors.blue, - size: 20.0, - ) - : Offstage(), + ? Icon( + Icons.check, + color: iconColor ?? Colors.blue, + size: 20.0, + ) + : Offstage(), ), - if (caption.isNotEmpty) Padding( - padding: EdgeInsets.only(left: 10), - child: Text( - caption, - style: TextStyle( - fontSize: 16.0, - color: Theme.of(context) - .primaryTextTheme! - .headline6! - .color!), - ) - ) + if (caption.isNotEmpty) + Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + caption, + style: TextStyle( + fontSize: 16.0, color: Theme.of(context).primaryTextTheme!.headline6!.color!), + )) ], ), ); } -} \ No newline at end of file +} diff --git a/lib/src/widgets/standard_list.dart b/lib/src/widgets/standard_list.dart index ae8978eb4..680cf6edd 100644 --- a/lib/src/widgets/standard_list.dart +++ b/lib/src/widgets/standard_list.dart @@ -1,6 +1,6 @@ import 'package:cake_wallet/palette.dart'; -import 'package:cake_wallet/src/widgets/standart_list_card.dart'; -import 'package:cake_wallet/src/widgets/standart_list_status_row.dart'; +import 'package:cake_wallet/src/widgets/standard_list_card.dart'; +import 'package:cake_wallet/src/widgets/standard_list_status_row.dart'; import 'package:flutter/material.dart'; class StandardListRow extends StatelessWidget { @@ -123,6 +123,7 @@ class SectionStandardList extends StatelessWidget { required this.itemBuilder, required this.sectionCount, required BuildContext context, + this.dividerPadding = const EdgeInsets.only(left: 24), this.themeColor, this.dividerThemeColor, this.sectionTitleBuilder, @@ -149,6 +150,7 @@ class SectionStandardList extends StatelessWidget { final List totalRows; final Color? themeColor; final Color? dividerThemeColor; + final EdgeInsets dividerPadding; List transform( bool hasTopSeparator, @@ -178,7 +180,7 @@ class SectionStandardList extends StatelessWidget { items.add(sectionIndex + 1 != sectionCount ? SectionHeaderListRow() - : StandardListSeparator(padding: EdgeInsets.only(left: 24))); + : StandardListSeparator(padding: dividerPadding)); } return items; @@ -215,7 +217,7 @@ class SectionStandardList extends StatelessWidget { return Container(); } - if (row is StandartListStatusRow || row is TradeDatailsStandartListCard) { + if (row is StandardListStatusRow || row is TradeDetailsStandardListCard) { return Container(); } diff --git a/lib/src/widgets/standart_list_card.dart b/lib/src/widgets/standard_list_card.dart similarity index 96% rename from lib/src/widgets/standart_list_card.dart rename to lib/src/widgets/standard_list_card.dart index 569a4e51f..7b2ecc2ce 100644 --- a/lib/src/widgets/standart_list_card.dart +++ b/lib/src/widgets/standard_list_card.dart @@ -2,8 +2,8 @@ import 'package:cake_wallet/palette.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/themes/theme_base.dart'; -class TradeDatailsStandartListCard extends StatelessWidget { - TradeDatailsStandartListCard( +class TradeDetailsStandardListCard extends StatelessWidget { + TradeDetailsStandardListCard( {required this.id, required this.create, required this.pair, diff --git a/lib/src/widgets/standart_list_status_row.dart b/lib/src/widgets/standard_list_status_row.dart similarity index 95% rename from lib/src/widgets/standart_list_status_row.dart rename to lib/src/widgets/standard_list_status_row.dart index d97c4f688..f958e83ae 100644 --- a/lib/src/widgets/standart_list_status_row.dart +++ b/lib/src/widgets/standard_list_status_row.dart @@ -3,8 +3,8 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.da import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class StandartListStatusRow extends StatelessWidget { - StandartListStatusRow({required this.title, required this.value}); +class StandardListStatusRow extends StatelessWidget { + StandardListStatusRow({required this.title, required this.value}); final String title; final String value; diff --git a/lib/src/widgets/standart_switch.dart b/lib/src/widgets/standard_switch.dart similarity index 81% rename from lib/src/widgets/standart_switch.dart rename to lib/src/widgets/standard_switch.dart index 3018fb5b4..929286058 100644 --- a/lib/src/widgets/standart_switch.dart +++ b/lib/src/widgets/standard_switch.dart @@ -1,17 +1,17 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class StandartSwitch extends StatefulWidget { - const StandartSwitch({required this.value, required this.onTaped}); +class StandardSwitch extends StatefulWidget { + const StandardSwitch({required this.value, required this.onTaped}); final bool value; final VoidCallback onTaped; @override - StandartSwitchState createState() => StandartSwitchState(); + StandardSwitchState createState() => StandardSwitchState(); } -class StandartSwitchState extends State { +class StandardSwitchState extends State { @override Widget build(BuildContext context) { return GestureDetector( diff --git a/lib/store/authentication_store.dart b/lib/store/authentication_store.dart index 626ca2a91..b734d7acb 100644 --- a/lib/store/authentication_store.dart +++ b/lib/store/authentication_store.dart @@ -4,7 +4,7 @@ part 'authentication_store.g.dart'; class AuthenticationStore = AuthenticationStoreBase with _$AuthenticationStore; -enum AuthenticationState { uninitialized, installed, allowed, denied } +enum AuthenticationState { uninitialized, installed, allowed } abstract class AuthenticationStoreBase with Store { AuthenticationStoreBase() : state = AuthenticationState.uninitialized; @@ -17,7 +17,4 @@ abstract class AuthenticationStoreBase with Store { @action void allowed() => state = AuthenticationState.allowed; - - @action - void denied() => state = AuthenticationState.denied; } diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index bee63b21e..87fa749a9 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -8,12 +8,11 @@ part'trade_filter_store.g.dart'; class TradeFilterStore = TradeFilterStoreBase with _$TradeFilterStore; abstract class TradeFilterStoreBase with Store { - TradeFilterStoreBase( - {this.displayXMRTO = true, - this.displayChangeNow = true, - this.displayMorphToken = true, - this.displaySimpleSwap = true, - }); + TradeFilterStoreBase() : displayXMRTO = true, + displayChangeNow = true, + displaySideShift = true, + displayMorphToken = true, + displaySimpleSwap = true; @observable bool displayXMRTO; @@ -21,26 +20,50 @@ abstract class TradeFilterStoreBase with Store { @observable bool displayChangeNow; + @observable + bool displaySideShift; + @observable bool displayMorphToken; @observable bool displaySimpleSwap; + @computed + bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap; + @action void toggleDisplayExchange(ExchangeProviderDescription provider) { switch (provider) { case ExchangeProviderDescription.changeNow: displayChangeNow = !displayChangeNow; break; + case ExchangeProviderDescription.sideShift: + displaySideShift = !displaySideShift; + break; + case ExchangeProviderDescription.simpleSwap: + displaySimpleSwap = !displaySimpleSwap; + break; case ExchangeProviderDescription.xmrto: displayXMRTO = !displayXMRTO; break; case ExchangeProviderDescription.morphToken: displayMorphToken = !displayMorphToken; break; - case ExchangeProviderDescription.simpleSwap: - displaySimpleSwap = !displaySimpleSwap; + case ExchangeProviderDescription.all: + if (displayAllTrades) { + displayChangeNow = false; + displaySideShift = false; + displayXMRTO = false; + displayMorphToken = false; + displaySimpleSwap = false; + } else { + displayChangeNow = true; + displaySideShift = true; + displayXMRTO = true; + displayMorphToken = true; + displaySimpleSwap = true; + } break; } } @@ -48,13 +71,15 @@ abstract class TradeFilterStoreBase with Store { List filtered({required List trades, required WalletBase wallet}) { final _trades = trades.where((item) => item.trade.walletId == wallet.id).toList(); - final needToFilter = !displayChangeNow || !displayXMRTO || !displayMorphToken || !displaySimpleSwap; + final needToFilter = !displayAllTrades; return needToFilter ? _trades .where((item) => (displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) || + (displaySideShift && + item.trade.provider == ExchangeProviderDescription.sideShift) || (displayChangeNow && item.trade.provider == ExchangeProviderDescription.changeNow) || diff --git a/lib/store/dashboard/trades_store.dart b/lib/store/dashboard/trades_store.dart index 6e763196b..72442b46f 100644 --- a/lib/store/dashboard/trades_store.dart +++ b/lib/store/dashboard/trades_store.dart @@ -31,13 +31,8 @@ abstract class TradesStoreBase with Store { void setTrade(Trade trade) => this.trade = trade; @action - Future updateTradeList() async { - if (trade == null) { - return; - } - - trades = tradesSource.values.map((trade) => TradeListItem( - trade: trade!, - settingsStore: settingsStore)).toList(); - } + Future updateTradeList() async => trades = + tradesSource.values.map((trade) => TradeListItem( + trade: trade, + settingsStore: settingsStore)).toList(); } \ No newline at end of file diff --git a/lib/store/dashboard/transaction_filter_store.dart b/lib/store/dashboard/transaction_filter_store.dart index 4444075b7..95264a23d 100644 --- a/lib/store/dashboard/transaction_filter_store.dart +++ b/lib/store/dashboard/transaction_filter_store.dart @@ -1,6 +1,8 @@ import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/filter_item.dart'; +import 'package:cake_wallet/generated/i18n.dart'; part 'transaction_filter_store.g.dart'; @@ -8,8 +10,8 @@ class TransactionFilterStore = TransactionFilterStoreBase with _$TransactionFilterStore; abstract class TransactionFilterStoreBase with Store { - TransactionFilterStoreBase( - {this.displayIncoming = true, this.displayOutgoing = true}); + TransactionFilterStoreBase() : displayIncoming = true, + displayOutgoing = true; @observable bool displayIncoming; @@ -23,11 +25,31 @@ abstract class TransactionFilterStoreBase with Store { @observable DateTime? endDate; - @action - void toggleIncoming() => displayIncoming = !displayIncoming; + @computed + bool get displayAll => displayIncoming && displayOutgoing; @action - void toggleOutgoing() => displayOutgoing = !displayOutgoing; + void toggleAll() { + if (displayAll) { + displayOutgoing = false; + displayIncoming = false; + } else { + displayOutgoing = true; + displayIncoming = true; + } + } + + + @action + void toggleIncoming() { + displayIncoming = !displayIncoming; + } + + + @action + void toggleOutgoing() { + displayOutgoing = !displayOutgoing; + } @action void changeStartDate(DateTime date) => startDate = date; @@ -37,8 +59,7 @@ abstract class TransactionFilterStoreBase with Store { List filtered({required List transactions}) { var _transactions = []; - final needToFilter = !displayOutgoing || - !displayIncoming || + final needToFilter = !displayAll || (startDate != null && endDate != null); if (needToFilter) { @@ -50,7 +71,7 @@ abstract class TransactionFilterStoreBase with Store { && (endDate?.isAfter(item.transaction.date) ?? false); } - if (allowed && (!displayOutgoing || !displayIncoming)) { + if (allowed && (!displayAll)) { allowed = (displayOutgoing && item.transaction.direction == TransactionDirection.outgoing) || diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 534ea67e9..b10e0d08d 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,9 +1,9 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_list.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -17,7 +17,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cw_core/node.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/entities/action_list_display_mode.dart'; -import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; part 'settings_store.g.dart'; @@ -29,6 +29,7 @@ abstract class SettingsStoreBase with Store { required FiatCurrency initialFiatCurrency, required BalanceDisplayMode initialBalanceDisplayMode, required bool initialSaveRecipientAddress, + required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, required bool initialExchangeEnabled, required ThemeBase initialTheme, @@ -40,13 +41,17 @@ abstract class SettingsStoreBase with Store { required this.shouldShowYatPopup, required this.isBitcoinBuyEnabled, required this.actionlistDisplayMode, + required this.pinTimeOutDuration, TransactionPriority? initialBitcoinTransactionPriority, - TransactionPriority? initialMoneroTransactionPriority}) + TransactionPriority? initialMoneroTransactionPriority, + TransactionPriority? initialHavenTransactionPriority, + TransactionPriority? initialLitecoinTransactionPriority}) : nodes = ObservableMap.of(nodes), _sharedPreferences = sharedPreferences, fiatCurrency = initialFiatCurrency, balanceDisplayMode = initialBalanceDisplayMode, shouldSaveRecipientAddress = initialSaveRecipientAddress, + fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, disableExchange = initialExchangeEnabled, currentTheme = initialTheme, @@ -63,6 +68,14 @@ abstract class SettingsStoreBase with Store { priority[WalletType.bitcoin] = initialBitcoinTransactionPriority; } + if (initialHavenTransactionPriority != null) { + priority[WalletType.haven] = initialHavenTransactionPriority; + } + + if (initialLitecoinTransactionPriority != null) { + priority[WalletType.litecoin] = initialLitecoinTransactionPriority; + } + reaction( (_) => fiatCurrency, (FiatCurrency fiatCurrency) => sharedPreferences.setString( @@ -74,11 +87,25 @@ abstract class SettingsStoreBase with Store { .setBool(PreferencesKey.shouldShowYatPopup, shouldShowYatPopup)); priority.observe((change) { - final key = change.key == WalletType.monero - ? PreferencesKey.moneroTransactionPriority - : PreferencesKey.bitcoinTransactionPriority; + final String? key; + switch (change.key) { + case WalletType.monero: + key = PreferencesKey.moneroTransactionPriority; + break; + case WalletType.bitcoin: + key = PreferencesKey.bitcoinTransactionPriority; + break; + case WalletType.litecoin: + key = PreferencesKey.litecoinTransactionPriority; + break; + case WalletType.haven: + key = PreferencesKey.havenTransactionPriority; + break; + default: + key = null; + } - if (change.newValue != null) { + if (change.newValue != null && key != null) { sharedPreferences.setInt(key, change.newValue!.serialize()); } }); @@ -89,6 +116,11 @@ abstract class SettingsStoreBase with Store { PreferencesKey.shouldSaveRecipientAddressKey, shouldSaveRecipientAddress)); + reaction( + (_) => fiatApiMode, + (FiatApiMode mode) => sharedPreferences.setInt( + PreferencesKey.currentFiatApiModeKey, mode.serialize())); + reaction( (_) => currentTheme, (ThemeBase theme) => @@ -110,14 +142,24 @@ abstract class SettingsStoreBase with Store { (String languageCode) => sharedPreferences.setString( PreferencesKey.currentLanguageCode, languageCode)); + reaction( + (_) => pinTimeOutDuration, + (PinCodeRequiredDuration pinCodeInterval) => sharedPreferences.setInt( + PreferencesKey.pinTimeOutDuration, pinCodeInterval.value)); + reaction( (_) => balanceDisplayMode, (BalanceDisplayMode mode) => sharedPreferences.setInt( PreferencesKey.currentBalanceDisplayModeKey, mode.serialize())); + reaction( + (_) => disableExchange, + (bool disableExchange) => sharedPreferences.setBool( + PreferencesKey.disableExchangeKey, disableExchange)); + this .nodes - .observe((change) { + .observe((change) { if (change.newValue != null && change.key != null) { _saveCurrentNode(change.newValue!, change.key!); } @@ -126,6 +168,7 @@ abstract class SettingsStoreBase with Store { static const defaultPinLength = 4; static const defaultActionsMode = 11; + static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes; @observable FiatCurrency fiatCurrency; @@ -139,6 +182,9 @@ abstract class SettingsStoreBase with Store { @observable BalanceDisplayMode balanceDisplayMode; + @observable + FiatApiMode fiatApiMode; + @observable bool shouldSaveRecipientAddress; @@ -154,6 +200,9 @@ abstract class SettingsStoreBase with Store { @observable int pinCodeLength; + @observable + PinCodeRequiredDuration pinTimeOutDuration; + @computed ThemeData get theme => currentTheme.themeData; @@ -190,39 +239,48 @@ abstract class SettingsStoreBase with Store { static Future load( {required Box nodeSource, required bool isBitcoinBuyEnabled, - TransactionPriority? initialMoneroTransactionPriority, - TransactionPriority? initialBitcoinTransactionPriority, FiatCurrency initialFiatCurrency = FiatCurrency.usd, BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance}) async { - if (initialBitcoinTransactionPriority == null) { - initialBitcoinTransactionPriority = bitcoin?.getMediumTransactionPriority(); - } - - if (initialMoneroTransactionPriority == null) { - initialMoneroTransactionPriority = monero?.getDefaultTransactionPriority(); - } final sharedPreferences = await getIt.getAsync(); final currentFiatCurrency = FiatCurrency.deserialize(raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!); - final savedMoneroTransactionPriority = + + TransactionPriority? moneroTransactionPriority = monero?.deserializeMoneroTransactionPriority( raw: sharedPreferences .getInt(PreferencesKey.moneroTransactionPriority)!); - final savedBitcoinTransactionPriority = + TransactionPriority? bitcoinTransactionPriority = bitcoin?.deserializeBitcoinTransactionPriority(sharedPreferences .getInt(PreferencesKey.bitcoinTransactionPriority)!); - final moneroTransactionPriority = - savedMoneroTransactionPriority ?? initialMoneroTransactionPriority; - final bitcoinTransactionPriority = - savedBitcoinTransactionPriority ?? initialBitcoinTransactionPriority; + + TransactionPriority? havenTransactionPriority; + TransactionPriority? litecoinTransactionPriority; + + if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) { + havenTransactionPriority = monero?.deserializeMoneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!); + } + if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) { + litecoinTransactionPriority = bitcoin?.deserializeLitecoinTransactionPriority( + sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!); + } + + moneroTransactionPriority ??= monero?.getDefaultTransactionPriority(); + bitcoinTransactionPriority ??= bitcoin?.getMediumTransactionPriority(); + havenTransactionPriority ??= monero?.getDefaultTransactionPriority(); + litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium(); + final currentBalanceDisplayMode = BalanceDisplayMode.deserialize( raw: sharedPreferences .getInt(PreferencesKey.currentBalanceDisplayModeKey)!); // FIX-ME: Check for which default value we should have here final shouldSaveRecipientAddress = sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false; + final currentFiatApiMode = FiatApiMode.deserialize( + raw: sharedPreferences + .getInt(PreferencesKey.currentFiatApiModeKey) ?? FiatApiMode.enabled.raw); final allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; @@ -234,13 +292,17 @@ abstract class SettingsStoreBase with Store { : ThemeType.bright.index; final savedTheme = ThemeList.deserialize( raw: sharedPreferences.getInt(PreferencesKey.currentTheme) ?? - legacyTheme ?? - 0); + legacyTheme); final actionListDisplayMode = ObservableList(); actionListDisplayMode.addAll(deserializeActionlistDisplayModes( sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ?? defaultActionsMode)); var pinLength = sharedPreferences.getInt(PreferencesKey.currentPinLength); + final timeOutDuration = sharedPreferences.getInt(PreferencesKey.pinTimeOutDuration); + final pinCodeTimeOutDuration = timeOutDuration != null + ? PinCodeRequiredDuration.deserialize(raw: timeOutDuration) + : defaultPinCodeTimeOutDuration; + // If no value if (pinLength == null || pinLength == 0) { pinLength = defaultPinLength; @@ -281,7 +343,7 @@ abstract class SettingsStoreBase with Store { if (havenNode != null) { nodes[WalletType.haven] = havenNode; } - + return SettingsStore( sharedPreferences: sharedPreferences, nodes: nodes, @@ -290,59 +352,104 @@ abstract class SettingsStoreBase with Store { initialFiatCurrency: currentFiatCurrency, initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, + initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialExchangeEnabled: disableExchange, initialTheme: savedTheme, actionlistDisplayMode: actionListDisplayMode, initialPinLength: pinLength, + pinTimeOutDuration: pinCodeTimeOutDuration, initialLanguageCode: savedLanguageCode, initialMoneroTransactionPriority: moneroTransactionPriority, initialBitcoinTransactionPriority: bitcoinTransactionPriority, + initialHavenTransactionPriority: havenTransactionPriority, + initialLitecoinTransactionPriority: litecoinTransactionPriority, shouldShowYatPopup: shouldShowYatPopup); } - // FIX-ME: Dead code + Future reload({required Box nodeSource}) async { - //Future reload( - // {required Box nodeSource, - // FiatCurrency initialFiatCurrency = FiatCurrency.usd, - // TransactionPriority? initialMoneroTransactionPriority, - // TransactionPriority? initialBitcoinTransactionPriority, - // BalanceDisplayMode initialBalanceDisplayMode = - // BalanceDisplayMode.availableBalance}) async { - - // if (initialBitcoinTransactionPriority == null) { - // initialBitcoinTransactionPriority = bitcoin?.getMediumTransactionPriority(); - // } + final sharedPreferences = await getIt.getAsync(); - // if (initialMoneroTransactionPriority == null) { - // initialMoneroTransactionPriority = monero?.getDefaultTransactionPriority(); - // } + fiatCurrency = FiatCurrency.deserialize( + raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!); - // final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) && - // (secrets.wyreApiKey?.isNotEmpty ?? false) && - // (secrets.wyreAccountId?.isNotEmpty ?? false); + priority[WalletType.monero] = monero?.deserializeMoneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ?? + priority[WalletType.monero]!; + priority[WalletType.bitcoin] = bitcoin?.deserializeBitcoinTransactionPriority( + sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ?? + priority[WalletType.bitcoin]!; - // final settings = await SettingsStoreBase.load( - // nodeSource: nodeSource, - // isBitcoinBuyEnabled: isBitcoinBuyEnabled, - // initialBalanceDisplayMode: initialBalanceDisplayMode, - // initialFiatCurrency: initialFiatCurrency, - // initialMoneroTransactionPriority: initialMoneroTransactionPriority, - // initialBitcoinTransactionPriority: initialBitcoinTransactionPriority); - // fiatCurrency = settings.fiatCurrency; - // actionlistDisplayMode = settings.actionlistDisplayMode; - // priority[WalletType.monero] = initialMoneroTransactionPriority; - // priority[WalletType.bitcoin] = initialBitcoinTransactionPriority; - // balanceDisplayMode = settings.balanceDisplayMode; - // shouldSaveRecipientAddress = settings.shouldSaveRecipientAddress; - // allowBiometricalAuthentication = settings.allowBiometricalAuthentication; - // currentTheme = settings.currentTheme; - // pinCodeLength = settings.pinCodeLength; - // languageCode = settings.languageCode; - // appVersion = settings.appVersion; - // shouldShowYatPopup = settings.shouldShowYatPopup; - //} + if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) { + priority[WalletType.haven] = monero?.deserializeMoneroTransactionPriority( + raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!) ?? + priority[WalletType.haven]!; + } + if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) { + priority[WalletType.litecoin] = bitcoin?.deserializeLitecoinTransactionPriority( + sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!) ?? + priority[WalletType.litecoin]!; + } + + balanceDisplayMode = BalanceDisplayMode.deserialize( + raw: sharedPreferences + .getInt(PreferencesKey.currentBalanceDisplayModeKey)!); + shouldSaveRecipientAddress = + sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; + allowBiometricalAuthentication = sharedPreferences + .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? + allowBiometricalAuthentication; + disableExchange = sharedPreferences.getBool(PreferencesKey.disableExchangeKey) ?? disableExchange; + final legacyTheme = + (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) + ? ThemeType.dark.index + : ThemeType.bright.index; + currentTheme = ThemeList.deserialize( + raw: sharedPreferences.getInt(PreferencesKey.currentTheme) ?? + legacyTheme); + actionlistDisplayMode = ObservableList(); + actionlistDisplayMode.addAll(deserializeActionlistDisplayModes( + sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ?? + defaultActionsMode)); + var pinLength = sharedPreferences.getInt(PreferencesKey.currentPinLength); + // If no value + if (pinLength == null || pinLength == 0) { + pinLength = pinCodeLength; + } + pinCodeLength = pinLength; + + languageCode = sharedPreferences.getString(PreferencesKey.currentLanguageCode) ?? languageCode; + shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? shouldShowYatPopup; + + final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey); + final bitcoinElectrumServerId = sharedPreferences + .getInt(PreferencesKey.currentBitcoinElectrumSererIdKey); + final litecoinElectrumServerId = sharedPreferences + .getInt(PreferencesKey.currentLitecoinElectrumSererIdKey); + final havenNodeId = sharedPreferences + .getInt(PreferencesKey.currentHavenNodeIdKey); + final moneroNode = nodeSource.get(nodeId); + final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId); + final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId); + final havenNode = nodeSource.get(havenNodeId); + + if (moneroNode != null) { + nodes[WalletType.monero] = moneroNode; + } + + if (bitcoinElectrumServer != null) { + nodes[WalletType.bitcoin] = bitcoinElectrumServer; + } + + if (litecoinElectrumServer != null) { + nodes[WalletType.litecoin] = litecoinElectrumServer; + } + + if (havenNode != null) { + nodes[WalletType.haven] = havenNode; + } + } Future _saveCurrentNode(Node node, WalletType walletType) async { switch (walletType) { diff --git a/lib/utils/payment_request.dart b/lib/utils/payment_request.dart index 26244059f..88730f037 100644 --- a/lib/utils/payment_request.dart +++ b/lib/utils/payment_request.dart @@ -1,22 +1,25 @@ class PaymentRequest { - PaymentRequest(this.address, this.amount, this.note); + PaymentRequest(this.address, this.amount, this.note, this.scheme); - factory PaymentRequest.fromUri(Uri uri) { + factory PaymentRequest.fromUri(Uri? uri) { var address = ""; var amount = ""; var note = ""; + var scheme = ""; if (uri != null) { address = uri.path; amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? ""; note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? ""; + scheme = uri.scheme; } - return PaymentRequest(address, amount, note); + return PaymentRequest(address, amount, note, scheme); } final String address; final String amount; final String note; + final String scheme; } \ No newline at end of file diff --git a/lib/utils/route_aware.dart b/lib/utils/route_aware.dart new file mode 100644 index 000000000..28c72c4a4 --- /dev/null +++ b/lib/utils/route_aware.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:cake_wallet/main.dart'; + +class RouteAwareWidget extends StatefulWidget { + RouteAwareWidget( + {required this.child, + this.pushToWidget, + this.pushToNextWidget, + this.popWidget, + this.popNextWidget}); + + final Widget child; + final Function()? pushToWidget; + final Function()? pushToNextWidget; + final Function()? popWidget; + final Function()? popNextWidget; + + @override + State createState() => RouteAwareWidgetState(); +} + +class RouteAwareWidgetState extends State with RouteAware { + @override + void didChangeDependencies() { + super.didChangeDependencies(); + routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute); + } + + @override + void dispose() { + routeObserver.unsubscribe(this); + super.dispose(); + } + + @override + void didPush() { + if (widget.pushToWidget != null) { + widget.pushToWidget!(); + } + } + + @override + void didPushNext() { + if (widget.pushToNextWidget != null) { + widget.pushToNextWidget!(); + } + } + + @override + void didPop() { + if (widget.popWidget != null) { + widget.popWidget!(); + } + } + + @override + void didPopNext() { + if (widget.popNextWidget != null) { + widget.popNextWidget!(); + } + } + + @override + Widget build(BuildContext context) => widget.child; +} diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart new file mode 100644 index 000000000..f800e3418 --- /dev/null +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -0,0 +1,56 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +part 'advanced_privacy_settings_view_model.g.dart'; + +class AdvancedPrivacySettingsViewModel = AdvancedPrivacySettingsViewModelBase + with _$AdvancedPrivacySettingsViewModel; + +abstract class AdvancedPrivacySettingsViewModelBase with Store { + AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore) + : _addCustomNode = false { + settings = [ + SwitcherListItem( + title: S.current.disable_fiat, + value: () => _settingsStore.fiatApiMode == FiatApiMode.disabled, + onValueChange: (_, bool value) => setFiatMode(value), + ), + SwitcherListItem( + title: S.current.disable_exchange, + value: () => _settingsStore.disableExchange, + onValueChange: (_, bool value) { + _settingsStore.disableExchange = value; + }, + ), + SwitcherListItem( + title: S.current.add_custom_node, + value: () => _addCustomNode, + onValueChange: (_, bool value) => _addCustomNode = value, + ), + ]; + } + + late List settings; + + @observable + bool _addCustomNode = false; + + final WalletType type; + final SettingsStore _settingsStore; + + @computed + bool get addCustomNode => _addCustomNode; + + @action + void setFiatMode(bool value) { + if (value) { + _settingsStore.fiatApiMode = FiatApiMode.disabled; + return; + } + _settingsStore.fiatApiMode = FiatApiMode.enabled; + } +} diff --git a/lib/view_model/auth_view_model.dart b/lib/view_model/auth_view_model.dart index 29ef46b47..e50f4db0c 100644 --- a/lib/view_model/auth_view_model.dart +++ b/lib/view_model/auth_view_model.dart @@ -14,10 +14,12 @@ part 'auth_view_model.g.dart'; class AuthViewModel = AuthViewModelBase with _$AuthViewModel; abstract class AuthViewModelBase with Store { - AuthViewModelBase(this._authService, this._sharedPreferences, - this._settingsStore, this._biometricAuth) + AuthViewModelBase( + this._authService, this._sharedPreferences, this._settingsStore, this._biometricAuth) : _failureCounter = 0, - state = InitialExecutionState(); + state = InitialExecutionState() { + reaction((_) => state, _saveLastAuthTime); + } static const maxFailedLogins = 3; static const banTimeout = 180; // 3 minutes @@ -28,8 +30,7 @@ abstract class AuthViewModelBase with Store { int get pinLength => _settingsStore.pinCodeLength; - bool get isBiometricalAuthenticationAllowed => - _settingsStore.allowBiometricalAuthentication; + bool get isBiometricalAuthenticationAllowed => _settingsStore.allowBiometricalAuthentication; @observable int _failureCounter; @@ -114,8 +115,14 @@ abstract class AuthViewModelBase with Store { state = ExecutedSuccessfullyState(); } } - } catch(e) { + } catch (e) { state = FailureState(e.toString()); } } + + void _saveLastAuthTime(ExecutionState state) { + if (state is ExecutedSuccessfullyState) { + _authService.saveLastAuthTime(); + } + } } diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index ec145ad44..5b0187fc8 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -7,27 +7,26 @@ import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/utils/mobx.dart'; +import 'package:cw_core/crypto_currency.dart'; part 'contact_list_view_model.g.dart'; -class ContactListViewModel = ContactListViewModelBase - with _$ContactListViewModel; +class ContactListViewModel = ContactListViewModelBase with _$ContactListViewModel; abstract class ContactListViewModelBase with Store { - ContactListViewModelBase(this.contactSource, this.walletInfoSource) + ContactListViewModelBase(this.contactSource, this.walletInfoSource, this._currency) : contacts = ObservableList(), walletContacts = [] { walletInfoSource.values.forEach((info) { if (info.addresses?.isNotEmpty ?? false) { info.addresses?.forEach((address, label) { - final name = label.isNotEmpty - ? info.name + ' ($label)' - : info.name; + final name = label.isNotEmpty ? info.name + ' ($label)' : info.name; walletContacts.add(WalletContact( - address, - name, - walletTypeToCryptoCurrency(info.type))); + address, + name, + walletTypeToCryptoCurrency(info.type), + )); }); } }); @@ -41,7 +40,18 @@ abstract class ContactListViewModelBase with Store { final Box walletInfoSource; final ObservableList contacts; final List walletContacts; + final CryptoCurrency? _currency; StreamSubscription? _subscription; + bool get isEditable => _currency == null; + Future delete(ContactRecord contact) async => contact.original.delete(); + + @computed + List get contactsToShow => + contacts.where((element) => _currency == null || element.type == _currency).toList(); + + @computed + List get walletContactsToShow => + walletContacts.where((element) => _currency == null || element.type == _currency).toList(); } diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 75d107b89..33fe01ba9 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/balance.dart'; @@ -10,7 +11,6 @@ import 'package:cake_wallet/entities/calculate_fiat_amount.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; -import 'package:flutter/cupertino.dart'; import 'package:mobx/mobx.dart'; part 'balance_view_model.g.dart'; @@ -71,6 +71,9 @@ abstract class BalanceViewModelBase with Store { @computed BalanceDisplayMode get savedDisplayMode => settingsStore.balanceDisplayMode; + @computed + bool get isFiatDisabled => settingsStore.fiatApiMode == FiatApiMode.disabled; + @computed String get asset { final typeFormatted = walletTypeToString(appStore.wallet!.type); @@ -180,8 +183,8 @@ abstract class BalanceViewModelBase with Store { return MapEntry(key, BalanceRecord( availableBalance: '---', additionalBalance: '---', - fiatAdditionalBalance: '---', - fiatAvailableBalance: '---', + fiatAdditionalBalance: isFiatDisabled ? '' : '---', + fiatAvailableBalance: isFiatDisabled ? '' : '---', asset: key, formattedAssetTitle: _formatterAsset(key))); } @@ -192,17 +195,17 @@ abstract class BalanceViewModelBase with Store { // throw Exception('Price is null for: $key'); // } - final additionalFiatBalance = fiatCurrency.toString() - + ' ' + final additionalFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString() + + ' ' + _getFiatBalance( price: price, - cryptoAmount: value.formattedAdditionalBalance); + cryptoAmount: value.formattedAdditionalBalance)); - final availableFiatBalance = fiatCurrency.toString() - + ' ' + final availableFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString() + + ' ' + _getFiatBalance( price: price, - cryptoAmount: value.formattedAvailableBalance); + cryptoAmount: value.formattedAvailableBalance)); return MapEntry(key, BalanceRecord( availableBalance: value.formattedAvailableBalance, diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 7f11b0c2f..57720d92f 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -58,25 +58,44 @@ abstract class DashboardViewModelBase with Store { isShowThirdYatIntroduction = false, filterItems = { S.current.transactions: [ + FilterItem( + value: () => transactionFilterStore.displayAll, + caption: S.current.all_transactions, + onChanged: transactionFilterStore.toggleAll), FilterItem( value: () => transactionFilterStore.displayIncoming, caption: S.current.incoming, - onChanged: (value) => transactionFilterStore.toggleIncoming()), + onChanged:transactionFilterStore.toggleIncoming), FilterItem( value: () => transactionFilterStore.displayOutgoing, caption: S.current.outgoing, - onChanged: (value) => transactionFilterStore.toggleOutgoing()), + onChanged: transactionFilterStore.toggleOutgoing), // FilterItem( // value: () => false, // caption: S.current.transactions_by_date, // onChanged: null), ], S.current.trades: [ + FilterItem( + value: () => tradeFilterStore.displayAllTrades, + caption: S.current.all_trades, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.all)), FilterItem( value: () => tradeFilterStore.displayChangeNow, - caption: 'Change.NOW', - onChanged: (value) => tradeFilterStore + caption: ExchangeProviderDescription.changeNow.title, + onChanged: () => tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.changeNow)), + FilterItem( + value: () => tradeFilterStore.displaySideShift, + caption: ExchangeProviderDescription.sideShift.title, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.sideShift)), + FilterItem( + value: () => tradeFilterStore.displaySimpleSwap, + caption: ExchangeProviderDescription.simpleSwap.title, + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), ] }, subname = '', diff --git a/lib/view_model/dashboard/filter_item.dart b/lib/view_model/dashboard/filter_item.dart index 0230899b4..0944e899c 100644 --- a/lib/view_model/dashboard/filter_item.dart +++ b/lib/view_model/dashboard/filter_item.dart @@ -1,3 +1,5 @@ +import 'package:mobx/mobx.dart'; + class FilterItem { FilterItem({ required this.value, @@ -6,5 +8,5 @@ class FilterItem { bool Function() value; String caption; - Function(bool) onChanged; + Function onChanged; } \ No newline at end of file diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 796397429..10ddf25e4 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -72,6 +72,18 @@ abstract class ExchangeTradeViewModelBase with Store { ? '\n\n' + S.current.xrp_extra_info : ''; + @computed + String get pendingTransactionFiatAmountValueFormatted => + sendViewModel.isFiatDisabled + ? '' : sendViewModel.pendingTransactionFiatAmount + + ' ' + sendViewModel.fiat.title; + + @computed + String get pendingTransactionFeeFiatAmountFormatted => + sendViewModel.isFiatDisabled + ? '' : sendViewModel.pendingTransactionFeeFiatAmount + + ' ' + sendViewModel.fiat.title; + @observable ObservableList items; diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 725208324..5c1f696b8 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -6,8 +6,8 @@ import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; -import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart'; +import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; @@ -42,9 +42,8 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore, - this.tradesStore, this._settingsStore, this.sharedPreferences, this._settingsViewModel) + this.tradesStore, this._settingsStore, this.sharedPreferences) : _cryptoNumberFormat = NumberFormat(), - isReverse = false, isFixedRateMode = false, isReceiveAmountEntered = false, depositAmount = '', @@ -112,7 +111,11 @@ abstract class ExchangeViewModelBase with Store { loadLimits(); reaction( (_) => isFixedRateMode, - (Object _) => loadLimits()); + (Object _) { + loadLimits(); + _bestRate = 0; + _calculateBestRate(); + }); } final WalletBase wallet; @@ -189,6 +192,19 @@ abstract class ExchangeViewModelBase with Store { ObservableList get templates => _exchangeTemplateStore.templates; + + @computed + TransactionPriority get transactionPriority { + final priority = _settingsStore.priority[wallet.type]; + + if (priority == null) { + throw Exception('Unexpected type ${wallet.type.toString()}'); + } + + return priority; + } + + bool get hasAllAmount => wallet.type == WalletType.bitcoin && depositCurrency == wallet.currency; @@ -198,11 +214,11 @@ abstract class ExchangeViewModelBase with Store { switch (wallet.type) { case WalletType.monero: case WalletType.haven: - return _settingsViewModel.transactionPriority == monero!.getMoneroTransactionPrioritySlow(); + return transactionPriority == monero!.getMoneroTransactionPrioritySlow(); case WalletType.bitcoin: - return _settingsViewModel.transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow(); + return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow(); case WalletType.litecoin: - return _settingsViewModel.transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow(); + return transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow(); default: return false; } @@ -214,14 +230,10 @@ abstract class ExchangeViewModelBase with Store { Limits limits; - bool isReverse; - NumberFormat _cryptoNumberFormat; final SettingsStore _settingsStore; - final SettingsViewModel _settingsViewModel; - double _bestRate = 0.0; late Timer bestRateSync; @@ -247,7 +259,6 @@ abstract class ExchangeViewModelBase with Store { @action Future changeReceiveAmount({required String amount}) async { receiveAmount = amount; - isReverse = true; if (amount.isEmpty) { depositAmount = ''; @@ -272,7 +283,6 @@ abstract class ExchangeViewModelBase with Store { @action Future changeDepositAmount({required String amount}) async { depositAmount = amount; - isReverse = false; if (amount.isEmpty) { depositAmount = ''; @@ -296,14 +306,17 @@ abstract class ExchangeViewModelBase with Store { } Future _calculateBestRate() async { + final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1; + final result = await Future.wait( _tradeAvailableProviders - .map((element) => element.calculateAmount( + .where((element) => !isFixedRateMode || element.supportsFixedRate) + .map((element) => element.fetchRate( from: depositCurrency, to: receiveCurrency, - amount: 1, + amount: amount, isFixedRateMode: isFixedRateMode, - isReceiveAmount: false)) + isReceiveAmount: isFixedRateMode)) ); _sortedAvailableProviders.clear(); @@ -334,7 +347,7 @@ abstract class ExchangeViewModelBase with Store { ? depositCurrency : receiveCurrency; - double lowestMin = double.maxFinite; + double? lowestMin = double.maxFinite; double? highestMax = 0.0; for (var provider in selectedProviders) { @@ -349,8 +362,8 @@ abstract class ExchangeViewModelBase with Store { to: to, isFixedRateMode: isFixedRateMode); - if (tempLimits.min != null && tempLimits.min! < lowestMin) { - lowestMin = tempLimits.min!; + if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) { + lowestMin = tempLimits.min; } if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) { highestMax = tempLimits.max; @@ -360,7 +373,7 @@ abstract class ExchangeViewModelBase with Store { } } - if (lowestMin < double.maxFinite) { + if (lowestMin != double.maxFinite) { limits = Limits(min: lowestMin, max: highestMax); limitsState = LimitsLoadedSuccessfully(limits: limits); @@ -388,7 +401,7 @@ abstract class ExchangeViewModelBase with Store { settleAddress: receiveAddress, refundAddress: depositAddress, ); - amount = depositAmount; + amount = isFixedRateMode ? receiveAmount : depositAmount; } if (provider is SimpleSwapExchangeProvider) { @@ -399,7 +412,7 @@ abstract class ExchangeViewModelBase with Store { address: receiveAddress, refundAddress: depositAddress, ); - amount = depositAmount; + amount = isFixedRateMode ? receiveAmount : depositAmount; } if (provider is XMRTOExchangeProvider) { @@ -411,7 +424,7 @@ abstract class ExchangeViewModelBase with Store { address: receiveAddress, refundAddress: depositAddress, isBTCRequest: isReceiveAmountEntered); - amount = depositAmount; + amount = isFixedRateMode ? receiveAmount : depositAmount; } if (provider is ChangeNowExchangeProvider) { @@ -422,8 +435,8 @@ abstract class ExchangeViewModelBase with Store { toAmount: receiveAmount.replaceAll(',', '.'), refundAddress: depositAddress, address: receiveAddress, - isReverse: isReverse); - amount = isReverse ? receiveAmount : depositAmount; + isReverse: isFixedRateMode); + amount = isFixedRateMode ? receiveAmount : depositAmount; } if (provider is MorphTokenExchangeProvider) { @@ -433,13 +446,13 @@ abstract class ExchangeViewModelBase with Store { amount: depositAmount.replaceAll(',', '.'), refundAddress: depositAddress, address: receiveAddress); - amount = depositAmount; + amount = isFixedRateMode ? receiveAmount : depositAmount; } amount = amount.replaceAll(',', '.'); if (limitsState is LimitsLoadedSuccessfully) { - if (double.parse(amount) < limits.min!) { + if (limits.max != null && double.parse(amount) < limits.min!) { continue; } else if (limits.max != null && double.parse(amount) > limits.max!) { continue; diff --git a/lib/view_model/ionia/ionia_custom_redeem_view_model.dart b/lib/view_model/ionia/ionia_custom_redeem_view_model.dart index 963604c15..5776443ee 100644 --- a/lib/view_model/ionia/ionia_custom_redeem_view_model.dart +++ b/lib/view_model/ionia/ionia_custom_redeem_view_model.dart @@ -1,29 +1,51 @@ +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; +import 'package:cake_wallet/ionia/ionia_service.dart'; import 'package:mobx/mobx.dart'; part 'ionia_custom_redeem_view_model.g.dart'; + class IoniaCustomRedeemViewModel = IoniaCustomRedeemViewModelBase with _$IoniaCustomRedeemViewModel; abstract class IoniaCustomRedeemViewModelBase with Store { - IoniaCustomRedeemViewModelBase(this.giftCard) - : amount = 0; + IoniaCustomRedeemViewModelBase({ + required this.giftCard, + required this.ioniaService, + }) : amount = 0, + redeemState = InitialExecutionState(); final IoniaGiftCard giftCard; + final IoniaService ioniaService; + + @observable + ExecutionState redeemState; + @observable double amount; @computed - double get remaining => amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0; + double get remaining => + amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0; @computed - String get formattedRemaining => remaining.toStringAsFixed(2); + String get formattedRemaining => remaining.toStringAsFixed(2); @computed bool get disableRedeem => amount > giftCard.remainingAmount; @action - void updateAmount(String text){ - amount = text.isEmpty ? 0 : (double.parse(text.replaceAll(',', '.')) ?? 0); + void updateAmount(String text) { + amount = double.tryParse(text.replaceAll(',', '.')) ?? 0; } -} \ No newline at end of file + @action + Future addCustomRedeem() async { + try { + redeemState = IsExecutingState(); + await ioniaService.redeem(giftCardId: giftCard.id, amount: amount); + redeemState = ExecutedSuccessfullyState(); + } catch (e) { + redeemState = FailureState(e.toString()); + } + } +} diff --git a/lib/view_model/ionia/ionia_gift_card_details_view_model.dart b/lib/view_model/ionia/ionia_gift_card_details_view_model.dart index 8e0a2795b..cbf5ebc78 100644 --- a/lib/view_model/ionia/ionia_gift_card_details_view_model.dart +++ b/lib/view_model/ionia/ionia_gift_card_details_view_model.dart @@ -6,21 +6,19 @@ import 'package:device_display_brightness/device_display_brightness.dart'; part 'ionia_gift_card_details_view_model.g.dart'; -class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase with _$IoniaGiftCardDetailsViewModel; +class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase + with _$IoniaGiftCardDetailsViewModel; abstract class IoniaGiftCardDetailsViewModelBase with Store { - - IoniaGiftCardDetailsViewModelBase({ - required this.ioniaService, - required this.giftCard}) - : redeemState = InitialExecutionState(), - remainingAmount = giftCard.remainingAmount, - brightness = 0; + IoniaGiftCardDetailsViewModelBase({required this.ioniaService, required this.giftCard}) + : redeemState = InitialExecutionState(), + remainingAmount = giftCard.remainingAmount, + brightness = 0; final IoniaService ioniaService; - + double brightness; - + @observable IoniaGiftCard giftCard; @@ -35,21 +33,22 @@ abstract class IoniaGiftCardDetailsViewModelBase with Store { giftCard.remainingAmount = remainingAmount; try { redeemState = IsExecutingState(); - await ioniaService.redeem(giftCard); + await ioniaService.redeem(giftCardId: giftCard.id, amount: giftCard.remainingAmount); giftCard = await ioniaService.getGiftCard(id: giftCard.id); redeemState = ExecutedSuccessfullyState(); - } catch(e) { + } catch (e) { redeemState = FailureState(e.toString()); } } @action - void updateRemaining(double amount){ - remainingAmount = amount; + Future refeshCard() async { + giftCard = await ioniaService.getGiftCard(id: giftCard.id); + remainingAmount = giftCard.remainingAmount; } void increaseBrightness() async { brightness = await DeviceDisplayBrightness.getBrightness(); await DeviceDisplayBrightness.setBrightness(1.0); } -} \ No newline at end of file +} diff --git a/lib/view_model/node_list/node_create_or_edit_view_model.dart b/lib/view_model/node_list/node_create_or_edit_view_model.dart index 31b83faff..a8f184589 100644 --- a/lib/view_model/node_list/node_create_or_edit_view_model.dart +++ b/lib/view_model/node_list/node_create_or_edit_view_model.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; -import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/node.dart'; import 'package:cw_core/wallet_type.dart'; @@ -11,14 +11,15 @@ class NodeCreateOrEditViewModel = NodeCreateOrEditViewModelBase with _$NodeCreateOrEditViewModel; abstract class NodeCreateOrEditViewModelBase with Store { - NodeCreateOrEditViewModelBase(this._nodeSource, this._wallet) + NodeCreateOrEditViewModelBase(this._nodeSource, this._walletType, this._settingsStore) : state = InitialExecutionState(), connectionState = InitialExecutionState(), useSSL = false, address = '', port = '', login = '', - password = ''; + password = '', + trusted = false; @observable ExecutionState state; @@ -41,12 +42,15 @@ abstract class NodeCreateOrEditViewModelBase with Store { @observable bool useSSL; + @observable + bool trusted; + @computed bool get isReady => address.isNotEmpty && port.isNotEmpty; - bool get hasAuthCredentials => _wallet.type == WalletType.monero || - _wallet.type == WalletType.haven; + bool get hasAuthCredentials => _walletType == WalletType.monero || + _walletType == WalletType.haven; String get uri { var uri = address; @@ -58,8 +62,9 @@ abstract class NodeCreateOrEditViewModelBase with Store { return uri; } - final WalletBase _wallet; + final WalletType _walletType; final Box _nodeSource; + final SettingsStore _settingsStore; @action void reset() { @@ -68,16 +73,22 @@ abstract class NodeCreateOrEditViewModelBase with Store { login = ''; password = ''; useSSL = false; + trusted = false; } @action - Future save() async { + Future save({bool saveAsCurrent = false}) async { try { state = IsExecutingState(); final node = - Node(uri: uri, type: _wallet.type, login: login, password: password, - useSSL: useSSL); + Node(uri: uri, type: _walletType, login: login, password: password, + useSSL: useSSL, trusted: trusted); await _nodeSource.add(node); + + if (saveAsCurrent) { + _settingsStore.nodes[_walletType] = node; + } + state = ExecutedSuccessfullyState(); } catch (e) { state = FailureState(e.toString()); @@ -89,7 +100,7 @@ abstract class NodeCreateOrEditViewModelBase with Store { try { connectionState = IsExecutingState(); final node = - Node(uri: uri, type: _wallet.type, login: login, password: password); + Node(uri: uri, type: _walletType, login: login, password: password); final isAlive = await node.requestNode(); connectionState = ExecutedSuccessfullyState(payload: isAlive); } catch (e) { diff --git a/lib/view_model/restore_from_backup_view_model.dart b/lib/view_model/restore_from_backup_view_model.dart index d3542a668..403ae3ff1 100644 --- a/lib/view_model/restore_from_backup_view_model.dart +++ b/lib/view_model/restore_from_backup_view_model.dart @@ -35,7 +35,7 @@ abstract class RestoreFromBackupViewModelBase with Store { try { state = IsExecutingState(); - if (filePath?.isEmpty ?? true) { + if (filePath.isEmpty) { state = FailureState('Backup file is not selected.'); return; } @@ -48,8 +48,7 @@ abstract class RestoreFromBackupViewModelBase with Store { final store = getIt.get(); ReactionDisposer? reaction; - // FIX-ME: SettingsStore reload - // await store.settingsStore.reload(nodeSource: getIt.get>()); + await store.settingsStore.reload(nodeSource: getIt.get>()); reaction = autorun((_) { final wallet = store.wallet; diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index cdef6faba..b6604789a 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,10 +1,10 @@ import 'package:cake_wallet/entities/balance_display_mode.dart'; +import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cake_wallet/view_model/send/send_template_view_model.dart'; -import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/template.dart'; @@ -42,7 +42,8 @@ abstract class SendViewModelBase with Store { : state = InitialExecutionState(), currencies = _wallet.balance.keys.toList(), selectedCryptoCurrency = _wallet.currency, - outputs = ObservableList() { + outputs = ObservableList(), + fiatFromSettings = _settingsStore.fiatCurrency { final priority = _settingsStore.priority[_wallet.type]; final priorities = priorityForWalletType(_wallet.type); @@ -133,11 +134,26 @@ abstract class SendViewModelBase with Store { Validator get textValidator => TextValidator(); + final FiatCurrency fiatFromSettings; + @observable PendingTransaction? pendingTransaction; @computed - String get balance => balanceViewModel.availableBalance ?? '0.0'; + String get balance => balanceViewModel.availableBalance; + + @computed + bool get isFiatDisabled => balanceViewModel.isFiatDisabled; + + @computed + String get pendingTransactionFiatAmountFormatted => + isFiatDisabled ? '' : pendingTransactionFiatAmount + + ' ' + fiat.title; + + @computed + String get pendingTransactionFeeFiatAmountFormatted => + isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + + ' ' + fiat.title; @computed bool get isReadyForSend => _wallet.syncStatus is SyncedSyncStatus; @@ -164,8 +180,13 @@ abstract class SendViewModelBase with Store { WalletType get walletType => _wallet.type; + String? get walletCurrencyName => _wallet.currency.name?.toLowerCase(); + bool get hasCurrecyChanger => walletType == WalletType.haven; + @computed + FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; + final WalletBase _wallet; final SettingsStore _settingsStore; final SendTemplateViewModel sendTemplateViewModel; @@ -208,7 +229,7 @@ abstract class SendViewModelBase with Store { state = TransactionCommitting(); await pendingTransaction!.commit(); - if (pendingTransaction!.id?.isNotEmpty ?? false) { + if (pendingTransaction!.id.isNotEmpty) { _settingsStore.shouldSaveRecipientAddress ? await transactionDescriptionBox.add(TransactionDescription( id: pendingTransaction!.id, @@ -283,4 +304,12 @@ abstract class SendViewModelBase with Store { bool _isEqualCurrency(String currency) => currency.toLowerCase() == _wallet.currency.title.toLowerCase(); + + @action + void onClose() => + _settingsStore.fiatCurrency = fiatFromSettings; + + @action + void setFiatCurrency(FiatCurrency fiat) => + _settingsStore.fiatCurrency = fiat; } diff --git a/lib/view_model/settings/display_settings_view_model.dart b/lib/view_model/settings/display_settings_view_model.dart new file mode 100644 index 000000000..bac6d9994 --- /dev/null +++ b/lib/view_model/settings/display_settings_view_model.dart @@ -0,0 +1,61 @@ +import 'package:cake_wallet/entities/balance_display_mode.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; + +part 'display_settings_view_model.g.dart'; + +class DisplaySettingsViewModel = DisplaySettingsViewModelBase with _$DisplaySettingsViewModel; + +abstract class DisplaySettingsViewModelBase with Store { + DisplaySettingsViewModelBase( + this._settingsStore, + ); + + final SettingsStore _settingsStore; + + @computed + FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; + + @computed + String get languageCode => _settingsStore.languageCode; + + @computed + BalanceDisplayMode get balanceDisplayMode => _settingsStore.balanceDisplayMode; + + @computed + bool get shouldDisplayBalance => balanceDisplayMode == BalanceDisplayMode.displayableBalance; + + @computed + ThemeBase get theme => _settingsStore.currentTheme; + + @computed + bool get disabledFiatApiMode => _settingsStore.fiatApiMode == FiatApiMode.disabled; + + @action + void setBalanceDisplayMode(BalanceDisplayMode value) => _settingsStore.balanceDisplayMode = value; + + @action + void setShouldDisplayBalance(bool value) { + if (value) { + _settingsStore.balanceDisplayMode = BalanceDisplayMode.displayableBalance; + } else { + _settingsStore.balanceDisplayMode = BalanceDisplayMode.hiddenBalance; + } + } + + @action + void onLanguageSelected(String code) { + _settingsStore.languageCode = code; + } + + @action + void setTheme(ThemeBase newTheme) { + _settingsStore.currentTheme = newTheme; + } + + @action + void setFiatCurrency(FiatCurrency value) => _settingsStore.fiatCurrency = value; +} diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart new file mode 100644 index 000000000..c57af50d9 --- /dev/null +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -0,0 +1,64 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cw_core/balance.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:mobx/mobx.dart'; +import 'package:package_info/package_info.dart'; + +part 'other_settings_view_model.g.dart'; + +class OtherSettingsViewModel = OtherSettingsViewModelBase with _$OtherSettingsViewModel; + +abstract class OtherSettingsViewModelBase with Store { + OtherSettingsViewModelBase(this._settingsStore, this._wallet) + : walletType = _wallet.type, + currentVersion = '' { + PackageInfo.fromPlatform() + .then((PackageInfo packageInfo) => currentVersion = packageInfo.version); + + final priority = _settingsStore.priority[_wallet.type]; + final priorities = priorityForWalletType(_wallet.type); + + if (!priorities.contains(priority)) { + _settingsStore.priority[_wallet.type] = priorities.first; + } + } + + final WalletType walletType; + final WalletBase, TransactionInfo> _wallet; + + @observable + String currentVersion; + + final SettingsStore _settingsStore; + + @computed + TransactionPriority get transactionPriority { + final priority = _settingsStore.priority[walletType]; + + if (priority == null) { + throw Exception('Unexpected type ${walletType.toString()}'); + } + + return priority; + } + + String getDisplayPriority(dynamic priority) { + final _priority = priority as TransactionPriority; + + if (_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin) { + final rate = bitcoin!.getFeeRate(_wallet, _priority); + return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate); + } + + return priority.toString(); + } + + void onDisplayPrioritySelected(TransactionPriority priority) => + _settingsStore.priority[_wallet.type] = priority; +} diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart new file mode 100644 index 000000000..f8c3e5b50 --- /dev/null +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -0,0 +1,38 @@ +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; + +part 'privacy_settings_view_model.g.dart'; + +class PrivacySettingsViewModel = PrivacySettingsViewModelBase with _$PrivacySettingsViewModel; + +abstract class PrivacySettingsViewModelBase with Store { + PrivacySettingsViewModelBase(this._settingsStore); + + final SettingsStore _settingsStore; + + @computed + bool get disableExchange => _settingsStore.disableExchange; + + @computed + bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; + + @computed + bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; + + @action + void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; + + @action + void setEnableExchange(bool value) => _settingsStore.disableExchange = value; + + @action + void setFiatMode(bool value) { + if (value) { + _settingsStore.fiatApiMode = FiatApiMode.disabled; + return; + } + _settingsStore.fiatApiMode = FiatApiMode.enabled; + } + +} diff --git a/lib/view_model/settings/security_settings_view_model.dart b/lib/view_model/settings/security_settings_view_model.dart new file mode 100644 index 000000000..c48223af6 --- /dev/null +++ b/lib/view_model/settings/security_settings_view_model.dart @@ -0,0 +1,41 @@ +import 'package:cake_wallet/core/auth_service.dart'; +import 'package:cake_wallet/entities/biometric_auth.dart'; +import 'package:cake_wallet/entities/pin_code_required_duration.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:mobx/mobx.dart'; + +part 'security_settings_view_model.g.dart'; + +class SecuritySettingsViewModel = SecuritySettingsViewModelBase with _$SecuritySettingsViewModel; + +abstract class SecuritySettingsViewModelBase with Store { + SecuritySettingsViewModelBase( + this._settingsStore, + this._authService, + ) : _biometricAuth = BiometricAuth(); + + final BiometricAuth _biometricAuth; + final SettingsStore _settingsStore; + final AuthService _authService; + + @computed + bool get allowBiometricalAuthentication => _settingsStore.allowBiometricalAuthentication; + + @computed + PinCodeRequiredDuration get pinCodeRequiredDuration => _settingsStore.pinTimeOutDuration; + + @action + Future biometricAuthenticated() async { + return await _biometricAuth.canCheckBiometrics() && await _biometricAuth.isAuthenticated(); + } + + @action + void setAllowBiometricalAuthentication(bool value) => + _settingsStore.allowBiometricalAuthentication = value; + + @action + setPinCodeRequiredDuration(PinCodeRequiredDuration duration) => + _settingsStore.pinTimeOutDuration = duration; + + bool checkPinCodeRiquired() => _authService.requireAuth(); +} diff --git a/lib/view_model/settings/settings_view_model.dart b/lib/view_model/settings/settings_view_model.dart deleted file mode 100644 index f85d2c459..000000000 --- a/lib/view_model/settings/settings_view_model.dart +++ /dev/null @@ -1,342 +0,0 @@ -import 'package:cake_wallet/entities/language_service.dart'; -import 'package:cake_wallet/store/yat/yat_store.dart'; -import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:mobx/mobx.dart'; -import 'package:package_info/package_info.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cw_core/wallet_base.dart'; -import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/entities/biometric_auth.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/entities/balance_display_mode.dart'; -import 'package:cake_wallet/entities/fiat_currency.dart'; -import 'package:cw_core/node.dart'; -import 'package:cake_wallet/monero/monero.dart'; -import 'package:cake_wallet/haven/haven.dart'; -import 'package:cake_wallet/entities/action_list_display_mode.dart'; -import 'package:cake_wallet/view_model/settings/version_list_item.dart'; -import 'package:cake_wallet/view_model/settings/picker_list_item.dart'; -import 'package:cake_wallet/view_model/settings/regular_list_item.dart'; -import 'package:cake_wallet/view_model/settings/settings_list_item.dart'; -import 'package:cake_wallet/view_model/settings/switcher_list_item.dart'; -import 'package:cake_wallet/src/screens/auth/auth_page.dart'; -import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cw_core/transaction_history.dart'; -import 'package:cw_core/balance.dart'; -import 'package:cw_core/transaction_info.dart'; -import 'package:cw_core/transaction_priority.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; -import 'package:cake_wallet/themes/theme_list.dart'; -import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; - -part 'settings_view_model.g.dart'; - -class SettingsViewModel = SettingsViewModelBase with _$SettingsViewModel; - -List priorityForWalletType(WalletType type) { - switch (type) { - case WalletType.monero: - return monero!.getTransactionPriorities(); - case WalletType.bitcoin: - return bitcoin!.getTransactionPriorities(); - case WalletType.litecoin: - return bitcoin!.getLitecoinTransactionPriorities(); - case WalletType.haven: - return haven!.getTransactionPriorities(); - default: - return []; - } -} - -abstract class SettingsViewModelBase with Store { - SettingsViewModelBase( - this._settingsStore, - this._yatStore, - WalletBase, - TransactionInfo> - wallet) - : itemHeaders = {}, - _walletType = wallet.type, - _biometricAuth = BiometricAuth(), - sections = >[], - currentVersion = '' { - PackageInfo.fromPlatform().then( - (PackageInfo packageInfo) => currentVersion = packageInfo.version); - - final priority = _settingsStore.priority[wallet.type]; - final priorities = priorityForWalletType(wallet.type); - - if (!priorities.contains(priority)) { - _settingsStore.priority[wallet.type] = priorities.first; - } - - //var connectYatUrl = YatLink.baseUrl + YatLink.signInSuffix; - //final connectYatUrlParameters = - // _yatStore.defineQueryParameters(); - - //if (connectYatUrlParameters.isNotEmpty) { - // connectYatUrl += YatLink.queryParameter + connectYatUrlParameters; - //} - - //var manageYatUrl = YatLink.baseUrl + YatLink.managePath; - //final manageYatUrlParameters = - // _yatStore.defineQueryParameters(); - - //if (manageYatUrlParameters.isNotEmpty) { - // manageYatUrl += YatLink.queryParameter + manageYatUrlParameters; - //} - - //var createNewYatUrl = YatLink.startFlowUrl; - //final createNewYatUrlParameters = - // _yatStore.defineQueryParameters(); - - //if (createNewYatUrlParameters.isNotEmpty) { - // createNewYatUrl += '?sub1=' + createNewYatUrlParameters; - //} - - - sections = [ - [ - SwitcherListItem( - title: S.current.settings_display_balance, - value: () => balanceDisplayMode == BalanceDisplayMode.displayableBalance, - onValueChange: (_, bool value) { - if (value) { - _settingsStore.balanceDisplayMode = BalanceDisplayMode.displayableBalance; - } else { - _settingsStore.balanceDisplayMode = BalanceDisplayMode.hiddenBalance; - } - }, - ), - if (!isHaven) - PickerListItem( - title: S.current.settings_currency, - searchHintText: S.current.search_currency, - items: FiatCurrency.all, - selectedItem: () => fiatCurrency, - onItemSelected: (FiatCurrency currency) => - setFiatCurrency(currency), - images: FiatCurrency.all.map( - (e) => Image.asset("assets/images/flags/${e.countryCode}.png")) - .toList(), - isGridView: true, - matchingCriteria: (FiatCurrency currency, String searchText) { - return currency.title.toLowerCase().contains(searchText) || currency.fullName.toLowerCase().contains(searchText); - }, - ), - PickerListItem( - title: S.current.settings_fee_priority, - items: priorityForWalletType(wallet.type), - displayItem: (dynamic priority) { - final _priority = priority as TransactionPriority; - - if (wallet.type == WalletType.bitcoin - || wallet.type == WalletType.litecoin) { - final rate = bitcoin!.getFeeRate(wallet, _priority); - return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate); - } - - return priority.toString(); - }, - selectedItem: () => transactionPriority, - onItemSelected: (TransactionPriority priority) => - _settingsStore.priority[wallet.type] = priority), - SwitcherListItem( - title: S.current.settings_save_recipient_address, - value: () => shouldSaveRecipientAddress, - onValueChange: (_, bool value) => - setShouldSaveRecipientAddress(value)) - ], - [ - RegularListItem( - title: S.current.settings_change_pin, - handler: (BuildContext context) { - Navigator.of(context).pushNamed(Routes.auth, arguments: - (bool isAuthenticatedSuccessfully, AuthPageState auth) { - auth.close( - route: isAuthenticatedSuccessfully ? Routes.setupPin : null, - arguments: (PinCodeState setupPinContext, - String _) { - setupPinContext.close(); - }, - ); - }); - }), - PickerListItem( - title: S.current.settings_change_language, - searchHintText: S.current.search_language, - items: LanguageService.list.keys.toList(), - displayItem: (dynamic code) { - return LanguageService.list[code] ?? ''; - }, - selectedItem: () => _settingsStore.languageCode, - onItemSelected: (String code) { - _settingsStore.languageCode = code; - }, - images: LanguageService.list.keys.map( - (e) => Image.asset("assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) - .toList(), - matchingCriteria: (String code, String searchText) { - return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; - }, - ), - SwitcherListItem( - title: S.current.settings_allow_biometrical_authentication, - value: () => allowBiometricalAuthentication, - onValueChange: (BuildContext context, bool value) { - if (value) { - Navigator.of(context).pushNamed(Routes.auth, arguments: - (bool isAuthenticatedSuccessfully, - AuthPageState auth) async { - if (isAuthenticatedSuccessfully) { - if (await _biometricAuth.canCheckBiometrics() && - await _biometricAuth.isAuthenticated()) { - setAllowBiometricalAuthentication( - isAuthenticatedSuccessfully); - } - } else { - setAllowBiometricalAuthentication( - isAuthenticatedSuccessfully); - } - - auth.close(); - }); - } else { - setAllowBiometricalAuthentication(value); - } - }), - SwitcherListItem( - title: S.current.disable_exchange, - value: () => _settingsStore.disableExchange, - onValueChange: (BuildContext context, bool value) { - _settingsStore.disableExchange = value; - }), - ChoicesListItem( - title: S.current.color_theme, - items: ThemeList.all, - selectedItem: theme, - onItemSelected: (ThemeBase theme) => _settingsStore.currentTheme = theme, - ), - ], - //[ - //if (_yatStore.emoji.isNotEmpty) ...[ - // LinkListItem( - // title: S.current.manage_yats, - // link: manageYatUrl, - // linkTitle: ''), - //] else ...[ - //LinkListItem( - // title: S.current.connect_yats, - // link: connectYatUrl, - // linkTitle: ''), - //LinkListItem( - // title: 'Create new Yats', - // link: createNewYatUrl, - // linkTitle: '') - //] - //], - [ - RegularListItem( - title: S.current.settings_terms_and_conditions, - handler: (BuildContext context) => - Navigator.of(context).pushNamed(Routes.readDisclaimer), - ) - ], - [VersionListItem(title: currentVersion)] - ]; - } - - @observable - String currentVersion; - - @computed - Node get node => _settingsStore.getCurrentNode(_walletType); - - @computed - FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; - - @computed - ObservableList get actionlistDisplayMode => - _settingsStore.actionlistDisplayMode; - - @computed - TransactionPriority get transactionPriority { - final priority = _settingsStore.priority[_walletType]; - - if (priority == null) { - throw Exception('Unexpected type ${_walletType.toString()}'); - } - - return priority; - } - - @computed - BalanceDisplayMode get balanceDisplayMode => - _settingsStore.balanceDisplayMode; - - @computed - bool get shouldSaveRecipientAddress => - _settingsStore.shouldSaveRecipientAddress; - - @computed - bool get allowBiometricalAuthentication => - _settingsStore.allowBiometricalAuthentication; - - @computed - ThemeBase get theme => _settingsStore.currentTheme; - - bool get isBitcoinBuyEnabled => _settingsStore.isBitcoinBuyEnabled; - - final Map itemHeaders; - List> sections; - final SettingsStore _settingsStore; - final YatStore _yatStore; - final WalletType _walletType; - final BiometricAuth _biometricAuth; - - @action - void setBalanceDisplayMode(BalanceDisplayMode value) => - _settingsStore.balanceDisplayMode = value; - - @action - void setFiatCurrency(FiatCurrency value) => - _settingsStore.fiatCurrency = value; - - @action - void setShouldSaveRecipientAddress(bool value) => - _settingsStore.shouldSaveRecipientAddress = value; - - @action - void setAllowBiometricalAuthentication(bool value) => - _settingsStore.allowBiometricalAuthentication = value; - - @action - void toggleTransactionsDisplay() => - actionlistDisplayMode.contains(ActionListDisplayMode.transactions) - ? _hideTransaction() - : _showTransaction(); - - @action - void toggleTradesDisplay() => - actionlistDisplayMode.contains(ActionListDisplayMode.trades) - ? _hideTrades() - : _showTrades(); - - @action - void _hideTransaction() => - actionlistDisplayMode.remove(ActionListDisplayMode.transactions); - - @action - void _hideTrades() => - actionlistDisplayMode.remove(ActionListDisplayMode.trades); - - @action - void _showTransaction() => - actionlistDisplayMode.add(ActionListDisplayMode.transactions); - - @action - void _showTrades() => actionlistDisplayMode.add(ActionListDisplayMode.trades); -} diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index f2985463c..5a1f78774 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -142,17 +142,5 @@ abstract class TradeDetailsViewModelBase with Store { items.add(TrackTradeListItem( title: 'Track', value: buildURL, onTap: () => launch(buildURL))); } - - if (trade.createdAt != null) { - items.add(StandartListItem( - title: S.current.trade_details_created_at, - value: trade.createdAt != null ? dateFormat.format(trade.createdAt!).toString() : '')); - } - - if (trade.from != null && trade.to != null) { - items.add(StandartListItem( - title: S.current.trade_details_pair, - value: '${trade.from.toString()} → ${trade.to.toString()}')); - } } } diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index 8992071fd..f8729715b 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -184,7 +184,7 @@ abstract class TransactionDetailsViewModelBase with Store { case WalletType.monero: return 'https://monero.com/tx/${txId}'; case WalletType.bitcoin: - return 'https://www.blockchain.com/btc/tx/${txId}'; + return 'https://mempool.space/tx/${txId}'; case WalletType.litecoin: return 'https://blockchair.com/litecoin/transaction/${txId}'; case WalletType.haven: @@ -199,7 +199,7 @@ abstract class TransactionDetailsViewModelBase with Store { case WalletType.monero: return S.current.view_transaction_on + 'Monero.com'; case WalletType.bitcoin: - return S.current.view_transaction_on + 'Blockchain.com'; + return S.current.view_transaction_on + 'mempool.space'; case WalletType.litecoin: return S.current.view_transaction_on + 'Blockchair.com'; case WalletType.haven: diff --git a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart index c9d2c77f5..43db6099f 100644 --- a/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart @@ -30,7 +30,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store { {required WalletBase wallet, dynamic item}) : isEdit = item != null, state = AddressEditOrCreateStateInitial(), - label = item?.name as String? ?? '', + label = item?.fullName as String? ?? '', _item = item, _wallet = wallet; diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index 0bbc68748..6d63675ba 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -1,5 +1,5 @@ +import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; -import 'package:cake_wallet/view_model/wallet_new_vm.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/di.dart'; @@ -15,9 +15,12 @@ part 'wallet_list_view_model.g.dart'; class WalletListViewModel = WalletListViewModelBase with _$WalletListViewModel; abstract class WalletListViewModelBase with Store { - WalletListViewModelBase(this._walletInfoSource, this._appStore, - this._walletLoadingService) - : wallets = ObservableList() { + WalletListViewModelBase( + this._walletInfoSource, + this._appStore, + this._walletLoadingService, + this._authService, + ) : wallets = ObservableList() { _updateList(); } @@ -27,6 +30,7 @@ abstract class WalletListViewModelBase with Store { final AppStore _appStore; final Box _walletInfoSource; final WalletLoadingService _walletLoadingService; + final AuthService _authService; WalletType get currentWalletType => _appStore.wallet!.type; @@ -47,12 +51,20 @@ abstract class WalletListViewModelBase with Store { void _updateList() { wallets.clear(); - wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem( - name: info.name, - type: info.type, - key: info.key, - isCurrent: info.name == _appStore.wallet!.name && - info.type == _appStore.wallet!.type, - isEnabled: availableWalletTypes.contains(info.type)))); + wallets.addAll( + _walletInfoSource.values.map( + (info) => WalletListItem( + name: info.name, + type: info.type, + key: info.key, + isCurrent: info.name == _appStore.wallet!.name && info.type == _appStore.wallet!.type, + isEnabled: availableWalletTypes.contains(info.type), + ), + ), + ); + } + + bool checkIfAuthRequired() { + return _authService.requireAuth(); } } diff --git a/model_generator.sh b/model_generator.sh new file mode 100644 index 000000000..f4ef8bdad --- /dev/null +++ b/model_generator.sh @@ -0,0 +1,5 @@ +cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. +cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. +cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. +cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. +flutter packages pub run build_runner build --delete-conflicting-outputs \ No newline at end of file diff --git a/pubspec_base.yaml b/pubspec_base.yaml index fe5e6efc4..7981c6690 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -34,7 +34,7 @@ dependencies: local_auth: ^2.1.0 package_info: ^2.0.0 #package_info_plus: ^1.4.2 - devicelocale: ^0.5.4 + devicelocale: ^0.4.3 auto_size_text: ^3.0.0 dotted_border: ^2.0.0+2 smooth_page_indicator: ^1.0.0+2 diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index f5170b84e..9ba0a7553 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -48,7 +48,7 @@ "outgoing" : "Ausgehend", "transactions_by_date" : "Transaktionen nach Datum", "trades" : "Börsen", - "filters" : "Filter", + "filter_by": "Filtern nach", "today" : "Heute", "yesterday" : "Gestern", "received" : "Empfangen", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "GETRENNT", "sync_status_connecting" : "VERBINDEN", "sync_status_connected" : "VERBUNDEN", + "sync_status_attempting_sync" : "SYNC VERSUCHEN", "transaction_priority_slow" : "Langsam", @@ -436,6 +437,7 @@ "provider_error" : "${provider}-Fehler", "use_ssl" : "SSL verwenden", + "trusted" : "Vertrauenswürdige", "color_theme" : "Farbthema", "light_theme" : "Hell", @@ -537,7 +539,9 @@ "wallet_name_exists": "Wallet mit diesem Namen existiert bereits", "market_place": "Marktplatz", "cake_pay_title": "Cake Pay-Geschenkkarten", - "cake_pay_subtitle": "Geschenkkarten kaufen und sofort einlösen", + "cake_pay_subtitle": "Kaufen Sie ermäßigte Geschenkkarten (nur USA)", + "cake_pay_web_cards_title": "Cake Pay-Webkarten", + "cake_pay_web_cards_subtitle": "Kaufen Sie weltweit Prepaid-Karten und Geschenkkarten", "about_cake_pay": "Mit Cake Pay können Sie ganz einfach Geschenkkarten mit virtuellen Vermögenswerten kaufen, die Sie sofort bei über 150.000 Händlern in den Vereinigten Staaten ausgeben können.", "cake_pay_account_note": "Melden Sie sich nur mit einer E-Mail-Adresse an, um Karten anzuzeigen und zu kaufen. Einige sind sogar mit Rabatt erhältlich!", "already_have_account": "Sie haben bereits ein Konto?", @@ -650,7 +654,30 @@ "low_fee_alert": "Sie verwenden derzeit eine niedrige Netzwerkgebührenpriorität. Dies kann zu langen Wartezeiten, unterschiedlichen Kursen oder stornierten Trades führen. Wir empfehlen, für ein besseres Erlebnis eine höhere Gebühr festzulegen.", "ignor": "Ignorieren", "use_suggested": "Vorgeschlagen verwenden", - "do_not_share_warning_text" : "Teilen Sie diese nicht mit anderen, einschließlich des Supports.\n\nSie werden Ihr Geld stehlen!", + "do_not_share_warning_text" : "Teilen Sie diese nicht mit anderen, einschließlich Support.\n\nIhr Geld kann und wird gestohlen werden!", "help": "hilfe", - "disable_exchange": "Exchange deaktivieren" + "all_transactions": "Alle Transaktionen", + "all_trades": "Alle Gewerke", + "connection_sync": "Verbindung und Synchronisierung", + "security_and_backup": "Sicherheit und Datensicherung", + "create_backup": "Backup erstellen", + "privacy_settings": "Datenschutzeinstellungen", + "privacy": "Datenschutz", + "display_settings": "Anzeigeeinstellungen", + "other_settings": "Andere Einstellungen", + "require_pin_after": "PIN anfordern nach", + "always": "immer", + "minutes_to_pin_code": "${minute} Minuten", + "disable_exchange": "Exchange deaktivieren", + "advanced_privacy_settings": "Erweiterte Datenschutzeinstellungen", + "settings_can_be_changed_later": "Diese Einstellungen können später in den App-Einstellungen geändert werden", + "add_custom_node": "Neuen benutzerdefinierten Knoten hinzufügen", + "disable_fiat": "Fiat deaktivieren", + "fiat_api": "Fiat API", + "disabled": "Deaktiviert", + "enabled": "Ermöglicht", + "tor_only": "Nur Tor", + "unmatched_currencies": "Die Währung Ihres aktuellen Wallets stimmt nicht mit der des gescannten QR überein", + "contact_list_contacts": "Kontakte", + "contact_list_wallets": "Meine Geldbörsen" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index b62dbd984..e9a0c4d49 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -48,7 +48,7 @@ "outgoing" : "Outgoing", "transactions_by_date" : "Transactions by date", "trades" : "Trades", - "filters" : "Filter", + "filter_by": "Filter by", "today" : "Today", "yesterday" : "Yesterday", "received" : "Received", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "DISCONNECTED", "sync_status_connecting" : "CONNECTING", "sync_status_connected" : "CONNECTED", + "sync_status_attempting_sync" : "ATTEMPTING SYNC", "transaction_priority_slow" : "Slow", @@ -436,6 +437,7 @@ "provider_error" : "${provider} error", "use_ssl" : "Use SSL", + "trusted" : "Trusted", "color_theme" : "Color theme", "light_theme" : "Light", @@ -537,7 +539,9 @@ "wallet_name_exists": "A wallet with that name already exists. Please choose a different name or rename the other wallet first.", "market_place": "Marketplace", "cake_pay_title": "Cake Pay Gift Cards", - "cake_pay_subtitle": "Buy gift cards and redeem instantly", + "cake_pay_subtitle": "Buy discounted gift cards (USA only)", + "cake_pay_web_cards_title": "Cake Pay Web Cards", + "cake_pay_web_cards_subtitle": "Buy worldwide prepaid cards and gift cards", "about_cake_pay": "Cake Pay allows you to easily buy gift cards with virtual assets, spendable instantly at over 150,000 merchants in the United States.", "cake_pay_account_note": "Sign up with just an email address to see and purchase cards. Some are even available at a discount!", "already_have_account": "Already have an account?", @@ -650,7 +654,30 @@ "low_fee_alert": "You currently are using a low network fee priority. This could cause long waits, different rates, or canceled trades. We recommend setting a higher fee for a better experience.", "ignor": "Ignore", "use_suggested": "Use Suggested", - "do_not_share_warning_text" : "Do not share these with anyone else, including support.\n\nThey will steal your money!", + "do_not_share_warning_text" : "Do not share these with anyone else, including support.\n\nYour funds can and will be stolen!", "help": "help", - "disable_exchange": "Disable exchange" + "all_transactions": "All transactions", + "all_trades": "All trades", + "connection_sync": "Connection and sync", + "security_and_backup": "Security and backup", + "create_backup": "Create backup", + "privacy_settings": "Privacy settings", + "privacy": "Privacy", + "display_settings": "Display settings", + "other_settings": "Other settings", + "require_pin_after": "Require PIN after", + "always": "Always", + "minutes_to_pin_code": "${minute} minutes", + "disable_exchange": "Disable exchange", + "advanced_privacy_settings": "Advanced Privacy Settings", + "settings_can_be_changed_later": "These settings can be changed later in the app settings", + "add_custom_node": "Add New Custom Node", + "disable_fiat": "Disable fiat", + "fiat_api": "Fiat API", + "disabled": "Disabled", + "enabled": "Enabled", + "tor_only": "Tor only", + "unmatched_currencies": "Your current wallet's currency does not match that of the scanned QR", + "contact_list_contacts": "Contacts", + "contact_list_wallets": "My Wallets" } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index beff1af8d..10c866676 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -48,7 +48,7 @@ "outgoing" : "Saliente", "transactions_by_date" : "Transacciones por fecha", "trades" : "Cambios", - "filters" : "Filtrar", + "filter_by": "Filtrado por", "today" : "Hoy", "yesterday" : "Ayer", "received" : "Recibido", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "DESCONECTADO", "sync_status_connecting" : "CONECTANDO", "sync_status_connected" : "CONECTADO", + "sync_status_attempting_sync" : "INTENTAR SINCRONIZAR", "transaction_priority_slow" : "Lento", @@ -436,6 +437,7 @@ "provider_error" : "${provider} error", "use_ssl" : "Utilice SSL", + "trusted" : "de confianza", "color_theme" : "Tema de color", "light_theme" : "Ligera", @@ -537,7 +539,9 @@ "wallet_name_exists": "Wallet con ese nombre ya ha existido", "market_place": "Mercado", "cake_pay_title": "Tarjetas de regalo Cake Pay", - "cake_pay_subtitle": "Compra tarjetas de regalo y canjéalas al instante", + "cake_pay_subtitle": "Compre tarjetas de regalo con descuento (solo EE. UU.)", + "cake_pay_web_cards_title": "Tarjetas Web Cake Pay", + "cake_pay_web_cards_subtitle": "Compre tarjetas de prepago y tarjetas de regalo en todo el mundo", "about_cake_pay": "Cake Pay le permite comprar fácilmente tarjetas de regalo con activos virtuales, gastables instantáneamente en más de 150 000 comerciantes en los Estados Unidos.", "cake_pay_account_note": "Regístrese con solo una dirección de correo electrónico para ver y comprar tarjetas. ¡Algunas incluso están disponibles con descuento!", "already_have_account": "¿Ya tienes una cuenta?", @@ -650,7 +654,30 @@ "low_fee_alert": "Actualmente está utilizando una prioridad de tarifa de red baja. Esto podría causar largas esperas, tarifas diferentes o transacciones canceladas. Recomendamos establecer una tarifa más alta para una mejor experiencia.", "ignor": "Pasar por alto", "use_suggested": "Usar sugerido", - "do_not_share_warning_text" : "No comparta estos con nadie más, incluido el soporte.\n\n¡Te robarán tu dinero!", + "do_not_share_warning_text" : "No comparta estos con nadie más, incluido el soporte.\n\n¡Sus fondos pueden ser y serán robados!", "help": "ayuda", - "disable_exchange": "Deshabilitar intercambio" + "all_transactions": "Todas las transacciones", + "all_trades": "Todos los oficios", + "connection_sync": "Conexión y sincronización", + "security_and_backup": "Seguridad y respaldo", + "create_backup": "Crear copia de seguridad", + "privacy_settings": "Configuración de privacidad", + "privacy": "Privacidad", + "display_settings": "Configuración de pantalla", + "other_settings": "Otras configuraciones", + "require_pin_after": "Requerir PIN después de", + "always": "siempre", + "minutes_to_pin_code": "${minute} minutos", + "disable_exchange": "Deshabilitar intercambio", + "advanced_privacy_settings": "Configuración avanzada de privacidad", + "settings_can_be_changed_later": "Estas configuraciones se pueden cambiar más tarde en la configuración de la aplicación", + "add_custom_node": "Agregar nuevo nodo personalizado", + "disable_fiat": "Deshabilitar fiat", + "fiat_api": "Fiat API", + "disabled": "Desactivado", + "enabled": "Activado", + "tor_only": "solo Tor", + "unmatched_currencies": "La moneda de su billetera actual no coincide con la del QR escaneado", + "contact_list_contacts": "Contactos", + "contact_list_wallets": "Mis billeteras" } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index fa1d01e22..99fbcd282 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -46,7 +46,7 @@ "outgoing" : "Sortantes", "transactions_by_date" : "Transactions par date", "trades" : "Échanges", - "filters" : "Filtre", + "filter_by": "Filtrer par", "today" : "Aujourd'hui", "yesterday" : "Hier", "received" : "Reçus", @@ -354,6 +354,7 @@ "sync_status_failed_connect" : "DÉCONNECTÉ", "sync_status_connecting" : "CONNEXION EN COURS", "sync_status_connected" : "CONNECTÉ", + "sync_status_attempting_sync" : "TENTATIVE DE SYNCHRONISATION", "transaction_priority_slow" : "Lent", @@ -434,6 +435,7 @@ "provider_error" : "Erreur de ${provider}", "use_ssl" : "Utiliser SSL", + "trusted" : "de confiance", "color_theme" : "Thème", "light_theme" : "Clair", @@ -532,10 +534,12 @@ "search_currency": "Rechercher une devise", "new_template" : "Nouveau Modèle", "electrum_address_disclaimer": "Nous générons de nouvelles adresses à chaque fois que vous en utilisez une, mais les adresses précédentes continuent à fonctionner", - "wallet_name_exists": "Un portefeuille portant ce nom existe déjà", + "wallet_name_exists": "Un portefeuille (wallet) portant ce nom existe déjà", "market_place": "Place de marché", "cake_pay_title": "Cartes cadeaux Cake Pay", - "cake_pay_subtitle": "Achetez des cartes-cadeaux et échangez-les instantanément", + "cake_pay_subtitle": "Achetez des cartes-cadeaux à prix réduit (États-Unis uniquement)", + "cake_pay_web_cards_title": "Cartes Web Cake Pay", + "cake_pay_web_cards_subtitle": "Achetez des cartes prépayées et des cartes-cadeaux dans le monde entier", "about_cake_pay": "Cake Pay vous permet d'acheter facilement des cartes-cadeaux avec des actifs virtuels, utilisables instantanément chez plus de 150 000 marchands aux États-Unis.", "cake_pay_account_note": "Inscrivez-vous avec juste une adresse e-mail pour voir et acheter des cartes. Certaines sont même disponibles à prix réduit !", "already_have_account": "Vous avez déjà un compte ?", @@ -558,7 +562,7 @@ "cakepay_prepaid_card": "Carte de débit prépayée Cake Pay", "no_id_needed": "Aucune pièce d'identité nécessaire !", "frequently_asked_questions": "Foire aux questions", - "debit_card_terms": "Le stockage et l'utilisation de votre numéro de carte de paiement (et des informations d'identification correspondant à votre numéro de carte de paiement) dans ce portefeuille numérique sont soumis aux conditions générales de l'accord du titulaire de carte applicable avec l'émetteur de la carte de paiement, en vigueur à partir de de temps en temps.", + "debit_card_terms": "Le stockage et l'utilisation de votre numéro de carte de paiement (et des informations d'identification correspondant à votre numéro de carte de paiement) dans ce portefeuille numérique peuvent être soumis aux conditions générales de l'accord du titulaire de carte parfois en vigueur avec l'émetteur de la carte de paiement.", "please_reference_document": "Veuillez vous référer aux documents ci-dessous pour plus d'informations.", "cardholder_agreement": "Contrat de titulaire de carte", "e_sign_consent": "Consentement de signature électronique", @@ -591,7 +595,7 @@ "gift_card_amount": "Montant de la carte-cadeau", "bill_amount": "Montant de la facture", "you_pay": "Vous payez", - "tip": "Astuce :", + "tip": "Pourboire :", "custom": "personnalisé", "by_cake_pay": "par Cake Pay", "expire": "Expire", @@ -613,7 +617,7 @@ "gift_card_balance_note": "Les cartes-cadeaux avec un solde restant apparaîtront ici", "gift_card_redeemed_note": "Les cartes-cadeaux que vous avez utilisées apparaîtront ici", "logout": "Déconnexion", - "add_tip": "Ajouter une astuce", + "add_tip": "Ajouter un pourboire", "percentageOf": "sur ${amount}", "is_percentage": "est", "search_category": "Catégorie de recherche", @@ -642,13 +646,36 @@ "custom_redeem_amount": "Montant d'échange personnalisé", "add_custom_redemption": "Ajouter un remboursement personnalisé", "remaining": "restant", - "delete_wallet": "Supprimer le portefeuille", - "delete_wallet_confirm_message" : "Êtes-vous sûr de vouloir supprimer le portefeuille ${wallet_name}?", + "delete_wallet": "Supprimer le portefeuille (wallet)", + "delete_wallet_confirm_message" : "Êtes-vous sûr de vouloir supprimer le portefeuille (wallet) ${wallet_name}?", "low_fee": "Frais modiques", "low_fee_alert": "Vous utilisez actuellement une priorité de frais de réseau peu élevés. Cela pourrait entraîner de longues attentes, des taux différents ou des transactions annulées. Nous vous recommandons de fixer des frais plus élevés pour une meilleure expérience.", "ignor": "Ignorer", "use_suggested": "Utilisation suggérée", - "do_not_share_warning_text" : "Ne les partagez avec personne d'autre, y compris avec l'assistance.\n\nIls vont voler votre argent!", - "help": "aider", - "disable_exchange": "Désactiver l'échange" + "do_not_share_warning_text" : "Ne les partagez avec personne, y compris avec l'assistance.\n\nVos fonds seraient inmanquablement volés !", + "help": "aide", + "all_transactions": "Toutes transactions", + "all_trades": "Tous échanges", + "connection_sync": "Connexion et synchronisation", + "security_and_backup": "Sécurité et sauvegarde", + "create_backup": "Créer une sauvegarde", + "privacy_settings": "Paramètres de confidentialité", + "privacy": "Confidentialité", + "display_settings": "Paramètres d'affichage", + "other_settings": "Autres paramètres", + "require_pin_after": "Code PIN requis après", + "always": "toujours", + "minutes_to_pin_code": "${minute} minutes", + "disable_exchange": "Désactiver l'échange", + "advanced_privacy_settings": "Paramètres de confidentialité avancés", + "settings_can_be_changed_later": "Ces paramètres peuvent être modifiés ultérieurement dans les paramètres de l'application", + "add_custom_node": "Ajouter un nouveau nœud personnalisé", + "disable_fiat": "Désactiver les montants en fiat", + "fiat_api": "Fiat API", + "disabled": "Désactivé", + "enabled": "Activé", + "tor_only": "Tor uniquement", + "unmatched_currencies": "La devise de votre portefeuille (wallet) actuel ne correspond pas à celle du QR code scanné", + "contact_list_contacts": "Contacts", + "contact_list_wallets": "Mes portefeuilles (wallets)" } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index c7b6ece40..a5c27ddb4 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -48,7 +48,7 @@ "outgoing" : "निवर्तमान", "transactions_by_date" : "तारीख से लेन-देन", "trades" : "ट्रेडों", - "filters" : "फ़िल्टर", + "filter_by": "के द्वारा छनित", "today" : "आज", "yesterday" : "बिता कल", "received" : "प्राप्त किया", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "डिस्कनेक्ट किया गया", "sync_status_connecting" : "कनेक्ट", "sync_status_connected" : "जुड़े हुए", + "sync_status_attempting_sync" : "सिंक करने का प्रयास", "transaction_priority_slow" : "धीरे", @@ -436,6 +437,7 @@ "provider_error" : "${provider} त्रुटि", "use_ssl" : "उपयोग SSL", + "trusted" : "भरोसा", "color_theme" : "रंग विषय", "light_theme" : "रोशनी", @@ -537,7 +539,9 @@ "wallet_name_exists": "उस नाम वाला वॉलेट पहले से मौजूद है", "market_place": "मार्केटप्लेस", "cake_pay_title": "केक पे गिफ्ट कार्ड्स", - "cake_pay_subtitle": "उपहार कार्ड खरीदें और तुरंत रिडीम करें", + "cake_pay_subtitle": "रियायती उपहार कार्ड खरीदें (केवल यूएसए)", + "cake_pay_web_cards_title": "केक भुगतान वेब कार्ड", + "cake_pay_web_cards_subtitle": "दुनिया भर में प्रीपेड कार्ड और गिफ्ट कार्ड खरीदें", "about_cake_pay": "केक पे आपको वर्चुअल संपत्ति के साथ आसानी से उपहार कार्ड खरीदने की अनुमति देता है, जिसे संयुक्त राज्य में 150,000 से अधिक व्यापारियों पर तुरंत खर्च किया जा सकता है।", "cake_pay_account_note": "कार्ड देखने और खरीदने के लिए केवल एक ईमेल पते के साथ साइन अप करें। कुछ छूट पर भी उपलब्ध हैं!", "ready_have_account": "क्या आपके पास पहले से ही एक खाता है?", @@ -650,7 +654,30 @@ "low_fee_alert": "आप वर्तमान में कम नेटवर्क शुल्क प्राथमिकता का उपयोग कर रहे हैं। यह लंबे इंतजार, अलग-अलग दरों या रद्द किए गए ट्रेडों का कारण बन सकता है। हम बेहतर अनुभव के लिए अधिक शुल्क निर्धारित करने की सलाह देते हैं।", "ignor": "नज़रअंदाज़ करना", "use_suggested": "सुझाए गए का प्रयोग करें", - "do_not_share_warning_text" : "इन्हें समर्थन सहित किसी और के साथ साझा न करें।\n\nवे आपका पैसा चुरा लेंगे!", + "do_not_share_warning_text" : "समर्थन सहित, इन्हें किसी और के साथ साझा न करें।\n\nआपके धन की चोरी हो सकती है और होगी!", "help": "मदद करना", - "disable_exchange": "एक्सचेंज अक्षम करें" + "all_transactions": "सभी लेन - देन", + "all_trades": "सभी व्यापार", + "connection_sync": "कनेक्शन और सिंक", + "security_and_backup": "सुरक्षा और बैकअप", + "create_backup": "बैकअप बनाएँ", + "privacy_settings": "गोपनीयता सेटिंग्स", + "privacy": "गोपनीयता", + "display_settings": "प्रदर्शन सेटिंग्स", + "other_settings": "अन्य सेटिंग्स", + "require_pin_after": "इसके बाद पिन आवश्यक है", + "always": "हमेशा", + "minutes_to_pin_code": "${minute} मिनट", + "disable_exchange": "एक्सचेंज अक्षम करें", + "advanced_privacy_settings": "उन्नत गोपनीयता सेटिंग्स", + "settings_can_be_changed_later": "इन सेटिंग्स को बाद में ऐप सेटिंग में बदला जा सकता है", + "add_custom_node": "नया कस्टम नोड जोड़ें", + "disable_fiat": "िएट को अक्षम करें", + "fiat_api": "फिएट पैसे API", + "disabled": "अक्षम", + "enabled": "सक्रिय", + "tor_only": "Tor केवल", + "unmatched_currencies": "आपके वर्तमान वॉलेट की मुद्रा स्कैन किए गए क्यूआर से मेल नहीं खाती" , + "contact_list_contacts": "संपर्क", + "contact_list_wallets": "मेरा बटुआ" } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 532a4d63a..9aa9f0b4f 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -48,7 +48,7 @@ "outgoing" : "Odlazno", "transactions_by_date" : "Transakcije prema datumu", "trades" : "Razmjene", - "filters" : "Filter", + "filter_by": "Filtrirati po", "today" : "Danas", "yesterday" : "Jučer", "received" : "Primljeno", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "ISKLJUČENO", "sync_status_connecting" : "SPAJANJE", "sync_status_connected" : "SPOJENO", + "sync_status_attempting_sync" : "POKUŠAJ SINKRONIZACIJE", "transaction_priority_slow" : "Sporo", @@ -436,6 +437,7 @@ "provider_error" : "${provider} greška", "use_ssl" : "Koristi SSL", + "trusted" : "vjerovao", "color_theme" : "Shema boja", "light_theme" : "Svijetla", @@ -537,7 +539,9 @@ "wallet_name_exists": "Novčanik s tim nazivom već postoji", "market_place": "Tržnica", "cake_pay_title": "Cake Pay poklon kartice", - "cake_pay_subtitle": "Kupite darovne kartice i odmah ih iskoristite", + "cake_pay_subtitle": "Kupite darovne kartice s popustom (samo SAD)", + "cake_pay_web_cards_title": "Cake Pay Web kartice", + "cake_pay_web_cards_subtitle": "Kupujte prepaid kartice i poklon kartice diljem svijeta", "about_cake_pay": "Cake Pay vam omogućuje jednostavnu kupnju darovnih kartica s virtualnim sredstvima, koja se trenutno mogu potrošiti kod više od 150 000 trgovaca u Sjedinjenim Državama.", "cake_pay_account_note": "Prijavite se samo s adresom e-pošte da biste vidjeli i kupili kartice. Neke su čak dostupne uz popust!", "already_have_account": "Već imate račun?", @@ -650,7 +654,30 @@ "low_fee_alert": "Trenutačno koristite niski prioritet mrežne naknade. To bi moglo uzrokovati duga čekanja, različite tečajeve ili otkazane trgovine. Preporučujemo postavljanje veće naknade za bolje iskustvo.", "ignor": "Zanemariti", "use_suggested": "Koristite predloženo", - "do_not_share_warning_text" : "Nemojte ih dijeliti ni s kim, uključujući podršku.\n\nUkrast će vam novac!", + "do_not_share_warning_text" : "Nemojte ih dijeliti ni s kim, uključujući podršku.\n\nVaša sredstva mogu i bit će ukradena!", "help": "pomozite", - "disable_exchange": "Onemogući exchange" + "all_transactions": "Sve transakcije", + "all_trades": "Svi obrti", + "connection_sync": "Povezivanje i sinkronizacija", + "security_and_backup": "Sigurnost i sigurnosna kopija", + "create_backup": "Stvori sigurnosnu kopiju", + "privacy_settings": "Postavke privatnosti", + "privacy": "Privatnost", + "display_settings": "Postavke zaslona", + "other_settings": "Ostale postavke", + "require_pin_after": "Zahtijevaj PIN nakon", + "always": "Uvijek", + "minutes_to_pin_code": "${minute} minuta", + "disable_exchange": "Onemogući exchange", + "advanced_privacy_settings": "Napredne postavke privatnosti", + "settings_can_be_changed_later": "Te se postavke mogu promijeniti kasnije u postavkama aplikacije", + "add_custom_node": "Dodaj novi prilagođeni čvor", + "disable_fiat": "Isključi, fiat", + "fiat_api": "Fiat API", + "disabled": "Onemogućeno", + "enabled": "Omogućeno", + "tor_only": "Samo Tor", + "unmatched_currencies": "Valuta vašeg trenutnog novčanika ne odgovara onoj na skeniranom QR-u", + "contact_list_contacts": "Kontakti", + "contact_list_wallets": "Moji novčanici" } diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index cb5209fc3..e90fc20c2 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -48,7 +48,7 @@ "outgoing" : "In uscita", "transactions_by_date" : "Transazioni per data", "trades" : "Scambi", - "filters" : "Filtri", + "filter_by": "Filtrirati po", "today" : "Oggi", "yesterday" : "Ieri", "received" : "Ricevuto", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "DISCONNESSO", "sync_status_connecting" : "CONNESSIONE", "sync_status_connected" : "CONNESSO", + "sync_status_attempting_sync" : "TENTATIVO DI SINCRONIZZAZIONE", "transaction_priority_slow" : "Bassa", @@ -436,6 +437,7 @@ "provider_error" : "${provider} errore", "use_ssl" : "Usa SSL", + "trusted" : "di fiducia", "color_theme" : "Colore tema", "light_theme" : "Bianco", @@ -537,7 +539,9 @@ "wallet_name_exists": "Il portafoglio con quel nome è già esistito", "market_place": "Mercato", "cake_pay_title": "Carte regalo Cake Pay", - "cake_pay_subtitle": "Acquista carte regalo e riscattale all'istante", + "cake_pay_subtitle": "Acquista buoni regalo scontati (solo USA)", + "cake_pay_web_cards_title": "Carte Web Cake Pay", + "cake_pay_web_cards_subtitle": "Acquista carte prepagate e carte regalo in tutto il mondo", "about_cake_pay": "Cake Pay ti consente di acquistare facilmente buoni regalo con asset virtuali, spendibili istantaneamente presso oltre 150.000 commercianti negli Stati Uniti.", "cake_pay_account_note": "Iscriviti con solo un indirizzo email per vedere e acquistare le carte. Alcune sono anche disponibili con uno sconto!", "already_have_account": "Hai già un account?", @@ -650,7 +654,30 @@ "low_fee_alert": "Attualmente stai utilizzando una priorità a tariffa di rete bassa. Ciò potrebbe causare lunghe attese, tariffe diverse o operazioni annullate. Ti consigliamo di impostare una tariffa più alta per un'esperienza migliore.", "ignor": "Ignorare", "use_suggested": "Usa suggerito", - "do_not_share_warning_text" : "Non condividerli con nessun altro, incluso il supporto.\n\nTi ruberanno i soldi!", + "do_not_share_warning_text" : "Non condividerli con nessun altro, incluso il supporto.\n\nI tuoi fondi possono e saranno rubati!", "help": "aiuto", - "disable_exchange": "Disabilita scambio" + "all_transactions": "Sve transakcije", + "all_trades": "Svi obrti", + "connection_sync": "Connessione e sincronizzazione", + "security_and_backup": "Sicurezza e backup", + "create_backup": "Crea backup", + "privacy_settings": "Impostazioni privacy", + "privacy": "Privacy", + "display_settings": "Impostazioni di visualizzazione", + "other_settings": "Altre impostazioni", + "require_pin_after": "Richiedi PIN dopo", + "always": "sempre", + "minutes_to_pin_code": "${minute} minuti", + "disable_exchange": "Disabilita scambio", + "advanced_privacy_settings": "Impostazioni avanzate sulla privacy", + "settings_can_be_changed_later": "Queste impostazioni possono essere modificate in seguito nelle impostazioni dell'app", + "add_custom_node": "Aggiungi nuovo nodo personalizzato", + "disable_fiat": "Disabilita fiat", + "fiat_api": "Fiat API", + "disabled": "Disabilitato", + "enabled": "Abilitato", + "tor_only": "Solo Tor", + "unmatched_currencies": "La valuta del tuo portafoglio attuale non corrisponde a quella del QR scansionato", + "contact_list_contacts": "Contatti", + "contact_list_wallets": "I miei portafogli" } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 2d1c291ce..3c5a5d951 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -48,7 +48,7 @@ "outgoing" : "発信", "transactions_by_date" : "日付ごとの取引", "trades" : "取引", - "filters" : "フィルタ", + "filter_by": "でフィルタリング", "today" : "今日", "yesterday" : "昨日", "received" : "受け取った", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "切断されました", "sync_status_connecting" : "接続中", "sync_status_connected" : "接続済み", + "sync_status_attempting_sync" : "同期を試みています", "transaction_priority_slow" : "スロー", @@ -436,6 +437,7 @@ "provider_error" : "${provider} エラー", "use_ssl" : "SSLを使用する", + "trusted" : "信頼できる", "color_theme" : "カラーテーマ", "light_theme" : "光", @@ -537,7 +539,9 @@ "wallet_name_exists": "その名前のウォレットはすでに存在しています", "market_place": "Marketplace", "cake_pay_title": "ケーキペイギフトカード", - "cake_pay_subtitle": "ギフトカードを購入してすぐに利用できます", + "cake_pay_subtitle": "割引ギフトカードを購入する (米国のみ)", + "cake_pay_web_cards_title": "Cake Pay ウェブカード", + "cake_pay_web_cards_subtitle": "世界中のプリペイド カードとギフト カードを購入する", "about_cake_pay": "Cake Payを使用すると、仮想資産を含むギフトカードを簡単に購入でき、米国内の150,000を超える加盟店ですぐに利用できます。", "cake_pay_account_note": "メールアドレスだけでサインアップして、カードを表示して購入できます。割引価格で利用できるカードもあります!", "already_have_account": "すでにアカウントをお持ちですか?", @@ -650,7 +654,30 @@ "low_fee_alert": "現在、低ネットワーク料金優先度を使用しています。これにより、長い待ち時間、異なるレート、またはキャンセルされた取引が発生する可能性があります。より良い体験のために、より高い料金を設定することをお勧めします。", "ignor": "無視", "use_suggested": "推奨を使用", - "do_not_share_warning_text" : "サポートを含め、これらを他の誰とも共有しないでください。\n\n彼らはあなたのお金を盗みます!", + "do_not_share_warning_text" : "サポートを含め、これらを他の誰とも共有しないでください。\n\nあなたの資金は盗まれる可能性があります!", "help": "ヘルプ", - "disable_exchange": "交換を無効にする" + "all_transactions": "全取引", + "all_trades": "すべての取引", + "connection_sync": "接続と同期", + "security_and_backup": "セキュリティとバックアップ", + "create_backup": "バックアップを作成", + "privacy_settings": "プライバシー設定", + "privacy": "プライバシー", + "display_settings": "表示設定", + "other_settings": "その他の設定", + "require_pin_after": "後に PIN が必要", + "always": "いつも", + "minutes_to_pin_code": "${minute} 分", + "disable_exchange": "交換を無効にする", + "advanced_privacy_settings": "高度なプライバシー設定", + "settings_can_be_changed_later": "これらの設定は、後でアプリの設定で変更できます", + "add_custom_node": "新しいカスタム ノードを追加", + "disable_fiat": "フィアットを無効にする", + "fiat_api": "不換紙幣 API", + "disabled": "無効", + "enabled": "有効", + "tor_only": "Torのみ", + "unmatched_currencies": "現在のウォレットの通貨がスキャンされたQRの通貨と一致しません", + "contact_list_contacts": "連絡先", + "contact_list_wallets": "マイウォレット" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index f24e736cb..216a3a143 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -48,7 +48,7 @@ "outgoing" : "나가는", "transactions_by_date" : "날짜 별 거래", "trades" : "거래", - "filters" : "필터", + "filter_by": "필터링 기준", "today" : "오늘", "yesterday" : "어제", "received" : "받았습니다", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "연결 해제", "sync_status_connecting" : "연결 중", "sync_status_connected" : "연결됨", + "sync_status_attempting_sync" : "동기화 시도 중", "transaction_priority_slow" : "느린", @@ -436,6 +437,7 @@ "provider_error" : "${provider} 오류", "use_ssl" : "SSL 사용", + "trusted" : "신뢰할 수 있는", "color_theme" : "색상 테마", "light_theme" : "빛", @@ -537,7 +539,9 @@ "wallet_name_exists": "해당 이름의 지갑이 이미 존재합니다.", "market_place": "마켓플레이스", "cake_pay_title": "케이크 페이 기프트 카드", - "cake_pay_subtitle": "기프트 카드를 구매하고 즉시 사용", + "cake_pay_subtitle": "할인된 기프트 카드 구매(미국만 해당)", + "cake_pay_web_cards_title": "케이크페이 웹카드", + "cake_pay_web_cards_subtitle": "전 세계 선불 카드 및 기프트 카드 구매", "about_cake_pay": "Cake Pay를 사용하면 미국 내 150,000개 이상의 가맹점에서 즉시 사용할 수 있는 가상 자산이 포함된 기프트 카드를 쉽게 구입할 수 있습니다.", "cake_pay_account_note": "이메일 주소로 가입하면 카드를 보고 구매할 수 있습니다. 일부는 할인된 가격으로 사용 가능합니다!", "already_have_account": "이미 계정이 있습니까?", @@ -650,7 +654,30 @@ "low_fee_alert": "현재 낮은 네트워크 요금 우선 순위를 사용하고 있습니다. 이로 인해 긴 대기 시간, 다른 요금 또는 취소된 거래가 발생할 수 있습니다. 더 나은 경험을 위해 더 높은 요금을 설정하는 것이 좋습니다.", "ignor": "무시하다", "use_suggested": "추천 사용", - "do_not_share_warning_text" : "지원을 포함하여 다른 사람과 이러한 정보를 공유하지 마십시오.\n\n그들은 당신의 돈을 훔칠 것입니다!", + "do_not_share_warning_text" : "지원을 포함하여 다른 사람과 이러한 정보를 공유하지 마십시오.\n\n귀하의 자금은 도난당할 수 있고 도난당할 수 있습니다!", "help": "돕다", - "disable_exchange": "교환 비활성화" + "all_transactions": "모든 거래 창구", + "all_trades": "A모든 거래", + "connection_sync": "연결 및 동기화", + "security_and_backup": "보안 및 백업", + "create_backup": "백업 생성", + "privacy_settings": "개인정보 설정", + "privacy": "프라이버시", + "display_settings": "디스플레이 설정", + "other_settings": "기타 설정", + "require_pin_after": "다음 이후에 PIN 필요", + "always": "언제나", + "minutes_to_pin_code": "${minute}분", + "disable_exchange": "교환 비활성화", + "advanced_privacy_settings": "고급 개인 정보 설정", + "settings_can_be_changed_later": "이 설정은 나중에 앱 설정에서 변경할 수 있습니다.", + "add_custom_node": "새 사용자 정의 노드 추가", + "disable_fiat": "법정화폐 비활성화", + "fiat_api": "명목 화폐 API", + "disabled": "장애가 있는", + "enabled": "사용", + "tor_only": "Tor 뿐", + "unmatched_currencies": "현재 지갑의 통화가 스캔한 QR의 통화와 일치하지 않습니다.", + "contact_list_contacts": "콘택트 렌즈", + "contact_list_wallets": "내 지갑" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 5663b0357..9ac0cd1d1 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -48,7 +48,7 @@ "outgoing" : "Uitgaande", "transactions_by_date" : "Transacties op datum", "trades" : "Trades", - "filters" : "Filter", + "filter_by": "Filteren op", "today" : "Vandaag", "yesterday" : "Gisteren", "received" : "Ontvangen", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "LOSGEKOPPELD", "sync_status_connecting" : "AANSLUITING", "sync_status_connected" : "VERBONDEN", + "sync_status_attempting_sync" : "SYNCHRONISATIE PROBEREN", "transaction_priority_slow" : "Langzaam", @@ -436,6 +437,7 @@ "provider_error" : "${provider} fout", "use_ssl" : "Gebruik SSL", + "trusted" : "vertrouwd", "color_theme" : "Kleur thema", "light_theme" : "Licht", @@ -537,7 +539,9 @@ "wallet_name_exists": "Portemonnee met die naam bestaat al", "market_place": "Marktplaats", "cake_pay_title": "Cake Pay-cadeaubonnen", - "cake_pay_subtitle": "Koop cadeaubonnen en wissel ze direct in", + "cake_pay_subtitle": "Koop cadeaubonnen met korting (alleen VS)", + "cake_pay_web_cards_title": "Cake Pay-webkaarten", + "cake_pay_web_cards_subtitle": "Koop wereldwijd prepaidkaarten en cadeaubonnen", "about_cake_pay": "Met Cake Pay kunt u eenvoudig cadeaubonnen kopen met virtuele activa, die direct kunnen worden uitgegeven bij meer dan 150.000 handelaren in de Verenigde Staten.", "cake_pay_account_note": "Meld u aan met alleen een e-mailadres om kaarten te bekijken en te kopen. Sommige zijn zelfs met korting verkrijgbaar!", "already_have_account": "Heb je al een account?", @@ -650,7 +654,30 @@ "low_fee_alert": "U gebruikt momenteel een lage prioriteit voor netwerkkosten. Dit kan lange wachttijden, andere tarieven of geannuleerde transacties veroorzaken. We raden aan een hogere vergoeding in te stellen voor een betere ervaring.", "ignor": "Negeren", "use_suggested": "Gebruik aanbevolen", - "do_not_share_warning_text" : "Deel deze met niemand anders, ook niet met support.\n\nZe zullen je geld stelen!", + "do_not_share_warning_text" : "Deel deze met niemand anders, ook niet met support.\n\nUw geld kan en zal worden gestolen!", "help": "helpen", - "disable_exchange": "Uitwisseling uitschakelen" + "all_transactions": "Alle transacties", + "all_trades": "Alle transacties", + "connection_sync": "Verbinding en synchronisatie", + "security_and_backup": "Beveiliging en back-up", + "create_backup": "Maak een back-up", + "privacy_settings": "Privacy-instellingen", + "privacy": "Privacy", + "display_settings": "Weergave-instellingen", + "other_settings": "Andere instellingen", + "require_pin_after": "Pincode vereist na", + "always": "altijd", + "minutes_to_pin_code": "${minute} minuten", + "disable_exchange": "Uitwisseling uitschakelen", + "advanced_privacy_settings": "Geavanceerde privacy-instellingen", + "settings_can_be_changed_later": "Deze instellingen kunnen later worden gewijzigd in de app-instellingen", + "add_custom_node": "Voeg een nieuw aangepast knooppunt toe", + "disable_fiat": "Schakel Fiat uit", + "fiat_api": "Fiat API", + "disabled": "Gehandicapt", + "enabled": "Ingeschakeld", + "tor_only": "Alleen Tor", + "unmatched_currencies": "De valuta van uw huidige portemonnee komt niet overeen met die van de gescande QR", + "contact_list_contacts": "Contacten", + "contact_list_wallets": "Mijn portefeuilles" } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index f2a414ae2..11bf637e5 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -48,7 +48,7 @@ "outgoing" : "Towarzyski", "transactions_by_date" : "Transakcje według daty", "trades" : "Transakcje", - "filters" : "Filtr", + "filter_by": "Filtruj według", "today" : "Dzisiaj", "yesterday" : "Wczoraj", "received" : "Odebrane", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "NIEPOWIĄZANY", "sync_status_connecting" : "ZŁĄCZONY", "sync_status_connected" : "POŁĄCZONY", + "sync_status_attempting_sync" : "PRÓBA SYNCHRONIZACJI", "transaction_priority_slow" : "Powolny", @@ -436,6 +437,7 @@ "provider_error" : "${provider} pomyłka", "use_ssl" : "Użyj SSL", + "trusted" : "zaufany", "color_theme" : "Motyw kolorystyczny", "light_theme" : "Lekki", @@ -537,7 +539,9 @@ "wallet_name_exists": "Portfel o tej nazwie już istnieje", "market_place": "Rynek", "cake_pay_title": "Karty podarunkowe Cake Pay", - "cake_pay_subtitle": "Kup karty podarunkowe i wykorzystaj je natychmiast", + "cake_pay_subtitle": "Kup karty upominkowe ze zniżką (tylko USA)", + "cake_pay_web_cards_title": "Cake Pay Web Cards", + "cake_pay_web_cards_subtitle": "Kupuj na całym świecie karty przedpłacone i karty podarunkowe", "about_cake_pay": "Cake Pay umożliwia łatwe kupowanie kart podarunkowych z wirtualnymi aktywami, które można natychmiast wydać u ponad 150 000 sprzedawców w Stanach Zjednoczonych.", "cake_pay_account_note": "Zarejestruj się, używając tylko adresu e-mail, aby przeglądać i kupować karty. Niektóre są nawet dostępne ze zniżką!", "already_have_account": "Masz już konto?", @@ -650,7 +654,30 @@ "low_fee_alert": "Obecnie korzystasz z niskiego priorytetu opłaty sieciowej. Może to spowodować długie oczekiwanie, różne stawki lub anulowane transakcje. Zalecamy ustawienie wyższej opłaty, aby zapewnić lepsze wrażenia.", "ignor": "Ignorować", "use_suggested": "Użyj sugerowane", - "do_not_share_warning_text" : "Nie udostępniaj ich nikomu innemu, w tym wsparcia.\n\nUkradną twoje pieniądze!", + "do_not_share_warning_text" : "Nie udostępniaj ich nikomu innemu, w tym pomocy.\n\nTwoje środki mogą i zostaną skradzione!", "help": "pomoc", - "disable_exchange": "Wyłącz wymianę" + "all_transactions": "Wszystkie transakcje", + "all_trades": "Wszystkie operacje", + "connection_sync": "Połączenie i synchronizacja", + "security_and_backup": "Bezpieczeństwo i kopia zapasowa", + "create_backup": "Utwórz kopię zapasową", + "privacy_settings": "Ustawienia prywatności", + "privacy": "Prywatność", + "display_settings": "Ustawienia wyświetlania", + "other_settings": "Inne ustawienia", + "require_pin_after": "Wymagaj kodu PIN po", + "always": "zawsze", + "minutes_to_pin_code": "${minute} minut", + "disable_exchange": "Wyłącz wymianę", + "advanced_privacy_settings": "Zaawansowane ustawienia prywatności", + "settings_can_be_changed_later": "Te ustawienia można później zmienić w ustawieniach aplikacji", + "add_custom_node": "Dodaj nowy węzeł niestandardowy", + "disable_fiat": "Wyłącz fiat", + "fiat_api": "API Fiata", + "disabled": "Wyłączone", + "enabled": "Włączony", + "tor_only": "Tylko Tor", + "unmatched_currencies": "Waluta Twojego obecnego portfela nie odpowiada walucie zeskanowanego kodu QR", + "contact_list_contacts": "Łączność", + "contact_list_wallets": "Moje portfele" } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 25318dc2d..8529aadb8 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -48,7 +48,7 @@ "outgoing" : "Enviadas", "transactions_by_date" : "Transações por data", "trades" : "Trocas", - "filters" : "Filtro", + "filter_by": "Filtrar por", "today" : "Hoje", "yesterday" : "Ontem", "received" : "Recebida", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "DESCONECTADO", "sync_status_connecting" : "CONECTANDO", "sync_status_connected" : "CONECTADO", + "sync_status_attempting_sync" : "TENTANDO SINCRONIZAR", "transaction_priority_slow" : "Lenta", @@ -436,6 +437,7 @@ "provider_error" : "${provider} erro", "use_ssl" : "Use SSL", + "trusted" : "confiável", "color_theme" : "Tema de cor", "light_theme" : "Luz", @@ -537,14 +539,15 @@ "wallet_name_exists": "A carteira com esse nome já existe", "market_place": "Mercado", "cake_pay_title": "Cartões de presente de pagamento de bolo", - "cake_pay_subtitle": "Compre vales-presente e resgate instantaneamente", + "cake_pay_subtitle": "Compre vales-presente com desconto (somente nos EUA)", + "cake_pay_web_cards_title": "Cartões Cake Pay Web", + "cake_pay_web_cards_subtitle": "Compre cartões pré-pagos e cartões-presente em todo o mundo", "about_cake_pay": "O Cake Pay permite que você compre facilmente cartões-presente com ativos virtuais, que podem ser gastos instantaneamente em mais de 150.000 comerciantes nos Estados Unidos.", "cake_pay_account_note": "Inscreva-se com apenas um endereço de e-mail para ver e comprar cartões. Alguns estão até com desconto!", "already_have_account": "Já tem uma conta?", "create_account": "Criar conta", "privacy_policy": "Política de privacidade", "welcome_to_cakepay": "Bem-vindo ao Cake Pay!", - "create_account": "Registar-se", "forgot_password": "Esqueci a senha", "reset_password": "Redefinir senha", "gift_cards": "Cartões de presente", @@ -650,7 +653,30 @@ "low_fee_alert": "No momento, você está usando uma prioridade de taxa de rede baixa. Isso pode causar longas esperas, taxas diferentes ou negociações canceladas. Recomendamos definir uma taxa mais alta para uma melhor experiência.", "ignor": "Ignorar", "use_suggested": "Uso sugerido", - "do_not_share_warning_text" : "Não os compartilhe com mais ninguém, incluindo suporte.\n\nEles vão roubar seu dinheiro!", + "do_not_share_warning_text" : "Não os compartilhe com mais ninguém, incluindo suporte.\n\nSeus fundos podem e serão roubados!", "help": "ajuda", - "disable_exchange": "Desativar troca" + "all_transactions": "Todas as transacções", + "all_trades": "Todas as negociações", + "connection_sync": "Conexão e sincronização", + "security_and_backup": "Segurança e backup", + "create_backup": "Criar backup", + "privacy_settings": "Configurações de privacidade", + "privacy": "Privacidade", + "display_settings": "Configurações de exibição", + "other_settings": "Outras configurações", + "require_pin_after": "Exigir PIN após", + "always": "sempre", + "minutes_to_pin_code": "${minute} minutos", + "disable_exchange": "Desativar troca", + "advanced_privacy_settings": "Configurações de privacidade avançadas", + "settings_can_be_changed_later": "Essas configurações podem ser alteradas posteriormente nas configurações do aplicativo", + "add_custom_node": "Adicionar novo nó personalizado", + "disable_fiat": "Desativar fiat", + "fiat_api": "API da Fiat", + "disabled": "Desabilitado", + "enabled": "Habilitado", + "tor_only": "Tor apenas", + "unmatched_currencies": "A moeda da sua carteira atual não corresponde à do QR digitalizado", + "contact_list_contacts": "Contatos", + "contact_list_wallets": "minhas carteiras" } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index a907e5765..1ba5fd85c 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -48,7 +48,7 @@ "outgoing" : "Исходящие", "transactions_by_date" : "Сортировать по дате", "trades" : "Сделки", - "filters" : "Фильтр", + "filter_by": "Фильтровать по", "today" : "Сегодня", "yesterday" : "Вчера", "received" : "Полученные", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "ОТКЛЮЧЕНО", "sync_status_connecting" : "ПОДКЛЮЧЕНИЕ", "sync_status_connected" : "ПОДКЛЮЧЕНО", + "sync_status_attempting_sync" : "ПОПЫТКА СИНХРОНИЗАЦИИ", "transaction_priority_slow" : "Медленный", @@ -368,7 +369,7 @@ "trade_for_not_created" : "Сделка для ${title} не создана.", "trade_not_created" : "Сделка не создана", "trade_id_not_found" : "Сделка ${tradeId} ${title} не найдена.", - "trade_not_found" : "Trade not found.", + "trade_not_found" : "Торговля не найдена.", "trade_state_pending" : "Ожидание", @@ -436,6 +437,7 @@ "provider_error" : "${provider} ошибка", "use_ssl" : "Использовать SSL", + "trusted" : "доверенный", "color_theme" : "Цветовая тема", "light_theme" : "Светлая", @@ -537,7 +539,9 @@ "wallet_name_exists": "Кошелек с таким именем уже существует", "market_place": "Торговая площадка", "cake_pay_title": "Подарочные карты Cake Pay", - "cake_pay_subtitle": "Купите подарочные карты и моментально погасите их", + "cake_pay_subtitle": "Покупайте подарочные карты со скидкой (только для США)", + "cake_pay_web_cards_title": "Веб-карты Cake Pay", + "cake_pay_web_cards_subtitle": "Покупайте карты предоплаты и подарочные карты по всему миру", "about_cake_pay": "Cake Pay позволяет вам легко покупать подарочные карты с виртуальными активами, которые можно мгновенно потратить в более чем 150 000 продавцов в Соединенных Штатах.", "cake_pay_account_note": "Зарегистрируйтесь, указав только адрес электронной почты, чтобы просматривать и покупать карты. Некоторые даже доступны со скидкой!", "already_have_account": "У вас уже есть аккаунт?", @@ -650,7 +654,30 @@ "low_fee_alert": "В настоящее время вы используете низкий приоритет платы за сеть. Это может привести к длительному ожиданию, изменению ставок или отмене сделок. Мы рекомендуем установить более высокую плату для лучшего опыта.", "ignor": "Игнорировать", "use_suggested": "Использовать предложенный", - "do_not_share_warning_text" : "Не делитесь ими с кем-либо еще, в том числе со службой поддержки.\n\nОни украдут ваши деньги!", + "do_not_share_warning_text" : "Не сообщайте их никому, включая техподдержку.\n\nВаши средства могут и будут украдены!", "help": "помощь", - "disable_exchange": "Отключить обмен" + "all_transactions": "Все транзакции", + "all_trades": "Все сделки", + "connection_sync": "Подключение и синхронизация", + "security_and_backup": "Безопасность и резервное копирование", + "create_backup": "Создать резервную копию", + "privacy_settings": "Настройки конфиденциальности", + "privacy": "Конфиденциальность", + "display_settings": "Настройки отображения", + "other_settings": "Другие настройки", + "require_pin_after": "Требовать ПИН после", + "always": "всегда", + "minutes_to_pin_code": "${minute} минут", + "disable_exchange": "Отключить обмен", + "advanced_privacy_settings": "Расширенные настройки конфиденциальности", + "settings_can_be_changed_later": "Эти настройки можно изменить позже в настройках приложения.", + "add_custom_node": "Добавить новый пользовательский узел", + "disable_fiat": "Отключить фиат", + "fiat_api": "Фиат API", + "disabled": "Отключено", + "enabled": "Включено", + "tor_only": "Только Tor", + "unmatched_currencies": "Валюта вашего текущего кошелька не соответствует валюте отсканированного QR-кода.", + "contact_list_contacts": "Контакты", + "contact_list_wallets": "Мои кошельки" } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb new file mode 100644 index 000000000..242a8d110 --- /dev/null +++ b/res/values/strings_th.arb @@ -0,0 +1,681 @@ +{ + "welcome" : "ยินดีต้อนรับสู่", + "cake_wallet" : "Cake Wallet", + "first_wallet_text" : "กระเป๋าสตางค์ที่สวยงามสำหรับ Monero, Bitcoin, Litecoin และ Haven", + "please_make_selection" : "โปรดเลือกตามด้านล่างเพื่อสร้างหรือกู้กระเป๋าของคุณ", + "create_new" : "สร้างกระเป๋าใหม่", + "restore_wallet" : "กู้กระเป๋า", + + "monero_com": "Monero.com ของ Cake Wallet", + "monero_com_wallet_text": "กระเป๋าสตางค์ที่สวยงามสำหรับ Monero", + + "haven_app": "Haven ของ Cake Wallet", + "haven_app_wallet_text": "กระเป๋าสตางค์ที่สวยงามสำหรับ Haven", + + "accounts" : "บัญชี", + "edit" : "แก้ไข", + "account" : "บัญชี", + "add" : "เพิ่ม", + + + "address_book" : "สมุดที่อยู่", + "contact" : "ผู้ติดต่อ", + "please_select" : "โปรดเลือก:", + "cancel" : "ยกเลิก", + "ok" : "ตกลง", + "contact_name" : "ชื่อผู้ติดต่อ", + "reset" : "รีเซ็ต", + "save" : "บันทึก", + "address_remove_contact" : "ลบผู้ติดต่อ", + "address_remove_content" : "คุณแน่ใจหรือว่าต้องการลบผู้ติดต่อที่เลือก?", + + + "authenticated" : "ได้รับการยืนยันสิทธิ์", + "authentication" : "การยืนยันสิทธิ์", + "failed_authentication" : "การยืนยันสิทธิ์ล้มเหลว ${state_error}", + + + "wallet_menu" : "เมนู", + "Blocks_remaining" : "${status} บล็อกที่เหลืออยู่", + "please_try_to_connect_to_another_node" : "โปรดลองเชื่อมต่อกับโหนดอื่น", + "xmr_hidden" : "ซ่อน", + "xmr_available_balance" : "ยอดคงเหลือที่สามารถใช้งานได้", + "xmr_full_balance" : "ยอดคงเหลือแบบเต็ม", + "send" : "ส่ง", + "receive" : "รับ", + "transactions" : "ธุรกรรม", + "incoming" : "ขาเข้า", + "outgoing" : "ขาออก", + "transactions_by_date" : "ธุรกรรมตามวันที่", + "trades" : "การซื้อขาย", + "filter_by": "กรองตาม", + "today" : "วันนี้", + "yesterday" : "เมื่อวาน", + "received" : "รับ", + "sent" : "ส่ง", + "pending" : " (อยู่ระหว่างดำเนินการ)", + "rescan" : "สแกนใหม่", + "reconnect" : "เชื่อมต่อใหม่", + "wallets" : "กระเป๋า", + "show_seed" : "แสดงซีด", + "show_keys" : "แสดงซีด/คีย์", + "address_book_menu" : "สมุดที่อยู่", + "reconnection" : "เชื่อมต่อใหม่", + "reconnect_alert_text" : "คุณแน่ใจหรือไม่ว่าต้องการเชื่อมต่อใหม่?", + + + "exchange" : "แลกเปลี่ยน", + "clear" : "ล้าง", + "refund_address" : "ที่อยู่สำหรับส่งคืน", + "change_exchange_provider" : "เปลี่ยนผู้ให้บริการแลกเปลี่ยน", + "you_will_send" : "แปลงจาก", + "you_will_get" : "แปลงเป็น", + "amount_is_guaranteed" : "จำนวนที่จะได้รับมีการรับประกัน", + "amount_is_estimate" : "จำนวนที่จะได้รับเป็นการประมาณการ", + "powered_by" : "พัฒนาขึ้นโดย ${title}", + "error" : "ข้อผิดพลาด", + "estimated" : "ประมาณการ", + "min_value" : "ขั้นต่ำ: ${value} ${currency}", + "max_value" : "ขั้นสูง: ${value} ${currency}", + "change_currency" : "เปลี่ยนสกุลเงิน", + "overwrite_amount" : "เขียนทับจำนวน", + "qr_payment_amount" : "QR code นี้มีจำนวนการชำระเงิน คุณต้องการเขียนทับค่าปัจจุบันหรือไม่?", + + "copy_id" : "คัดลอก ID", + "exchange_result_write_down_trade_id" : "โปรดคัดลอกหรือบันทึก ID ของการซื้อขายเพื่อดำเนินการต่อไป", + "trade_id" : "ID การซื้อขาย:", + "copied_to_clipboard" : "คัดลอกไปยังหน่วยความจำของคอมพิวเตอร์", + "saved_the_trade_id" : "ฉันได้บันทึก ID ของการซื้อขายแล้ว", + "fetching" : "กำลังโหลด", + "id" : "ID: ", + "amount" : "จำนวน: ", + "payment_id" : "ID การชำระเงิน: ", + "status" : "สถานะ: ", + "offer_expires_in" : "ข้อเสนอจะหมดอายุใน: ", + "trade_is_powered_by" : "การซื้อขายนี้จัดสร้างโดย ${provider}", + "copy_address" : "คัดลอกที่อยู่", + "exchange_result_confirm" : "โดยกดปุ่มยืนยัน, คุณจะส่ง ${fetchingLabel} ${from} จากกระเป๋าของคุณที่เรียกว่า ${walletName} ไปยังที่อยู่ที่แสดงข้างล่าง หรือคุณสามารถส่งจากกระเป๋าภายนอกไปยังที่อยู่/รหัส QR ด้านล่าง\n\nโปรดกดปุ่มยืนยันเพื่อดำเนินการต่อหรือกลับไปเปลี่ยนจำนวน", + "exchange_result_description" : "คุณต้องส่งอย่างน้อย ${fetchingLabel} ${from} ไปยังที่อยู่ที่แสดงบนหน้าถัดไป หากคุณส่งจำนวนน้อยกว่า ${fetchingLabel} ${from} อาจจะไม่ถูกแปลงและอาจไม่ถูกเรียกคืน", + "exchange_result_write_down_ID" : "*โปรดคัดลอกหรือเขียนรหัสของคุณด้านบน", + "confirm" : "ยืนยัน", + "confirm_sending" : "ยืนยันการส่ง", + "commit_transaction_amount_fee" : "ยืนยันธุรกรรม\nจำนวน: ${amount}\nค่าธรรมเนียม: ${fee}", + "sending" : "กำลังส่ง", + "transaction_sent" : "ธุรกรรมถูกส่ง!", + "expired" : "หมดอายุ", + "time" : "${minutes}m ${seconds}s", + "send_xmr" : "ส่ง XMR", + "exchange_new_template" : "เทมเพลทใหม่", + + "faq" : "คำถามที่พบบ่อย", + + + "enter_your_pin" : "ใส่ PIN ของคุณ", + "loading_your_wallet" : "กำลังโหลดกระเป๋าของคุณ", + + + "new_wallet" : "กระเป๋าใหม่", + "wallet_name" : "ชื่อกระเป๋า", + "continue_text" : "ดำเนินการต่อ", + "choose_wallet_currency" : "โปรดเลือกสกุลเงินของกระเป๋า:", + + + "node_new" : "โหนดใหม่", + "node_address" : "ที่อยู่โหนด", + "node_port" : "พอร์ตโหนด", + "login" : "เข้าสู่ระบบ", + "password" : "รหัสผ่าน", + "nodes" : "โหนด", + "node_reset_settings_title" : "รีเซ็ตการตั้งค่า", + "nodes_list_reset_to_default_message" : "คุณแน่ใจหรือว่าต้องการรีเซ็ตการตั้งค่าเป็นค่าเริ่มต้น?", + "change_current_node" : "คุณแน่ใจหรือว่าต้องการเปลี่ยนโหนดปัจจุบันเป็น ${node}?", + "change" : "เปลี่ยน", + "remove_node" : "ลบโหนด", + "remove_node_message" : "คุณแน่ใจหรือว่าต้องการลบโหนดที่เลือก?", + "remove" : "ลบ", + "delete" : "ลบ", + "add_new_node" : "เพิ่มโหนดใหม่", + "change_current_node_title" : "เปลี่ยนโหนดปัจจุบัน", + "node_test" : "ทดสอบ", + "node_connection_successful" : "เชื่อมต่อสำเร็จ", + "node_connection_failed" : "เชื่อมต่อล้มเหลว", + "new_node_testing" : "การทดสอบโหนดใหม่", + + + "use" : "สลับไปที่ ", + "digit_pin" : "-หลัก PIN", + + + "share_address" : "แชร์ที่อยู่", + "receive_amount" : "จำนวน", + "subaddresses" : "ที่อยู่ย่อย", + "addresses" : "ที่อยู่", + "scan_qr_code" : "สแกน QR code เพื่อรับที่อยู่", + "qr_fullscreen" : "แตะเพื่อเปิดหน้าจอ QR code แบบเต็มจอ", + "rename" : "เปลี่ยนชื่อ", + "choose_account" : "เลือกบัญชี", + "create_new_account" : "สร้างบัญชีใหม่", + "accounts_subaddresses" : "บัญชีและที่อยู่ย่อย", + + + "restore_restore_wallet" : "กู้กระเป๋า", + "restore_title_from_seed_keys" : "กู้จาก seed/keys", + "restore_description_from_seed_keys" : "เรียกกระเป๋าของคุณกลับมาจาก seed/keys ที่คุณได้บันทึกไว้ในที่ปลอดภัย", + "restore_next" : "ถัดไป", + "restore_title_from_backup" : "กู้จากการสำรองข้อมูล", + "restore_description_from_backup" : "คุณสามารถกู้แอพ Cake Wallet ทั้งหมดจากไฟล์สำรองข้อมูลของคุณ", + "restore_seed_keys_restore" : "กู้จาก Seed/Keys", + "restore_title_from_seed" : "กู้จาก seed", + "restore_description_from_seed" : "กู้กระเป๋าของคุณจากรหัสผสมของ 25 คำหรือ 13 คำ", + "restore_title_from_keys" : "กู้จาก keys", + "restore_description_from_keys" : "กู้กระเป๋าของคุณจากการกดปุ่มที่สร้างขึ้นจาก private keys ของคุณที่บันทึกไว้", + "restore_wallet_name" : "ชื่อกระเป๋า", + "restore_address" : "ที่อยู่", + "restore_view_key_private" : "คีย์สำหรับดู (ส่วนตัว)", + "restore_spend_key_private" : "คีย์สำหรับใช้ (ส่วนตัว)", + "restore_recover" : "กู้", + "restore_wallet_restore_description" : "คำอธิบายการกู้กระเป๋า", + "restore_new_seed" : "ซีดใหม่", + "restore_active_seed" : "ซีดที่ใช้งานอยู่", + "restore_bitcoin_description_from_seed" : "กู้กระเป๋าของคุณจากรหัสผสมของ 24 คำ", + "restore_bitcoin_description_from_keys" : "กู้กระเป๋าของคุณจากสตริง WIF ที่สร้างขึ้นจากคีย์ส่วนตัวของคุณ", + "restore_bitcoin_title_from_keys" : "กู้จาก WIF", + "restore_from_date_or_blockheight" : "โปรดป้อนวันที่หลายวันก่อนที่คุณสร้างกระเป๋านี้ หรือหากคุณรู้ความสูงของบล็อก (blockheight) โปรดป้อนมันแทน", + + + "seed_reminder" : "โปรดเขียนข้อมูลนี้ลงสมุดเพื่อความปลอดภัยหากคุณสูญเสียหรือล้างโทรศัพท์ของคุณ", + "seed_title" : "Seed", + "seed_share" : "แบ่งปัน seed", + "copy" : "คัดลอก", + + + "seed_language_choose" : "โปรดเลือกภาษาของ seed:", + "seed_choose" : "เลือกภาษาของ seed", + "seed_language_next" : "ถัดไป", + "seed_language_english" : "อังกฤษ", + "seed_language_chinese" : "จีน", + "seed_language_dutch" : "ดัตช์", + "seed_language_german" : "เยอรมัน", + "seed_language_japanese" : "ญี่ปุ่น", + "seed_language_portuguese" : "โปรตุเกส", + "seed_language_russian" : "รัสเซีย", + "seed_language_spanish" : "สเปน", + "seed_language_french" : "ฝรั่งเศส", + "seed_language_italian" : "อิตาลี", + + + "send_title" : "ส่ง", + "send_your_wallet" : "กระเป๋าของคุณ", + "send_address" : "ที่อยู่ ${cryptoCurrency}", + "send_payment_id" : "ID การชำระเงิน (ไม่จำเป็น)", + "all" : "ทั้งหมด", + "send_error_minimum_value" : "จำนวนขั้นต่ำของจำนวนเงินคือ 0.01", + "send_error_currency" : "สกุลเงินสามารถเป็นเลขเท่านั้น", + "send_estimated_fee" : "ค่าธรรมเนียมที่คาดการณ์:", + "send_priority" : "ในขณะนี้ค่าธรรมเนียมถูกตั้งค่าเป็นความสำคัญ ${transactionPriority} \nความสำคัญของธุรกรรมสามารถปรับได้ในการตั้งค่า", + "send_creating_transaction" : "กำลังสร้างธุรกรรม", + "send_templates" : "แม่แบบ", + "send_new" : "ใหม่", + "send_amount" : "จำนวน:", + "send_fee" : "ค่าธรรมเนียม:", + "send_name" : "ชื่อ", + "send_got_it" : "เข้าใจ", + "send_sending" : "กำลังส่ง...", + "send_success" : "คุณได้ส่ง ${crypto} เรียบร้อยแล้ว", + + + "settings_title" : "การตั้งค่า", + "settings_nodes" : "จุดเชื่อมต่อ", + "settings_current_node" : "จุดเชื่อมต่อปัจจุบัน", + "settings_wallets" : "กระเป๋าสตางค์", + "settings_display_balance" : "แสดงยอดคงเหลือ", + "settings_currency" : "สกุลเงิน", + "settings_fee_priority" : "ความสำคัญของค่าธรรมเนียม", + "settings_save_recipient_address" : "บันทึกที่อยู่ผู้รับ", + "settings_personal" : "ส่วนตัว", + "settings_change_pin" : "เปลี่ยน PIN", + "settings_change_language" : "เปลี่ยนภาษา", + "settings_allow_biometrical_authentication" : "อนุญาตให้ใช้การยืนยันตัวตนทางระบบชีวภาพ", + "settings_dark_mode" : "โหมดดำ", + "settings_transactions" : "ธุรกรรม", + "settings_trades" : "การซื้อขาย", + "settings_display_on_dashboard_list" : "แสดงบนรายการแดชบอร์ด", + "settings_all" : "ทั้งหมด", + "settings_only_trades" : "เฉพาะการซื้อขาย", + "settings_only_transactions" : "เฉพาะธุรกรรม", + "settings_none" : "ไม่มี", + "settings_support" : "สนับสนุน", + "settings_terms_and_conditions" : "ข้อกำหนดและเงื่อนไข", + "pin_is_incorrect" : "PIN ไม่ถูกต้อง", + + + "setup_pin" : "ตั้งค่า PIN", + "enter_your_pin_again" : "ใส่ PIN ของคุณอีกครั้ง", + "setup_successful" : "การตั้งค่า PIN ของคุณสำเร็จแล้ว!", + + "wallet_keys" : "ซีดของกระเป๋า/คีย์", + "wallet_seed" : "ซีดของกระเป๋า", + "private_key" : "คีย์ส่วนตัว", + "public_key" : "คีย์สาธารณะ", + "view_key_private" : "คีย์มุมมอง (ส่วนตัว)", + "view_key_public" : "คีย์มุมมอง (สาธารณะ)", + "spend_key_private" : "คีย์จ่าย (ส่วนตัว)", + "spend_key_public" : "คีย์จ่าย (สาธารณะ)", + "copied_key_to_clipboard" : "คัดลอก ${key} ไปยัง Clipboard แล้ว", + + + "new_subaddress_title" : "ที่อยู่ใหม่", + "new_subaddress_label_name" : "ชื่อป้ายกำกับ", + "new_subaddress_create" : "สร้าง", + + "address_label" : "ป้ายกำกับที่อยู่", + + "subaddress_title" : "รายการที่อยู่ย่อย", + + "trade_details_title" : "รายละเอียดการแลกเปลี่ยน", + "trade_details_id" : "รหัส", + "trade_details_state" : "สถานะ", + "trade_details_fetching" : "กำลังเรียกข้อมูล", + "trade_details_provider" : "ผู้ให้บริการ", + "trade_details_created_at" : "สร้างเมื่อ", + "trade_details_pair" : "คู่", + "trade_details_copied" : "${title} คัดลอกไปยัง Clipboard", + + + "trade_history_title" : "ประวัติการซื้อขาย", + + + "transaction_details_title" : "รายละเอียดการทำรายการ", + "transaction_details_transaction_id" : "ไอดีการทำรายการ", + "transaction_details_date" : "วันที่", + "transaction_details_height" : "ความสูง", + "transaction_details_amount" : "จำนวน", + "transaction_details_fee" : "ค่าธรรมเนียม", + "transaction_details_copied" : "${title} ถูกคัดลอกไปยังคลิปบอร์ด", + "transaction_details_recipient_address" : "ที่อยู่ผู้รับ", + + + "wallet_list_title" : "กระเป๋า Monero", + "wallet_list_create_new_wallet" : "สร้างกระเป๋าใหม่", + "wallet_list_restore_wallet" : "กู้กระเป๋า", + "wallet_list_load_wallet" : "โหลดกระเป๋า", + "wallet_list_loading_wallet" : "กำลังโหลดกระเป๋า ${wallet_name}", + "wallet_list_failed_to_load" : "ไม่สามารถโหลดกระเป๋า ${wallet_name} ได้ ${error}", + "wallet_list_removing_wallet" : "กำลังลบกระเป๋า ${wallet_name}", + "wallet_list_failed_to_remove" : "ไม่สามารถลบกระเป๋า ${wallet_name} ได้ ${error}", + + + "widgets_address" : "ที่อยู่", + "widgets_restore_from_blockheight" : "กู้คืนจากระดับบล็อก", + "widgets_restore_from_date" : "กู้คืนจากวันที่", + "widgets_or" : "หรือ", + "widgets_seed" : "ซีด", + + + "router_no_route" : "ไม่มีเส้นทางที่กำหนดไว้สำหรับ ${name}", + + + "error_text_account_name" : "ชื่อบัญชีสามารถเป็นเพียงตัวอักษรหรือตัวเลขเท่านั้น\nและต้องมีความยาวระหว่าง 1 ถึง 15 ตัวอักษร", + "error_text_contact_name" : "ชื่อผู้ติดต่อไม่สามารถมีสัญลักษณ์ ` , '\" ได้\nและต้องมีความยาวระหว่าง 1 ถึง 32 ตัวอักษร", + "error_text_address" : "ที่อยู่กระเป๋าจะต้องสอดคล้องกับประเภท\nของเหรียญคริปโตเนียม", + "error_text_node_address" : "โปรดป้อนที่อยู่ iPv4", + "error_text_node_port" : "พอร์ตโหนดสามารถมีตัวเลขเท่านั้นระหว่าง 0 ถึง 65535", + "error_text_payment_id" : "Payment ID สามารถมีขนาดระหว่าง 16 ถึง 64 ตัวอักษรตามแบบ hex", + "error_text_xmr" : "มูลค่า XMR ไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 12", + "error_text_fiat" : "มูลค่าของจำนวนเงินไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 2", + "error_text_subaddress_name" : "ชื่อสำหรับที่อยู่ย่อยจะต้องประกอบด้วยตัวอักษร ตัวเลข และสัญลักษณ์ _ - และมีความยาวระหว่าง 1 ถึง 20 ตัวอักษร", + "error_text_amount" : "จำนวนจะต้องประกอบด้วยตัวเลขเท่านั้น", + "error_text_wallet_name" : "ชื่อกระเป๋าสตางค์จะต้องประกอบด้วยตัวอักษร ตัวเลข และสัญลักษณ์ _ - และมีความยาวระหว่าง 1 ถึง 33 ตัวอักษร", + "error_text_keys" : "คีย์ของกระเป๋าสตางค์จะต้องประกอบด้วยตัวอักษรฐาน 16 จำนวน 64 ตัว", + "error_text_crypto_currency" : "จำนวนทศนิยมจะต้องน้อยกว่าหรือเท่ากับ 12", + "error_text_minimal_limit" : "การซื้อขายกับ ${provider} ไม่สามารถดำเนินการได้ จำนวนน้อยกว่าขั้นต่ำ: ${min} ${currency}", + "error_text_maximum_limit" : "การซื้อขายกับ ${provider} ไม่สามารถดำเนินการได้ จำนวนมากกว่าขั้นสูง: ${max} ${currency}", + "error_text_limits_loading_failed" : "การซื้อขายกับ ${provider} ไม่สามารถดำเนินการได้ ไม่สามารถโหลดขอบเขตได้", + "error_text_template" : "ชื่อแม่แบบและที่อยู่ไม่สามารถมีสัญลักษณ์ ` , '\" และต้องมีความยาวระหว่าง 1 ถึง 106 ตัวอักษร", + + + "auth_store_ban_timeout" : "หมดเวลาห้าม", + "auth_store_banned_for" : "ถูกห้ามสำหรับ ", + "auth_store_banned_minutes" : " นาที", + "auth_store_incorrect_password" : "รหัสผ่านไม่ถูกต้อง", + "wallet_store_monero_wallet" : "กระเป๋า Monero", + "wallet_restoration_store_incorrect_seed_length" : "ความยาวของซีดไม่ถูกต้อง", + + + "full_balance" : "ยอดคงเหลือทั้งหมด", + "available_balance" : "ยอดคงเหลือที่ใช้งานได้", + "hidden_balance" : "ยอดคงเหลือซ่อนอยู่", + + + "sync_status_syncronizing" : "กำลังซิงโครไนซ์", + "sync_status_syncronized" : "ซิงโครไนซ์แล้ว", + "sync_status_not_connected" : "ไม่ได้เชื่อมต่อ", + "sync_status_starting_sync" : "กำลังเริ่มซิงโครไนซ์", + "sync_status_failed_connect" : "การเชื่อมต่อล้มเหลว", + "sync_status_connecting" : "กำลังเชื่อมต่อ", + "sync_status_connected" : "เชื่อมต่อแล้ว", + "sync_status_attempting_sync" : "พยายามซิงโครไนซ์", + + + "transaction_priority_slow" : "ช้า", + "transaction_priority_regular" : "ปกติ", + "transaction_priority_medium" : "ปานกลาง", + "transaction_priority_fast" : "เร็ว", + "transaction_priority_fastest" : "เร็วที่สุด", + + + "trade_for_not_created" : "การแลกเปลี่ยนสำหรับ ${title} ยังไม่ได้ถูกสร้างขึ้น", + "trade_not_created" : "การแลกเปลี่ยนยังไม่ได้ถูกสร้าง", + "trade_id_not_found" : "ไม่พบการแลกเปลี่ยน ${tradeId} ของ ${title}", + "trade_not_found" : "ไม่พบการแลกเปลี่ยน", + + + "trade_state_pending" : "รอดำเนินการ", + "trade_state_confirming" : "กำลังยืนยัน", + "trade_state_trading" : "กำลังแลกเปลี่ยน", + "trade_state_traded" : "ถูกแลกเปลี่ยน", + "trade_state_complete" : "เสร็จสมบูรณ์", + "trade_state_to_be_created" : "จะถูกสร้าง", + "trade_state_unpaid" : "ยังไม่ได้จ่าย", + "trade_state_underpaid" : "จ่ายไม่ครบ", + "trade_state_paid_unconfirmed" : "จ่ายแล้วแต่ยังไม่ได้ยืนยัน", + "trade_state_paid" : "จ่ายแล้ว", + "trade_state_btc_sent" : "ส่ง BTC แล้ว", + "trade_state_timeout" : "เวลาหมด", + "trade_state_created" : "ถูกสร้าง", + "trade_state_finished" : "เสร็จสิ้น", + + "change_language" : "เปลี่ยนภาษา", + "change_language_to" : "เปลี่ยนภาษาเป็น ${language}?", + + "paste" : "วาง", + "restore_from_seed_placeholder" : "โปรดป้อนหรือวาง seed ของคุณที่นี่", + "add_new_word" : "เพิ่มคำใหม่", + "incorrect_seed" : "ข้อความที่ป้อนไม่ถูกต้อง", + + "biometric_auth_reason" : "สแกนลายนิ้วมือของคุณเพื่อยืนยันตัวตน", + "version" : "เวอร์ชัน ${currentVersion}", + + "openalias_alert_title" : "พบที่อยู่", + "openalias_alert_content" : "คุณกำลังจะส่งเงินไปยัง\n${recipient_name}", + + "card_address" : "ที่อยู่:", + "buy" : "ซื้อ", + "sell": "ขาย", + + "placeholder_transactions" : "ธุรกรรมของคุณจะปรากฏที่นี่", + "placeholder_contacts" : "รายชื่อผู้ติดต่อของคุณจะปรากฏที่นี่", + + "template" : "แบบฟอร์ม", + "confirm_delete_template" : "การดำเนินการนี้จะลบแบบฟอร์มนี้ คุณต้องการดำเนินการต่อหรือไม่?", + "confirm_delete_wallet" : "การดำเนินการนี้จะลบกระเป๋านี้ คุณต้องการดำเนินการต่อหรือไม่?", + + "picker_description" : "ในการเลือก ChangeNOW หรือ MorphToken โปรดเปลี่ยนคู่แลกเปลี่ยนก่อน", + + "change_wallet_alert_title" : "เปลี่ยนกระเป๋าปัจจุบัน", + "change_wallet_alert_content" : "คุณต้องการเปลี่ยนกระเป๋าปัจจุบันเป็น ${wallet_name} หรือไม่?", + + "creating_new_wallet" : "กำลังสร้างกระเป๋าใหม่", + "creating_new_wallet_error" : "ข้อผิดพลาด: ${description}", + + "seed_alert_title" : "ความสนใจ", + "seed_alert_content" : "Seed เป็นเพียงวิธีเดียวที่จะกู้กระเป๋าของคุณ คุณได้เขียนมันขึ้นลงกระดาษหรือไม่?", + "seed_alert_back" : "กลับ", + "seed_alert_yes" : "ใช่ ฉันได้เขียน", + + "exchange_sync_alert_content" : "โปรดรอจนกว่ากระเป๋าของคุณจะถูกซิงค์", + + "pre_seed_title" : "สำคัญ", + "pre_seed_description" : "บนหน้าถัดไปคุณจะเห็นชุดของคำ ${words} คำ นี่คือ seed ของคุณที่ไม่ซ้ำใดๆ และเป็นความลับเพียงของคุณ และนี่คือเพียงวิธีเดียวที่จะกู้กระเป๋าของคุณในกรณีที่สูญหายหรือมีปัญหา มันเป็นความรับผิดชอบของคุณเพื่อเขียนมันลงบนกระดาษและจัดเก็บไว้ในที่ปลอดภัยนอกแอป Cake Wallet", + "pre_seed_button_text" : "ฉันเข้าใจ แสดง seed ของฉัน", + + "xmr_to_error" : "ข้อผิดพลาด XMR.TO", + "xmr_to_error_description" : "จำนวนไม่ถูกต้อง จำกัดขั้นสูง 8 หลักหลังจุดทศนิยม", + + "provider_error" : "ข้อผิดพลาด ${provider}", + + "use_ssl" : "ใช้ SSL", + "trusted" : "มั่นคง", + + "color_theme" : "ธีมสี", + "light_theme" : "สว่าง", + "bright_theme" : "สดใส", + "dark_theme" : "เข้ม", + "enter_your_note" : "ใส่บันทึกของคุณ...", + "note_optional" : "บันทึก (ไม่จำเป็น)", + "note_tap_to_change" : "หมายเหตุ (กดเพื่อเปลี่ยน)", + "view_in_block_explorer" : "ดูใน Block Explorer", + "view_transaction_on" : "ดูการทำธุรกรรมบน ", + "transaction_key" : "รหัสธุรกรรม", + "confirmations" : "การยืนยัน", + "recipient_address" : "ที่อยู่ผู้รับ", + + "extra_id" : "ไอดีเพิ่มเติม:", + "destination_tag" : "แท็กปลายทาง:", + "memo" : "หมายเหตุ:", + + "backup" : "สำรองข้อมูล", + "change_password" : "เปลี่ยนรหัสผ่าน", + "backup_password" : "รหัสผ่านสำรองข้อมูล", + "write_down_backup_password" : "โปรดจดรหัสผ่านสำรองข้อมูลของคุณลงบนกระดาษ ซึ่งจะใช้ในการนำเข้าไฟล์สำรองข้อมูลของคุณ", + "export_backup" : "ส่งออกข้อมูลสำรอง", + "save_backup_password" : "โปรดตรวจสอบให้แน่ใจว่าคุณได้บันทึกรหัสผ่านสำรองข้อมูลแล้ว คุณจะไม่สามารถนำเข้าไฟล์สำรองข้อมูลของคุณโดยไม่ใช้รหัสผ่านนั้น", + "backup_file" : "ไฟล์สำรองข้อมูล", + + "edit_backup_password" : "แก้ไขรหัสผ่านสำรอง", + "save_backup_password_alert" : "บันทึกรหัสผ่านสำรอง", + "change_backup_password_alert" : "ไฟล์สำรองที่ผ่านมาจะไม่สามารถนำเข้าด้วยรหัสผ่านสำรองใหม่ได้ รหัสผ่านสำรองใหม่จะถูกใช้เฉพาะสำหรับไฟล์สำรองใหม่ คุณแน่ใจหรือว่าต้องการเปลี่ยนรหัสผ่านสำรอง?", + + "enter_backup_password" : "ป้อนรหัสผ่านสำรองที่นี่", + "select_backup_file" : "เลือกไฟล์สำรอง", + "import" : "นำเข้า", + "please_select_backup_file" : "โปรดเลือกไฟล์สำรองและป้อนรหัสผ่านสำรอง", + + "fixed_rate": "อัตราคงที่", + "fixed_rate_alert": "คุณจะสามารถป้อนจำนวนที่ได้รับเมื่อเลือกโหมดอัตราคงที่ คุณต้องการสลับไปที่โหมดอัตราคงที่?", + + "xlm_extra_info": "โปรดอย่าลืมระบุ Memo ID ในขณะที่ส่งธุรกรรม XLM สำหรับการแลกเปลี่ยน", + "xrp_extra_info": "โปรดอย่าลืมระบุ Destination Tag ในขณะที่ส่งธุรกรรม XRP สำหรับการแลกเปลี่ยน", + + "exchange_incorrect_current_wallet_for_xmr" : "หากคุณต้องการแลกเปลี่ยน XMR จากยอดคงเหลือ Monero ใน Cake Wallet ของคุณ กรุณาเปลี่ยนเป็นกระเป๋า Monero ก่อน", + "confirmed" : "ได้รับการยืนยัน", + "unconfirmed" : "ยังไม่ได้รับการยืนยัน", + "displayable" : "สามารถแสดงได้", + + "submit_request" : "ส่งคำขอ", + + "buy_alert_content" : "ในปัจจุบันเรารองรับการซื้อ Bitcoin และ Litecoin เท่านั้น หากต้องการซื้อ Bitcoin หรือ Litecoin โปรดสร้างหรือเปลี่ยนเป็นกระเป๋า Bitcoin หรือ Litecoin ของคุณ", + "sell_alert_content" : "ในปัจจุบันเรารองรับการขาย Bitcoin เท่านั้น หากต้องการขาย Bitcoin โปรดสร้างหรือเปลี่ยนเป็นกระเป๋า Bitcoin ของคุณ", + + "outdated_electrum_wallet_description" : "กระเป๋า Bitcoin ใหม่ที่สร้างใน Cake มี seed ขนาด 24 คำ ซึ่งจำเป็นต้องสร้างกระเป๋า Bitcoin ใหม่และโอนทุกเงินของคุณไปยังกระเป๋าใหม่ขนาด 24 คำ และหยุดใช้กระเป๋าที่มี seed ขนาด 12 คำ กรุณาทำด่วนเพื่อรักษาเงินของคุณ", + "understand" : "ฉันเข้าใจ", + + "apk_update" : "ปรับปรุง APK", + + "buy_bitcoin" : "ซื้อ Bitcoin", + "buy_with" : "ซื้อด้วย", + "moonpay_alert_text" : "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}", + + "outdated_electrum_wallet_receive_warning": "หากกระเป๋านี้มีซีดีที่มี 12 คำและถูกสร้างขึ้นใน Cake อย่าโอน Bitcoin เข้ากระเป๋านี้ ทุกจำนวน BTC ที่โอนเข้ากระเป๋านี้อาจสูญหาย สร้างกระเป๋าใหม่ที่มีซีดีที่มี 24 คำ (กดที่เมนูที่มุมขวาบนแล้วเลือก Wallets และเลือก Create New Wallet จากนั้นเลือก Bitcoin) และย้าย BTC ไปที่นั้นทันที กระเป๋า BTC ที่มีซีดีที่มี 24 คำของ Cake ปลอดภัย", + "do_not_show_me": "อย่าแสดงข้อความนี้อีก", + + "unspent_coins_title" : "เหรียญที่ไม่ได้ใช้", + "unspent_coins_details_title" : "รายละเอียดเหรียญที่ไม่ได้ใช้", + "freeze" : "ดักจับ", + "frozen" : "ถูกดักจับ", + "coin_control" : "การควบคุมเหรียญ (ตัวเลือก)", + + "address_detected" : "ตรวจพบที่อยู่", + "address_from_domain" : "ที่อยู่นี้มาจาก ${domain} บน Unstoppable Domains", + + "add_receiver" : "เพิ่มผู้รับอื่น ๆ (ตัวเลือก)", + + "manage_yats" : "จัดการ Yats", + "yat_alert_title" : "ส่งและรับเหรียญออนไลน์ง่ายขึ้นด้วย Yat", + "yat_alert_content" : "ผู้ใช้งาน Cake Wallet สามารถส่งและรับเหรียญออนไลน์ทุกเหรียญที่ชื่นชอบของพวกเขาได้ง่ายขึ้นด้วยชื่อผู้ใช้งานที่พิเศษที่ระบุด้วยอีโมจิ", + "get_your_yat" : "รับ Yat ของคุณ", + "connect_an_existing_yat" : "เชื่อมต่อ Yat ที่มีอยู่", + "connect_yats": "เชื่อมต่อ Yats", + "yat_address" : "ที่อยู่ Yat", + "yat" : "Yat", + "address_from_yat" : "ที่อยู่นี้มาจาก ${emoji} บน Yat", + "yat_error" : "ข้อผิดพลาดของ Yat", + "yat_error_content" : "ไม่มีที่อยู่ที่เชื่อมต่อกับ Yat นี้ ลองใช้ Yat อื่น", + "choose_address" : "\n\nโปรดเลือกที่อยู่:", + "yat_popup_title" : "ที่อยู่กระเป๋าของคุณสามารถถูกอัปโหลดเป็นอิโมจิ", + "yat_popup_content" : "ขณะนี้คุณสามารถส่งและรับเหรียญคริปโตใน Cake Wallet ด้วย Yat ของคุณ - ชื่อผู้ใช้ที่สั้นมีอิโมจิ คุณสามารถจัดการ Yat ได้ทุกเวลาบนหน้าจอการตั้งค่า", + "second_intro_title" : "อิโมจิที่อยู่เดียวที่จะควบคุมพวกเขาทั้งหมด", + "second_intro_content" : "Yat ของคุณเป็นอิโมจิที่อยู่เดียวที่จะแทนที่ทุกที่อยู่และเลขฐานสิบหกของคุณสำหรับเหรียญคริปโตทุกชนิด", + "third_intro_title" : "Yat ปฏิบัติตนอย่างดีกับผู้อื่น", + "third_intro_content" : "Yat อาศัยอยู่นอก Cake Wallet ด้วย ที่อยู่กระเป๋าใดๆ ทั่วโลกสามารถแทนด้วย Yat ได้อีกด้วย!", + "learn_more" : "ศึกษาเพิ่มเติม", + "search": "ค้นหา", + "search_language": "ค้นหาภาษา", + "search_currency": "ค้นหาสกุลเงิน", + "new_template" : "แม่แบบใหม่", + "electrum_address_disclaimer": "เราสร้างที่อยู่ใหม่ทุกครั้งที่คุณใช้หนึ่งอย่าง แต่ที่อยู่เก่ายังสามารถใช้ได้ต่อไป", + "wallet_name_exists": "กระเป๋าที่มีชื่อนี้มีอยู่แล้ว โปรดเลือกชื่ออื่นหรือเปลี่ยนชื่อกระเป๋าอื่นก่อน", + "market_place": "ตลาดพื้นที่", + "cake_pay_title": "บัตรของขวัญ Cake Pay", + "cake_pay_subtitle": "ซื้อบัตรของขวัญราคาถูก (สำหรับสหรัฐอเมริกาเท่านั้น)", + "cake_pay_web_cards_title": "Cake Pay Web Cards", + "cake_pay_web_cards_subtitle": "ซื้อบัตรพร้อมเงินระดับโลกและบัตรของขวัญ", + "about_cake_pay": "Cake Pay ช่วยให้คุณสามารถซื้อบัตรของขวัญง่ายๆ ด้วยการใช้สินทรัพย์อนุกรม ซื้อใช้ได้ทันทีกับมากกว่า 150,000 ร้านค้าในสหรัฐอเมริกา", + "cake_pay_account_note": "ลงทะเบียนด้วยอีเมลเพียงอย่างเดียวเพื่อดูและซื้อบัตร บางบัตรอาจมีส่วนลด!", + "already_have_account": "มีบัญชีอยู่แล้ว?", + "create_account": "สร้างบัญชี", + "privacy_policy": "นโยบายความเป็นส่วนตัว", + "welcome_to_cakepay": "ยินดีต้อนรับสู่ Cake Pay!", + "sign_up": "สมัครสมาชิก", + "forgot_password": "ลืมรหัสผ่าน", + "reset_password": "รีเซ็ตรหัสผ่าน", + "gift_cards": "บัตรของขวัญ", + "setup_your_debit_card": "ตั้งค่าบัตรเดบิตของคุณ", + "no_id_required": "ไม่จำเป็นต้องใช้บัตรประจำตัว ฝากเงินและใช้งานได้ทุกที่", + "how_to_use_card": "วิธีใช้บัตรนี้", + "purchase_gift_card": "ซื้อบัตรของขวัญ", + "verification": "การตรวจสอบ", + "fill_code": "โปรดกรอกรหัสยืนยันที่ส่งไปยังอีเมลของคุณ", + "dont_get_code": "ไม่ได้รับรหัส?", + "resend_code": "โปรดส่งอีกครั้ง", + "debit_card": "บัตรเดบิต", + "cakepay_prepaid_card": "บัตรเดบิตเติมเงินของ CakePay", + "no_id_needed": "ไม่จำเป็นต้องใช้บัตรประชาชน!", + "frequently_asked_questions": "คำถามที่พบบ่อย", + "debit_card_terms": "การเก็บรักษาและใช้หมายเลขบัตรจ่ายเงิน (และข้อมูลประจำตัวที่เกี่ยวข้องกับหมายเลขบัตรจ่ายเงิน) ในกระเป๋าดิจิทัลนี้ จะต้องยึดถือข้อกำหนดและเงื่อนไขของข้อตกลงผู้ใช้บัตรของผู้ถือบัตรที่เกี่ยวข้องกับบัตรผู้ถือบัตร ซึ่งจะมีผลตั้งแต่เวลานั้น", + "please_reference_document": "โปรดอ้างอิงเอกสารด้านล่างสำหรับข้อมูลเพิ่มเติม", + "cardholder_agreement": "ข้อตกลงผู้ใช้บัตร", + "e_sign_consent": "การยอมรับ E-Sign", + "agree_and_continue": "ยอมรับและดำเนินการต่อ", + "email_address": "ที่อยู่อีเมล", + "agree_to": "การสร้างบัญชีของคุณยอมรับเงื่อนไขของ", + "and": "และ", + "enter_code": "กรอกรหัส", + "congratulations": "ขอแสดงความยินดี!", + "you_now_have_debit_card": "ขณะนี้คุณมีบัตรเดบิต", + "min_amount": "จำนวนขั้นต่ำ: ${value}", + "max_amount": "จำนวนสูงสุด: ${value}", + "enter_amount": "กรอกจำนวน", + "billing_address_info": "ถ้าถูกร้องขอที่อยู่สำหรับการวางบิล ให้ใช้ที่อยู่จัดส่งของคุณ", + "order_physical_card": "สั่งซื้อบัตรดิบิต", + "add_value": "เพิ่มมูลค่า", + "activate": "เปิดใช้งาน", + "get_a": "รับ ", + "digital_and_physical_card": "บัตรเดบิตดิจิตอลและบัตรพื้นฐาน", + "get_card_note": "ที่คุณสามารถเติมเงินด้วยสกุลเงินดิจิตอล ไม่จำเป็นต้องใส่ข้อมูลเพิ่มเติม!", + "signup_for_card_accept_terms": "ลงทะเบียนสำหรับบัตรและยอมรับเงื่อนไข", + "add_fund_to_card": "เพิ่มเงินสำรองไว้บนบัตร (ถึง ${value})", + "use_card_info_two": "เงินจะถูกแปลงค่าเป็นดอลลาร์สหรัฐเมื่อถือไว้ในบัญชีสำรองเงิน ไม่ใช่สกุลเงินดิจิตอล", + "use_card_info_three": "ใช้บัตรดิจิตอลออนไลน์หรือผ่านวิธีการชำระเงินแบบไม่ต้องใช้บัตรกระดาษ", + "optionally_order_card": "เลือกเพิ่มสั่งการ์ดจริง", + "hide_details" : "ซ่อนรายละเอียด", + "show_details" : "แสดงรายละเอียด", + "upto": "สูงสุด ${value}", + "discount": "ประหยัด ${value}%", + "gift_card_amount": "จำนวนบัตรของขวัญ", + "bill_amount": "จำนวนบิล", + "you_pay": "คุณจ่าย", + "tip": "เพิ่มค่าตอบแทน:", + "custom": "กำหนดเอง", + "by_cake_pay": "โดย Cake Pay", + "expires": "หมดอายุ", + "mm": "เดือน", + "yy": "ปี", + "online": "ออนไลน์", + "offline": "ออฟไลน์", + "gift_card_number": "หมายเลขบัตรของขวัญ", + "pin_number": "หมายเลข PIN", + "total_saving": "ประหยัดรวม", + "last_30_days": "30 วันล่าสุด", + "avg_savings": "ประหยัดเฉลี่ย", + "view_all": "ดูทั้งหมด", + "active_cards": "บัตรที่ใช้งานได้", + "delete_account": "ลบบัญชี", + "cards": "บัตร", + "active": "ทำงาน", + "redeemed": "แลกของขวัญ", + "gift_card_balance_note": "บัตรของขวัญที่มียอดคงเหลือจะปรากฏที่นี่", + "gift_card_redeemed_note": "บัตรของขวัญที่คุณแลกไปแล้วจะปรากฏที่นี่", + "logout": "ออกจากระบบ", + "add_tip": "เพิ่มคำแนะนำ", + "percentageOf": "${amount} ของ", + "is_percentage": "เป็น", + "search_category": "ค้นหาหมวดหมู่", + "mark_as_redeemed": "ทำเครื่องหมายว่าเคยใช้แล้ว", + "more_options": "ตัวเลือกเพิ่มเติม", + "awaiting_payment_confirmation": "รอการยืนยันการชำระเงิน", + "transaction_sent_notice": "ถ้าหน้าจอไม่ขึ้นหลังจาก 1 นาทีแล้ว ให้ตรวจสอบ block explorer และอีเมลของคุณ", + "agree": "ยอมรับ", + "in_store": "ในร้าน", + "generating_gift_card": "กำลังสร้างบัตรของขวัญ", + "payment_was_received": "การชำระเงินของคุณได้รับการรับทราบแล้ว", + "proceed_after_one_minute": "หากหน้าจอไม่ดำเนินการหลังจาก 1 นาทีโปรดตรวจสอบอีเมลของคุณ", + "order_id": "เลขที่ออร์เดอร์", + "gift_card_is_generated": "บัตรของขวัญถูกสร้างขึ้น", + "open_gift_card": "เปิดบัตรของขวัญ", + "contact_support": "ติดต่อฝ่ายสนับสนุน", + "gift_cards_unavailable": "บัตรของขวัญจะมีจำหน่ายเฉพาะกับ Monero, Bitcoin, และ Litecoin เท่านั้นในขณะนี้", + "introducing_cake_pay": "ยินดีต้อนรับสู่ Cake Pay!", + "cake_pay_learn_more": "ซื้อและเบิกบัตรของขวัญในแอพพลิเคชันทันที!\nกระแทกขวาไปซ้ายเพื่อเรียนรู้เพิ่มเติม", + "automatic": "อัตโนมัติ", + "fixed_pair_not_supported": "คู่ความสัมพันธ์ที่ถูกกำหนดไว้นี้ไม่สนับสนุนกับหุ้นที่เลือก", + "variable_pair_not_supported": "คู่ความสัมพันธ์ที่เปลี่ยนแปลงได้นี้ไม่สนับสนุนกับหุ้นที่เลือก", + "none_of_selected_providers_can_exchange": "ไม่มีผู้ให้บริการที่เลือกที่สามารถแลกเปลี่ยนนี้ได้", + "choose_one": "เลือกหนึ่งรายการ", + "choose_from_available_options": "เลือกจากตัวเลือกที่มีอยู่:", + "custom_redeem_amount": "จำนวนรับคืนที่กำหนดเอง", + "add_custom_redemption": "เพิ่มการรับคืนที่กำหนดเอง", + "remaining": "เหลืออยู่", + "delete_wallet": "ลบกระเป๋า", + "delete_wallet_confirm_message" : "คุณแน่ใจหรือว่าต้องการลบกระเป๋า${wallet_name}?", + "low_fee": "ค่าธรรมเนียมต่ำ", + "low_fee_alert": "ขณะนี้คุณกำลังใช้ค่าธรรมเนียมของเครือข่ายที่มีความสำคัญต่ำ ซึ่งอาจทำให้เกิดการรอนาน ราคาที่แตกต่างกัน หรือยกเลิกการซื้อขาย เราแนะนำให้กำหนดค่าธรรมเนียมที่สูงขึ้นเพื่อประสบการณ์ที่ดีขึ้น", + "ignor": "ละเว้น", + "use_suggested": "ใช้ที่แนะนำ", + "do_not_share_warning_text" : "อย่าแชร์ข้อมูลนี้กับใครอื่น รวมถึงฝ่ายสนับสนุนด้วย\n\nการเงินของคุณอาจถูกขโมยโดยไม่หวังดี!", + "help": "ช่วยเหลือ", + "all_transactions": "การทำธุรกรรมทั้งหมด", + "all_trades": "การซื้อขายทั้งหมด", + "connection_sync": "การเชื่อมต่อและการซิงค์", + "security_and_backup": "ความปลอดภัยและการสำรองข้อมูล", + "create_backup": "สร้างการสำรองข้อมูล", + "privacy_settings": "การตั้งค่าความเป็นส่วนตัว", + "privacy": "ความเป็นส่วนตัว", + "display_settings": "การตั้งค่าการแสดงผล", + "other_settings": "การตั้งค่าอื่น ๆ", + "require_pin_after": "ต้องการ PIN หลังจาก", + "always": "เสมอ", + "minutes_to_pin_code": "${minute} นาที", + "disable_exchange": "ปิดใช้งานการแลกเปลี่ยน", + "advanced_privacy_settings": "การตั้งค่าความเป็นส่วนตัวขั้นสูง", + "settings_can_be_changed_later" : "การตั้งค่านี้สามารถเปลี่ยนแปลงได้ภายหลังในการตั้งค่าแอพฯ", + "add_custom_node" : "เพิ่มจุดโหนดแบบกำหนดเอง", + "disable_fiat" : "ปิดใช้งานสกุลเงินตรา", + "fiat_api" : "API สกุลเงินตรา", + "disabled" : "ปิดใช้งาน", + "enabled" : "เปิดใช้งาน", + "tor_only" : "Tor เท่านั้น", + "unmatched_currencies" : "สกุลเงินของกระเป๋าปัจจุบันของคุณไม่ตรงกับของ QR ที่สแกน", + "contact_list_contacts": "ติดต่อ", + "contact_list_wallets": "กระเป๋าเงินของฉัน" +} diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 91042e91b..4fad9a19d 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -48,7 +48,7 @@ "outgoing" : "Вихідні", "transactions_by_date" : "Сортувати по даті", "trades" : "Торгові операції", - "filters" : "Фільтр", + "filter_by": "Фільтрувати по", "today" : "Сьогодні", "yesterday" : "Вчора", "received" : "Отримані", @@ -355,6 +355,7 @@ "sync_status_failed_connect" : "ВІДКЛЮЧЕНО", "sync_status_connecting" : "ПІДКЛЮЧЕННЯ", "sync_status_connected" : "ПІДКЛЮЧЕНО", + "sync_status_attempting_sync" : "СПРОБА СИНХРОНІЗАЦІЇ", "transaction_priority_slow" : "Повільний", @@ -435,6 +436,7 @@ "provider_error" : "${provider} помилка", "use_ssl" : "Використати SSL", + "trusted" : "довіряють", "color_theme" : "Кольорова тема", "light_theme" : "Світла", @@ -536,7 +538,9 @@ "wallet_name_exists": "Гаманець з такою назвою вже існує", "market_place": "Ринок", "cake_pay_title": "Подарункові картки Cake Pay", - "cake_pay_subtitle": "Купуйте подарункові картки та використовуйте їх миттєво", + "cake_pay_subtitle": "Купуйте подарункові картки зі знижкою (тільки для США)", + "cake_pay_web_cards_title": "Веб-картки Cake Pay", + "cake_pay_web_cards_subtitle": "Купуйте передоплачені та подарункові картки по всьому світу", "about_cake_pay": "Cake Pay дозволяє вам легко купувати подарункові картки з віртуальними активами, які можна миттєво витратити в понад 150 000 продавців у Сполучених Штатах.", "cake_pay_account_note": "Зареєструйтеся, використовуючи лише адресу електронної пошти, щоб переглядати та купувати картки. Деякі навіть доступні зі знижкою!", "already_have_account": "Вже є обліковий запис?", @@ -649,7 +653,30 @@ "low_fee_alert": "Зараз ви використовуєте низький пріоритет плати за мережу. Це може спричинити тривале очікування, інший курс або скасування угод. Ми рекомендуємо встановити вищу плату для кращого досвіду.", "ignor": "Ігнорувати", "use_suggested": "Використати запропоноване", - "do_not_share_warning_text" : "Не повідомляйте їх нікому, включно зі службою підтримки.\n\nВони вкрадуть ваші гроші!", + "do_not_share_warning_text" : "Не діліться цим нікому, включно зі службою підтримки.\n\nВаші кошти можуть і будуть вкрадені!", "help": "допомога", - "disable_exchange": "Вимкнути exchange" + "all_transactions": "Всі транзакції", + "all_trades": "Всі операції", + "connection_sync": "Підключення та синхронізація", + "security_and_backup": "Безпека та резервне копіювання", + "create_backup": "Створити резервну копію", + "privacy_settings": "Налаштування конфіденційності", + "privacy": "Конфіденційність", + "display_settings": "Налаштування дисплея", + "other_settings": "Інші налаштування", + "require_pin_after": "Вимагати PIN після", + "always": "Завжди", + "minutes_to_pin_code": "${minute} хвилин", + "disable_exchange": "Вимкнути exchange", + "advanced_privacy_settings": "Розширені налаштування конфіденційності", + "settings_can_be_changed_later": "Ці параметри можна змінити пізніше в налаштуваннях програми", + "add_custom_node": "Додати новий спеціальний вузол", + "disable_fiat": "Вимкнути фиат", + "fiat_api": "Фіат API", + "disabled": "Вимкнено", + "enabled": "Увімкнено", + "tor_only": "Тільки Tor", + "unmatched_currencies": "Валюта вашого гаманця не збігається з валютою сканованого QR-коду", + "contact_list_contacts": "Контакти", + "contact_list_wallets": "Мої гаманці" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 958e80d14..7c0f4ab74 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -48,7 +48,7 @@ "outgoing" : "发送", "transactions_by_date" : "按日期交易", "trades" : "交易", - "filters" : "过滤", + "filter_by": "过滤", "today" : "今天", "yesterday" : "昨天", "received" : "已收到", @@ -356,6 +356,7 @@ "sync_status_failed_connect" : "断线", "sync_status_connecting" : "连接中", "sync_status_connected" : "已连接", + "sync_status_attempting_sync" : "嘗試同步", "transaction_priority_slow" : "慢速", @@ -435,6 +436,7 @@ "provider_error" : "${provider} 错误", "use_ssl" : "使用SSL", + "trusted" : "值得信赖", "color_theme" : "主题", "light_theme" : "艳丽", @@ -535,7 +537,9 @@ "wallet_name_exists": "同名的钱包已经存在", "market_place": "市场", "cake_pay_title": "Cake Pay 礼品卡", - "cake_pay_subtitle": "购买礼品卡并立即兑换", + "cake_pay_subtitle": "购买打折礼品卡(仅限美国)", + "cake_pay_web_cards_title": "蛋糕支付网络卡", + "cake_pay_web_cards_subtitle": "购买全球预付卡和礼品卡", "about_cake_pay": "Cake Pay 让您可以轻松购买带有虚拟资产的礼品卡,可立即在美国超过 150,000 家商家消费。", "cake_pay_account_note": "只需使用電子郵件地址註冊即可查看和購買卡片。有些甚至可以打折!", "already_have_account": "已经有账号了?", @@ -648,7 +652,30 @@ "low_fee_alert": "您当前正在使用低网络费用优先级。这可能会导致长时间等待、不同的费率或取消交易。我们建议设置更高的费用以获得更好的体验。", "ignor": "忽视", "use_suggested": "使用建议", - "do_not_share_warning_text" : "不要與其他任何人分享這些內容,包括支持。\n\n他們會偷你的錢!", + "do_not_share_warning_text" : "请勿与其他任何人分享这些信息,包括支持人员。\n\n您的资金可能而且将会被盗!", "help": "帮助", - "disable_exchange": "禁用交换" + "all_transactions": "所有交易", + "all_trades": "所有的变化", + "connection_sync": "连接和同步", + "security_and_backup": "安全和备份", + "create_backup": "创建备份", + "privacy_settings": "隐私设置", + "privacy":"隐私", + "display_settings": "显示设置", + "other_settings": "其他设置", + "require_pin_after": "之后需要 PIN", + "always": "总是", + "minutes_to_pin_code": "${minute} 分钟", + "disable_exchange": "禁用交换", + "advanced_privacy_settings": "高级隐私设置", + "settings_can_be_changed_later": "稍后可以在应用设置中更改这些设置", + "add_custom_node": "添加新的自定义节点", + "disable_fiat": "禁用法令", + "fiat_api": "法币API", + "disabled": "禁用", + "enabled": "启用", + "tor_only": "仅限 Tor", + "unmatched_currencies": "您当前钱包的货币与扫描的 QR 的货币不匹配", + "contact_list_contacts": "联系人", + "contact_list_wallets": "我的钱包" } diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index d361c8dfa..c74d97c0f 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.2.1" -MONERO_COM_BUILD_NUMBER=32 +MONERO_COM_VERSION="1.2.5" +MONERO_COM_BUILD_NUMBER=36 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.5.1" -CAKEWALLET_BUILD_NUMBER=136 +CAKEWALLET_VERSION="4.5.6" +CAKEWALLET_BUILD_NUMBER=141 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" diff --git a/scripts/android/build_haven.sh b/scripts/android/build_haven.sh index 6d22f7778..5f3c9e50a 100755 --- a/scripts/android/build_haven.sh +++ b/scripts/android/build_haven.sh @@ -1,7 +1,7 @@ #!/bin/sh . ./config.sh -HAVEN_VERSION=tags/v2.2.2 +HAVEN_VERSION=tags/v3.0.0 HAVEN_SRC_DIR=${WORKDIR}/haven git clone https://github.com/haven-protocol-org/haven-main.git ${HAVEN_SRC_DIR} diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 8bb945680..8588bfcb2 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.2.1" -MONERO_COM_BUILD_NUMBER=29 +MONERO_COM_VERSION="1.2.5" +MONERO_COM_BUILD_NUMBER=33 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.5.1" -CAKEWALLET_BUILD_NUMBER=133 +CAKEWALLET_VERSION="4.5.6" +CAKEWALLET_BUILD_NUMBER=138 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/ios/build_haven.sh b/scripts/ios/build_haven.sh index cc79d7a26..3199e3286 100755 --- a/scripts/ios/build_haven.sh +++ b/scripts/ios/build_haven.sh @@ -4,7 +4,7 @@ HAVEN_URL="https://github.com/haven-protocol-org/haven-main.git" HAVEN_DIR_PATH="${EXTERNAL_IOS_SOURCE_DIR}/haven" -HAVEN_VERSION=tags/v2.2.2 +HAVEN_VERSION=tags/v3.0.0 BUILD_TYPE=release PREFIX=${EXTERNAL_IOS_DIR} DEST_LIB_DIR=${EXTERNAL_IOS_LIB_DIR}/haven diff --git a/tool/configure.dart b/tool/configure.dart index 0d3d175c4..5ae5a2111 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -75,6 +75,7 @@ abstract class Bitcoin { List getTransactionPriorities(); List getLitecoinTransactionPriorities(); TransactionPriority deserializeBitcoinTransactionPriority(int raw); + TransactionPriority deserializeLitecoinTransactionPriority(int raw); int getFeeRate(Object wallet, TransactionPriority priority); Future generateNewAddress(Object wallet); Object createBitcoinTransactionCredentials(List outputs, {required TransactionPriority priority, int? feeRate}); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 64c7beefd..e5202a829 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -28,6 +28,7 @@ class SecretKey { SecretKey('simpleSwapApiKey', () => ''), SecretKey('anypayToken', () => ''), SecretKey('onramperApiKey', () => ''), + SecretKey('ioniaClientId', () => ''), ]; final String name;