Merge branch 'main' of github.com:cake-tech/cake_wallet into linux/password-direct-input

This commit is contained in:
M 2023-04-19 15:18:43 -04:00
commit 2ff6cf43b3
205 changed files with 8790 additions and 2327 deletions

View file

@ -7,3 +7,7 @@ Please include a summary of the changes and which issue is fixed / feature is ad
# Pull Request - Checklist
- [ ] Initial Manual Tests Passed
- [ ] Double check modified code and verify it with the feature/task requirements
- [ ] Format code
- [ ] Look for code duplication
- [ ] Clear naming for variables and methods

View file

@ -5,7 +5,7 @@ on:
branches: [ main ]
jobs:
test:
PR_test_build:
runs-on: ubuntu-20.04
env:
@ -99,6 +99,7 @@ jobs:
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 changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> 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
@ -107,13 +108,19 @@ jobs:
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 simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> 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
echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart
echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> 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
run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties
- name: Build
run: |

2
.gitignore vendored
View file

@ -141,4 +141,4 @@ macos/Runner/Info.plist
macos/Runner/DebugProfile.entitlements
macos/Runner/Release.entitlements
lib/core/secure_storage.dart
lib/core/secure_storage.dart

View file

@ -90,12 +90,12 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque
- French
- German
- Italian
- Portugese
- Portuguese
- Dutch
- Polish
- Croatian
- Russian
- Ukranian
- Ukrainian
- Hindi
- Japanese
- Chinese
@ -104,6 +104,10 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque
- Arabic
- Turkish
- Burmese
- Urdu
- Bulgarian
- Czech
- Indonesian
## Add a new language

View file

@ -1,27 +0,0 @@
# Refund Policy
This Refund Policy covers returns, refunds, and cancellations for your purchases from Cake Technologies, LLC (“Company,” “we,” or “us”).
### Returns
We do not accept returns of cryptocurrencies. Once cryptocurrencies are delivered to the provided wallet address, we cannot accept a return. If we allow you to sell cryptocurrencies to us, you may optionally engage in another trade to convert the cryptocurrency to another asset at the price at that point in time.
### Refunds
We do not issue refunds except in the case of an error resulting in a non-delivery of cryptocurrencies. In this case, we will decide whether to complete the trade or refund the account. We endeavor to complete an investigation to determine whether an error occurred and to make this decision within ten (10) days of receipt of your error report, and we will do so in any case within ninety (90) days. We reserve the right to issue a refund for the equivalent amount in another asset at the exchange rate then offered by us should we determine an error occurred.
### Cancellations
Once you make a payment to us, trades cannot be cancelled. In nearly all cases, we deliver cryptocurrency to your address in a matter of seconds or minutes. If you have not received your purchased cryptocurrency within six (6) hours of completing payment and have confirmed that your wallet is connected to a reliable node and fully synchronized with the network, please contact us.
### Network Fees
We may charge a network fee for the delivery of cryptocurrencies. This will be clearly shown in the checkout page. If we issue a refund, we may choose to deduct any network fees from the refund, including any network fees incurred in processing the refund.
### Fraud
If you believe a cryptocurrency purchase was conducted fraudulently, please alert your bank or card issuer, as applicable, and follow their instructions. Please be aware that fraudulent transactions may result in the loss of your money with no recourse.
### Contact Us
If you believe that there has been an error in processing your payment, please contact us at support@cakewallet.com.

View file

@ -11,6 +11,7 @@ import io.flutter.plugin.common.MethodChannel;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
@ -40,14 +41,6 @@ public class MainActivity extends FlutterFragmentActivity {
try {
switch (call.method) {
case "enableWakeScreen":
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "disableWakeScreen":
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "sec_random":
int count = call.argument("count");
SecureRandom random = new SecureRandom();
@ -56,7 +49,7 @@ public class MainActivity extends FlutterFragmentActivity {
handler.post(() -> result.success(bytes));
break;
case "getUnstoppableDomainAddress":
int version = Build.VERSION.SDK_INT;
int version = Build.VERSION.SDK_INT;
if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) {
getUnstoppableDomainAddress(call, result);
} else {
@ -87,4 +80,10 @@ public class MainActivity extends FlutterFragmentActivity {
}
});
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
}

View file

@ -40,14 +40,6 @@ public class MainActivity extends FlutterFragmentActivity {
try {
switch (call.method) {
case "enableWakeScreen":
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "disableWakeScreen":
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "sec_random":
int count = call.argument("count");
SecureRandom random = new SecureRandom();

View file

@ -40,14 +40,6 @@ public class MainActivity extends FlutterFragmentActivity {
try {
switch (call.method) {
case "enableWakeScreen":
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "disableWakeScreen":
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "sec_random":
int count = call.argument("count");
SecureRandom random = new SecureRandom();

View file

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.6.21'
repositories {
google()
jcenter()

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

BIN
assets/images/flags/are.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

BIN
assets/images/flags/arg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

BIN
assets/images/flags/bgd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

BIN
assets/images/flags/chl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

BIN
assets/images/flags/col.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

BIN
assets/images/flags/egy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

BIN
assets/images/flags/gha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

BIN
assets/images/flags/gtm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

BIN
assets/images/flags/irn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

BIN
assets/images/flags/mar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

BIN
assets/images/flags/nga.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

BIN
assets/images/flags/pak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

BIN
assets/images/flags/twn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

BIN
assets/images/flags/vnm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

BIN
assets/images/trocador.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -13,9 +13,6 @@
-
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

View file

@ -6,5 +6,5 @@ class BitcoinTransactionWrongBalanceException implements Exception {
final CryptoCurrency currency;
@override
String toString() => 'Wrong balance. Not enough ${currency.title} on your balance.';
String toString() => 'You do not have enough ${currency.title} to send this amount.';
}

View file

@ -40,7 +40,7 @@ class ElectrumClient {
unterminatedString = '';
static const connectionTimeout = Duration(seconds: 5);
static const aliveTimerDuration = Duration(seconds: 2);
static const aliveTimerDuration = Duration(seconds: 4);
bool get isConnected => _isConnected;
Socket? socket;
@ -358,7 +358,7 @@ class ElectrumClient {
Future<dynamic> callWithTimeout(
{required String method,
List<Object> params = const [],
int timeout = 2000}) async {
int timeout = 4000}) async {
try {
final completer = Completer<dynamic>();
_id += 1;

View file

@ -48,7 +48,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@override
@computed
String get address => receiveAddresses.first.address;
String get address {
if (receiveAddresses.isEmpty) {
return generateNewAddress().address;
}
return receiveAddresses.first.address;
}
@override
set address(String addr) => null;
@ -121,8 +127,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return address;
}
Future<BitcoinAddressRecord> generateNewAddress(
{bitcoin.HDWallet? hd, bool isHidden = false}) async {
BitcoinAddressRecord generateNewAddress(
{bitcoin.HDWallet? hd, bool isHidden = false}) {
currentReceiveAddressIndex += 1;
// FIX-ME: Check logic for whichi HD should be used here ???
final address = BitcoinAddressRecord(
@ -165,7 +171,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
var hasAddrUse = true;
List<BitcoinAddressRecord> addrs;
if (addresses.isNotEmpty) {
addrs = addresses
.where((addr) => addr.isHidden == isHidden)
@ -179,7 +185,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
hd: hd,
isHidden: isHidden);
}
while(hasAddrUse) {
final addr = addrs.last.address;
hasAddrUse = await _hasAddressUsed(addr);

View file

@ -771,5 +771,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.18.0 <3.0.0"
dart: ">=2.19.0 <4.0.0"
flutter: ">=3.0.0"

View file

@ -1,6 +1,7 @@
import 'package:cw_core/currency.dart';
import 'package:cw_core/enumerable_item.dart';
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implements Currency {
const CryptoCurrency({
String title = '',
int raw = -1,
@ -117,7 +118,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
static const xusd = CryptoCurrency(title: 'XUSD', tag: 'XHV', raw: 29, name: 'xusd');
static const ape = CryptoCurrency(title: 'APE', tag: 'ETH', fullName: 'ApeCoin', raw: 30, name: 'ape', iconPath: 'assets/images/ape_icon.png');
static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'C-CHAIN', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png');
static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'AVAXC', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png');
static const btt = CryptoCurrency(title: 'BTT', tag: 'ETH', fullName: 'BitTorrent', raw: 32, name: 'btt', iconPath: 'assets/images/btt_icon.png');
static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/bttbsc_icon.png');
static const doge = CryptoCurrency(title: 'DOGE', fullName: 'Dogecoin', raw: 34, name: 'doge', iconPath: 'assets/images/doge_icon.png');
@ -162,6 +163,14 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
return acc;
});
static final Map<String, CryptoCurrency> _fullNameCurrencyMap =
[...all, ...havenCurrencies].fold<Map<String, CryptoCurrency>>(<String, CryptoCurrency>{}, (acc, item) {
if(item.fullName != null){
acc.addAll({item.fullName!.toLowerCase(): item});
}
return acc;
});
static CryptoCurrency deserialize({required int raw}) {
if (CryptoCurrency._rawCurrencyMap[raw] == null) {
@ -180,6 +189,16 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!;
}
static CryptoCurrency fromFullName(String name) {
if (CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()] == null) {
final s = 'Unexpected token: $name for CryptoCurrency fromFullName';
throw ArgumentError.value(name, 'Fullname', s);
}
return CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()]!;
}
@override
String toString() => title;
}

View file

@ -0,0 +1,6 @@
abstract class Currency {
String get name;
String? get tag;
String? get fullName;
String? get iconPath;
}

View file

@ -1,4 +1,6 @@
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
// FIXME: Hardcoded values; Works only for monero
@ -93,7 +95,7 @@ int getMoneroHeigthByDate({required DateTime date}) {
int height = 0;
try {
if ((dates[raw] == null)||(dates[raw] == lastHeight)) {
if ((dates[raw] == null) || (dates[raw] == lastHeight)) {
startHeight = dates.values.toList()[dates.length - 2];
endHeight = dates.values.toList()[dates.length - 1];
final heightPerDay = (endHeight - startHeight) / 31;
@ -118,3 +120,86 @@ int getMoneroHeigthByDate({required DateTime date}) {
return height;
}
const havenDates = {
"2023-03": 1309180,
"2023-01": 1266810,
"2022-12": 1244510,
"2022-11": 1222970,
"2022-10": 1200700,
"2022-09": 1179140,
"2022-08": 1156870,
"2022-07": 1134600,
"2022-06": 1113030,
"2022-05": 1090800,
"2022-04": 1069250,
"2022-03": 1047000,
"2022-02": 1026960,
"2022-01": 1004700,
"2021-12": 982400,
"2021-11": 961000,
"2021-10": 938600,
"2021-09": 917000,
"2021-08": 894800,
"2021-07": 886000,
"2021-06": 867300,
"2021-05": 845000,
"2021-04": 823500,
"2021-03": 801500,
"2021-02": 781000,
"2021-01": 759000,
"2020-12": 736500,
"2020-11": 715000,
"2020-10": 693000,
"2020-09": 671000,
"2020-08": 649000,
"2020-07": 626600,
"2020-06": 605000,
"2020-05": 582700,
"2020-04": 561100,
"2020-03": 539000,
"2020-02": 518000,
"2020-01": 496000,
"2019-12": 473400,
"2019-11": 451900,
"2019-10": 429600,
"2019-09": 408000,
"2019-08": 385700,
"2019-07": 363800,
"2019-06": 342200,
"2019-05": 320000,
"2019-04": 298400,
"2019-03": 276000,
"2019-02": 256000,
"2019-01": 233700,
"2018-12": 211400,
"2018-11": 189800,
"2018-10": 167500,
"2018-09": 145900,
"2018-08": 123700,
"2018-07": 101400,
"2018-06": 80000,
"2018-05": 57550,
"2018-04": 32000,
"2018-03": 8500
};
DateTime formatMapKey(String key) => dateFormat.parse(key);
int getHavenHeightByDate({required DateTime date}) {
String closestKey =
havenDates.keys.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => '');
return havenDates[closestKey] ?? 0;
}
Future<int> getHavenCurrentHeight() async {
final response = await http.get(Uri.parse('https://explorer.havenprotocol.org/api/networkinfo'));
if (response.statusCode == 200) {
final info = jsonDecode(response.body);
return info['data']['height'] as int;
} else {
throw Exception('Failed to load current blockchain height');
}
}

View file

@ -73,6 +73,25 @@ class Node extends HiveObject with Keyable {
}
}
@override
bool operator ==(other) =>
other is Node &&
(other.uriRaw == uriRaw &&
other.login == login &&
other.password == password &&
other.typeRaw == typeRaw &&
other.useSSL == useSSL &&
other.trusted == trusted);
@override
int get hashCode =>
uriRaw.hashCode ^
login.hashCode ^
password.hashCode ^
typeRaw.hashCode ^
useSSL.hashCode ^
trusted.hashCode;
@override
dynamic get keyIndex {
_keyIndex ??= key;

View file

@ -177,7 +177,7 @@ packages:
source: hosted
version: "2.0.1"
file:
dependency: transitive
dependency: "direct main"
description:
name: file
url: "https://pub.dartlang.org"
@ -584,5 +584,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.18.0 <3.0.0"
dart: ">=2.19.0 <4.0.0"
flutter: ">=3.0.0"

View file

@ -13,6 +13,7 @@ dependencies:
flutter:
sdk: flutter
http: ^0.13.4
file: ^6.1.4
path_provider: ^2.0.11
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1

View file

@ -166,14 +166,14 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
if (hasMultiDestination) {
if (outputs.any((item) => item.sendAll
|| (item.formattedCryptoAmount ?? 0) <= 0)) {
throw HavenTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
throw HavenTransactionCreationException('You do not have enough coins to send this amount.');
}
final int totalAmount = outputs.fold(0, (acc, value) =>
acc + (value.formattedCryptoAmount ?? 0));
if (unlockedBalance < totalAmount) {
throw HavenTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
throw HavenTransactionCreationException('You do not have enough coins to send this amount.');
}
final moneroOutputs = outputs.map((output) =>
@ -204,7 +204,7 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
throw HavenTransactionCreationException(
'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
}
pendingTransactionDescription =

View file

@ -5,147 +5,168 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
url: "https://pub.dev"
source: hosted
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.3.2"
asn1lib:
dependency: transitive
description:
name: asn1lib
url: "https://pub.dartlang.org"
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
url: "https://pub.dev"
source: hosted
version: "1.4.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
url: "https://pub.dev"
source: hosted
version: "2.9.0"
version: "2.10.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.0"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
url: "https://pub.dartlang.org"
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
url: "https://pub.dev"
source: hosted
version: "2.3.3"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
url: "https://pub.dev"
source: hosted
version: "7.2.7"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
url: "https://pub.dev"
source: hosted
version: "8.4.4"
version: "8.4.3"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
url: "https://pub.dev"
source: hosted
version: "1.2.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
url: "https://pub.dev"
source: hosted
version: "3.0.2"
cw_core:
@ -159,44 +180,50 @@ packages:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
encrypt:
dependency: transitive
description:
name: encrypt
url: "https://pub.dartlang.org"
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
url: "https://pub.dev"
source: hosted
version: "5.0.1"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: "direct main"
description:
name: ffi
url: "https://pub.dartlang.org"
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "2.0.1"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -206,7 +233,8 @@ packages:
dependency: "direct main"
description:
name: flutter_mobx
url: "https://pub.dartlang.org"
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
url: "https://pub.dev"
source: hosted
version: "2.0.6+5"
flutter_test:
@ -218,252 +246,288 @@ packages:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
url: "https://pub.dev"
source: hosted
version: "2.2.0"
hive:
dependency: transitive
description:
name: hive
url: "https://pub.dartlang.org"
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_generator:
dependency: "direct dev"
description:
name: hive_generator
url: "https://pub.dartlang.org"
sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
intl:
dependency: "direct main"
description:
name: intl
url: "https://pub.dartlang.org"
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
url: "https://pub.dev"
source: hosted
version: "0.17.0"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
url: "https://pub.dev"
source: hosted
version: "4.8.0"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
url: "https://pub.dev"
source: hosted
version: "0.12.12"
version: "0.12.13"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.1.5"
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
url: "https://pub.dev"
source: hosted
version: "1.0.4"
mobx:
dependency: "direct main"
description:
name: mobx
url: "https://pub.dartlang.org"
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3+1"
mobx_codegen:
dependency: "direct dev"
description:
name: mobx_codegen
url: "https://pub.dartlang.org"
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
url: "https://pub.dev"
source: hosted
version: "1.8.2"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
url: "https://pub.dev"
source: hosted
version: "2.0.13"
version: "2.0.12"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
url: "https://pub.dev"
source: hosted
version: "2.0.24"
version: "2.0.22"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
url: "https://pub.dartlang.org"
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.1.7"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
url: "https://pub.dev"
source: hosted
version: "2.0.7"
version: "2.1.5"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
url: "https://pub.dev"
source: hosted
version: "3.6.2"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
version: "1.2.1"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
url: "https://pub.dev"
source: hosted
version: "1.4.0"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
url: "https://pub.dev"
source: hosted
version: "1.0.3"
sky_engine:
@ -475,121 +539,138 @@ packages:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
source_helper:
dependency: transitive
description:
name: source_helper
url: "https://pub.dartlang.org"
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.9.0"
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
url: "https://pub.dev"
source: hosted
version: "0.4.12"
version: "0.4.16"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
url: "https://pub.dev"
source: hosted
version: "2.3.0"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
url: "https://pub.dev"
source: hosted
version: "2.6.1"
version: "3.1.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "0.2.0+3"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.18.0 <3.0.0"
dart: ">=2.19.0 <3.0.0"
flutter: ">=3.0.0"

View file

@ -12,7 +12,7 @@ environment:
dependencies:
flutter:
sdk: flutter
ffi: ^1.1.2
ffi: ^2.0.1
http: ^0.13.4
path_provider: ^2.0.11
mobx: ^2.0.7+4

View file

@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: args
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.3.2"
asn1lib:
dependency: transitive
description:
@ -221,10 +221,10 @@ packages:
dependency: transitive
description:
name: mobx
sha256: "6738620307a424d2c9ad8b873f4dce391c44e9135eb4e75668ac8202fec7a9b8"
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3+1"
path:
dependency: transitive
description:
@ -237,42 +237,42 @@ packages:
dependency: transitive
description:
name: path_provider
sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
url: "https://pub.dev"
source: hosted
version: "2.0.14"
version: "2.0.12"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: da97262be945a72270513700a92b39dd2f4a54dad55d061687e2e37a6390366a
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
url: "https://pub.dev"
source: hosted
version: "2.0.25"
version: "2.0.22"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: ad4c4d011830462633f03eb34445a45345673dfd4faf1ab0b4735fbd93b19183
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.1.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.1.7"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
@ -293,18 +293,18 @@ packages:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
url: "https://pub.dev"
source: hosted
version: "3.7.3"
version: "3.6.2"
process:
dependency: transitive
description:
@ -394,10 +394,10 @@ packages:
dependency: transitive
description:
name: xdg_directories
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "0.2.0+3"
sdks:
dart: ">=2.18.1 <3.0.0"
flutter: ">=3.3.0"
flutter: ">=3.0.0"

View file

@ -187,14 +187,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (hasMultiDestination) {
if (outputs.any((item) => item.sendAll
|| (item.formattedCryptoAmount ?? 0) <= 0)) {
throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
}
final int totalAmount = outputs.fold(0, (acc, value) =>
acc + (value.formattedCryptoAmount ?? 0));
if (unlockedBalance < totalAmount) {
throw MoneroTransactionCreationException('Wrong balance. Not enough XMR on your balance.');
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
}
final moneroOutputs = outputs.map((output) {
@ -229,7 +229,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
throw MoneroTransactionCreationException(
'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
}
pendingTransactionDescription =

View file

@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: args
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.3.2"
asn1lib:
dependency: transitive
description:
@ -69,10 +69,10 @@ packages:
dependency: transitive
description:
name: build_daemon
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.0"
build_resolvers:
dependency: "direct dev"
description:
@ -109,10 +109,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0"
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
url: "https://pub.dev"
source: hosted
version: "8.4.4"
version: "8.4.3"
characters:
dependency: transitive
description:
@ -204,10 +204,10 @@ packages:
dependency: "direct main"
description:
name: ffi
sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18"
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "2.0.1"
file:
dependency: transitive
description:
@ -220,10 +220,10 @@ packages:
dependency: transitive
description:
name: fixnum
sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec"
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -382,10 +382,10 @@ packages:
dependency: "direct main"
description:
name: mobx
sha256: "6738620307a424d2c9ad8b873f4dce391c44e9135eb4e75668ac8202fec7a9b8"
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3+1"
mobx_codegen:
dependency: "direct dev"
description:
@ -414,50 +414,50 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9"
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
url: "https://pub.dev"
source: hosted
version: "2.0.13"
version: "2.0.12"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7"
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
url: "https://pub.dev"
source: hosted
version: "2.0.24"
version: "2.0.22"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "026b97a6c29da75181a37aae2eba9227f5fe13cb2838c6b975ce209328b8ab4e"
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.1.7"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: a34ecd7fb548f8e57321fd8e50d865d266941b54e6c3b7758cf8f37c24116905
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
url: "https://pub.dev"
source: hosted
version: "2.0.7"
version: "2.1.5"
platform:
dependency: transitive
description:
@ -470,10 +470,10 @@ packages:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.3"
pointycastle:
dependency: transitive
description:
@ -510,10 +510,10 @@ packages:
dependency: transitive
description:
name: pubspec_parse
sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
version: "1.2.1"
shelf:
dependency: transitive
description:
@ -651,18 +651,18 @@ packages:
dependency: transitive
description:
name: win32
sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
url: "https://pub.dev"
source: hosted
version: "2.6.1"
version: "3.1.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "0.2.0+3"
yaml:
dependency: transitive
description:
@ -672,5 +672,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.18.0 <3.0.0"
dart: ">=2.19.0 <4.0.0"
flutter: ">=3.0.0"

View file

@ -12,7 +12,7 @@ environment:
dependencies:
flutter:
sdk: flutter
ffi: ^1.1.2
ffi: ^2.0.1
http: ^0.13.4
path_provider: ^2.0.11
mobx: ^2.0.7+4

View file

@ -7,7 +7,26 @@ PODS:
- connectivity (0.0.1):
- Flutter
- Reachability
- CryptoSwift (1.3.2)
- CryptoSwift (1.6.0)
- cw_haven (0.0.1):
- cw_haven/Boost (= 0.0.1)
- cw_haven/Haven (= 0.0.1)
- cw_haven/OpenSSL (= 0.0.1)
- cw_haven/Sodium (= 0.0.1)
- cw_shared_external
- Flutter
- cw_haven/Boost (0.0.1):
- cw_shared_external
- Flutter
- cw_haven/Haven (0.0.1):
- cw_shared_external
- Flutter
- cw_haven/OpenSSL (0.0.1):
- cw_shared_external
- Flutter
- cw_haven/Sodium (0.0.1):
- cw_shared_external
- Flutter
- cw_monero (0.0.2):
- cw_monero/Boost (= 0.0.2)
- cw_monero/Monero (= 0.0.2)
@ -46,16 +65,18 @@ PODS:
- Flutter
- device_info (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- devicelocale (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.2):
- DKImagePickerController/Core (4.3.4):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.2)
- DKImagePickerController/PhotoGallery (4.3.2):
- DKImagePickerController/ImageDataManager (4.3.4)
- DKImagePickerController/PhotoGallery (4.3.4):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.2)
- DKImagePickerController/Resource (4.3.4)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
@ -83,11 +104,21 @@ PODS:
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- flutter_inappwebview (0.0.1):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
- OrderedSet (~> 5.0)
- flutter_inappwebview/Core (0.0.1):
- Flutter
- OrderedSet (~> 5.0)
- flutter_mailer (0.0.1):
- Flutter
- flutter_secure_storage (3.3.1):
- Flutter
- local_auth_ios (0.0.1):
- Flutter
- MTBBarcodeScanner (5.0.11)
- OrderedSet (5.0.0)
- package_info (0.0.1):
- Flutter
- path_provider_foundation (0.0.1):
@ -98,16 +129,16 @@ PODS:
- platform_device_id (0.0.1):
- Flutter
- Reachability (3.2)
- SDWebImage (5.9.1):
- SDWebImage/Core (= 5.9.1)
- SDWebImage/Core (5.9.1)
- SDWebImage (5.15.5):
- SDWebImage/Core (= 5.15.5)
- SDWebImage/Core (5.15.5)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- SwiftProtobuf (1.18.0)
- SwiftyGif (5.3.0)
- SwiftProtobuf (1.21.0)
- SwiftyGif (5.4.4)
- uni_links (0.0.1):
- Flutter
- UnstoppableDomainsResolution (4.0.0):
@ -117,20 +148,22 @@ PODS:
- Flutter
- wakelock (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
DEPENDENCIES:
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
- connectivity (from `.symlinks/plugins/connectivity/ios`)
- CryptoSwift
- cw_haven (from `.symlinks/plugins/cw_haven/ios`)
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
- device_info (from `.symlinks/plugins/device_info/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
@ -143,7 +176,6 @@ DEPENDENCIES:
- UnstoppableDomainsResolution (~> 4.0.0)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
@ -152,6 +184,7 @@ SPEC REPOS:
- DKImagePickerController
- DKPhotoGallery
- MTBBarcodeScanner
- OrderedSet
- Reachability
- SDWebImage
- SwiftProtobuf
@ -163,6 +196,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/barcode_scan2/ios"
connectivity:
:path: ".symlinks/plugins/connectivity/ios"
cw_haven:
:path: ".symlinks/plugins/cw_haven/ios"
cw_monero:
:path: ".symlinks/plugins/cw_monero/ios"
cw_shared_external:
@ -171,12 +206,18 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_display_brightness/ios"
device_info:
:path: ".symlinks/plugins/device_info/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
devicelocale:
:path: ".symlinks/plugins/devicelocale/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
flutter_mailer:
:path: ".symlinks/plugins/flutter_mailer/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
local_auth_ios:
@ -199,41 +240,43 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS:
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
BigInt: f668a80089607f521586bbe29513d708491ef2f7
connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
CryptoSwift: 093499be1a94b0cae36e6c26b70870668cb56060
CryptoSwift: 562f8eceb40e80796fffc668b0cad9313284cfa6
cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a
cw_monero: 4cf3b96f2da8e95e2ef7d6703dd4d2c509127b7d
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
devicelocale: b22617f40038496deffba44747101255cee005b0
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
local_auth_ios: 0d333dde7780f669e66f19d2ff6005f3ea84008d
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SDWebImage: a990c053fff71e388a10f3357edb0be17929c9c5
SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
SwiftProtobuf: c3c12645230d9b09c72267e0de89468c5543bd86
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
SwiftProtobuf: afced68785854575756db965e9da52bbf3dc45e7
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f
PODFILE CHECKSUM: ae71bdf0eb731a1ffc399c122f6aa4dea0cb5f6f

View file

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@ -227,6 +227,7 @@
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -241,6 +242,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);

View file

@ -105,4 +105,12 @@ import UnstoppableDomainsResolution
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func applicationWillResignActive(_: UIApplication ) {
self.window?.isHidden = true;
}
override func applicationDidBecomeActive(_: UIApplication) {
self.window?.isHidden = false;
}
}

View file

@ -0,0 +1,211 @@
import 'dart:convert';
import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anonpay/anonpay_request.dart';
import 'package:cake_wallet/anonpay/anonpay_status_response.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:http/http.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
class AnonPayApi {
const AnonPayApi({
this.useTorOnly = false,
required this.wallet,
});
final bool useTorOnly;
final WalletBase wallet;
static const anonpayRef = secrets.anonPayReferralCode;
static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
static const clearNetAuthority = 'trocador.app';
static const markup = secrets.trocadorExchangeMarkup;
static const anonPayPath = '/anonpay';
static const anonPayStatus = '/anonpay/status';
static const coinPath = 'api/coin';
static const apiKey = secrets.trocadorApiKey;
Future<AnonpayStatusResponse> paymentStatus(String id) async {
final authority = await _getAuthority();
final response = await get(Uri.https(authority, "$anonPayStatus/$id"));
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final status = responseJSON['Status'] as String;
final fiatAmount = responseJSON['Fiat_Amount'] as double?;
final fiatEquiv = responseJSON['Fiat_Equiv'] as String?;
final amountTo = responseJSON['AmountTo'] as double?;
final coinTo = responseJSON['CoinTo'] as String;
final address = responseJSON['Address'] as String;
return AnonpayStatusResponse(
status: status,
fiatAmount: fiatAmount,
amountTo: amountTo,
coinTo: coinTo,
address: address,
fiatEquiv: fiatEquiv,
);
}
Future<AnonpayInvoiceInfo> createInvoice(AnonPayRequest request) async {
final description = Uri.encodeComponent(request.description);
final body = <String, dynamic>{
'ticker_to': request.cryptoCurrency.title.toLowerCase(),
'network_to': _networkFor(request.cryptoCurrency),
'address': request.address,
'name': request.name,
'description': description,
'email': request.email,
'ref': anonpayRef,
'markup': markup,
'direct': 'False',
};
if (request.amount != null) {
body['amount'] = request.amount;
}
if (request.fiatEquivalent != null) {
body['fiat_equiv'] = request.fiatEquivalent;
}
final authority = await _getAuthority();
final response = await get(Uri.https(authority, anonPayPath, body));
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['ID'] as String;
final url = responseJSON['url'] as String;
final urlOnion = responseJSON['url_onion'] as String;
final statusUrl = responseJSON['status_url'] as String;
final statusUrlOnion = responseJSON['status_url_onion'] as String;
final statusInfo = await paymentStatus(id);
return AnonpayInvoiceInfo(
invoiceId: id,
clearnetUrl: url,
onionUrl: urlOnion,
status: statusInfo.status,
fiatAmount: statusInfo.fiatAmount,
fiatEquiv: statusInfo.fiatEquiv,
amountTo: statusInfo.amountTo,
coinTo: statusInfo.coinTo,
address: statusInfo.address,
clearnetStatusUrl: statusUrl,
onionStatusUrl: statusUrlOnion,
walletId: wallet.id,
createdAt: DateTime.now(),
provider: 'Trocador AnonPay invoice',
);
}
Future<AnonpayDonationLinkInfo> generateDonationLink(AnonPayRequest request) async {
final body = <String, dynamic>{
'ticker_to': request.cryptoCurrency.title.toLowerCase(),
'network_to': _networkFor(request.cryptoCurrency),
'address': request.address,
'ref': anonpayRef,
'direct': 'True',
};
if (request.name.isNotEmpty) {
body['name'] = request.name;
}
if (request.description.isNotEmpty) {
body['description'] = request.description;
}
if (request.email.isNotEmpty) {
body['email'] = request.email;
}
final clearnetUrl = Uri.https(clearNetAuthority, anonPayPath, body);
final onionUrl = Uri.https(onionApiAuthority, anonPayPath, body);
return AnonpayDonationLinkInfo(
clearnetUrl: clearnetUrl.toString(),
onionUrl: onionUrl.toString(),
address: request.address,
);
}
Future<Limits> fetchLimits({
FiatCurrency? fiatCurrency,
required CryptoCurrency cryptoCurrency,
}) async {
double fiatRate = 0.0;
if (fiatCurrency != null) {
fiatRate = await FiatConversionService.fetchPrice(
crypto: cryptoCurrency,
fiat: fiatCurrency,
torOnly: useTorOnly,
);
}
final params = <String, String>{
'api_key': apiKey,
'ticker': cryptoCurrency.title.toLowerCase(),
'name': cryptoCurrency.name,
};
final String apiAuthority = await _getAuthority();
final uri = Uri.https(apiAuthority, coinPath, params);
final response = await get(uri);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseJSON = json.decode(response.body) as List<dynamic>;
if (responseJSON.isEmpty) {
throw Exception('No data');
}
final coinJson = responseJSON.first as Map<String, dynamic>;
final minimum = coinJson['minimum'] as double;
final maximum = coinJson['maximum'] as double;
if (fiatCurrency != null) {
return Limits(
min: double.tryParse((minimum * fiatRate).toStringAsFixed(2)),
max: double.tryParse((maximum * fiatRate).toStringAsFixed(2)),
);
}
return Limits(
min: minimum,
max: maximum,
);
}
String _networkFor(CryptoCurrency currency) {
switch (currency) {
case CryptoCurrency.usdt:
return CryptoCurrency.btc.title.toLowerCase();
default:
return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet';
}
}
String _normalizeTag(String tag) {
switch (tag) {
case 'ETH':
return 'ERC20';
default:
return tag.toLowerCase();
}
}
Future<String> _getAuthority() async {
try {
if (useTorOnly) {
return onionApiAuthority;
}
final uri = Uri.https(onionApiAuthority, '/anonpay');
await get(uri);
return onionApiAuthority;
} catch (e) {
return clearNetAuthority;
}
}
}

View file

@ -0,0 +1,13 @@
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
class AnonpayDonationLinkInfo implements AnonpayInfoBase{
final String clearnetUrl;
final String onionUrl;
final String address;
AnonpayDonationLinkInfo({
required this.clearnetUrl,
required this.onionUrl,
required this.address,
});
}

View file

@ -0,0 +1,5 @@
abstract class AnonpayInfoBase {
String get clearnetUrl;
String get onionUrl;
String get address;
}

View file

@ -0,0 +1,57 @@
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cw_core/keyable.dart';
import 'package:hive/hive.dart';
part 'anonpay_invoice_info.g.dart';
@HiveType(typeId: AnonpayInvoiceInfo.typeId)
class AnonpayInvoiceInfo extends HiveObject with Keyable implements AnonpayInfoBase {
@HiveField(0)
final String invoiceId;
@HiveField(1)
String status;
@HiveField(2)
final double? fiatAmount;
@HiveField(3)
final String? fiatEquiv;
@HiveField(4)
final double? amountTo;
@HiveField(5)
final String coinTo;
@HiveField(6)
final String address;
@HiveField(7)
final String clearnetUrl;
@HiveField(8)
final String onionUrl;
@HiveField(9)
final String clearnetStatusUrl;
@HiveField(10)
final String onionStatusUrl;
@HiveField(11)
final DateTime createdAt;
@HiveField(12)
final String walletId;
@HiveField(13)
final String provider;
static const typeId = 10;
static const boxName = 'AnonpayInvoiceInfo';
AnonpayInvoiceInfo({
required this.invoiceId,
required this.clearnetUrl,
required this.onionUrl,
required this.clearnetStatusUrl,
required this.onionStatusUrl,
required this.status,
this.fiatAmount,
this.fiatEquiv,
this.amountTo,
required this.coinTo,
required this.address,
required this.createdAt,
required this.walletId,
required this.provider,
});
}

View file

@ -0,0 +1,21 @@
import 'package:cw_core/crypto_currency.dart';
class AnonPayRequest {
CryptoCurrency cryptoCurrency;
String address;
String name;
String? amount;
String email;
String description;
String? fiatEquivalent;
AnonPayRequest({
required this.cryptoCurrency,
required this.address,
required this.name,
required this.email,
this.amount,
required this.description,
this.fiatEquivalent,
});
}

View file

@ -0,0 +1,17 @@
class AnonpayStatusResponse {
final String status;
final double? fiatAmount;
final String? fiatEquiv;
final double? amountTo;
final String coinTo;
final String address;
const AnonpayStatusResponse({
required this.status,
this.fiatAmount,
this.fiatEquiv,
this.amountTo,
required this.coinTo,
required this.address,
});
}

View file

@ -57,7 +57,7 @@ class AnyPayPayment {
List<String> get outAddresses {
return instructions
.map((instuction) => instuction.outputs.map((out) => out.address))
.map((instruction) => instruction.outputs.map((out) => out.address))
.expand((e) => e)
.toList();
}

View file

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:cw_core/crypto_currency.dart';
@ -55,6 +56,7 @@ class AnyPayApi {
final response = await post(url, headers: headers, body: utf8.encode(json.encode(body)));
if (response.statusCode != 200) {
ExceptionHandler.onError(FlutterErrorDetails(exception: response));
throw Exception('Unexpected response http code: ${response.statusCode}');
}

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cw_core/wallet_base.dart';
class OnRamperBuyProvider {
@ -10,19 +11,59 @@ class OnRamperBuyProvider {
final SettingsStore _settingsStore;
final WalletBase _wallet;
static const _baseUrl = 'widget.onramper.com';
static const _baseUrl = 'buy.onramper.com';
static String get _apiKey => secrets.onramperApiKey;
Uri requestUrl(bool darkMode) {
Uri requestUrl() {
String primaryColor,
secondaryColor,
primaryTextColor,
secondaryTextColor,
containerColor,
cardColor;
switch (_settingsStore.currentTheme.type) {
case ThemeType.bright:
primaryColor = '815dfbff';
secondaryColor = 'ffffff';
primaryTextColor = '141519';
secondaryTextColor = '6b6f80';
containerColor = 'ffffff';
cardColor = 'f2f0faff';
break;
case ThemeType.light:
primaryColor = '2194ffff';
secondaryColor = 'ffffff';
primaryTextColor = '141519';
secondaryTextColor = '6b6f80';
containerColor = 'ffffff';
cardColor = 'e5f7ff';
break;
case ThemeType.dark:
primaryColor = '456effff';
secondaryColor = '1b2747ff';
primaryTextColor = 'ffffff';
secondaryTextColor = 'ffffff';
containerColor = '19233C';
cardColor = '232f4fff';
break;
}
return Uri.https(_baseUrl, '', <String, dynamic>{
'apiKey': _apiKey,
'defaultCrypto': _wallet.currency.title,
'defaultFiat': _settingsStore.fiatCurrency.title,
'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}',
'darkMode': darkMode.toString(),
'supportSell': "false",
'supportSwap': "false"
'supportSwap': "false",
'primaryColor': primaryColor,
'secondaryColor': secondaryColor,
'primaryTextColor': primaryTextColor,
'secondaryTextColor': secondaryTextColor,
'containerColor': containerColor,
'cardColor': cardColor
});
}
}

View file

@ -0,0 +1,24 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/wallet_base.dart';
class PayfuraBuyProvider {
PayfuraBuyProvider({required SettingsStore settingsStore, required WalletBase wallet})
: this._settingsStore = settingsStore,
this._wallet = wallet;
final SettingsStore _settingsStore;
final WalletBase _wallet;
static const _baseUrl = 'exchange.payfura.com';
Uri requestUrl() {
return Uri.https(_baseUrl, '', <String, dynamic>{
'apiKey': secrets.payfuraApiKey,
'to': _wallet.currency.title,
'from': _settingsStore.fiatCurrency.title,
'walletAddress': '${_wallet.currency.title}:${_wallet.walletAddresses.address}',
'mode': 'buy'
});
}
}

View file

@ -1,32 +1,59 @@
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/crypto_currency.dart';
class AmountValidator extends TextValidator {
AmountValidator({required WalletType type, bool isAutovalidate = false})
AmountValidator({required CryptoCurrency currency, bool isAutovalidate = false}) {
symbolsAmountValidator =
SymbolsAmountValidator(isAutovalidate: isAutovalidate);
decimalAmountValidator = DecimalAmountValidator(currency: currency,isAutovalidate: isAutovalidate);
}
late final SymbolsAmountValidator symbolsAmountValidator;
late final DecimalAmountValidator decimalAmountValidator;
String? call(String? value) => symbolsAmountValidator(value) ?? decimalAmountValidator(value);
}
class SymbolsAmountValidator extends TextValidator {
SymbolsAmountValidator({required bool isAutovalidate})
: super(
errorMessage: S.current.error_text_amount,
pattern: _pattern(type),
errorMessage: S.current.error_text_amount,
pattern: _pattern(),
isAutovalidate: isAutovalidate,
minLength: 0,
maxLength: 0);
static String _pattern() => '^([0-9]+([.\,][0-9]+)?|[.\,][0-9]+)\$';
}
class DecimalAmountValidator extends TextValidator {
DecimalAmountValidator({required CryptoCurrency currency, required bool isAutovalidate })
: super(
errorMessage: S.current.decimal_places_error,
pattern: _pattern(currency),
isAutovalidate: isAutovalidate,
minLength: 0,
maxLength: 0);
static String _pattern(WalletType type) {
switch (type) {
case WalletType.monero:
return '^([0-9]+([.\,][0-9]{0,12})?|[.\,][0-9]{1,12})\$';
case WalletType.bitcoin:
return '^([0-9]+([.\,][0-9]{0,8})?|[.\,][0-9]{1,8})\$';
static String _pattern(CryptoCurrency currency) {
switch (currency) {
case CryptoCurrency.xmr:
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
case CryptoCurrency.btc:
return '^([0-9]+([.\,][0-9]{1,8})?|[.\,][0-9]{1,8})\$';
default:
return '';
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
}
}
}
class AllAmountValidator extends TextValidator {
AllAmountValidator() : super(
errorMessage: S.current.error_text_amount,
pattern: S.current.all,
minLength: 0,
maxLength: 0);
AllAmountValidator()
: super(
errorMessage: S.current.error_text_amount,
pattern: S.current.all,
minLength: 0,
maxLength: 0);
}

View file

@ -218,7 +218,7 @@ class BackupService {
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 exchangeStatus = data[PreferencesKey.exchangeStatusKey] as int?;
final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?;
final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?;
final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?;
@ -281,9 +281,9 @@ class BackupService {
await _sharedPreferences.setInt(
PreferencesKey.currentTheme, currentTheme);
if (disableExchange != null)
await _sharedPreferences.setBool(
PreferencesKey.disableExchangeKey, disableExchange);
if (exchangeStatus != null)
await _sharedPreferences.setInt(
PreferencesKey.exchangeStatusKey, exchangeStatus);
if (currentDefaultSettingsMigrationVersion != null)
await _sharedPreferences.setInt(
@ -432,8 +432,8 @@ class BackupService {
_sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
PreferencesKey.currentTheme:
_sharedPreferences.getInt(PreferencesKey.currentTheme),
PreferencesKey.disableExchangeKey:
_sharedPreferences.getBool(PreferencesKey.disableExchangeKey),
PreferencesKey.exchangeStatusKey:
_sharedPreferences.getInt(PreferencesKey.exchangeStatusKey),
PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences
.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion),
PreferencesKey.bitcoinTransactionPriority:

View file

@ -3,19 +3,35 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
const fiatApiAuthority = 'fiat-api.cakewallet.com';
const fiatApiPath = '/v1/rates';
const _fiatApiClearNetAuthority = 'fiat-api.cakewallet.com';
const _fiatApiOnionAuthority = 'n4z7bdcmwk2oyddxvzaap3x2peqcplh3pzdy7tpkk5ejz5n4mhfvoxqd.onion';
const _fiatApiPath = '/v2/rates';
Future<double> _fetchPrice(Map<String, dynamic> args) async {
final crypto = args['crypto'] as CryptoCurrency;
final fiat = args['fiat'] as FiatCurrency;
final torOnly = args['torOnly'] as bool;
final Map<String, String> queryParams = {
'interval_count': '1',
'base': crypto.toString(),
'quote': fiat.toString(),
'key' : secrets.fiatApiKey,
};
double price = 0.0;
try {
final fiatStringified = fiat.toString();
final uri = Uri.https(fiatApiAuthority, fiatApiPath,
<String, String>{'convert': fiatStringified});
late final Uri uri;
if (torOnly) {
uri = Uri.http(_fiatApiOnionAuthority, _fiatApiPath, queryParams);
} else {
uri = Uri.https(_fiatApiClearNetAuthority, _fiatApiPath, queryParams);
}
final response = await get(uri);
if (response.statusCode != 200) {
@ -23,13 +39,10 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final data = responseJSON['data'] as List<dynamic>;
final results = responseJSON['results'] as Map<String, dynamic>;
for (final item in data) {
if (item['symbol'] == crypto.title) {
price = item['quote'][fiatStringified]['price'] as double;
break;
}
if (results.isNotEmpty) {
price = results.values.first as double;
}
return price;
@ -38,12 +51,14 @@ Future<double> _fetchPrice(Map<String, dynamic> args) async {
}
}
Future<double> _fetchPriceAsync(
CryptoCurrency crypto, FiatCurrency fiat) async =>
compute(_fetchPrice, {'fiat': fiat, 'crypto': crypto});
Future<double> _fetchPriceAsync(CryptoCurrency crypto, FiatCurrency fiat, bool torOnly) async =>
compute(_fetchPrice, {'fiat': fiat, 'crypto': crypto, 'torOnly': torOnly});
class FiatConversionService {
static Future<double> fetchPrice(
CryptoCurrency crypto, FiatCurrency fiat) async =>
await _fetchPriceAsync(crypto, fiat);
static Future<double> fetchPrice({
required CryptoCurrency crypto,
required FiatCurrency fiat,
required bool torOnly,
}) async =>
await _fetchPriceAsync(crypto, fiat, torOnly);
}

View file

@ -22,13 +22,13 @@ class WalletLoadingService {
final wallet = await walletService.openWallet(name, walletPassword);
if (type == WalletType.monero) {
await upateMoneroWalletPassword(wallet);
await updateMoneroWalletPassword(wallet);
}
return wallet;
}
Future<void> upateMoneroWalletPassword(WalletBase wallet) async {
Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
@ -37,8 +37,8 @@ class WalletLoadingService {
}
final password = generateWalletPassword();
// Save new generated password with backup key for case
// if wallet will change password, but it will faild to updated in secure storage
// Save new generated password with backup key for case where
// wallet will change password, but it will fail to update in secure storage
final bakWalletName = '#__${wallet.name}_bak__#';
await keyService.saveWalletPassword(walletName: bakWalletName, password: password);
await wallet.changePassword(password);

View file

@ -4,12 +4,29 @@ import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/receive_page_option.dart';
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/anonpay_details/anonpay_details_page.dart';
import 'package:cake_wallet/src/screens/buy/onramper_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/buy/payfura_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_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';
@ -22,6 +39,14 @@ import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.da
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.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';
@ -188,6 +213,7 @@ late Box<ExchangeTemplate> _exchangeTemplates;
late Box<TransactionDescription> _transactionDescriptionBox;
late Box<Order> _ordersSource;
late Box<UnspentCoinsInfo>? _unspentCoinsInfoSource;
late Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
Future setup(
{required Box<WalletInfo> walletInfoSource,
@ -198,7 +224,9 @@ Future setup(
required Box<ExchangeTemplate> exchangeTemplates,
required Box<TransactionDescription> transactionDescriptionBox,
required Box<Order> ordersSource,
Box<UnspentCoinsInfo>? unspentCoinsInfoSource}) async {
Box<UnspentCoinsInfo>? unspentCoinsInfoSource,
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource
}) async {
_walletInfoSource = walletInfoSource;
_nodeSource = nodeSource;
_contactSource = contactSource;
@ -208,21 +236,22 @@ Future setup(
_transactionDescriptionBox = transactionDescriptionBox;
_ordersSource = ordersSource;
_unspentCoinsInfoSource = unspentCoinsInfoSource;
_anonpayInvoiceInfoSource = anonpayInvoiceInfoSource;
if (!_isSetupFinished) {
getIt.registerSingletonAsync<SharedPreferences>(
() => SharedPreferences.getInstance());
}
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
(secrets.wyreApiKey?.isNotEmpty ?? false) &&
(secrets.wyreAccountId?.isNotEmpty ?? false);
final isBitcoinBuyEnabled = (secrets.wyreSecretKey.isNotEmpty ?? false) &&
(secrets.wyreApiKey.isNotEmpty ?? false) &&
(secrets.wyreAccountId.isNotEmpty ?? false);
final settingsStore = await SettingsStoreBase.load(
nodeSource: _nodeSource,
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
// Enforce darkTheme on other platforms till the design for other themes is completed
initialTheme: Platform.isIOS || Platform.isAndroid ? null : ThemeList.darkTheme,
// Enforce darkTheme on platforms other than mobile till the design for other themes is completed
initialTheme: DeviceInfo.instance.isMobile ? null : ThemeList.darkTheme,
);
if (_isSetupFinished) {
@ -256,6 +285,8 @@ Future setup(
appStore: getIt.get<AppStore>(),
secureStorage: getIt.get<SecureStorage>())
..init());
getIt.registerSingleton<AnonpayTransactionsStore>(AnonpayTransactionsStore(
anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource));
final secretStore =
await SecretStoreBase.load(getIt.get<SecureStorage>());
@ -321,7 +352,9 @@ Future setup(
transactionFilterStore: getIt.get<TransactionFilterStore>(),
settingsStore: settingsStore,
yatStore: getIt.get<YatStore>(),
ordersStore: getIt.get<OrdersStore>()));
ordersStore: getIt.get<OrdersStore>(),
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>())
);
getIt.registerFactory<AuthService>(() => AuthService(
secureStorage: getIt.get<SecureStorage>(),
@ -369,23 +402,65 @@ Future setup(
.registerFactoryParam<AuthPage, void Function(bool, AuthPageState), bool>(
(onAuthFinished, closable) => AuthPage(getIt.get<AuthViewModel>(),
onAuthenticationFinished: onAuthFinished,
closable: closable ?? false));
closable: closable));
getIt.registerFactory(() =>
BalancePage(dashboardViewModel: getIt.get<DashboardViewModel>(), settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactory<DashboardPage>(() => DashboardPage(
balancePage: getIt.get<BalancePage>(),
balancePage: getIt.get<BalancePage>(),
dashboardViewModel: getIt.get<DashboardViewModel>(),
addressListViewModel: getIt.get<WalletAddressListViewModel>(),
desktopSidebarViewModel: getIt.get<DesktopSidebarViewModel>(),
));
getIt.registerFactory<DesktopSidebarWrapper>(() {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
return DesktopSidebarWrapper(
dashboardViewModel: getIt.get<DashboardViewModel>(),
desktopSidebarViewModel: getIt.get<DesktopSidebarViewModel>(),
child: getIt.get<DesktopDashboardPage>(param1: _navigatorKey),
desktopNavigatorKey: _navigatorKey,
);
});
getIt.registerFactoryParam<DesktopDashboardPage, GlobalKey<NavigatorState>, void>(
(desktopKey, _) => DesktopDashboardPage(
balancePage: getIt.get<BalancePage>(),
dashboardViewModel: getIt.get<DashboardViewModel>(),
addressListViewModel: getIt.get<WalletAddressListViewModel>(),
desktopKey: desktopKey,
));
getIt.registerFactory<TransactionsPage>(() => TransactionsPage(dashboardViewModel: getIt.get<DashboardViewModel>()));
getIt.registerFactoryParam<ReceiveOptionViewModel, ReceivePageOption?, void>((pageOption, _) => ReceiveOptionViewModel(
getIt.get<AppStore>().wallet!, pageOption));
getIt.registerFactoryParam<AnonInvoicePageViewModel, List<dynamic>, void>((args, _) {
final address = args.first as String;
final pageOption = args.last as ReceivePageOption;
return AnonInvoicePageViewModel(
getIt.get<AnonPayApi>(),
address,
getIt.get<SettingsStore>(),
getIt.get<AppStore>().wallet!,
_anonpayInvoiceInfoSource,
getIt.get<SharedPreferences>(),
pageOption,
);
});
getIt.registerFactoryParam<AnonPayInvoicePage, List<dynamic>, void>((List<dynamic> args, _) {
final pageOption = args.last as ReceivePageOption;
return AnonPayInvoicePage(
getIt.get<AnonInvoicePageViewModel>(param1: args),
getIt.get<ReceiveOptionViewModel>(param1: pageOption));
});
getIt.registerFactory<ReceivePage>(() => ReceivePage(
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
getIt.registerFactory<AddressPage>(() => AddressPage(
addressListViewModel: getIt.get<WalletAddressListViewModel>(),
walletViewModel: getIt.get<DashboardViewModel>()));
dashboardViewModel: getIt.get<DashboardViewModel>(),
receiveOptionViewModel: getIt.get<ReceiveOptionViewModel>()));
getIt.registerFactoryParam<WalletAddressEditOrCreateViewModel, WalletAddressListItem?, void>(
(WalletAddressListItem? item, _) => WalletAddressEditOrCreateViewModel(
@ -419,13 +494,25 @@ Future setup(
getIt.registerFactory(() => SendTemplatePage(
sendTemplateViewModel: getIt.get<SendTemplateViewModel>()));
getIt.registerFactory(() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>(),
getIt.get<AuthService>(),
),
);
if (DeviceInfo.instance.isMobile) {
getIt.registerFactory(() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>(),
getIt.get<AuthService>(),
),
);
} else {
// register wallet list view model as singleton on desktop since it can be accessed
// from multiple places at the same time (Wallets DropDown, Wallets List in settings)
getIt.registerLazySingleton(() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>(),
getIt.get<AuthService>(),
),
);
}
getIt.registerFactory(() =>
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>()));
@ -497,7 +584,7 @@ Future setup(
isNewWalletCreated: isWalletCreated));
getIt
.registerFactory(() => WalletKeysViewModel(getIt.get<AppStore>().wallet!));
.registerFactory(() => WalletKeysViewModel(getIt.get<AppStore>()));
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
@ -517,8 +604,7 @@ Future setup(
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
return NodeListViewModel(
_nodeSource, appStore.wallet!, appStore.settingsStore);
return NodeListViewModel(_nodeSource, appStore);
});
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<NodeListViewModel>(), getIt.get<DashboardViewModel>()));
@ -535,19 +621,29 @@ Future setup(
(WalletType? type, _) => NodeCreateOrEditViewModel(
_nodeSource,
type ?? getIt.get<AppStore>().wallet!.type,
getIt.get<SettingsStore>(),
getIt.get<SettingsStore>()
));
getIt.registerFactory(
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
getIt.registerFactoryParam<NodeCreateOrEditPage, Node?, bool?>(
(Node? editingNode, bool? isSelected) => NodeCreateOrEditPage(
nodeCreateOrEditViewModel: getIt.get<NodeCreateOrEditViewModel>(),
editingNode: editingNode,
isSelected: isSelected));
getIt.registerLazySingleton<OnRamperBuyProvider>(() => OnRamperBuyProvider(
getIt.registerFactory<OnRamperBuyProvider>(() => OnRamperBuyProvider(
settingsStore: getIt.get<AppStore>().settingsStore,
wallet: getIt.get<AppStore>().wallet!,
));
getIt.registerFactory(() => OnRamperPage(getIt.get<OnRamperBuyProvider>()));
getIt.registerFactory<PayfuraBuyProvider>(() => PayfuraBuyProvider(
settingsStore: getIt.get<AppStore>().settingsStore,
wallet: getIt.get<AppStore>().wallet!,
));
getIt.registerFactory(() => PayFuraPage(getIt.get<PayfuraBuyProvider>()));
getIt.registerFactory(() => ExchangeViewModel(
getIt.get<AppStore>().wallet!,
_tradesSource,
@ -736,8 +832,8 @@ Future setup(
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>(),
walletType: getIt.get<AppStore>().wallet!.type));
getIt.registerFactoryParam<FullscreenQRPage, String, bool>(
(String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,));
getIt.registerFactoryParam<FullscreenQRPage, String, int?>(
(String qrData, int? version) => FullscreenQRPage(qrData: qrData, version: version,));
getIt.registerFactory(() => IoniaApi());
@ -844,10 +940,29 @@ Future setup(
getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get<IoniaAccountViewModel>()));
getIt.registerFactory(() => AnonPayApi(useTorOnly: getIt.get<SettingsStore>().exchangeStatus == ExchangeApiMode.torOnly,
wallet: getIt.get<AppStore>().wallet!)
);
getIt.registerFactory(() => DesktopWalletSelectionDropDown(getIt.get<WalletListViewModel>()));
getIt.registerFactory(() => DesktopSidebarViewModel());
getIt.registerFactoryParam<AnonpayDetailsViewModel, AnonpayInvoiceInfo, void>(
(AnonpayInvoiceInfo anonpayInvoiceInfo, _)
=> AnonpayDetailsViewModel(
anonPayApi: getIt.get<AnonPayApi>(),
anonpayInvoiceInfo: anonpayInvoiceInfo,
settingsStore: getIt.get<SettingsStore>(),
));
getIt.registerFactoryParam<AnonPayReceivePage, AnonpayInfoBase, void>(
(AnonpayInfoBase anonpayInvoiceInfo, _) => AnonPayReceivePage(invoiceInfo: anonpayInvoiceInfo));
getIt.registerFactoryParam<AnonpayDetailsPage, AnonpayInvoiceInfo, void>(
(AnonpayInvoiceInfo anonpayInvoiceInfo, _)
=> AnonpayDetailsPage(anonpayDetailsViewModel: getIt.get<AnonpayDetailsViewModel>(param1: anonpayInvoiceInfo)));
getIt.registerFactoryParam<IoniaPaymentStatusViewModel, IoniaAnyPayPaymentInfo, AnyPayPaymentCommittedInfo>(
(IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo)
=> IoniaPaymentStatusViewModel(
@ -920,4 +1035,4 @@ Future setup(
instanceName: 'wallet_password_login');
_isSetupFinished = true;
}
}

View file

@ -1,10 +1,12 @@
import 'dart:io' show File, Platform;
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:share_plus/share_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/wallet_type.dart';
@ -37,6 +39,9 @@ Future defaultSettingsMigration(
await ios_migrate_v1(walletInfoSource, tradeSource, contactSource);
}
// check current nodes for nullability regardless of the version
await checkCurrentNodes(nodes, sharedPreferences);
final currentVersion = sharedPreferences
.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) ??
0;
@ -142,7 +147,9 @@ Future defaultSettingsMigration(
case 19:
await validateBitcoinSavedTransactionPriority(sharedPreferences);
break;
case 20:
await migrateExchangeStatus(sharedPreferences);
break;
default:
break;
}
@ -501,3 +508,15 @@ Future<void> changeDefaultHavenNode(
await node.save();
});
}
Future<void> migrateExchangeStatus(SharedPreferences sharedPreferences) async {
final isExchangeDisabled = sharedPreferences.getBool(PreferencesKey.disableExchangeKey);
if (isExchangeDisabled == null) {
return;
}
await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, isExchangeDisabled
? ExchangeApiMode.disabled.raw : ExchangeApiMode.enabled.raw);
await sharedPreferences.remove(PreferencesKey.disableExchangeKey);
}

View file

@ -0,0 +1,39 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/enumerable_item.dart';
class ExchangeApiMode extends EnumerableItem<int> with Serializable<int> {
const ExchangeApiMode({required String title, required int raw}) : super(title: title, raw: raw);
static const all = [ExchangeApiMode.enabled, ExchangeApiMode.torOnly, ExchangeApiMode.disabled];
static const enabled = ExchangeApiMode(raw: 0, title: 'Enabled');
static const torOnly = ExchangeApiMode(raw: 1, title: 'Tor only');
static const disabled = ExchangeApiMode(raw: 2, title: 'Disabled');
static ExchangeApiMode 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 ExchangeApiMode deserialize');
}
}
@override
String toString() {
switch (this) {
case ExchangeApiMode.enabled:
return S.current.enabled;
case ExchangeApiMode.torOnly:
return S.current.tor_only;
case ExchangeApiMode.disabled:
return S.current.disabled;
default:
return '';
}
}
}

View file

@ -1,6 +1,7 @@
import 'package:cw_core/currency.dart';
import 'package:cw_core/enumerable_item.dart';
class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
class FiatCurrency extends EnumerableItem<String> with Serializable<String> implements Currency {
const FiatCurrency({required String symbol, required this.countryCode, required this.fullName}) : super(title: symbol, raw: symbol);
final String countryCode;
@ -9,76 +10,106 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
static List<FiatCurrency> get all => _all.values.toList();
static List<FiatCurrency> get currenciesAvailableToBuyWith =>
[aud, brl, cad, chf, czk, eur, dkk, gbp, hkd, ils, jpy, krw, mxn, myr, nok, nzd, pln, sek, sgd, thb, usd, zar];
[aud, bgn, brl, cad, chf, clp, cop, czk, dkk, egp, eur, gbp, gtq, hkd, hrk, huf, idr, ils, inr, isk, jpy, krw, mad, mxn, myr, ngn, nok, nzd, php, pkr, pln, ron, sek, sgd, thb, twd, usd, vnd, zar];
static const ars = FiatCurrency(symbol: 'ARS', countryCode: "arg", fullName: "Argentine Peso");
static const aud = FiatCurrency(symbol: 'AUD', countryCode: "aus", fullName: "Australian Dollar");
static const bdt = FiatCurrency(symbol: 'BDT', countryCode: "bgd", fullName: "Bangladeshi Taka");
static const bgn = FiatCurrency(symbol: 'BGN', countryCode: "bgr", fullName: "Bulgarian Lev");
static const brl = FiatCurrency(symbol: 'BRL', countryCode: "bra", fullName: "Brazilian Real");
static const cad = FiatCurrency(symbol: 'CAD', countryCode: "cad", fullName: "Canadian Dollar");
static const chf = FiatCurrency(symbol: 'CHF', countryCode: "che", fullName: "Swiss Franc");
static const clp = FiatCurrency(symbol: 'CLP', countryCode: "chl", fullName: "Chilean Peso");
static const cny = FiatCurrency(symbol: 'CNY', countryCode: "chn", fullName: "Chinese Yuan");
static const cop = FiatCurrency(symbol: 'COP', countryCode: "col", fullName: "Colombian Peso");
static const czk = FiatCurrency(symbol: 'CZK', countryCode: "czk", fullName: "Czech Koruna");
static const eur = FiatCurrency(symbol: 'EUR', countryCode: "eur", fullName: "Euro");
static const dkk = FiatCurrency(symbol: 'DKK', countryCode: "dnk", fullName: "Danish Krone");
static const gbp = FiatCurrency(symbol: 'GBP', countryCode: "gbr", fullName: "Pound sterling");
static const egp = FiatCurrency(symbol: 'EGP', countryCode: "egy", fullName: "Egyptian Pound");
static const eur = FiatCurrency(symbol: 'EUR', countryCode: "eur", fullName: "Euro");
static const gbp = FiatCurrency(symbol: 'GBP', countryCode: "gbr", fullName: "Pound Sterling");
static const ghs = FiatCurrency(symbol: 'GHS', countryCode: "gha", fullName: "Ghanaian Cedi");
static const gtq = FiatCurrency(symbol: 'GTQ', countryCode: "gtm", fullName: "Guatemalan Quetzal");
static const hkd = FiatCurrency(symbol: 'HKD', countryCode: "hkg", fullName: "Hong Kong Dollar");
static const hrk = FiatCurrency(symbol: 'HRK', countryCode: "hrv", fullName: "Croatian Kuna");
static const huf = FiatCurrency(symbol: 'HUF', countryCode: "hun", fullName: "Hungarian Forint");
static const idr = FiatCurrency(symbol: 'IDR', countryCode: "idn", fullName: "Indonesian Rupiah");
static const ils = FiatCurrency(symbol: 'ILS', countryCode: "isr", fullName: "Israeli New Shekel");
static const inr = FiatCurrency(symbol: 'INR', countryCode: "ind", fullName: "Indian Rupee");
static const isk = FiatCurrency(symbol: 'ISK', countryCode: "isl", fullName: "Icelandic Króna");
static const jpy = FiatCurrency(symbol: 'JPY', countryCode: "jpn", fullName: "Japanese Yen equals");
static const krw = FiatCurrency(symbol: 'KRW', countryCode: "kor", fullName: "South Korean won");
static const irr = FiatCurrency(symbol: 'IRR', countryCode: "irn", fullName: "Iranian Rial");
static const isk = FiatCurrency(symbol: 'ISK', countryCode: "isl", fullName: "Icelandic Krona Króna");
static const jpy = FiatCurrency(symbol: 'JPY', countryCode: "jpn", fullName: "Japanese Yen");
static const krw = FiatCurrency(symbol: 'KRW', countryCode: "kor", fullName: "South Korean Won");
static const mad = FiatCurrency(symbol: 'MAD', countryCode: "mar", fullName: "Moroccan Dirham");
static const mxn = FiatCurrency(symbol: 'MXN', countryCode: "mex", fullName: "Mexican Peso");
static const myr = FiatCurrency(symbol: 'MYR', countryCode: "mys", fullName: "Malaysian Ringgit");
static const ngn = FiatCurrency(symbol: 'NGN', countryCode: "nga", fullName: "Nigerian Naira");
static const nok = FiatCurrency(symbol: 'NOK', countryCode: "nor", fullName: "Norwegian Krone");
static const nzd = FiatCurrency(symbol: 'NZD', countryCode: "nzl", fullName: "New Zealand Dollar");
static const php = FiatCurrency(symbol: 'PHP', countryCode: "phl", fullName: "Philippine peso");
static const pln = FiatCurrency(symbol: 'PLN', countryCode: "pol", fullName: "Poland złoty");
static const php = FiatCurrency(symbol: 'PHP', countryCode: "phl", fullName: "Philippine Peso");
static const pkr = FiatCurrency(symbol: 'PKR', countryCode: "pak", fullName: "Pakistani Rupee");
static const pln = FiatCurrency(symbol: 'PLN', countryCode: "pol", fullName: "Poland Zloty złoty");
static const ron = FiatCurrency(symbol: 'RON', countryCode: "rou", fullName: "Romanian Leu");
static const rub = FiatCurrency(symbol: 'RUB', countryCode: "rus", fullName: "Russian Ruble");
static const sar = FiatCurrency(symbol: 'SAR', countryCode: "sau", fullName: "Saudi Riyal");
static const sek = FiatCurrency(symbol: 'SEK', countryCode: "swe", fullName: "Swedish Krona");
static const sgd = FiatCurrency(symbol: 'SGD', countryCode: "sgp", fullName: "Singapore Dollar");
static const thb = FiatCurrency(symbol: 'THB', countryCode: "tha", fullName: "Thai Baht");
static const thb = FiatCurrency(symbol: 'THB', countryCode: "tha", fullName: "New Thaiwan Dollar");
static const twd = FiatCurrency(symbol: 'TWD', countryCode: "twn", fullName: "Thai Baht");
static const uah = FiatCurrency(symbol: 'UAH', countryCode: "ukr", fullName: "Ukrainian Hryvnia");
static const usd = FiatCurrency(symbol: 'USD', countryCode: "usa", fullName: "United States Dollar");
static const vef = FiatCurrency(symbol: 'VEF', countryCode: "ven", fullName: "Venezuelan Bolivar Bolívar");
static const vnd = FiatCurrency(symbol: 'VND', countryCode: "vnm", fullName: "Vietnamese Dong đồng");
static const zar = FiatCurrency(symbol: 'ZAR', countryCode: "saf", fullName: "South African Rand");
static const vef = FiatCurrency(symbol: 'VEF', countryCode: "ven", fullName: "Venezuelan Bolívar");
static final _all = {
FiatCurrency.ars.raw: FiatCurrency.ars,
FiatCurrency.aud.raw: FiatCurrency.aud,
FiatCurrency.bdt.raw: FiatCurrency.bdt,
FiatCurrency.bgn.raw: FiatCurrency.bgn,
FiatCurrency.brl.raw: FiatCurrency.brl,
FiatCurrency.cad.raw: FiatCurrency.cad,
FiatCurrency.chf.raw: FiatCurrency.chf,
FiatCurrency.clp.raw: FiatCurrency.clp,
FiatCurrency.cny.raw: FiatCurrency.cny,
FiatCurrency.cop.raw: FiatCurrency.cop,
FiatCurrency.czk.raw: FiatCurrency.czk,
FiatCurrency.eur.raw: FiatCurrency.eur,
FiatCurrency.dkk.raw: FiatCurrency.dkk,
FiatCurrency.egp.raw: FiatCurrency.egp,
FiatCurrency.eur.raw: FiatCurrency.eur,
FiatCurrency.gbp.raw: FiatCurrency.gbp,
FiatCurrency.ghs.raw: FiatCurrency.ghs,
FiatCurrency.gtq.raw: FiatCurrency.gtq,
FiatCurrency.hkd.raw: FiatCurrency.hkd,
FiatCurrency.hrk.raw: FiatCurrency.hrk,
FiatCurrency.huf.raw: FiatCurrency.huf,
FiatCurrency.idr.raw: FiatCurrency.idr,
FiatCurrency.ils.raw: FiatCurrency.ils,
FiatCurrency.inr.raw: FiatCurrency.inr,
FiatCurrency.irr.raw: FiatCurrency.irr,
FiatCurrency.isk.raw: FiatCurrency.isk,
FiatCurrency.jpy.raw: FiatCurrency.jpy,
FiatCurrency.krw.raw: FiatCurrency.krw,
FiatCurrency.mad.raw: FiatCurrency.mad,
FiatCurrency.mxn.raw: FiatCurrency.mxn,
FiatCurrency.myr.raw: FiatCurrency.myr,
FiatCurrency.ngn.raw: FiatCurrency.ngn,
FiatCurrency.nok.raw: FiatCurrency.nok,
FiatCurrency.nzd.raw: FiatCurrency.nzd,
FiatCurrency.php.raw: FiatCurrency.php,
FiatCurrency.pkr.raw: FiatCurrency.pkr,
FiatCurrency.pln.raw: FiatCurrency.pln,
FiatCurrency.ron.raw: FiatCurrency.ron,
FiatCurrency.rub.raw: FiatCurrency.rub,
FiatCurrency.sar.raw: FiatCurrency.sar,
FiatCurrency.sek.raw: FiatCurrency.sek,
FiatCurrency.sgd.raw: FiatCurrency.sgd,
FiatCurrency.thb.raw: FiatCurrency.thb,
FiatCurrency.twd.raw: FiatCurrency.twd,
FiatCurrency.uah.raw: FiatCurrency.uah,
FiatCurrency.usd.raw: FiatCurrency.usd,
FiatCurrency.zar.raw: FiatCurrency.zar,
FiatCurrency.vef.raw: FiatCurrency.vef
FiatCurrency.vef.raw: FiatCurrency.vef,
FiatCurrency.vnd.raw: FiatCurrency.vnd,
FiatCurrency.zar.raw: FiatCurrency.zar
};
static FiatCurrency deserialize({required String raw}) => _all[raw]!;
@ -88,4 +119,13 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
@override
int get hashCode => raw.hashCode ^ title.hashCode;
@override
String get name => raw;
@override
String? get tag => null;
@override
String get iconPath => "assets/images/flags/$countryCode.png";
}

View file

@ -23,6 +23,10 @@ class LanguageService {
'ar': 'العربية (Arabic)',
'tr': 'Türkçe (Turkish)',
'my': 'မြန်မာ (Burmese)',
'bg': 'Български (Bulgarian)',
'cs': 'čeština (Czech)',
'ur': 'اردو (Urdu)',
'id': 'Bahasa Indonesia (Indonesian)'
};
static const Map<String, String> localeCountryCode = {
@ -45,6 +49,10 @@ class LanguageService {
'ar': 'sau',
'tr': 'tur',
'my': 'mmr',
'bg': 'bgr',
'cs': 'czk',
'ur': 'pak',
'id': 'idn'
};
static final list = <String, String> {};

View file

@ -1,11 +1,11 @@
import 'dart:io';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/device_info.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/wallet_type.dart';
@ -47,12 +47,22 @@ class MainActions {
switch (walletType) {
case WalletType.bitcoin:
case WalletType.litecoin:
if (Platform.isIOS || Platform.isAndroid) {
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pushNamed(Routes.onramperPage);
} else {
final uri = getIt
.get<OnRamperBuyProvider>()
.requestUrl(Theme.of(context).brightness == Brightness.dark);
.requestUrl();
await launchUrl(uri);
}
break;
case WalletType.monero:
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pushNamed(Routes.payfuraPage);
} else {
final uri = getIt
.get<PayfuraBuyProvider>()
.requestUrl();
await launchUrl(uri);
}
break;

View file

@ -25,7 +25,8 @@ class AddressResolver {
'888',
'nft',
'dao',
'blockchain'
'blockchain',
'polygon'
];
static String? extractAddressByType({required String raw, required CryptoCurrency type}) {

View file

@ -13,6 +13,7 @@ class PreferencesKey {
static const allowBiometricalAuthenticationKey =
'allow_biometrical_authentication';
static const disableExchangeKey = 'disable_exchange';
static const exchangeStatusKey = 'exchange_status';
static const currentTheme = 'current_theme';
static const isDarkThemeLegacy = 'dark_theme';
static const displayActionListModeKey = 'display_list_mode';
@ -30,10 +31,15 @@ class PreferencesKey {
static const pinTimeOutDuration = 'pin_timeout_duration';
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
static const lastPopupDate = 'last_popup_date';
static const lastAppReviewDate = 'last_app_review_date';
static String moneroWalletUpdateV1Key(String name)
=> '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
static const exchangeProvidersSelection = 'exchange-providers-selection';
static const clearnetDonationLink = 'clearnet_donation_link';
static const onionDonationLink = 'onion_donation_link';
static const shouldShowMarketPlaceInDashboard = 'should_show_marketplace_in_dashboard';
}

View file

@ -0,0 +1,23 @@
enum ReceivePageOption {
mainnet,
anonPayInvoice,
anonPayDonationLink;
@override
String toString() {
String label = '';
switch (this) {
case ReceivePageOption.mainnet:
label = 'Mainnet';
break;
case ReceivePageOption.anonPayInvoice:
label = 'Trocador AnonPay Invoice';
break;
case ReceivePageOption.anonPayDonationLink:
label = 'Trocador AnonPay Donation Link';
break;
}
return label;
}
}

View file

@ -1,5 +1,4 @@
import 'dart:io';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:flutter/services.dart';
const channel = MethodChannel('com.cake_wallet/native_utils');
@ -8,7 +7,7 @@ Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async
var address = '';
try {
if (Platform.isAndroid || Platform.isIOS) {
if (DeviceInfo.instance.isMobile) {
address = await channel.invokeMethod<String>(
'getUnstoppableDomainAddress',
<String, String> {

View file

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:http/http.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cw_core/crypto_currency.dart';
@ -24,7 +25,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
.expand((i) => i)
.toList());
static const apiKey = secrets.changeNowApiKey;
static final apiKey = DeviceInfo.instance.isMobile ? secrets.changeNowApiKey : secrets.changeNowApiKeyDesktop;
static const apiAuthority = 'api.changenow.io';
static const createTradePath = '/v2/exchange';
static const findTradeByIdPath = '/v2/exchange/by-id';
@ -269,6 +270,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
: currency.title.toLowerCase();
}
}
}
String normalizeCryptoCurrency(CryptoCurrency currency) {

View file

@ -14,6 +14,7 @@ abstract class ExchangeProvider {
bool get isAvailable;
bool get isEnabled;
bool get supportsFixedRate;
bool get supportsOnionAddress => false;
@override
String toString() => title;

View file

@ -1,31 +1,30 @@
import 'package:cw_core/enumerable_item.dart';
class ExchangeProviderDescription extends EnumerableItem<int>
with Serializable<int> {
const ExchangeProviderDescription({
required String title,
required int raw,
required this.image,
this.horizontalLogo = false})
class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<int> {
const ExchangeProviderDescription(
{required String title, required int raw, required this.image, this.horizontalLogo = false})
: super(title: title, raw: raw);
final bool horizontalLogo;
final String image;
static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0, image: 'assets/images/xmrto.png');
static const xmrto =
ExchangeProviderDescription(title: 'XMR.TO', raw: 0, image: 'assets/images/xmrto.png');
static const changeNow =
ExchangeProviderDescription(title: 'ChangeNOW', raw: 1, image: 'assets/images/changenow.png');
static const morphToken =
ExchangeProviderDescription(title: 'MorphToken', raw: 2, image: 'assets/images/morph.png');
static const sideShift =
static const sideShift =
ExchangeProviderDescription(title: 'SideShift', raw: 3, image: 'assets/images/sideshift.png');
static const simpleSwap =
ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png');
static const simpleSwap = ExchangeProviderDescription(
title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png');
static const all =
ExchangeProviderDescription(title: 'All trades', raw: 5, image:'');
static const trocador =
ExchangeProviderDescription(title: 'Trocador', raw: 5, image: 'assets/images/trocador.png');
static const all = ExchangeProviderDescription(title: 'All trades', raw: 6, image: '');
static ExchangeProviderDescription deserialize({required int raw}) {
switch (raw) {
@ -40,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem<int>
case 4:
return simpleSwap;
case 5:
return trocador;
case 6:
return all;
default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');

View file

@ -6,6 +6,7 @@ import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade.dart';
@ -29,7 +30,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
static const rangePath = '/v1/get_ranges';
static const getExchangePath = '/v1/get_exchange';
static const createExchangePath = '/v1/create_exchange';
static const apiKey = secrets.simpleSwapApiKey;
static final apiKey = DeviceInfo.instance.isMobile ? secrets.simpleSwapApiKey : secrets.simpleSwapApiKeyDesktop;
@override
ExchangeProviderDescription get description =>

View file

@ -8,21 +8,25 @@ part 'trade.g.dart';
@HiveType(typeId: Trade.typeId)
class Trade extends HiveObject {
Trade(
{required this.id,
required this.amount,
ExchangeProviderDescription? provider,
CryptoCurrency? from,
CryptoCurrency? to,
TradeState? state,
this.createdAt,
this.expiredAt,
this.inputAddress,
this.extraId,
this.outputTransaction,
this.refundAddress,
this.walletId,
this.payoutAddress}) {
Trade({
required this.id,
required this.amount,
ExchangeProviderDescription? provider,
CryptoCurrency? from,
CryptoCurrency? to,
TradeState? state,
this.createdAt,
this.expiredAt,
this.inputAddress,
this.extraId,
this.outputTransaction,
this.refundAddress,
this.walletId,
this.payoutAddress,
this.password,
this.providerId,
this.providerName,
}) {
if (provider != null) {
providerRaw = provider.raw;
}
@ -92,16 +96,23 @@ class Trade extends HiveObject {
@HiveField(13)
String? payoutAddress;
@HiveField(14)
String? password;
@HiveField(15)
String? providerId;
@HiveField(16)
String? providerName;
static Trade fromMap(Map<String, Object?> map) {
return Trade(
id: map['id'] as String,
provider: ExchangeProviderDescription.deserialize(
raw: map['provider'] as int),
provider: ExchangeProviderDescription.deserialize(raw: map['provider'] as int),
from: CryptoCurrency.deserialize(raw: map['input'] as int),
to: CryptoCurrency.deserialize(raw: map['output'] as int),
createdAt: map['date'] != null
? DateTime.fromMillisecondsSinceEpoch(map['date'] as int)
: null,
createdAt:
map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null,
amount: map['amount'] as String,
walletId: map['wallet_id'] as String);
}

View file

@ -0,0 +1,312 @@
import 'dart:convert';
import 'package:cake_wallet/exchange/exchange_pair.dart';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/trocador/trocador_request.dart';
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:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:http/http.dart';
class TrocadorExchangeProvider extends ExchangeProvider {
TrocadorExchangeProvider({this.useTorOnly = false})
: _lastUsedRateId = '',
super(pairList: _supportedPairs());
bool useTorOnly;
static const List<CryptoCurrency> _notSupported = [
CryptoCurrency.scrt,
CryptoCurrency.stx,
CryptoCurrency.zaddr,
];
static List<ExchangePair> _supportedPairs() {
final supportedCurrencies =
CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList();
return supportedCurrencies
.map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.expand((i) => i)
.toList();
}
static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion';
static const clearNetAuthority = 'trocador.app';
static const apiKey = secrets.trocadorApiKey;
static const markup = secrets.trocadorExchangeMarkup;
static const newRatePath = '/api/new_rate';
static const createTradePath = 'api/new_trade';
static const tradePath = 'api/trade';
static const coinPath = 'api/coin';
String _lastUsedRateId;
@override
Future<bool> checkIsAvailable() async => true;
@override
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) {
final _request = request as TrocadorRequest;
return _createTrade(request: _request, isFixedRateMode: isFixedRateMode);
}
Future<Trade> _createTrade({
required TrocadorRequest request,
required bool isFixedRateMode,
}) async {
final params = <String, String>{
'api_key': apiKey,
'ticker_from': request.from.title.toLowerCase(),
'ticker_to': request.to.title.toLowerCase(),
'network_from': _networkFor(request.from),
'network_to': _networkFor(request.to),
'payment': isFixedRateMode ? 'True' : 'False',
'min_kycrating': 'C',
'markup': markup,
'best_only': 'True',
if (!isFixedRateMode) 'amount_from': request.fromAmount,
if (isFixedRateMode) 'amount_to': request.toAmount,
'address': request.address,
'refund': request.refundAddress
};
if (isFixedRateMode) {
await fetchRate(
from: request.from,
to: request.to,
amount: double.tryParse(request.toAmount) ?? 0,
isFixedRateMode: true,
isReceiveAmount: true,
);
params['id'] = _lastUsedRateId;
}
final uri = await _getUri(createTradePath, params);
final response = await get(uri);
if (response.statusCode == 400) {
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error'] as String;
final message = responseJSON['message'] as String;
throw Exception('${error}\n$message');
}
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['trade_id'] as String;
final inputAddress = responseJSON['address_provider'] as String;
final refundAddress = responseJSON['refund_address'] as String;
final status = responseJSON['status'] as String;
final state = TradeState.deserialize(raw: status);
final payoutAddress = responseJSON['address_user'] as String;
final date = responseJSON['date'] as String;
final password = responseJSON['password'] as String;
final providerId = responseJSON['id_provider'] as String;
final providerName = responseJSON['provider'] as String;
return Trade(
id: id,
from: request.from,
to: request.to,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
state: state,
password: password,
providerId: providerId,
providerName: providerName,
createdAt: DateTime.tryParse(date)?.toLocal(),
amount: responseJSON['amount_from']?.toString() ?? request.fromAmount,
payoutAddress: payoutAddress);
}
@override
ExchangeProviderDescription get description => ExchangeProviderDescription.trocador;
@override
Future<Limits> fetchLimits(
{required CryptoCurrency from,
required CryptoCurrency to,
required bool isFixedRateMode}) async {
final params = <String, String>{
'api_key': apiKey,
'ticker': from.title.toLowerCase(),
'name': from.name,
};
final uri = await _getUri(coinPath, params);
final response = await get(uri);
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseJSON = json.decode(response.body) as List<dynamic>;
if (responseJSON.isEmpty) {
throw Exception('No data');
}
final coinJson = responseJSON.first as Map<String, dynamic>;
return Limits(
min: coinJson['minimum'] as double,
max: coinJson['maximum'] as double,
);
}
@override
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode,
required bool isReceiveAmount}) async {
try {
if (amount == 0) {
return 0.0;
}
final params = <String, String>{
'api_key': apiKey,
'ticker_from': from.title.toLowerCase(),
'ticker_to': to.title.toLowerCase(),
'network_from': _networkFor(from),
'network_to': _networkFor(to),
if (!isFixedRateMode) 'amount_from': amount.toString(),
if (isFixedRateMode) 'amount_to': amount.toString(),
'payment': isFixedRateMode ? 'True' : 'False',
'min_kycrating': 'C',
'markup': markup,
'best_only': 'True',
};
final uri = await _getUri(newRatePath, params);
final response = await get(uri);
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final fromAmount = double.parse(responseJSON['amount_from'].toString());
final toAmount = double.parse(responseJSON['amount_to'].toString());
final rateId = responseJSON['trade_id'] as String? ?? '';
if (rateId.isNotEmpty) {
_lastUsedRateId = rateId;
}
return isReceiveAmount ? (amount / fromAmount) : (toAmount / amount);
} catch (e) {
print(e.toString());
return 0.0;
}
}
@override
Future<Trade> findTradeById({required String id}) async {
final uri = await _getUri(tradePath, {'api_key': apiKey, 'id': id});
return get(uri).then((response) {
if (response.statusCode != 200) {
throw Exception('Unexpected http status: ${response.statusCode}');
}
final responseListJson = json.decode(response.body) as List;
final responseJSON = responseListJson.first;
final id = responseJSON['trade_id'] as String;
final payoutAddress = responseJSON['address_user'] as String;
final refundAddress = responseJSON['refund_address'] as String;
final inputAddress = responseJSON['address_provider'] as String;
final fromAmount = responseJSON['amount_from']?.toString() ?? '0';
final from = CryptoCurrency.fromString(responseJSON['ticker_from'] as String);
final to = CryptoCurrency.fromString(responseJSON['ticker_to'] as String);
final state = TradeState.deserialize(raw: responseJSON['status'] as String);
final date = DateTime.parse(responseJSON['date'] as String);
final password = responseJSON['password'] as String;
final providerId = responseJSON['id_provider'] as String;
final providerName = responseJSON['provider'] as String;
return Trade(
id: id,
from: from,
to: to,
provider: description,
inputAddress: inputAddress,
refundAddress: refundAddress,
createdAt: date,
amount: fromAmount,
state: state,
payoutAddress: payoutAddress,
password: password,
providerId: providerId,
providerName: providerName,
);
});
}
@override
bool get isAvailable => true;
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
bool get supportsOnionAddress => true;
@override
String get title => 'Trocador';
String _networkFor(CryptoCurrency currency) {
switch (currency) {
case CryptoCurrency.eth:
return 'ERC20';
case CryptoCurrency.maticpoly:
return 'Mainnet';
case CryptoCurrency.usdcpoly:
return 'MATIC';
case CryptoCurrency.zec:
return 'Mainnet';
default:
return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet';
}
}
String _normalizeTag(String tag) {
switch (tag) {
case 'ETH':
return 'ERC20';
case 'TRX':
return 'TRC20';
default:
return tag.toLowerCase();
}
}
Future<Uri> _getUri(String path, Map<String, String> queryParams) async {
if (!supportsOnionAddress) {
return Uri.https(clearNetAuthority, path, queryParams);
}
final uri = Uri.http(onionApiAuthority, path, queryParams);
if (useTorOnly) {
return uri;
}
try {
await get(uri);
return uri;
} catch (e) {
return Uri.https(clearNetAuthority, path, queryParams);
}
}
}

View file

@ -0,0 +1,21 @@
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cw_core/crypto_currency.dart';
class TrocadorRequest extends TradeRequest {
TrocadorRequest(
{required this.from,
required this.to,
required this.address,
required this.fromAmount,
required this.toAmount,
required this.refundAddress,
required this.isReverse});
CryptoCurrency from;
CryptoCurrency to;
String address;
String fromAmount;
String toAmount;
String refundAddress;
bool isReverse;
}

View file

@ -1,346 +1,331 @@
part of 'haven.dart';
class CWHavenAccountList extends HavenAccountList {
CWHavenAccountList(this._wallet);
final Object _wallet;
CWHavenAccountList(this._wallet);
@override
@computed
final Object _wallet;
@override
@computed
ObservableList<Account> get accounts {
final havenWallet = _wallet as HavenWallet;
final accounts = havenWallet.walletAddresses.accountList
.accounts
.map((acc) => Account(id: acc.id, label: acc.label))
.toList();
return ObservableList<Account>.of(accounts);
final havenWallet = _wallet as HavenWallet;
final accounts = havenWallet.walletAddresses.accountList.accounts
.map((acc) => Account(id: acc.id, label: acc.label))
.toList();
return ObservableList<Account>.of(accounts);
}
@override
void update(Object wallet) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.accountList.update();
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.accountList.update();
}
@override
void refresh(Object wallet) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.accountList.refresh();
}
void refresh(Object wallet) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.accountList.refresh();
}
@override
@override
List<Account> getAll(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.walletAddresses.accountList
.getAll()
.map((acc) => Account(id: acc.id, label: acc.label))
.toList();
final havenWallet = wallet as HavenWallet;
return havenWallet.walletAddresses.accountList
.getAll()
.map((acc) => Account(id: acc.id, label: acc.label))
.toList();
}
@override
Future<void> addAccount(Object wallet, {required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.accountList.addAccount(label: label);
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.accountList.addAccount(label: label);
}
@override
Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.accountList
.setLabelAccount(
accountIndex: accountIndex,
label: label);
Future<void> setLabelAccount(Object wallet,
{required int accountIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.accountList
.setLabelAccount(accountIndex: accountIndex, label: label);
}
}
class CWHavenSubaddressList extends MoneroSubaddressList {
CWHavenSubaddressList(this._wallet);
final Object _wallet;
CWHavenSubaddressList(this._wallet);
@override
@computed
final Object _wallet;
@override
@computed
ObservableList<Subaddress> get subaddresses {
final havenWallet = _wallet as HavenWallet;
final subAddresses = havenWallet.walletAddresses.subaddressList
.subaddresses
.map((sub) => Subaddress(
id: sub.id,
address: sub.address,
label: sub.label))
.toList();
return ObservableList<Subaddress>.of(subAddresses);
final havenWallet = _wallet as HavenWallet;
final subAddresses = havenWallet.walletAddresses.subaddressList.subaddresses
.map((sub) => Subaddress(id: sub.id, address: sub.address, label: sub.label))
.toList();
return ObservableList<Subaddress>.of(subAddresses);
}
@override
void update(Object wallet, {required int accountIndex}) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
}
@override
void refresh(Object wallet, {required int accountIndex}) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
}
@override
List<Subaddress> getAll(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.walletAddresses
.subaddressList
.getAll()
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
.toList();
final havenWallet = wallet as HavenWallet;
return havenWallet.walletAddresses.subaddressList
.getAll()
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
.toList();
}
@override
Future<void> addSubaddress(Object wallet, {required int accountIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.subaddressList
.addSubaddress(
accountIndex: accountIndex,
label: label);
Future<void> addSubaddress(Object wallet,
{required int accountIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.subaddressList
.addSubaddress(accountIndex: accountIndex, label: label);
}
@override
Future<void> setLabelSubaddress(Object wallet,
{required int accountIndex, required int addressIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.subaddressList
.setLabelSubaddress(
accountIndex: accountIndex,
addressIndex: addressIndex,
label: label);
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.subaddressList
.setLabelSubaddress(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
}
class CWHavenWalletDetails extends HavenWalletDetails {
CWHavenWalletDetails(this._wallet);
final Object _wallet;
CWHavenWalletDetails(this._wallet);
@computed
final Object _wallet;
@computed
@override
Account get account {
final havenWallet = _wallet as HavenWallet;
final acc = havenWallet.walletAddresses.account as monero_account.Account;
return Account(id: acc.id, label: acc.label);
final havenWallet = _wallet as HavenWallet;
final acc = havenWallet.walletAddresses.account as monero_account.Account;
return Account(id: acc.id, label: acc.label);
}
@computed
@override
HavenBalance get balance {
final havenWallet = _wallet as HavenWallet;
final balance = havenWallet.balance;
throw Exception('Unimplemented');
//return HavenBalance(
// fullBalance: balance.fullBalance,
// unlockedBalance: balance.unlockedBalance);
}
HavenBalance get balance {
final havenWallet = _wallet as HavenWallet;
final balance = havenWallet.balance;
throw Exception('Unimplemented');
//return HavenBalance(
// fullBalance: balance.fullBalance,
// unlockedBalance: balance.unlockedBalance);
}
}
class CWHaven extends Haven {
@override
HavenAccountList getAccountList(Object wallet) {
return CWHavenAccountList(wallet);
}
@override
MoneroSubaddressList getSubaddressList(Object wallet) {
return CWHavenSubaddressList(wallet);
}
return CWHavenAccountList(wallet);
}
@override
TransactionHistoryBase getTransactionHistory(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.transactionHistory;
}
MoneroSubaddressList getSubaddressList(Object wallet) {
return CWHavenSubaddressList(wallet);
}
@override
HavenWalletDetails getMoneroWalletDetails(Object wallet) {
return CWHavenWalletDetails(wallet);
}
TransactionHistoryBase getTransactionHistory(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.transactionHistory;
}
@override
int getHeigthByDate({required DateTime date}) {
return getMoneroHeigthByDate(date: date);
}
@override
TransactionPriority getDefaultTransactionPriority() {
return MoneroTransactionPriority.automatic;
}
HavenWalletDetails getMoneroWalletDetails(Object wallet) {
return CWHavenWalletDetails(wallet);
}
@override
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
return MoneroTransactionPriority.deserialize(raw: raw);
}
int getHeightByDate({required DateTime date}) => getHavenHeightByDate(date: date);
@override
List<TransactionPriority> getTransactionPriorities() {
return MoneroTransactionPriority.all;
}
Future<int> getCurrentHeight() => getHavenCurrentHeight();
@override
List<String> getMoneroWordList(String language) {
switch (language.toLowerCase()) {
case 'english':
return EnglishMnemonics.words;
case 'chinese (simplified)':
return ChineseSimplifiedMnemonics.words;
case 'dutch':
return DutchMnemonics.words;
case 'german':
return GermanMnemonics.words;
case 'japanese':
return JapaneseMnemonics.words;
case 'portuguese':
return PortugueseMnemonics.words;
case 'russian':
return RussianMnemonics.words;
case 'spanish':
return SpanishMnemonics.words;
case 'french':
return FrenchMnemonics.words;
case 'italian':
return ItalianMnemonics.words;
default:
return EnglishMnemonics.words;
}
}
TransactionPriority getDefaultTransactionPriority() {
return MoneroTransactionPriority.automatic;
}
@override
WalletCredentials createHavenRestoreWalletFromKeysCredentials({
required String name,
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
return MoneroTransactionPriority.deserialize(raw: raw);
}
@override
List<TransactionPriority> getTransactionPriorities() {
return MoneroTransactionPriority.all;
}
@override
List<String> getMoneroWordList(String language) {
switch (language.toLowerCase()) {
case 'english':
return EnglishMnemonics.words;
case 'chinese (simplified)':
return ChineseSimplifiedMnemonics.words;
case 'dutch':
return DutchMnemonics.words;
case 'german':
return GermanMnemonics.words;
case 'japanese':
return JapaneseMnemonics.words;
case 'portuguese':
return PortugueseMnemonics.words;
case 'russian':
return RussianMnemonics.words;
case 'spanish':
return SpanishMnemonics.words;
case 'french':
return FrenchMnemonics.words;
case 'italian':
return ItalianMnemonics.words;
default:
return EnglishMnemonics.words;
}
}
@override
WalletCredentials createHavenRestoreWalletFromKeysCredentials(
{required String name,
required String spendKey,
required String viewKey,
required String address,
required String password,
required String language,
required int height}) {
return HavenRestoreWalletFromKeysCredentials(
name: name,
spendKey: spendKey,
viewKey: viewKey,
address: address,
password: password,
language: language,
height: height);
}
@override
WalletCredentials createHavenRestoreWalletFromSeedCredentials({
required String name,
required String password,
required int height,
required String mnemonic}) {
return HavenRestoreWalletFromSeedCredentials(
name: name,
password: password,
height: height,
mnemonic: mnemonic);
}
return HavenRestoreWalletFromKeysCredentials(
name: name,
spendKey: spendKey,
viewKey: viewKey,
address: address,
password: password,
language: language,
height: height);
}
@override
WalletCredentials createHavenNewWalletCredentials({
required String name,
required String language,
String? password}) {
return HavenNewWalletCredentials(
name: name,
password: password,
language: language);
}
WalletCredentials createHavenRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required int height,
required String mnemonic}) {
return HavenRestoreWalletFromSeedCredentials(
name: name, password: password, height: height, mnemonic: mnemonic);
}
@override
Map<String, String> getKeys(Object wallet) {
final havenWallet = wallet as HavenWallet;
final keys = havenWallet.keys;
return <String, String>{
'privateSpendKey': keys.privateSpendKey,
WalletCredentials createHavenNewWalletCredentials(
{required String name, required String language, String? password}) {
return HavenNewWalletCredentials(name: name, password: password, language: language);
}
@override
Map<String, String> getKeys(Object wallet) {
final havenWallet = wallet as HavenWallet;
final keys = havenWallet.keys;
return <String, String>{
'privateSpendKey': keys.privateSpendKey,
'privateViewKey': keys.privateViewKey,
'publicSpendKey': keys.publicSpendKey,
'publicViewKey': keys.publicViewKey};
}
'publicViewKey': keys.publicViewKey
};
}
@override
Object createHavenTransactionCreationCredentials({
required List<Output> outputs,
required TransactionPriority priority,
required String assetType}) {
return HavenTransactionCreationCredentials(
outputs: outputs.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as MoneroTransactionPriority,
assetType: assetType);
}
Object createHavenTransactionCreationCredentials(
{required List<Output> outputs,
required TransactionPriority priority,
required String assetType}) {
return HavenTransactionCreationCredentials(
outputs: outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as MoneroTransactionPriority,
assetType: assetType);
}
@override
String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount);
}
@override
double formatterMoneroAmountToDouble({required int amount}) {
return moneroAmountToDouble(amount: amount);
}
String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount);
}
@override
int formatterMoneroParseAmount({required String amount}) {
return moneroParseAmount(amount: amount);
}
double formatterMoneroAmountToDouble({required int amount}) {
return moneroAmountToDouble(amount: amount);
}
@override
Account getCurrentAccount(Object wallet) {
final havenWallet = wallet as HavenWallet;
final acc = havenWallet.walletAddresses.account as monero_account.Account;
return Account(id: acc.id, label: acc.label);
}
int formatterMoneroParseAmount({required String amount}) {
return moneroParseAmount(amount: amount);
}
@override
void setCurrentAccount(Object wallet, int id, String label) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.account = monero_account.Account(id: id, label: label);
}
Account getCurrentAccount(Object wallet) {
final havenWallet = wallet as HavenWallet;
final acc = havenWallet.walletAddresses.account as monero_account.Account;
return Account(id: acc.id, label: acc.label);
}
@override
void onStartup() {
monero_wallet_api.onStartup();
}
void setCurrentAccount(Object wallet, int id, String label) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.account = monero_account.Account(id: id, label: label);
}
@override
int getTransactionInfoAccountId(TransactionInfo tx) {
final havenTransactionInfo = tx as HavenTransactionInfo;
return havenTransactionInfo.accountIndex;
}
void onStartup() {
monero_wallet_api.onStartup();
}
@override
WalletService createHavenWalletService(Box<WalletInfo> walletInfoSource) {
return HavenWalletService(walletInfoSource);
}
int getTransactionInfoAccountId(TransactionInfo tx) {
final havenTransactionInfo = tx as HavenTransactionInfo;
return havenTransactionInfo.accountIndex;
}
@override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
final havenWallet = wallet as HavenWallet;
return havenWallet.getTransactionAddress(accountIndex, addressIndex);
}
WalletService createHavenWalletService(Box<WalletInfo> walletInfoSource) {
return HavenWalletService(walletInfoSource);
}
@override
CryptoCurrency assetOfTransaction(TransactionInfo tx) {
final transaction = tx as HavenTransactionInfo;
final asset = CryptoCurrency.fromString(transaction.assetType);
return asset;
}
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
final havenWallet = wallet as HavenWallet;
return havenWallet.getTransactionAddress(accountIndex, addressIndex);
}
@override
List<AssetRate> getAssetRate()
=> getRate()
.map((rate) => AssetRate(rate.getAssetType(), rate.getRate()))
.toList();
CryptoCurrency assetOfTransaction(TransactionInfo tx) {
final transaction = tx as HavenTransactionInfo;
final asset = CryptoCurrency.fromString(transaction.assetType);
return asset;
}
@override
List<AssetRate> getAssetRate() =>
getRate().map((rate) => AssetRate(rate.getAssetType(), rate.getRate())).toList();
}

View file

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/buy/order.dart';
@ -101,6 +102,10 @@ Future<void> main() async {
Hive.registerAdapter(UnspentCoinsInfoAdapter());
}
if (!Hive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) {
Hive.registerAdapter(AnonpayInvoiceInfoAdapter());
}
final secureStorage = secureStorageShared;
final transactionDescriptionsBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
@ -121,6 +126,7 @@ Future<void> main() async {
final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates =
await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final anonpayInvoiceInfo = await Hive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
Box<UnspentCoinsInfo>? unspentCoinsInfoSource;
if (!isMoneroOnly) {
@ -140,6 +146,7 @@ Future<void> main() async {
exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 19);
runApp(App());
}, (error, stackTrace) async {
@ -159,6 +166,7 @@ Future<void> initialSetup(
required Box<ExchangeTemplate> exchangeTemplates,
required Box<TransactionDescription> transactionDescriptions,
required SecureStorage secureStorage,
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfo,
Box<UnspentCoinsInfo>? unspentCoinsInfoSource,
int initialMigrationVersion = 15}) async {
LanguageService.loadLocaleList();
@ -179,6 +187,7 @@ Future<void> initialSetup(
exchangeTemplates: exchangeTemplates,
transactionDescriptionBox: transactionDescriptions,
ordersSource: ordersSource,
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
unspentCoinsInfoSource: unspentCoinsInfoSource,
);
await bootstrap(navigatorKey);

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cake_wallet/reactions/fiat_rate_update.dart';
import 'package:cake_wallet/reactions/on_current_fiat_api_mode_change.dart';
import 'package:cake_wallet/reactions/on_current_node_change.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
@ -24,13 +25,14 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
.get<SharedPreferences>()
.getString(PreferencesKey.currentWalletName);
if (currentWalletName != null) {
authenticationStore.state = AuthenticationState.installed;
authenticationStore.installed();
}
startAuthenticationStateChange(authenticationStore, navigatorKey);
startCurrentWalletChangeReaction(
appStore, settingsStore, fiatConversionStore);
startCurrentFiatChangeReaction(appStore, settingsStore, fiatConversionStore);
startCurrentFiatApiModeChangeReaction(appStore, settingsStore, fiatConversionStore);
startOnCurrentNodeChangeReaction(appStore);
startFiatRateUpdate(appStore, settingsStore, fiatConversionStore);
}

View file

@ -26,7 +26,9 @@ Future<void> startFiatRateUpdate(
} else {
fiatConversionStore.prices[appStore.wallet!.currency] =
await FiatConversionService.fetchPrice(
appStore.wallet!.currency, settingsStore.fiatCurrency);
crypto: appStore.wallet!.currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
}
} catch (e) {
print(e);

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:flutter/widgets.dart';
import 'package:mobx/mobx.dart';
@ -17,19 +18,16 @@ void startAuthenticationStateChange(
if (state == AuthenticationState.installed && !SettingsStoreBase.walletPasswordDirectInput) {
try {
await loadCurrentWallet();
} catch (e) {
loginError = e;
} catch (error, stack) {
loginError = error;
ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
}
return;
}
if (state == AuthenticationState.allowed) {
// Temporary workaround for the issue with desktopKey dispose
// TODO: Remove this workaround and fix global key issue
Future.delayed(Duration(milliseconds: 500), () async {
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
});
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
}
});
}

View file

@ -0,0 +1,25 @@
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';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/app_store.dart';
ReactionDisposer? _onCurrentFiatCurrencyChangeDisposer;
void startCurrentFiatApiModeChangeReaction(AppStore appStore,
SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
_onCurrentFiatCurrencyChangeDisposer?.reaction.dispose();
_onCurrentFiatCurrencyChangeDisposer = reaction(
(_) => settingsStore.fiatApiMode, (FiatApiMode fiatApiMode) async {
if (appStore.wallet == null || fiatApiMode == FiatApiMode.disabled) {
return;
}
fiatConversionStore.prices[appStore.wallet!.currency] =
await FiatConversionService.fetchPrice(
crypto: appStore.wallet!.currency,
fiat: settingsStore.fiatCurrency,
torOnly: fiatApiMode == FiatApiMode.torOnly);
});
}

View file

@ -18,7 +18,10 @@ void startCurrentFiatChangeReaction(AppStore appStore,
}
final cryptoCurrency = appStore.wallet!.currency;
fiatConversionStore.prices[appStore.wallet!.currency] =
await FiatConversionService.fetchPrice(cryptoCurrency, fiatCurrency);
fiatConversionStore.prices[cryptoCurrency] =
await FiatConversionService.fetchPrice(
crypto: cryptoCurrency,
fiat: fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
});
}

View file

@ -67,11 +67,10 @@ void startCurrentWalletChangeReaction(AppStore appStore,
await wallet.connectToNode(node: node);
if (wallet.type == WalletType.haven) {
settingsStore.fiatCurrency = FiatCurrency.usd;
await updateHavenRate(fiatConversionStore);
}
if (wallet.walletInfo.address?.isEmpty ?? true) {
if (wallet.walletInfo.address.isEmpty) {
wallet.walletInfo.address = wallet.walletAddresses.address;
if (wallet.walletInfo.isInBox) {
@ -95,7 +94,9 @@ void startCurrentWalletChangeReaction(AppStore appStore,
fiatConversionStore.prices[wallet.currency] = 0;
fiatConversionStore.prices[wallet.currency] =
await FiatConversionService.fetchPrice(
wallet.currency, settingsStore.fiatCurrency);
crypto: wallet.currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly);
} catch (e) {
print(e.toString());
}

View file

@ -1,10 +1,16 @@
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
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/payfura_page.dart';
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
@ -88,6 +94,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.da
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';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart';
late RouteSettings currentRouteSettings;
@ -356,8 +363,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<OtherSettingsPage>());
case Routes.newNode:
final args = settings.arguments as Map<String, dynamic>?;
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NodeCreateOrEditPage>());
builder: (_) => getIt.get<NodeCreateOrEditPage>(
param1: args?['editingNode'] as Node?,
param2: args?['isSelected'] as bool?));
case Routes.login:
return CupertinoPageRoute<void>(
@ -489,7 +499,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) =>
getIt.get<FullscreenQRPage>(
param1: args['qrData'] as String,
param2: args['isLight'] as bool,
param2: args['version'] as int?,
));
case Routes.ioniaWelcomePage:
@ -558,6 +569,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.onramperPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<OnRamperPage>());
case Routes.payfuraPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<PayFuraPage>());
case Routes.advancedPrivacySettings:
final type = settings.arguments as WalletType;
@ -567,12 +581,23 @@ Route<dynamic> createRoute(RouteSettings settings) {
getIt.get<NodeCreateOrEditViewModel>(param1: type),
));
case Routes.anonPayInvoicePage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args));
case Routes.anonPayReceivePage:
final anonInvoiceViewData = settings.arguments as AnonpayInfoBase;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonPayReceivePage>(param1: anonInvoiceViewData));
case Routes.anonPayDetailsPage:
final anonInvoiceViewData = settings.arguments as AnonpayInvoiceInfo;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonpayDetailsPage>(param1: anonInvoiceViewData));
case Routes.desktop_actions:
return PageRouteBuilder(
opaque: false,
pageBuilder: (_, __, ___) => DesktopDashboardActions(getIt<DashboardViewModel>()),
);
case Routes.desktop_settings_page:
return CupertinoPageRoute<void>(
builder: (_) => DesktopSettingsPage());
@ -580,13 +605,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.empty_no_route:
return MaterialPageRoute<void>(
builder: (_) => SizedBox.shrink());
case Routes.transactionsPage:
return CupertinoPageRoute<void>(
settings: settings,
fullscreenDialog: true,
builder: (_) => getIt.get<TransactionsPage>());
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(

View file

@ -36,6 +36,7 @@ class Routes {
static const exchangeTrade = '/exchange_trade';
static const restoreWalletFromSeedDetails = '/restore_from_seed_details';
static const exchange = '/exchange';
static const settings = '/settings';
static const desktop_settings_page = '/desktop_settings_page';
static const empty_no_route = '/empty_no_route';
static const unlock = '/auth_not_closable';
@ -87,4 +88,10 @@ class Routes {
static const transactionsPage = '/transactions_page';
static const walletPasswordUnlock = '/wallet_password_unlock';
static const walletUnlockLoadable = '/wallet_unlock_loadable';
static const anonPayInvoicePage = '/anon_pay_invoice_page';
static const anonPayReceivePage = '/anon_pay_receive_page';
static const anonPayDetailsPage = '/anon_pay_details_page';
static const payfuraPage = '/pay_fura_page';
static const desktop_actions = '/desktop_actions';
static const transactionsPage = '/transactions_page';
}

View file

@ -0,0 +1,56 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.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';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/src/widgets/standard_list.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/anonpay_details_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AnonpayDetailsPage extends BasePage {
AnonpayDetailsPage({required this.anonpayDetailsViewModel});
@override
String get title => S.current.invoice_details;
final AnonpayDetailsViewModel anonpayDetailsViewModel;
@override
Widget body(BuildContext context) {
return SectionStandardList(
context: context,
sectionCount: 1,
itemCounter: (int _) => anonpayDetailsViewModel.items.length,
itemBuilder: (_, __, index) {
final item = anonpayDetailsViewModel.items[index];
if (item is DetailsListStatusItem) {
return StandardListStatusRow(title: item.title, value: item.value);
}
if (item is TradeDetailsListCardItem) {
return TradeDetailsStandardListCard(
id: item.id,
create: item.createdAt,
pair: item.pair,
currentTheme: anonpayDetailsViewModel.settingsStore.currentTheme.type,
onTap: item.onTap,
);
}
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: item.value));
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
},
child: ListRow(title: '${item.title}:', value: item.value),
);
});
}
}

View file

@ -1,10 +1,11 @@
import 'dart:io';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:share_plus/share_plus.dart';
import 'package:cross_file/cross_file.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
@ -28,8 +29,7 @@ class BackupPage extends BasePage {
@override
Widget trailing(BuildContext context) => TrailButton(
caption: S.of(context).change_password,
onPressed: () =>
Navigator.of(context).pushNamed(Routes.editBackupPassword),
onPressed: () => Navigator.of(context).pushNamed(Routes.editBackupPassword),
textColor: Palette.blueCraiola);
@override
@ -52,9 +52,8 @@ class BackupPage extends BasePage {
child: Observer(
builder: (_) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text:
backupViewModelBase.backupPassword));
Clipboard.setData(
ClipboardData(text: backupViewModelBase.backupPassword));
showBar<void>(
context,
S.of(context).transaction_details_copied(
@ -109,8 +108,10 @@ class BackupPage extends BasePage {
if (Platform.isAndroid) {
onExportAndroid(context, backup);
} else if (Platform.isIOS) {
await share(backup, context);
} else {
await share(backup);
await _saveFile(backup);
}
},
actionLeftButton: () => Navigator.of(dialogContext).pop());
@ -134,24 +135,35 @@ class BackupPage extends BasePage {
return;
}
await backupViewModelBase.saveToDownload(
backup.name, backup.content);
await backupViewModelBase.saveToDownload(backup.name, backup.content);
Navigator.of(dialogContext).pop();
},
actionLeftButton: () async {
Navigator.of(dialogContext).pop();
await share(backup);
await share(backup, context);
});
});
}
Future<void> share(BackupExportFile backup) async {
const mimeType = 'application/*';
Future<void> share(BackupExportFile backup, BuildContext context) async {
final path = await backupViewModelBase.saveBackupFileLocally(backup);
await Share.shareXFiles(<XFile>[XFile(
path,
name: backup.name,
mimeType: mimeType)]);
await ShareUtil.shareFile(filePath: path, fileName: backup.name, context: context);
await backupViewModelBase.removeBackupFileLocally(backup);
}
Future<void> _saveFile(BackupExportFile backup) async {
String? outputFile = await FilePicker.platform
.saveFile(dialogTitle: 'Save Your File to desired location', fileName: backup.name);
try {
File returnedFile = File(outputFile!);
await returnedFile.writeAsBytes(backup.content);
} catch (exception, stackTrace) {
ExceptionHandler.onError(FlutterErrorDetails(
exception: exception,
stack: stackTrace,
library: "Export Backup",
));
}
}
}

View file

@ -66,7 +66,8 @@ class EditBackupPasswordPage extends BasePage {
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
await editBackupPasswordViewModel.save();
Navigator.of(dialogContext)..pop()..pop();
Navigator.of(dialogContext).pop();
Navigator.of(context).pop();
},
actionLeftButton: () => Navigator.of(dialogContext).pop());
});

View file

@ -58,19 +58,24 @@ abstract class BasePage extends StatelessWidget {
bool isMobileView = ResponsiveLayoutUtil.instance.isMobile(context);
return SizedBox(
height: isMobileView ? 37 : 45,
width: isMobileView ? 37 : 45,
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent),
return MergeSemantics(
child: SizedBox(
height: isMobileView ? 37 : 45,
width: isMobileView ? 37 : 45,
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: canUseCloseIcon && !isMobileView ? 'Close' : 'Back',
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith(
(states) => Colors.transparent),
),
onPressed: () => onClose(context),
child:
canUseCloseIcon && !isMobileView ? _closeButton : _backButton,
),
onPressed: () => onClose(context),
child: canUseCloseIcon && !isMobileView
? _closeButton
: _backButton,
),
),
),
);

View file

@ -1,9 +1,7 @@
import 'dart:io';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
@ -17,27 +15,16 @@ class OnRamperPage extends BasePage {
@override
Widget body(BuildContext context) {
final darkMode = Theme.of(context).brightness == Brightness.dark;
return OnRamperPageBody(
onRamperBuyProvider: _onRamperBuyProvider,
darkMode: darkMode,
backgroundColor: darkMode ? backgroundDarkColor : backgroundLightColor,
);
return OnRamperPageBody(_onRamperBuyProvider);
}
}
class OnRamperPageBody extends StatefulWidget {
OnRamperPageBody({
required this.onRamperBuyProvider,
required this.darkMode,
required this.backgroundColor,
});
OnRamperPageBody(this.onRamperBuyProvider);
final OnRamperBuyProvider onRamperBuyProvider;
final Color backgroundColor;
final bool darkMode;
Uri get uri => onRamperBuyProvider.requestUrl(darkMode);
Uri get uri => onRamperBuyProvider.requestUrl();
@override
OnRamperPageBodyState createState() => OnRamperPageBodyState();

View file

@ -0,0 +1,58 @@
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
class PayFuraPage extends BasePage {
PayFuraPage(this._PayfuraBuyProvider);
final PayfuraBuyProvider _PayfuraBuyProvider;
@override
String get title => S.current.buy;
@override
Widget body(BuildContext context) {
return PayFuraPageBody(_PayfuraBuyProvider);
}
}
class PayFuraPageBody extends StatefulWidget {
PayFuraPageBody(this._PayfuraBuyProvider);
final PayfuraBuyProvider _PayfuraBuyProvider;
Uri get uri => _PayfuraBuyProvider.requestUrl();
@override
PayFuraPageBodyState createState() => PayFuraPageBodyState();
}
class PayFuraPageBodyState extends State<PayFuraPageBody> {
PayFuraPageBodyState();
@override
Widget build(BuildContext context) {
return InAppWebView(
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(transparentBackground: true),
),
initialUrlRequest: URLRequest(url: widget.uri),
androidOnPermissionRequest: (_, __, resources) async {
bool permissionGranted = await Permission.camera.status == PermissionStatus.granted;
if (!permissionGranted) {
permissionGranted = await Permission.camera.request().isGranted;
}
return PermissionRequestResponse(
resources: resources,
action: permissionGranted
? PermissionRequestResponseAction.GRANT
: PermissionRequestResponseAction.DENY,
);
},
);
}
}

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@ -153,8 +154,8 @@ class ContactPage extends BasePage {
items: contactViewModel.currencies,
title: S.of(context).please_select,
hintText: S.of(context).search_currency,
onItemSelected: (CryptoCurrency item) =>
contactViewModel.currency = item),
onItemSelected: (Currency item) =>
contactViewModel.currency = item as CryptoCurrency),
context: context);
}

View file

@ -1,8 +1,9 @@
import 'dart:async';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
@ -10,7 +11,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@ -30,13 +30,11 @@ class DashboardPage extends StatelessWidget {
required this.balancePage,
required this.dashboardViewModel,
required this.addressListViewModel,
required this.desktopSidebarViewModel,
});
final BalancePage balancePage;
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
final DesktopSidebarViewModel desktopSidebarViewModel;
@override
Widget build(BuildContext context) {
@ -47,15 +45,7 @@ class DashboardPage extends StatelessWidget {
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
)
: DesktopSidebarWrapper(
desktopSidebarViewModel: desktopSidebarViewModel,
dashboardViewModel: dashboardViewModel,
child: DesktopDashboardPage(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
),
),
: getIt.get<DesktopSidebarWrapper>(),
);
}
}
@ -114,19 +104,34 @@ class _DashboardPageView extends BasePage {
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: () => onOpenEndDrawer(),
child: menuButton));
child: Semantics(label: 'Menu', child: menuButton)));
}
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
final controller = PageController(initialPage: 1);
var pages = <Widget>[];
int get initialPage => dashboardViewModel.shouldShowMarketPlaceInDashboard ? 1 : 0;
ObservableList<Widget> pages = ObservableList<Widget>();
bool _isEffectsInstalled = false;
StreamSubscription<bool>? _onInactiveSub;
@override
Widget body(BuildContext context) {
final controller = PageController(initialPage: initialPage);
reaction((_) => dashboardViewModel.shouldShowMarketPlaceInDashboard, (bool value) {
if (!dashboardViewModel.shouldShowMarketPlaceInDashboard) {
controller.jumpToPage(0);
}
pages.clear();
_isEffectsInstalled = false;
_setEffects(context);
if (value) {
controller.jumpToPage(1);
} else {
controller.jumpToPage(0);
}
});
_setEffects(context);
return SafeArea(
@ -135,23 +140,32 @@ class _DashboardPageView extends BasePage {
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: PageView.builder(
controller: controller,
itemCount: pages.length,
itemBuilder: (context, index) => pages[index])),
child: Observer(builder: (context) {
return PageView.builder(
controller: controller,
itemCount: pages.length,
itemBuilder: (context, index) => pages[index]);
})),
Padding(
padding: EdgeInsets.only(bottom: 24, top: 10),
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).indicatorColor,
activeDotColor:
Theme.of(context).accentTextTheme!.headline4!.backgroundColor!),
child: Observer(builder: (context) {
return ExcludeSemantics(
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).indicatorColor,
activeDotColor: Theme.of(context)
.accentTextTheme!
.headline4!
.backgroundColor!),
),
);
}
)),
Observer(builder: (_) {
return ClipRect(
@ -174,27 +188,38 @@ class _DashboardPageView extends BasePage {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: MainActions.all
.where((element) => element.canShow?.call(dashboardViewModel) ?? true)
.map((action) => ActionButton(
image: Image.asset(action.image,
height: 24,
width: 24,
color: action.isEnabled?.call(dashboardViewModel) ?? true
? Theme.of(context)
.accentTextTheme
.headline2!
.backgroundColor!
: Theme.of(context)
.accentTextTheme
.headline3!
.backgroundColor!),
title: action.name(context),
onClick: () async => await action.onTap(context, dashboardViewModel),
textColor: action.isEnabled?.call(dashboardViewModel) ?? true
? null
: Theme.of(context)
.accentTextTheme
.headline3!
.backgroundColor!,
.map((action) => Semantics(
button: true,
enabled: (action.isEnabled
?.call(dashboardViewModel) ??
true),
child: ActionButton(
image: Image.asset(action.image,
height: 24,
width: 24,
color: action.isEnabled?.call(
dashboardViewModel) ??
true
? Theme.of(context)
.accentTextTheme
.headline2!
.backgroundColor!
: Theme.of(context)
.accentTextTheme
.headline3!
.backgroundColor!),
title: action.name(context),
onClick: () async => await action.onTap(
context, dashboardViewModel),
textColor: action.isEnabled
?.call(dashboardViewModel) ??
true
? null
: Theme.of(context)
.accentTextTheme
.headline3!
.backgroundColor!,
),
))
.toList(),
),
@ -211,9 +236,15 @@ class _DashboardPageView extends BasePage {
if (_isEffectsInstalled) {
return;
}
pages.add(MarketPlacePage(dashboardViewModel: dashboardViewModel));
pages.add(balancePage);
pages.add(TransactionsPage(dashboardViewModel: dashboardViewModel));
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(Semantics(
label: 'Marketplace Page',
child: MarketPlacePage(dashboardViewModel: dashboardViewModel)));
}
pages.add(Semantics(label: 'Balance Page', child: balancePage));
pages.add(Semantics(
label: 'Transactions Page',
child: TransactionsPage(dashboardViewModel: dashboardViewModel)));
_isEffectsInstalled = true;
autorun((_) async {
@ -222,7 +253,8 @@ class _DashboardPageView extends BasePage {
}
await Future<void>.delayed(Duration(seconds: 1));
await showPopUp<void>(
if (context.mounted) {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
@ -231,6 +263,7 @@ class _DashboardPageView extends BasePage {
buttonText: S.of(context).understand,
buttonAction: () => Navigator.of(context).pop());
});
}
});
var needToPresentYat = false;

View file

@ -17,13 +17,13 @@ class DesktopDashboardPage extends StatelessWidget {
required this.balancePage,
required this.dashboardViewModel,
required this.addressListViewModel,
required this.desktopKey,
});
final BalancePage balancePage;
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
static final GlobalKey<NavigatorState> desktopKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> desktopKey;
bool _isEffectsInstalled = false;
StreamSubscription<bool>? _onInactiveSub;
@ -109,4 +109,3 @@ class DesktopDashboardPage extends StatelessWidget {
});
}
}

View file

@ -1,7 +1,6 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_navbar.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu.dart';
@ -20,15 +19,15 @@ class DesktopSidebarWrapper extends BasePage {
final Widget child;
final DesktopSidebarViewModel desktopSidebarViewModel;
final DashboardViewModel dashboardViewModel;
final GlobalKey<NavigatorState> desktopNavigatorKey;
DesktopSidebarWrapper({
required this.child,
required this.desktopSidebarViewModel,
required this.dashboardViewModel,
required this.desktopNavigatorKey,
});
static Key _pageViewKey = GlobalKey();
@override
ObstructingPreferredSizeWidget appBar(BuildContext context) => DesktopDashboardNavbar(
leading: Padding(
@ -41,34 +40,16 @@ class DesktopSidebarWrapper extends BasePage {
),
trailing: InkWell(
onTap: () {
String? currentPath;
DesktopDashboardPage.desktopKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
switch (currentPath) {
case Routes.transactionsPage:
desktopSidebarViewModel.resetSidebar();
break;
default:
desktopSidebarViewModel.resetSidebar();
Future.delayed(Duration(milliseconds: 10), () {
desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
DesktopDashboardPage.desktopKey.currentState?.pushNamed(Routes.transactionsPage);
});
}
Navigator.of(context).pushNamed(
Routes.unlock,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close();
}
},
);
},
child: Observer(
builder: (_) {
return Image.asset(
desktopSidebarViewModel.currentPage == SidebarItem.transactions
? selectedIconPath
: unselectedIconPath,
);
},
),
child: Icon(Icons.lock_outline),
),
);
@ -99,18 +80,31 @@ class DesktopSidebarWrapper extends BasePage {
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.dashboard),
),
SideMenuItem(
icon: Icons.lock_outline,
onTap: () {
Navigator.of(context).pushNamed(
Routes.unlock,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close();
}
},
);
String? currentPath;
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
switch (currentPath) {
case Routes.transactionsPage:
desktopSidebarViewModel.resetSidebar();
break;
default:
desktopSidebarViewModel.resetSidebar();
Future.delayed(Duration(milliseconds: 10), () {
desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
});
}
},
)
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions,
imagePath: desktopSidebarViewModel.currentPage == SidebarItem.transactions
? selectedIconPath
: unselectedIconPath,
),
],
bottomItems: [
SideMenuItem(
@ -127,7 +121,6 @@ class DesktopSidebarWrapper extends BasePage {
}),
Expanded(
child: PageView(
key: _pageViewKey,
controller: pageController,
physics: NeverScrollableScrollPhysics(),
children: [
@ -161,13 +154,11 @@ class DesktopSidebarWrapper extends BasePage {
);
}
final desktopKey = DesktopDashboardPage.desktopKey;
void _setEffects() async {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath;
desktopKey.currentState?.popUntil((route) {
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
@ -176,7 +167,7 @@ class DesktopSidebarWrapper extends BasePage {
}
if (currentPath == Routes.transactionsPage) {
Navigator.of(desktopKey.currentContext!).pop();
Navigator.of(desktopNavigatorKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
});

View file

@ -1,9 +1,14 @@
import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/receive_page_option.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
@ -13,24 +18,25 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/di.dart';
class AddressPage extends BasePage {
AddressPage({
required this.addressListViewModel,
required this.walletViewModel})
: _cryptoAmountFocus = FocusNode();
required this.dashboardViewModel,
required this.receiveOptionViewModel,
}) : _cryptoAmountFocus = FocusNode();
final WalletAddressListViewModel addressListViewModel;
final DashboardViewModel walletViewModel;
final DashboardViewModel dashboardViewModel;
final ReceiveOptionViewModel receiveOptionViewModel;
final FocusNode _cryptoAmountFocus;
@override
String get title => S.current.receive;
@override
Color get backgroundLightColor => currentTheme.type == ThemeType.bright
? Colors.transparent : Colors.white;
Color get backgroundLightColor =>
currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override
Color get backgroundDarkColor => Colors.transparent;
@ -38,6 +44,8 @@ class AddressPage extends BasePage {
@override
bool get resizeToAvoidBottomInset => false;
bool effectsInstalled = false;
@override
Color get titleColor => Colors.white;
@ -45,16 +53,8 @@ class AddressPage extends BasePage {
bool get canUseCloseIcon => true;
@override
Widget middle(BuildContext context) {
return Text(
title,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!),
);
}
Widget middle(BuildContext context) =>
PresentReceiveOptionPicker(receiveOptionViewModel: receiveOptionViewModel);
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
@ -69,52 +69,57 @@ class AddressPage extends BasePage {
@override
Widget? trailing(BuildContext context) {
final shareImage =
Image.asset('assets/images/share.png',
final shareImage = Image.asset('assets/images/share.png',
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!);
return !addressListViewModel.hasAddressList ? Material(
color: Colors.transparent,
child: IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: () {
ShareUtil.share(
text: addressListViewModel.address.address,
context: context,
);
},
icon: shareImage,
),
) : null;
return !addressListViewModel.hasAddressList
? Material(
color: Colors.transparent,
child: IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: () {
ShareUtil.share(
text: addressListViewModel.address.address,
context: context,
);
},
icon: shareImage,
),
)
: null;
}
@override
Widget body(BuildContext context) {
_setEffects(context);
autorun((_) async {
if (!walletViewModel.isOutdatedElectrumWallet
|| !walletViewModel.settingsStore.shouldShowReceiveWarning) {
if (!dashboardViewModel.isOutdatedElectrumWallet ||
!dashboardViewModel.settingsStore.shouldShowReceiveWarning) {
return;
}
await Future<void>.delayed(Duration(seconds: 1));
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).pre_seed_title,
alertContent: S.of(context).outdated_electrum_wallet_receive_warning,
leftButtonText: S.of(context).understand,
actionLeftButton: () => Navigator.of(context).pop(),
rightButtonText: S.of(context).do_not_show_me,
actionRightButton: () {
walletViewModel.settingsStore.setShouldShowReceiveWarning(false);
Navigator.of(context).pop();
});
});
if (context.mounted) {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).pre_seed_title,
alertContent: S.of(context).outdated_electrum_wallet_receive_warning,
leftButtonText: S.of(context).understand,
actionLeftButton: () => Navigator.of(context).pop(),
rightButtonText: S.of(context).do_not_show_me,
actionRightButton: () {
dashboardViewModel.settingsStore.setShouldShowReceiveWarning(false);
Navigator.of(context).pop();
});
});
}
});
return KeyboardActions(
@ -123,8 +128,7 @@ class AddressPage extends BasePage {
tapOutsideToDismiss: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor:
Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!,
keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
@ -136,29 +140,25 @@ class AddressPage extends BasePage {
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
child: Column(
children: <Widget>[
Expanded(
child: Observer(builder: (_) => QRWidget(
addressListViewModel: addressListViewModel,
amountTextFieldFocusNode: _cryptoAmountFocus,
isAmountFieldShow: !addressListViewModel.hasAccounts,
isLight: walletViewModel.settingsStore.currentTheme.type == ThemeType.light))
Expanded(
child: Observer(builder: (_) => QRWidget(
addressListViewModel: addressListViewModel,
amountTextFieldFocusNode: _cryptoAmountFocus,
isAmountFieldShow: !addressListViewModel.hasAccounts,
isLight: dashboardViewModel.settingsStore.currentTheme.type == ThemeType.light))
),
Observer(builder: (_) {
return addressListViewModel.hasAddressList
? GestureDetector(
onTap: () =>
Navigator.of(context).pushNamed(Routes.receive),
onTap: () => Navigator.of(context).pushNamed(Routes.receive),
child: Container(
height: 50,
padding: EdgeInsets.only(left: 24, right: 12),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(25)),
borderRadius: BorderRadius.all(Radius.circular(25)),
border: Border.all(
color:
Theme.of(context).textTheme!.subtitle1!.color!,
width: 1),
color: Theme.of(context).textTheme.subtitle1!.color!, width: 1),
color: Theme.of(context).buttonColor),
child: Row(
mainAxisSize: MainAxisSize.max,
@ -167,42 +167,78 @@ class AddressPage extends BasePage {
Observer(
builder: (_) => Text(
addressListViewModel.hasAccounts
? S
.of(context)
.accounts_subaddresses
? S.of(context).accounts_subaddresses
: S.of(context).addresses,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.accentTextTheme
.headline2!
.backgroundColor!),
)),
Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
color:
Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
)
],
),
),
)
: Text(
S.of(context).electrum_address_disclaimer,
: Text(S.of(context).electrum_address_disclaimer,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
color: Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!));
color: Theme.of(context).accentTextTheme.headline3!.backgroundColor!));
})
],
),
));
}
void _setEffects(BuildContext context) {
if (effectsInstalled) {
return;
}
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
switch (option) {
case ReceivePageOption.anonPayInvoice:
Navigator.pushReplacementNamed(
context,
Routes.anonPayInvoicePage,
arguments: [addressListViewModel.address.address, option],
);
break;
case ReceivePageOption.anonPayDonationLink:
final sharedPreferences = getIt.get<SharedPreferences>();
final clearnetUrl = sharedPreferences.getString(PreferencesKey.clearnetDonationLink);
final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink);
if (clearnetUrl != null && onionUrl != null) {
Navigator.pushReplacementNamed(
context,
Routes.anonPayReceivePage,
arguments: AnonpayDonationLinkInfo(
clearnetUrl: clearnetUrl,
onionUrl: onionUrl,
address: addressListViewModel.address.address,
),
);
} else {
Navigator.pushReplacementNamed(
context,
Routes.anonPayInvoicePage,
arguments: [addressListViewModel.address.address, option],
);
}
break;
default:
}
});
effectsInstalled = true;
}
}

View file

@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
class AnonpayTransactionRow extends StatelessWidget {
AnonpayTransactionRow({
required this.provider,
required this.createdAt,
required this.currency,
required this.onTap,
required this.amount,
});
final VoidCallback? onTap;
final String provider;
final String createdAt;
final String amount;
final String currency;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
color: Colors.transparent,
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_getImage(),
SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
Text(provider,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!)),
Text(amount + ' ' + currency,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!))
]),
SizedBox(height: 5),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
Text(createdAt,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).textTheme.overline!.backgroundColor!))
])
],
))
],
),
));
}
Widget _getImage() => ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.asset('assets/images/trocador.png', width: 36, height: 36));
}

View file

@ -0,0 +1,144 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/rounded_checkbox.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
class PresentReceiveOptionPicker extends StatelessWidget {
PresentReceiveOptionPicker({required this.receiveOptionViewModel});
final ReceiveOptionViewModel receiveOptionViewModel;
@override
Widget build(BuildContext context) {
final arrowBottom =
Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
return TextButton(
onPressed: () => _showPicker(context),
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
splashFactory: NoSplash.splashFactory,
foregroundColor: MaterialStateProperty.all(Colors.transparent),
overlayColor: MaterialStateProperty.all(Colors.transparent),
),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
S.current.receive,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!),
),
Observer(
builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(),
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.headline5!.color!)))
],
),
SizedBox(width: 5),
Padding(
padding: EdgeInsets.only(top: 12),
child: arrowBottom,
)
],
),
);
}
void _showPicker(BuildContext context) async {
await showPopUp<void>(
builder: (BuildContext popUpContext) => Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
body: AlertBackground(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(),
Container(
margin: EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Theme.of(context).backgroundColor,
),
child: Padding(
padding: const EdgeInsets.only(top: 24, bottom: 24),
child: (ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: receiveOptionViewModel.options.length,
itemBuilder: (_, index) {
final option = receiveOptionViewModel.options[index];
return InkWell(
onTap: () {
Navigator.pop(popUpContext);
receiveOptionViewModel.selectReceiveOption(option);
},
child: Padding(
padding: const EdgeInsets.only(left: 24, right: 24),
child: Observer(builder: (_) {
final value = receiveOptionViewModel.selectedReceiveOption;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(option.toString(),
textAlign: TextAlign.left,
style: textSmall(
color: Theme.of(context).primaryTextTheme.headline6!.color!,
).copyWith(
fontWeight:
value == option ? FontWeight.w800 : FontWeight.w500,
)),
RoundedCheckbox(
value: value == option,
)
],
);
}),
),
);
},
separatorBuilder: (_, index) => SizedBox(height: 30),
)),
),
),
Spacer(),
Container(
margin: EdgeInsets.only(bottom: 40),
child: InkWell(
onTap: () => Navigator.pop(context),
child: CircleAvatar(
child: Icon(
Icons.close,
color: Palette.darkBlueCraiola,
),
backgroundColor: Colors.white,
),
),
)
],
),
),
),
context: context,
);
}
}

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