Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-237-enhance-error-catching-reporting

This commit is contained in:
OmarHatem 2023-01-06 16:18:21 +02:00
commit baeff1ea14
166 changed files with 4306 additions and 2203 deletions

View file

@ -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

151
.github/workflows/pr_test_build.yml vendored Normal file
View file

@ -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"'"}'

View file

@ -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.

View file

@ -16,7 +16,7 @@
android:requestLegacyExternalStorage="true">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:launchMode="singleInstance"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
@ -39,6 +39,15 @@
android:scheme="cakewallet"
android:host="y.at" />
</intent-filter>
<!-- currencies qr code scheme -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bitcoin" />
<data android:scheme="monero" />
<data android:scheme="litecoin" />
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

View file

@ -1,31 +1,24 @@
-
uri: xmr-node-uk.cakewallet.com:18081
uri: xmr-node.cakewallet.com:18081
is_default: true
-
uri: xmr-node-eu.cakewallet.com:18081
uri: cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081
is_default: false
-
uri: xmr-node-usa-east.cakewallet.com:18081
uri: node.sethforprivacy.com:18089
is_default: false
-
uri: nodes.hashvault.pro:18081
is_default: false
-
uri: node.c3pool.com:18081
is_default: false
-
uri: node.supportxmr.com:18081
is_default: false
-
uri: node.community.rino.io:18081
is_default: false
-
uri: node.moneroworld.com:18089
is_default: false
-
uri: node.xmr.pt:18081
is_default: false
-
uri: node.monero.net:18081
is_default: false
-
uri: opennode.xmr-tw.org:18089
is_default: false
-
uri: node.imonero.org:18081
is_default: false
uri: node.c3pool.com:18081
is_default: false
uri: xmr.prprpr.icu:18081
is_default: false

View file

@ -303,7 +303,7 @@ class ElectrumClient {
Future<List<int>> 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)

View file

@ -72,7 +72,7 @@ abstract class ElectrumTransactionHistoryBase
txs.entries.forEach((entry) {
final val = entry.value;
if (val is Map<String, Object>) {
if (val is Map<String, dynamic>) {
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;
}
}
}

View file

@ -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;

View file

@ -129,7 +129,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
@override
Future<void> startSync() async {
try {
syncStatus = StartingSyncStatus();
syncStatus = AttemptingSyncStatus();
await walletAddresses.discoverAddresses();
await updateTransactions();
_subscribeForUpdates();
@ -191,8 +191,10 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
throw BitcoinTransactionNoInputsException();
}
final allAmountFee = feeAmountForPriority(
transactionCredentials.priority!, inputs.length, outputs.length);
final allAmountFee = transactionCredentials.feeRate != null
? feeAmountWithFeeRate(transactionCredentials.feeRate!, inputs.length, outputs.length)
: feeAmountForPriority(transactionCredentials.priority!, inputs.length, outputs.length);
final allAmount = allInputsAmount - allAmountFee;
var credentialsAmount = 0;

View file

@ -1,20 +1,18 @@
import 'package:cw_core/enumerable_item.dart';
import 'package:hive/hive.dart';
part 'crypto_currency.g.dart';
@HiveType(typeId: 0)
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
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<int> with Serializable<int> {
CryptoCurrency.ape,
CryptoCurrency.avaxc,
CryptoCurrency.btt,
CryptoCurrency.bttbsc,
CryptoCurrency.bttc,
CryptoCurrency.doge,
CryptoCurrency.firo,
CryptoCurrency.usdttrc20,
@ -53,7 +51,6 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
CryptoCurrency.xvg,
CryptoCurrency.usdcpoly,
CryptoCurrency.dcr,
CryptoCurrency.husd,
CryptoCurrency.kmd,
CryptoCurrency.mana,
CryptoCurrency.maticpoly,
@ -70,339 +67,116 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
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<int, CryptoCurrency> _rawCurrencyMap =
[...all, ...havenCurrencies].fold<Map<int, CryptoCurrency>>(<int, CryptoCurrency>{}, (acc, item) {
acc.addAll({item.raw: item});
return acc;
});
static final Map<String, CryptoCurrency> _nameCurrencyMap =
[...all, ...havenCurrencies].fold<Map<String, CryptoCurrency>>(<String, CryptoCurrency>{}, (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

View file

@ -21,22 +21,6 @@ class MoneroTransactionPriority extends TransactionPriority {
static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4);
static const standard = slow;
static List<MoneroTransactionPriority> 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:

View file

@ -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<String, dynamic>;

View file

@ -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;
}

View file

@ -927,6 +927,16 @@ extern "C"
return static_cast<int32_t>(rates.size());
}
void set_trusted_daemon(bool arg)
{
m_wallet->setTrustedDaemon(arg);
}
bool trusted_daemon()
{
return m_wallet->trustedDaemon();
}
#ifdef __cplusplus
}
#endif

View file

@ -137,4 +137,8 @@ typedef get_rate = Pointer<Int64> Function();
typedef size_of_rate = Int32 Function();
typedef update_rate = Void Function();
typedef update_rate = Void Function();
typedef set_trusted_daemon = Void Function(Int8 trusted);
typedef trusted_daemon = Int8 Function();

View file

@ -135,4 +135,8 @@ typedef GetRate = Pointer<Int64> Function();
typedef SizeOfRate = int Function();
typedef UpdateRate = void Function();
typedef UpdateRate = void Function();
typedef SetTrustedDaemon = void Function(int);
typedef TrustedDaemon = int Function();

View file

@ -116,6 +116,14 @@ final rescanBlockchainAsyncNative = havenApi
.lookup<NativeFunction<rescan_blockchain>>('rescan_blockchain')
.asFunction<RescanBlockchainAsync>();
final setTrustedDaemonNative = havenApi
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
.asFunction<SetTrustedDaemon>();
final trustedDaemonNative = havenApi
.lookup<NativeFunction<trusted_daemon>>('trusted_daemon')
.asFunction<TrustedDaemon>();
int getSyncingHeight() => getSyncingHeightNative();
bool isNeededToRefresh() => isNeededToRefreshNative() != 0;
@ -351,3 +359,7 @@ Future<bool> isConnected() => compute(_isConnected, 0);
Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted));
Future<bool> trustedDaemon() async => trustedDaemonNative() != 0;

View file

@ -121,6 +121,8 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
password: node.password,
useSSL: node.useSSL ?? false,
isLightWallet: false); // FIXME: hardcoded value
haven_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();
} catch (e) {
syncStatus = FailedSyncStatus();
@ -135,7 +137,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
} catch (_) {}
try {
syncStatus = StartingSyncStatus();
syncStatus = AttemptingSyncStatus();
haven_wallet.startRefresh();
_setListeners();
_listener?.start();

View file

@ -783,6 +783,16 @@ extern "C"
return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str());
}
void set_trusted_daemon(bool arg)
{
m_wallet->setTrustedDaemon(arg);
}
bool trusted_daemon()
{
return m_wallet->trustedDaemon();
}
#ifdef __cplusplus
}
#endif

View file

@ -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

View file

@ -125,4 +125,8 @@ typedef rescan_blockchain = Void Function();
typedef get_subaddress_label = Pointer<Utf8> Function(
Int32 accountIndex,
Int32 addressIndex);
Int32 addressIndex);
typedef set_trusted_daemon = Void Function(Int8 trusted);
typedef trusted_daemon = Int8 Function();

View file

@ -123,4 +123,8 @@ typedef RescanBlockchainAsync = void Function();
typedef GetSubaddressLabel = Pointer<Utf8> Function(
int accountIndex,
int addressIndex);
int addressIndex);
typedef SetTrustedDaemon = void Function(int);
typedef TrustedDaemon = int Function();

View file

@ -120,6 +120,14 @@ final getSubaddressLabelNative = moneroApi
.lookup<NativeFunction<get_subaddress_label>>('get_subaddress_label')
.asFunction<GetSubaddressLabel>();
final setTrustedDaemonNative = moneroApi
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
.asFunction<SetTrustedDaemon>();
final trustedDaemonNative = moneroApi
.lookup<NativeFunction<trusted_daemon>>('trusted_daemon')
.asFunction<TrustedDaemon>();
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));
}
}
Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted));
Future<bool> trustedDaemon() async => trustedDaemonNative() != 0;

View file

@ -136,6 +136,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
password: node.password,
useSSL: node.isSSL,
isLightWallet: false); // FIXME: hardcoded value
monero_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();
} catch (e) {
syncStatus = FailedSyncStatus();
@ -150,7 +152,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
} catch (_) {}
try {
syncStatus = StartingSyncStatus();
syncStatus = AttemptingSyncStatus();
monero_wallet.startRefresh();
_setListeners();
_listener?.start();

View file

@ -55,7 +55,7 @@ You may download and install the latest version of Android Studio [here](https:/
### 3. Installing Flutter
Need to install flutter with version `2.0.4`. For this please check section [Install Flutter manually](https://docs.flutter.dev/get-started/install/linux#install-flutter-manually). When flutter repository is downloaded please open it with `cd <flutter-path>` 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`

View file

@ -21,18 +21,48 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>y.at</string>
<key>CFBundleURLSchemes</key>
<array>
<string>cakewallet</string>
</array>
</dict>
</array>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>y.at</string>
<key>CFBundleURLSchemes</key>
<array>
<string>cakewallet</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>bitcoin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bitcoin</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>monero</string>
<key>CFBundleURLSchemes</key>
<array>
<string>monero</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>litecoin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>litecoin</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>

View file

@ -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;

View file

@ -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:

View file

@ -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<void> setPassword(String password) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
@ -19,8 +26,7 @@ class AuthService with Store {
Future<bool> 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;
}
}

View file

@ -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);

View file

@ -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) {

View file

@ -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>(() => AuthService(
secureStorage: getIt.get<FlutterSecureStorage>(),
sharedPreferences: getIt.get<SharedPreferences>()));
sharedPreferences: getIt.get<SharedPreferences>(),
settingsStore: getIt.get<SettingsStore>(),
),
);
getIt.registerFactory<AuthViewModel>(() => AuthViewModel(
getIt.get<AuthService>(),
@ -343,7 +355,7 @@ Future setup(
onAuthenticationFinished: onAuthFinished,
closable: closable ?? false));
getIt.registerFactory(() =>
getIt.registerFactory(() =>
BalancePage(dashboardViewModel: getIt.get<DashboardViewModel>(), settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactory<DashboardPage>(() => DashboardPage( balancePage: getIt.get<BalancePage>(), walletViewModel: getIt.get<DashboardViewModel>(), addressListViewModel: getIt.get<WalletAddressListViewModel>()));
@ -376,9 +388,11 @@ Future setup(
getIt.get<BalanceViewModel>(),
_transactionDescriptionBox));
getIt.registerFactory(
() => SendPage(sendViewModel: getIt.get<SendViewModel>(),
settingsViewModel: getIt.get<SettingsViewModel>()));
getIt.registerFactoryParam<SendPage, PaymentRequest?, void>(
(PaymentRequest? initialPaymentRequest, _) => SendPage(
sendViewModel: getIt.get<SendViewModel>(),
initialPaymentRequest: initialPaymentRequest,
));
getIt.registerFactory(() => SendTemplatePage(
sendTemplateViewModel: getIt.get<SendTemplateViewModel>()));
@ -386,7 +400,10 @@ Future setup(
getIt.registerFactory(() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>()));
getIt.get<WalletLoadingService>(),
getIt.get<AuthService>(),
),
);
getIt.registerFactory(() =>
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>()));
@ -434,12 +451,20 @@ Future setup(
getIt.get<MoneroAccountEditOrCreateViewModel>(param1: account)));
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
final yatStore = getIt.get<YatStore>();
return SettingsViewModel(appStore.settingsStore, yatStore, appStore.wallet!);
return DisplaySettingsViewModel(getIt.get<SettingsStore>());
});
getIt.registerFactory(() => SettingsPage(getIt.get<SettingsViewModel>()));
getIt.registerFactory(() {
return PrivacySettingsViewModel(getIt.get<SettingsStore>());
});
getIt.registerFactory(() {
return OtherSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
});
getIt.registerFactory(() {
return SecuritySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AuthService>());
});
getIt
.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!));
@ -458,12 +483,11 @@ Future setup(
(ContactRecord? contact, _) =>
ContactViewModel(_contactSource, contact: contact));
getIt.registerFactory(
() => ContactListViewModel(_contactSource, _walletInfoSource));
getIt.registerFactoryParam<ContactListViewModel, CryptoCurrency?, void>(
(CryptoCurrency? cur, _) => ContactListViewModel(_contactSource, _walletInfoSource, cur));
getIt.registerFactoryParam<ContactListPage, bool, void>(
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
isEditable: isEditable));
getIt.registerFactoryParam<ContactListPage, CryptoCurrency?, void>((CryptoCurrency? cur, _)
=> ContactListPage(getIt.get<ContactListViewModel>(param1: cur)));
getIt.registerFactoryParam<ContactPage, ContactRecord?, void>(
(ContactRecord? contact, _) =>
@ -475,10 +499,22 @@ Future setup(
_nodeSource, appStore.wallet!, appStore.settingsStore);
});
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<NodeListViewModel>(), getIt.get<DashboardViewModel>()));
getIt.registerFactory(() =>
NodeCreateOrEditViewModel(_nodeSource, getIt.get<AppStore>().wallet!));
getIt.registerFactory(() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>()));
getIt.registerFactory(() => PrivacyPage(getIt.get<PrivacySettingsViewModel>()));
getIt.registerFactory(() => DisplaySettingsPage(getIt.get<DisplaySettingsViewModel>()));
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
getIt.registerFactoryParam<NodeCreateOrEditViewModel, WalletType?, void>(
(WalletType? type, _) => NodeCreateOrEditViewModel(
_nodeSource,
type ?? getIt.get<AppStore>().wallet!.type,
getIt.get<SettingsStore>(),
));
getIt.registerFactory(
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
@ -494,7 +530,6 @@ Future setup(
getIt.get<TradesStore>(),
getIt.get<AppStore>().settingsStore,
getIt.get<SharedPreferences>(),
getIt.get<SettingsViewModel>(),
));
getIt.registerFactory(() => ExchangeTradeViewModel(
@ -678,7 +713,7 @@ Future setup(
getIt.registerFactoryParam<FullscreenQRPage, String, bool>(
(String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,));
getIt.registerFactory(() => IoniaApi());
getIt.registerFactory(() => AnyPayApi());
@ -698,7 +733,7 @@ Future setup(
getIt.registerFactoryParam<IoniaMerchPurchaseViewModel, double, IoniaMerchant>((double amount, merchant) {
return IoniaMerchPurchaseViewModel(
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
amount: amount,
ioniaMerchant: merchant,
sendViewModel: getIt.get<SendViewModel>()
@ -741,31 +776,32 @@ Future setup(
ioniaService: getIt.get<IoniaService>(),
giftCard: giftCard);
});
getIt.registerFactoryParam<IoniaCustomTipViewModel, List, void>((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<IoniaGiftCardDetailPage, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) {
return IoniaGiftCardDetailPage(getIt.get<IoniaGiftCardDetailsViewModel>(param1: giftCard));
});
getIt.registerFactoryParam<IoniaMoreOptionsPage, List, void>((List args, _){
final giftCard = args.first as IoniaGiftCard;
return IoniaMoreOptionsPage(giftCard);
return IoniaMoreOptionsPage(giftCard);
});
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) => IoniaCustomRedeemViewModel(giftCard));
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _)
=> IoniaCustomRedeemViewModel(giftCard: giftCard, ioniaService: getIt.get<IoniaService>()));
getIt.registerFactoryParam<IoniaCustomRedeemPage, List, void>((List args, _){
final giftCard = args.first as IoniaGiftCard;
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard) );
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard) );
});
@ -794,5 +830,8 @@ Future setup(
(IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo)
=> IoniaPaymentStatusPage(getIt.get<IoniaPaymentStatusViewModel>(param1: paymentInfo, param2: committedInfo)));
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>((type, _) =>
AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
_isSetupFinished = true;
}
}

View file

@ -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<void> 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<void> addOnionNode(Box<Node> 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<void> replaceNodesMigration({required Box<Node> nodes}) async {
@ -176,7 +207,7 @@ Future<void> 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<Node> nodes}) {
@ -223,7 +254,7 @@ Future<void> 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<void> changeLitecoinCurrentElectrumServerToDefault(
@ -232,7 +263,7 @@ Future<void> 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<void> changeHavenCurrentNodeToDefault(
@ -252,7 +283,7 @@ Future<void> 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<void> updateNodeTypes({required Box<Node> nodes}) async {
Future<void> addBitcoinElectrumServerList({required Box<Node> 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<void> addLitecoinElectrumServerList({required Box<Node> 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<void> addHavenNodeList({required Box<Node> 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<void> addAddressesForMoneroWallets(
@ -431,7 +474,7 @@ Future<void> 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 =

View file

@ -0,0 +1,39 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/enumerable_item.dart';
class FiatApiMode extends EnumerableItem<int> with Serializable<int> {
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 '';
}
}
}

View file

@ -18,7 +18,8 @@ class LanguageService {
'uk': 'Українська (Ukrainian)',
'zh': '中文 (Chinese)',
'hr': 'Hrvatski (Croatian)',
'it': 'Italiano (Italian)'
'it': 'Italiano (Italian)',
'th': 'ภาษาไทย (Thai)'
};
static const Map<String, String> localeCountryCode = {
@ -36,7 +37,8 @@ class LanguageService {
'uk': 'ukr',
'zh': 'chn',
'hr': 'hrv',
'it': 'ita'
'it': 'ita',
'th': 'tha'
};
static final list = <String, String> {};

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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}';

View file

@ -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<TransactionPriority> 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 [];
}
}

View file

@ -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 = <String, String>{
'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<String, dynamic>;
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<double> calculateAmount(
Future<double> 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 = <String, String>{
'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;

View file

@ -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<Trade> findTradeById({required String id});
Future<double> calculateAmount({
Future<double> fetchRate({
required CryptoCurrency from,
required CryptoCurrency to,
required double amount,

View file

@ -24,6 +24,9 @@ class ExchangeProviderDescription extends EnumerableItem<int>
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<int>
return sideShift;
case 4:
return simpleSwap;
case 5:
return all;
default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
}

View file

@ -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<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,

View file

@ -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<CryptoCurrency> _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<ExchangePair> _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<double> calculateAmount(
Future<double> 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';

View file

@ -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<double> calculateAmount(
Future<double> 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';

View file

@ -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<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,

View file

@ -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,

View file

@ -148,8 +148,8 @@ class IoniaService {
// Redeem
Future<void> redeem(IoniaGiftCard giftCard) async {
await chargeGiftCard(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
Future<void> redeem({required int giftCardId, required double amount}) async {
await chargeGiftCard(giftCardId: giftCardId, amount: amount);
}
// Get Gift Card

View file

@ -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<NavigatorState>();
final rootKey = GlobalKey<RootState>();
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
Future<void> main() async {
@ -166,7 +168,7 @@ Future<void> 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<App> with SingleTickerProviderStateMixin {
//_handleInitialUri();
}
@override
void dispose() {
stream?.cancel();
super.dispose();
}
Future<void> _handleInitialUri() async {
try {
final uri = await getInitialUri();
@ -329,11 +325,12 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
Widget build(BuildContext context) {
return Observer(builder: (BuildContext context) {
final appStore = getIt.get<AppStore>();
final authService = getIt.get<AuthService>();
final settingsStore = appStore.settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>();
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<App> with SingleTickerProviderStateMixin {
appStore: appStore,
authenticationStore: authenticationStore,
navigatorKey: navigatorKey,
authService: authService,
child: MaterialApp(
navigatorObservers: [routeObserver],
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
theme: settingsStore.theme,

View file

@ -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<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
final currentWalletName = getIt
.get<SharedPreferences>()
.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);
}

View file

@ -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<void> startFiatRateUpdate(AppStore appStore, SettingsStore settingsStore,
FiatConversionStore fiatConversionStore) async {
Future<void> 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);
}
});
}

View file

@ -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;
}
});
}

View file

@ -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;
}

View file

@ -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>, TransactionInfo>?
wallet) async {
try {
if (wallet == null) {
if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
return;
}

View file

@ -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<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<DashboardPage>());
case Routes.send:
final initialPaymentRequest = settings.arguments as PaymentRequest?;
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<SendPage>());
fullscreenDialog: true, builder: (_) => getIt.get<SendPage>(
param1: initialPaymentRequest,
));
case Routes.sendTemplate:
return CupertinoPageRoute<void>(
@ -274,10 +286,26 @@ Route<dynamic> createRoute(RouteSettings settings) {
param2: false),
onWillPop: () async => false));
case Routes.nodeList:
case Routes.connectionSync:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NodeListPage>());
builder: (_) => getIt.get<ConnectionSyncPage>());
case Routes.securityBackupPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SecurityBackupPage>());
case Routes.privacyPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<PrivacyPage>());
case Routes.displaySettingsPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<DisplaySettingsPage>());
case Routes.otherSettingsPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<OtherSettingsPage>());
case Routes.newNode:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NodeCreateOrEditPage>());
@ -298,11 +326,13 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.addressBook:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<ContactListPage>(param1: true));
builder: (_) =>
getIt.get<ContactListPage>());
case Routes.pickerAddressBook:
final selectedCurrency = settings.arguments as CryptoCurrency;
return MaterialPageRoute<void>(
builder: (_) => getIt.get<ContactListPage>(param1: false));
builder: (_) => getIt.get<ContactListPage>(param1: selectedCurrency));
case Routes.addressBookAddContact:
return CupertinoPageRoute<void>(
@ -360,9 +390,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<ExchangeTemplatePage>());
case Routes.settings:
return MaterialPageRoute<void>(builder: (_) => getIt.get<SettingsPage>());
case Routes.rescan:
return MaterialPageRoute<void>(builder: (_) => getIt.get<RescanPage>());
@ -475,6 +502,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.onramperPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<OnRamperPage>());
case Routes.advancedPrivacySettings:
final type = settings.arguments as WalletType;
return CupertinoPageRoute<void>(
builder: (_) => AdvancedPrivacySettingsPage(
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
getIt.get<NodeCreateOrEditViewModel>(param1: type),
));
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(

View file

@ -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';
}

View file

@ -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: <Widget>[
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<bool> showAlertDialog(BuildContext context) async {
return await showPopUp<bool>(
context: context,

View file

@ -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

View file

@ -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<void> _presentReconnectAlert(BuildContext context) async {
await showPopUp<void>(
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();
}
}

View file

@ -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,
);
}

View file

@ -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: <Widget>[
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: <Widget>[
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: <Widget>[
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<DateTime> 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)
],
),
);
}
}
}

View file

@ -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);
}
}

View file

@ -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));
}

View file

@ -40,7 +40,6 @@ class ExchangePage extends BasePage {
final ExchangeViewModel exchangeViewModel;
final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>();
final checkBoxKey = GlobalKey<StandardCheckboxState>();
final _formKey = GlobalKey<FormState>();
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);

View file

@ -56,7 +56,7 @@ class CurrencyPickerState extends State<CurrencyPicker> {
.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;
}

View file

@ -395,7 +395,8 @@ class ExchangeCardState extends State<ExchangeCard> {
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency
),
)

View file

@ -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<ExchangeTradeForm> {
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<ExchangeTradeForm> {
});
},
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);
});

View file

@ -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),

View file

@ -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<void>(context,
S.of(context).transaction_details_copied(title));
title: title,
subTitle: subTitle,
onTap: () {
Clipboard.setData(ClipboardData(text: subTitle));
showBar<void>(context, S.of(context).transaction_details_copied(title));
});
}
@ -184,10 +186,10 @@ class IoniaGiftCardDetailPage extends BasePage {
showPopUp<void>(
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,
);
);
});
}
}

View file

@ -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),
),

View file

@ -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;

View file

@ -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),

View file

@ -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];

View file

@ -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<AdvancedPrivacySettingsBody> {
_AdvancedPrivacySettingsBodyState();
final _formKey = GlobalKey<FormState>();
@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,
),
),
),
],
),
),
);
}
}

View file

@ -200,18 +200,30 @@ class _WalletNameFormState extends State<WalletNameForm> {
]
]),
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),
),
],
)),
);
}

View file

@ -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: <Widget>[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _addressController,
hintText: S.of(context).node_address,
validator: NodeAddressValidator(),
)
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
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: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _loginController,
hintText: S.of(context).login,
)
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
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(

View file

@ -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<void>(
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<void>(
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<bool>(
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,
),
],
);
}

View file

@ -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<FormState> 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: <Widget>[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _addressController,
hintText: S.of(context).node_address,
validator: NodeAddressValidator(),
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
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: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _loginController,
hintText: S.of(context).login,
))
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
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,
),
),
],
),
),
]
],
),
);
}
}

View file

@ -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),
);
}
}

View file

@ -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<OrderDetailsPageBody> {
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<OrderDetailsPageBody> {
Clipboard.setData(ClipboardData(text: '${item.value}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: StandartListRow(
child: ListRow(
title: '${item.title}', value: '${item.value}'));
}
});

View file

@ -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,

View file

@ -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<NavigatorState> navigatorKey;
final AuthService authService;
final Widget child;
@override
@ -26,22 +31,56 @@ class Root extends StatefulWidget {
class RootState extends State<Root> with WidgetsBindingObserver {
RootState()
: _isInactiveController = StreamController<bool>.broadcast(),
_isInactive = false,
_postFrameCallback = false;
: _isInactiveController = StreamController<bool>.broadcast(),
_isInactive = false,
_requestAuth = true,
_postFrameCallback = false;
Stream<bool> get isInactive => _isInactiveController.stream;
StreamController<bool> _isInactiveController;
bool _isInactive;
bool _postFrameCallback;
bool _requestAuth;
StreamSubscription<Uri?>? stream;
Uri? launchUri;
@override
void initState() {
_requestAuth = widget.authService.requireAuth();
_isInactiveController = StreamController<bool>.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<void> 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<Root> 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<Root> 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<Root> 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);

View file

@ -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<FormState>(),fiatFromSettings = settingsViewModel.fiatCurrency;
SendPage({
required this.sendViewModel,
this.initialPaymentRequest,
}) : _formKey = GlobalKey<FormState>();
final SendViewModel sendViewModel;
final SettingsViewModel settingsViewModel;
final GlobalKey<FormState> _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,

View file

@ -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<ChooseYatAddressButtons> {
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];

View file

@ -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<SendCard>
with AutomaticKeepAliveClientMixin<SendCard> {
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<SendCard>
final Output output;
final SendViewModel sendViewModel;
final PaymentRequest? initialPaymentRequest;
final TextEditingController addressController;
final TextEditingController cryptoAmountController;
@ -62,6 +70,27 @@ class SendCardState extends State<SendCard>
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<void>(
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<SendCard>
await output.fetchParsedAddress(context);
},
validator: validator,
selectedCurrency: sendViewModel.currency,
);
}),
if (output.isParsedAddress) Padding(
@ -332,7 +362,8 @@ class SendCardState extends State<SendCard>
],
),
)),
Padding(
if (!sendViewModel.isFiatDisabled)
Padding(
padding: const EdgeInsets.only(top: 20),
child: BaseTextFormField(
focusNode: fiatAmountFocus,
@ -438,8 +469,9 @@ class SendCardState extends State<SendCard>
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<SendCard>
}
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<SendCard>
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;
}

View file

@ -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<void>(
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<void> _presentReconnectAlert(BuildContext context) async {
await showPopUp<void>(
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<bool>(
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,
),
],
);
}

View file

@ -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<FiatCurrency>(
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<String>(
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<ThemeBase>(
title: S.current.color_theme,
items: ThemeList.all,
selectedItem: _displaySettingsViewModel.theme,
onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme),
),
),
],
),
);
});
}
}

View file

@ -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))
]),
);
});
}
}

View file

@ -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);
})
],
);
}),
);
}
}

View file

@ -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<PinCodeWidget> 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<PinCodeRequiredDuration>(
title: S.current.require_pin_after,
items: PinCodeRequiredDuration.values,
selectedItem: _securitySettingsViewModel.pinCodeRequiredDuration,
onItemSelected: (PinCodeRequiredDuration code) {
_securitySettingsViewModel.setPinCodeRequiredDuration(code);
},
);
}),
]),
);
}
}

View file

@ -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<Object>(
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();
});
}
}

View file

@ -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<ItemType extends Object> extends StandardListRow {
class SettingsPickerCell<ItemType> extends StandardListRow {
SettingsPickerCell(
{required String title,
required this.selectedItem,

View file

@ -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));
}

View file

@ -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<TradeDetailsPageBody> {
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<TradeDetailsPageBody> {
Clipboard.setData(ClipboardData(text: '${item.value}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: StandartListRow(
child: ListRow(
title: '${item.title}', value: '${item.value}'));
});
});

View file

@ -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),
);
}

View file

@ -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);
}

Some files were not shown because too many files have changed in this diff Show more