mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-03 17:40:43 +00:00
Merge branch 'main' into CW-292-Save-historical-fiat-API-rate
This commit is contained in:
commit
b787810f6a
89 changed files with 1369 additions and 794 deletions
3
.github/workflows/pr_test_build.yml
vendored
3
.github/workflows/pr_test_build.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
||||||
- name: Flutter action
|
- name: Flutter action
|
||||||
uses: subosito/flutter-action@v1
|
uses: subosito/flutter-action@v1
|
||||||
with:
|
with:
|
||||||
flutter-version: "3.10.x"
|
flutter-version: "3.19.5"
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
- name: Install package dependencies
|
- name: Install package dependencies
|
||||||
|
@ -151,6 +151,7 @@ jobs:
|
||||||
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
|
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
|
||||||
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
|
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
|
||||||
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
|
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
|
||||||
|
echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
|
||||||
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
|
||||||
|
|
||||||
- name: Rename app
|
- name: Rename app
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -94,6 +94,7 @@ android/app/key.jks
|
||||||
**/tool/.evm-secrets-config.json
|
**/tool/.evm-secrets-config.json
|
||||||
**/tool/.ethereum-secrets-config.json
|
**/tool/.ethereum-secrets-config.json
|
||||||
**/tool/.solana-secrets-config.json
|
**/tool/.solana-secrets-config.json
|
||||||
|
**/tool/.nano-secrets-config.json
|
||||||
**/tool/.tron-secrets-config.json
|
**/tool/.tron-secrets-config.json
|
||||||
**/lib/.secrets.g.dart
|
**/lib/.secrets.g.dart
|
||||||
**/cw_evm/lib/.secrets.g.dart
|
**/cw_evm/lib/.secrets.g.dart
|
||||||
|
|
|
@ -91,6 +91,13 @@
|
||||||
<data android:scheme="tron-wallet" />
|
<data android:scheme="tron-wallet" />
|
||||||
<data android:scheme="tron_wallet" />
|
<data android:scheme="tron_wallet" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<!-- nano-gpt link scheme -->
|
||||||
|
<intent-filter android:autoVerify="true">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="nano-gpt" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
|
|
5
assets/banano_node_list.yml
Normal file
5
assets/banano_node_list.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-
|
||||||
|
uri: kaliumapi.appditto.com
|
||||||
|
path: /api
|
||||||
|
useSSL: true
|
||||||
|
is_default: true
|
|
@ -1,4 +1,8 @@
|
||||||
-
|
-
|
||||||
uri: api.trongrid.io
|
uri: api.trongrid.io
|
||||||
is_default: true
|
is_default: true
|
||||||
|
useSSL: true
|
||||||
|
-
|
||||||
|
uri: tron-rpc.publicnode.com:443
|
||||||
|
is_default: false
|
||||||
useSSL: true
|
useSSL: true
|
|
@ -43,11 +43,14 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.0.9
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,13 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
|
|
@ -259,10 +259,16 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||||
element.tag == walletCurrency?.tag));
|
element.tag == walletCurrency?.tag));
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
|
// search by fullName if not found by title:
|
||||||
|
try {
|
||||||
|
return CryptoCurrency.all.firstWhere((element) => element.fullName?.toLowerCase() == name);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) {
|
if (CryptoCurrency._nameCurrencyMap[name.toLowerCase()] == null) {
|
||||||
final s = 'Unexpected token: $name for CryptoCurrency fromString';
|
final s = 'Unexpected token: $name for CryptoCurrency fromString';
|
||||||
throw ArgumentError.value(name, 'name', s);
|
throw ArgumentError.value(name, 'name', s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!;
|
return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
cw_core/lib/window_size.dart
Normal file
22
cw_core/lib/window_size.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
const MethodChannel _channel = MethodChannel('com.cake_wallet/native_utils');
|
||||||
|
|
||||||
|
Future<void> setDefaultMinimumWindowSize() async {
|
||||||
|
if (!Platform.isMacOS) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = await _channel.invokeMethod(
|
||||||
|
'setMinWindowSize',
|
||||||
|
{'width': 500, 'height': 700},
|
||||||
|
) as bool;
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
print("Failed to set minimum window size.");
|
||||||
|
}
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
print("Failed to set minimum window size: '${e.message}'.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,11 +28,13 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.0.9
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^2.0.1
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
@ -24,11 +24,13 @@ dependency_overrides:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/web3dart.git
|
url: https://github.com/cake-tech/web3dart.git
|
||||||
ref: cake
|
ref: cake
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
|
|
|
@ -36,11 +36,12 @@ dependency_overrides:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/ledger-flutter.git
|
url: https://github.com/cake-tech/ledger-flutter.git
|
||||||
ref: cake
|
ref: cake
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
|
|
@ -24,11 +24,14 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.0.9
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,14 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
build_resolvers: ^2.0.9
|
build_resolvers: ^2.0.9
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
import 'package:cw_core/balance.dart';
|
import 'package:cw_core/balance.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
|
|
||||||
|
BigInt stringAmountToBigIntBanano(String amount) {
|
||||||
|
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerBanano));
|
||||||
|
}
|
||||||
|
|
||||||
class BananoBalance extends Balance {
|
class BananoBalance extends Balance {
|
||||||
final BigInt currentBalance;
|
final BigInt currentBalance;
|
||||||
final BigInt receivableBalance;
|
final BigInt receivableBalance;
|
||||||
|
|
||||||
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
|
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(0, 0);
|
||||||
|
|
||||||
|
BananoBalance.fromFormattedString(
|
||||||
|
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||||
|
: currentBalance = stringAmountToBigIntBanano(formattedCurrentBalance),
|
||||||
|
receivableBalance = stringAmountToBigIntBanano(formattedReceivableBalance),
|
||||||
|
super(0, 0);
|
||||||
|
|
||||||
|
BananoBalance.fromRawString(
|
||||||
|
{required String currentBalance, required String receivableBalance})
|
||||||
|
: currentBalance = BigInt.parse(currentBalance),
|
||||||
|
receivableBalance = BigInt.parse(receivableBalance),
|
||||||
|
super(0, 0);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get formattedAvailableBalance {
|
String get formattedAvailableBalance {
|
||||||
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
|
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:cw_core/balance.dart';
|
import 'package:cw_core/balance.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
|
|
||||||
BigInt stringAmountToBigInt(String amount) {
|
BigInt stringAmountToBigIntNano(String amount) {
|
||||||
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
|
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ class NanoBalance extends Balance {
|
||||||
|
|
||||||
NanoBalance.fromFormattedString(
|
NanoBalance.fromFormattedString(
|
||||||
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||||
: currentBalance = stringAmountToBigInt(formattedCurrentBalance),
|
: currentBalance = stringAmountToBigIntNano(formattedCurrentBalance),
|
||||||
receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
|
receivableBalance = stringAmountToBigIntNano(formattedReceivableBalance),
|
||||||
super(0, 0);
|
super(0, 0);
|
||||||
|
|
||||||
NanoBalance.fromRawString(
|
NanoBalance.fromRawString(
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:nanodart/nanodart.dart';
|
||||||
import 'package:cw_core/node.dart';
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:cw_nano/.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class NanoClient {
|
class NanoClient {
|
||||||
static const Map<String, String> CAKE_HEADERS = {
|
static const Map<String, String> CAKE_HEADERS = {
|
||||||
|
@ -52,10 +53,19 @@ class NanoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> getHeaders() {
|
||||||
|
if (_node!.uri == "https://rpc.nano.to") {
|
||||||
|
return CAKE_HEADERS..addAll({
|
||||||
|
"key": secrets.nano2ApiKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return CAKE_HEADERS;
|
||||||
|
}
|
||||||
|
|
||||||
Future<NanoBalance> getBalance(String address) async {
|
Future<NanoBalance> getBalance(String address) async {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
_node!.uri,
|
_node!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
"action": "account_balance",
|
"action": "account_balance",
|
||||||
|
@ -82,7 +92,7 @@ class NanoClient {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
_node!.uri,
|
_node!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: jsonEncode(
|
body: jsonEncode(
|
||||||
{
|
{
|
||||||
"action": "account_info",
|
"action": "account_info",
|
||||||
|
@ -94,7 +104,7 @@ class NanoClient {
|
||||||
final data = await jsonDecode(response.body);
|
final data = await jsonDecode(response.body);
|
||||||
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
|
return AccountInfoResponse.fromJson(data as Map<String, dynamic>);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("error while getting account info");
|
print("error while getting account info $e");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +159,7 @@ class NanoClient {
|
||||||
Future<String> requestWork(String hash) async {
|
Future<String> requestWork(String hash) async {
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
_powNode!.uri,
|
_powNode!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: json.encode(
|
body: json.encode(
|
||||||
{
|
{
|
||||||
"action": "work_generate",
|
"action": "work_generate",
|
||||||
|
@ -192,7 +202,7 @@ class NanoClient {
|
||||||
|
|
||||||
final processResponse = await http.post(
|
final processResponse = await http.post(
|
||||||
_node!.uri,
|
_node!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: processBody,
|
body: processBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -351,7 +361,7 @@ class NanoClient {
|
||||||
});
|
});
|
||||||
final processResponse = await http.post(
|
final processResponse = await http.post(
|
||||||
_node!.uri,
|
_node!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: processBody,
|
body: processBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -367,7 +377,7 @@ class NanoClient {
|
||||||
required String privateKey,
|
required String privateKey,
|
||||||
}) async {
|
}) async {
|
||||||
final receivableResponse = await http.post(_node!.uri,
|
final receivableResponse = await http.post(_node!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: jsonEncode({
|
body: jsonEncode({
|
||||||
"action": "receivable",
|
"action": "receivable",
|
||||||
"account": destinationAddress,
|
"account": destinationAddress,
|
||||||
|
@ -417,7 +427,7 @@ class NanoClient {
|
||||||
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
|
||||||
try {
|
try {
|
||||||
final response = await http.post(_node!.uri,
|
final response = await http.post(_node!.uri,
|
||||||
headers: CAKE_HEADERS,
|
headers: getHeaders(),
|
||||||
body: jsonEncode({
|
body: jsonEncode({
|
||||||
"action": "account_history",
|
"action": "account_history",
|
||||||
"account": address,
|
"account": address,
|
||||||
|
|
|
@ -32,10 +32,13 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,14 @@ dependency_overrides:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/web3dart.git
|
url: https://github.com/cake-tech/web3dart.git
|
||||||
ref: cake
|
ref: cake
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
@ -26,10 +26,13 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
build_runner: ^2.1.11
|
build_runner: ^2.4.7
|
||||||
mobx_codegen: ^2.0.7
|
mobx_codegen: ^2.0.7
|
||||||
hive_generator: ^1.1.3
|
hive_generator: ^1.1.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
watcher: ^1.1.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
|
|
|
@ -367,7 +367,7 @@ class TronClient {
|
||||||
) async {
|
) async {
|
||||||
// This is introduce to server as a limit in cases where feeLimit is 0
|
// This is introduce to server as a limit in cases where feeLimit is 0
|
||||||
// The transaction signing will fail if the feeLimit is explicitly 0.
|
// The transaction signing will fail if the feeLimit is explicitly 0.
|
||||||
int defaultFeeLimit = 100000;
|
int defaultFeeLimit = 269000;
|
||||||
|
|
||||||
final block = await _provider!.request(TronRequestGetNowBlock());
|
final block = await _provider!.request(TronRequestGetNowBlock());
|
||||||
// Create the transfer contract
|
// Create the transfer contract
|
||||||
|
@ -401,8 +401,9 @@ class TronClient {
|
||||||
final tronBalanceInt = tronBalance.toInt();
|
final tronBalanceInt = tronBalance.toInt();
|
||||||
|
|
||||||
if (feeLimit > tronBalanceInt) {
|
if (feeLimit > tronBalanceInt) {
|
||||||
|
final feeInTrx = TronHelper.fromSun(BigInt.parse(feeLimit.toString()));
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.',
|
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.\nTransaction fee: $feeInTrx TRX',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,6 +443,9 @@ class TronClient {
|
||||||
|
|
||||||
if (!request.isSuccess) {
|
if (!request.isSuccess) {
|
||||||
log("Tron TRC20 error: ${request.error} \n ${request.respose}");
|
log("Tron TRC20 error: ${request.error} \n ${request.respose}");
|
||||||
|
throw Exception(
|
||||||
|
'An error occured while creating the transfer request. Please try again.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final feeLimit = await getFeeLimit(
|
final feeLimit = await getFeeLimit(
|
||||||
|
@ -454,8 +458,9 @@ class TronClient {
|
||||||
final tronBalanceInt = tronBalance.toInt();
|
final tronBalanceInt = tronBalance.toInt();
|
||||||
|
|
||||||
if (feeLimit > tronBalanceInt) {
|
if (feeLimit > tronBalanceInt) {
|
||||||
|
final feeInTrx = TronHelper.fromSun(BigInt.parse(feeLimit.toString()));
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.',
|
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up. Transaction fee: $feeInTrx TRX',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,16 @@
|
||||||
<string>nano-wallet</string>
|
<string>nano-wallet</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>nano-gpt</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>nano-gpt</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
|
|
|
@ -42,12 +42,7 @@ class AuthService with Store {
|
||||||
Future<void> setPassword(String password) async {
|
Future<void> setPassword(String password) async {
|
||||||
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
||||||
final encodedPassword = encodedPinCode(pin: password);
|
final encodedPassword = encodedPinCode(pin: password);
|
||||||
// secure storage has a weird bug on macOS, where overwriting a key doesn't work, unless
|
await writeSecureStorage(secureStorage, key: key, value: encodedPassword);
|
||||||
// we delete what's there first:
|
|
||||||
if (Platform.isMacOS) {
|
|
||||||
await secureStorage.delete(key: key);
|
|
||||||
}
|
|
||||||
await secureStorage.write(key: key, value: encodedPassword);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> canAuthenticate() async {
|
Future<bool> canAuthenticate() async {
|
||||||
|
@ -74,7 +69,11 @@ class AuthService with Store {
|
||||||
|
|
||||||
void saveLastAuthTime() {
|
void saveLastAuthTime() {
|
||||||
int timestamp = DateTime.now().millisecondsSinceEpoch;
|
int timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||||
secureStorage.write(key: SecureKey.lastAuthTimeMilliseconds, value: timestamp.toString());
|
writeSecureStorage(
|
||||||
|
secureStorage,
|
||||||
|
key: SecureKey.lastAuthTimeMilliseconds,
|
||||||
|
value: timestamp.toString(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> requireAuth() async {
|
Future<bool> requireAuth() async {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
import 'package:cake_wallet/themes/theme_list.dart';
|
import 'package:cake_wallet/themes/theme_list.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -275,7 +276,7 @@ class BackupService {
|
||||||
if (currentTransactionPriorityKeyLegacy != null)
|
if (currentTransactionPriorityKeyLegacy != null)
|
||||||
await _sharedPreferences.setInt(
|
await _sharedPreferences.setInt(
|
||||||
PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy);
|
PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy);
|
||||||
|
|
||||||
if (currentBitcoinElectrumSererId != null)
|
if (currentBitcoinElectrumSererId != null)
|
||||||
await _sharedPreferences.setInt(
|
await _sharedPreferences.setInt(
|
||||||
PreferencesKey.currentBitcoinElectrumSererIdKey, currentBitcoinElectrumSererId);
|
PreferencesKey.currentBitcoinElectrumSererIdKey, currentBitcoinElectrumSererId);
|
||||||
|
@ -373,16 +374,15 @@ class BackupService {
|
||||||
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
||||||
final backupPassword = keychainJSON[backupPasswordKey] as String;
|
final backupPassword = keychainJSON[backupPasswordKey] as String;
|
||||||
|
|
||||||
await _flutterSecureStorage.delete(key: backupPasswordKey);
|
await writeSecureStorage(_flutterSecureStorage, key: backupPasswordKey, value: backupPassword);
|
||||||
await _flutterSecureStorage.write(key: backupPasswordKey, value: backupPassword);
|
|
||||||
|
|
||||||
keychainWalletsInfo.forEach((dynamic rawInfo) async {
|
keychainWalletsInfo.forEach((dynamic rawInfo) async {
|
||||||
final info = rawInfo as Map<String, dynamic>;
|
final info = rawInfo as Map<String, dynamic>;
|
||||||
await importWalletKeychainInfo(info);
|
await importWalletKeychainInfo(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
await _flutterSecureStorage.delete(key: pinCodeKey);
|
await writeSecureStorage(_flutterSecureStorage,
|
||||||
await _flutterSecureStorage.write(key: pinCodeKey, value: encodedPinCode(pin: decodedPin));
|
key: pinCodeKey, value: encodedPinCode(pin: decodedPin));
|
||||||
|
|
||||||
keychainDumpFile.deleteSync();
|
keychainDumpFile.deleteSync();
|
||||||
}
|
}
|
||||||
|
@ -401,16 +401,15 @@ class BackupService {
|
||||||
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
||||||
final backupPassword = keychainJSON[backupPasswordKey] as String;
|
final backupPassword = keychainJSON[backupPasswordKey] as String;
|
||||||
|
|
||||||
await _flutterSecureStorage.delete(key: backupPasswordKey);
|
await writeSecureStorage(_flutterSecureStorage, key: backupPasswordKey, value: backupPassword);
|
||||||
await _flutterSecureStorage.write(key: backupPasswordKey, value: backupPassword);
|
|
||||||
|
|
||||||
keychainWalletsInfo.forEach((dynamic rawInfo) async {
|
keychainWalletsInfo.forEach((dynamic rawInfo) async {
|
||||||
final info = rawInfo as Map<String, dynamic>;
|
final info = rawInfo as Map<String, dynamic>;
|
||||||
await importWalletKeychainInfo(info);
|
await importWalletKeychainInfo(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
await _flutterSecureStorage.delete(key: pinCodeKey);
|
await writeSecureStorage(_flutterSecureStorage,
|
||||||
await _flutterSecureStorage.write(key: pinCodeKey, value: encodedPinCode(pin: decodedPin));
|
key: pinCodeKey, value: encodedPinCode(pin: decodedPin));
|
||||||
|
|
||||||
keychainDumpFile.deleteSync();
|
keychainDumpFile.deleteSync();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,7 @@ class KeyService {
|
||||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||||
final encodedPassword = encodeWalletPassword(password: password);
|
final encodedPassword = encodeWalletPassword(password: password);
|
||||||
|
|
||||||
await _secureStorage.delete(key: key);
|
await writeSecureStorage(_secureStorage, key: key, value: encodedPassword);
|
||||||
await _secureStorage.write(key: key, value: encodedPassword);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteWalletPassword({required String walletName}) async {
|
Future<void> deleteWalletPassword({required String walletName}) async {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
// For now, we can create a utility function to handle this.
|
// For now, we can create a utility function to handle this.
|
||||||
//
|
//
|
||||||
|
@ -25,3 +26,13 @@ Future<String?> readSecureStorage(FlutterSecureStorage secureStorage, String key
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> writeSecureStorage(FlutterSecureStorage secureStorage,
|
||||||
|
{required String key, required String value}) async {
|
||||||
|
// delete the value before writing on macOS because of a weird bug
|
||||||
|
// https://github.com/mogol/flutter_secure_storage/issues/581
|
||||||
|
if (Platform.isMacOS) {
|
||||||
|
await secureStorage.delete(key: key);
|
||||||
|
}
|
||||||
|
await secureStorage.write(key: key, value: value);
|
||||||
|
}
|
||||||
|
|
145
lib/di.dart
145
lib/di.dart
|
@ -26,6 +26,7 @@ import 'package:cake_wallet/entities/contact.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
import 'package:cake_wallet/entities/exchange_api_mode.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/parse_address_from_domain.dart';
|
||||||
|
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||||
import 'package:cake_wallet/tron/tron.dart';
|
import 'package:cake_wallet/tron/tron.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
||||||
import 'package:cw_core/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
|
@ -268,6 +269,7 @@ Future<void> setup({
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
|
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
|
||||||
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
|
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
|
||||||
required FlutterSecureStorage secureStorage,
|
required FlutterSecureStorage secureStorage,
|
||||||
|
required GlobalKey<NavigatorState> navigatorKey,
|
||||||
}) async {
|
}) async {
|
||||||
_walletInfoSource = walletInfoSource;
|
_walletInfoSource = walletInfoSource;
|
||||||
_nodeSource = nodeSource;
|
_nodeSource = nodeSource;
|
||||||
|
@ -431,68 +433,89 @@ Future<void> setup({
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
getIt.registerFactory<AuthPage>(() {
|
getIt.registerLazySingleton<LinkViewModel>(() {
|
||||||
return AuthPage(getIt.get<AuthViewModel>(),
|
return LinkViewModel(
|
||||||
|
appStore: getIt.get<AppStore>(),
|
||||||
|
settingsStore: getIt.get<SettingsStore>(),
|
||||||
|
authenticationStore: getIt.get<AuthenticationStore>(),
|
||||||
|
navigatorKey: navigatorKey,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
getIt.registerFactory<AuthPage>(instanceName: 'login', () {
|
||||||
|
return AuthPage(getIt.get<AuthViewModel>(), closable: false,
|
||||||
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
|
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
final authStore = getIt.get<AuthenticationStore>();
|
|
||||||
final appStore = getIt.get<AppStore>();
|
|
||||||
final useTotp = appStore.settingsStore.useTOTP2FA;
|
|
||||||
final shouldUseTotp2FAToAccessWallets =
|
|
||||||
appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
|
||||||
if (useTotp && shouldUseTotp2FAToAccessWallets) {
|
|
||||||
authPageState.close(
|
|
||||||
route: Routes.totpAuthCodePage,
|
|
||||||
arguments: TotpAuthArgumentsModel(
|
|
||||||
isForSetup: false,
|
|
||||||
isClosable: false,
|
|
||||||
onTotpAuthenticationFinished: (bool isAuthenticatedSuccessfully,
|
|
||||||
TotpAuthCodePageState totpAuthPageState) async {
|
|
||||||
if (!isAuthenticatedSuccessfully) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (appStore.wallet != null) {
|
|
||||||
authStore.allowed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
totpAuthPageState.changeProcessText('Loading the wallet');
|
|
||||||
|
|
||||||
if (loginError != null) {
|
|
||||||
totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}');
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactionDisposer? _reaction;
|
|
||||||
_reaction = reaction((_) => appStore.wallet, (Object? _) {
|
|
||||||
_reaction?.reaction.dispose();
|
|
||||||
authStore.allowed();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (appStore.wallet != null) {
|
|
||||||
authStore.allowed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
authPageState.changeProcessText('Loading the wallet');
|
|
||||||
|
|
||||||
if (loginError != null) {
|
|
||||||
authPageState.changeProcessText('ERROR: ${loginError.toString()}');
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactionDisposer? _reaction;
|
|
||||||
_reaction = reaction((_) => appStore.wallet, (Object? _) {
|
|
||||||
_reaction?.reaction.dispose();
|
|
||||||
authStore.allowed();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, closable: false);
|
final authStore = getIt.get<AuthenticationStore>();
|
||||||
}, instanceName: 'login');
|
final appStore = getIt.get<AppStore>();
|
||||||
|
final useTotp = appStore.settingsStore.useTOTP2FA;
|
||||||
|
final shouldUseTotp2FAToAccessWallets =
|
||||||
|
appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
||||||
|
if (useTotp && shouldUseTotp2FAToAccessWallets) {
|
||||||
|
authPageState.close(
|
||||||
|
route: Routes.totpAuthCodePage,
|
||||||
|
arguments: TotpAuthArgumentsModel(
|
||||||
|
isForSetup: false,
|
||||||
|
isClosable: false,
|
||||||
|
onTotpAuthenticationFinished:
|
||||||
|
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuthPageState) async {
|
||||||
|
if (!isAuthenticatedSuccessfully) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (appStore.wallet != null) {
|
||||||
|
authStore.allowed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
totpAuthPageState.changeProcessText('Loading the wallet');
|
||||||
|
|
||||||
|
if (loginError != null) {
|
||||||
|
totpAuthPageState.changeProcessText('ERROR: ${loginError.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactionDisposer? _reaction;
|
||||||
|
_reaction = reaction((_) => appStore.wallet, (Object? _) {
|
||||||
|
_reaction?.reaction.dispose();
|
||||||
|
authStore.allowed();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// wallet is already loaded:
|
||||||
|
if (appStore.wallet != null) {
|
||||||
|
// goes to the dashboard:
|
||||||
|
authStore.allowed();
|
||||||
|
// trigger any deep links:
|
||||||
|
final linkViewModel = getIt.get<LinkViewModel>();
|
||||||
|
if (linkViewModel.currentLink != null) {
|
||||||
|
linkViewModel.handleLink();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the wallet:
|
||||||
|
|
||||||
|
authPageState.changeProcessText('Loading the wallet');
|
||||||
|
|
||||||
|
if (loginError != null) {
|
||||||
|
authPageState.changeProcessText('ERROR: ${loginError.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactionDisposer? _reaction;
|
||||||
|
_reaction = reaction((_) => appStore.wallet, (Object? _) {
|
||||||
|
_reaction?.reaction.dispose();
|
||||||
|
authStore.allowed();
|
||||||
|
final linkViewModel = getIt.get<LinkViewModel>();
|
||||||
|
if (linkViewModel.currentLink != null) {
|
||||||
|
linkViewModel.handleLink();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl());
|
getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl());
|
||||||
|
|
||||||
|
@ -851,8 +874,10 @@ Future<void> setup({
|
||||||
tradesStore: getIt.get<TradesStore>(),
|
tradesStore: getIt.get<TradesStore>(),
|
||||||
sendViewModel: getIt.get<SendViewModel>()));
|
sendViewModel: getIt.get<SendViewModel>()));
|
||||||
|
|
||||||
getIt.registerFactory(
|
getIt.registerFactoryParam<ExchangePage, PaymentRequest?, void>(
|
||||||
() => ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>()));
|
(PaymentRequest? paymentRequest, __) {
|
||||||
|
return ExchangePage(getIt.get<ExchangeViewModel>(), getIt.get<AuthService>(), paymentRequest);
|
||||||
|
});
|
||||||
|
|
||||||
getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>()));
|
getIt.registerFactory(() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>()));
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,29 @@
|
||||||
import 'package:local_auth/local_auth.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:flutter_local_authentication/flutter_local_authentication.dart';
|
||||||
|
|
||||||
class BiometricAuth {
|
class BiometricAuth {
|
||||||
final _localAuth = LocalAuthentication();
|
final _flutterLocalAuthenticationPlugin = FlutterLocalAuthentication();
|
||||||
|
|
||||||
Future<bool> isAuthenticated() async {
|
Future<bool> isAuthenticated() async {
|
||||||
try {
|
try {
|
||||||
return await _localAuth.authenticate(
|
final authenticated = await _flutterLocalAuthenticationPlugin.authenticate();
|
||||||
localizedReason: S.current.biometric_auth_reason,
|
return authenticated;
|
||||||
options: AuthenticationOptions(
|
} catch (e) {
|
||||||
biometricOnly: true,
|
|
||||||
useErrorDialogs: true,
|
|
||||||
stickyAuth: false));
|
|
||||||
} on PlatformException catch (e) {
|
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> canCheckBiometrics() async {
|
Future<bool> canCheckBiometrics() async {
|
||||||
|
bool canAuthenticate;
|
||||||
try {
|
try {
|
||||||
return await _localAuth.canCheckBiometrics;
|
canAuthenticate = await _flutterLocalAuthenticationPlugin.canAuthenticate();
|
||||||
} on PlatformException catch (e) {
|
await _flutterLocalAuthenticationPlugin.setTouchIDAuthenticationAllowableReuseDuration(0);
|
||||||
print(e);
|
} catch (error) {
|
||||||
|
print("Exception checking support. $error");
|
||||||
|
canAuthenticate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return canAuthenticate;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -147,8 +148,8 @@ Future<void> ios_migrate_pin() async {
|
||||||
|
|
||||||
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
|
||||||
final encodedPassword = encodedPinCode(pin: pinPassword);
|
final encodedPassword = encodedPinCode(pin: pinPassword);
|
||||||
await flutterSecureStorage.delete(key: key);
|
await writeSecureStorage(flutterSecureStorage, key: key, value: encodedPassword);
|
||||||
await flutterSecureStorage.write(key: key, value: encodedPassword);
|
|
||||||
await prefs.setBool('ios_migration_pin_completed', true);
|
await prefs.setBool('ios_migration_pin_completed', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:cw_core/cake_hive.dart';
|
import 'package:cw_core/cake_hive.dart';
|
||||||
|
|
||||||
|
@ -10,8 +11,7 @@ Future<List<int>> getEncryptionKey(
|
||||||
key = CakeHive.generateSecureKey();
|
key = CakeHive.generateSecureKey();
|
||||||
final keyStringified = key.join(',');
|
final keyStringified = key.join(',');
|
||||||
String storageKey = 'transactionDescriptionsBoxKey';
|
String storageKey = 'transactionDescriptionsBoxKey';
|
||||||
await secureStorage.delete(key: storageKey);
|
await writeSecureStorage(secureStorage, key: storageKey, value: keyStringified);
|
||||||
await secureStorage.write(key: storageKey, value: keyStringified);
|
|
||||||
} else {
|
} else {
|
||||||
key = stringifiedKey.split(',').map((i) => int.parse(i)).toList();
|
key = stringifiedKey.split(',').map((i) => int.parse(i)).toList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/openalias_record.dart';
|
||||||
import 'package:cake_wallet/entities/parsed_address.dart';
|
import 'package:cake_wallet/entities/parsed_address.dart';
|
||||||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||||
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
||||||
|
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||||
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
import 'package:cake_wallet/mastodon/mastodon_api.dart';
|
||||||
import 'package:cake_wallet/nostr/nostr_api.dart';
|
import 'package:cake_wallet/nostr/nostr_api.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
@ -71,8 +72,8 @@ class AddressResolver {
|
||||||
return emailRegex.hasMatch(address);
|
return emailRegex.hasMatch(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor this to take Crypto currency instead of ticker, or at least pass in the tag as well
|
Future<ParsedAddress> resolve(BuildContext context, String text, CryptoCurrency currency) async {
|
||||||
Future<ParsedAddress> resolve(BuildContext context, String text, String ticker) async {
|
final ticker = currency.title;
|
||||||
try {
|
try {
|
||||||
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
if (text.startsWith('@') && !text.substring(1).contains('@')) {
|
||||||
if (settingsStore.lookupsTwitter) {
|
if (settingsStore.lookupsTwitter) {
|
||||||
|
@ -116,8 +117,7 @@ class AddressResolver {
|
||||||
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
|
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
|
||||||
|
|
||||||
if (mastodonUser != null) {
|
if (mastodonUser != null) {
|
||||||
String? addressFromBio = extractAddressByType(
|
String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency);
|
||||||
raw: mastodonUser.note, type: CryptoCurrency.fromString(ticker));
|
|
||||||
|
|
||||||
if (addressFromBio != null) {
|
if (addressFromBio != null) {
|
||||||
return ParsedAddress.fetchMastodonAddress(
|
return ParsedAddress.fetchMastodonAddress(
|
||||||
|
@ -131,8 +131,8 @@ class AddressResolver {
|
||||||
|
|
||||||
if (pinnedPosts.isNotEmpty) {
|
if (pinnedPosts.isNotEmpty) {
|
||||||
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
|
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
|
||||||
String? addressFromPinnedPost = extractAddressByType(
|
String? addressFromPinnedPost =
|
||||||
raw: userPinnedPostsText, type: CryptoCurrency.fromString(ticker));
|
extractAddressByType(raw: userPinnedPostsText, type: currency);
|
||||||
|
|
||||||
if (addressFromPinnedPost != null) {
|
if (addressFromPinnedPost != null) {
|
||||||
return ParsedAddress.fetchMastodonAddress(
|
return ParsedAddress.fetchMastodonAddress(
|
||||||
|
@ -162,6 +162,16 @@ class AddressResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final thorChainAddress = await ThorChainExchangeProvider.lookupAddressByName(text);
|
||||||
|
if (thorChainAddress != null) {
|
||||||
|
String? address =
|
||||||
|
thorChainAddress[ticker] ?? (ticker == 'RUNE' ? thorChainAddress['THOR'] : null);
|
||||||
|
if (address != null) {
|
||||||
|
return ParsedAddress.thorChainAddress(address: address, name: text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final formattedName = OpenaliasRecord.formatDomainName(text);
|
final formattedName = OpenaliasRecord.formatDomainName(text);
|
||||||
final domainParts = formattedName.split('.');
|
final domainParts = formattedName.split('.');
|
||||||
final name = domainParts.last;
|
final name = domainParts.last;
|
||||||
|
@ -204,7 +214,7 @@ class AddressResolver {
|
||||||
|
|
||||||
if (nostrUserData != null) {
|
if (nostrUserData != null) {
|
||||||
String? addressFromBio = extractAddressByType(
|
String? addressFromBio = extractAddressByType(
|
||||||
raw: nostrUserData.about, type: CryptoCurrency.fromString(ticker));
|
raw: nostrUserData.about, type: currency);
|
||||||
if (addressFromBio != null) {
|
if (addressFromBio != null) {
|
||||||
return ParsedAddress.nostrAddress(
|
return ParsedAddress.nostrAddress(
|
||||||
address: addressFromBio,
|
address: addressFromBio,
|
||||||
|
|
|
@ -11,7 +11,8 @@ enum ParseFrom {
|
||||||
ens,
|
ens,
|
||||||
contact,
|
contact,
|
||||||
mastodon,
|
mastodon,
|
||||||
nostr
|
nostr,
|
||||||
|
thorChain
|
||||||
}
|
}
|
||||||
|
|
||||||
class ParsedAddress {
|
class ParsedAddress {
|
||||||
|
@ -133,6 +134,14 @@ class ParsedAddress {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory ParsedAddress.thorChainAddress({required String address, required String name}) {
|
||||||
|
return ParsedAddress(
|
||||||
|
addresses: [address],
|
||||||
|
name: name,
|
||||||
|
parseFrom: ParseFrom.thorChain,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final List<String> addresses;
|
final List<String> addresses;
|
||||||
final String name;
|
final String name;
|
||||||
final String description;
|
final String description;
|
||||||
|
|
|
@ -34,11 +34,13 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
static final isRefundAddressSupported = [CryptoCurrency.eth];
|
static final isRefundAddressSupported = [CryptoCurrency.eth];
|
||||||
|
|
||||||
static const _baseURL = 'thornode.ninerealms.com';
|
static const _baseNodeURL = 'thornode.ninerealms.com';
|
||||||
|
static const _baseURL = 'midgard.ninerealms.com';
|
||||||
static const _quotePath = '/thorchain/quote/swap';
|
static const _quotePath = '/thorchain/quote/swap';
|
||||||
static const _txInfoPath = '/thorchain/tx/status/';
|
static const _txInfoPath = '/thorchain/tx/status/';
|
||||||
static const _affiliateName = 'cakewallet';
|
static const _affiliateName = 'cakewallet';
|
||||||
static const _affiliateBps = '175';
|
static const _affiliateBps = '175';
|
||||||
|
static const _nameLookUpPath= 'v2/thorname/lookup/';
|
||||||
|
|
||||||
final Box<Trade> tradesStore;
|
final Box<Trade> tradesStore;
|
||||||
|
|
||||||
|
@ -154,7 +156,7 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> findTradeById({required String id}) async {
|
Future<Trade> findTradeById({required String id}) async {
|
||||||
if (id.isEmpty) throw Exception('Trade id is empty');
|
if (id.isEmpty) throw Exception('Trade id is empty');
|
||||||
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
|
||||||
final uri = Uri.https(_baseURL, '$_txInfoPath$formattedId');
|
final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
|
||||||
final response = await http.get(uri);
|
final response = await http.get(uri);
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode == 404) {
|
||||||
|
@ -206,8 +208,35 @@ class ThorChainExchangeProvider extends ExchangeProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, String>?>? lookupAddressByName(String name) async {
|
||||||
|
final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
|
||||||
|
final response = await http.get(uri);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final body = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
final entries = body['entries'] as List<dynamic>?;
|
||||||
|
|
||||||
|
if (entries == null || entries.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> chainToAddressMap = {};
|
||||||
|
|
||||||
|
for (final entry in entries) {
|
||||||
|
final chain = entry['chain'] as String;
|
||||||
|
final address = entry['address'] as String;
|
||||||
|
chainToAddressMap[chain] = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chainToAddressMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
|
||||||
Uri uri = Uri.https(_baseURL, _quotePath, params);
|
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
|
||||||
|
|
||||||
final response = await http.get(uri);
|
final response = await http.get(uri);
|
||||||
|
|
||||||
|
|
|
@ -751,6 +751,50 @@ class HaMaterialLocalizations extends GlobalMaterialLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get scrimOnTapHintRaw => "Scrip on Tap";
|
String get scrimOnTapHintRaw => "Scrip on Tap";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement collapsedHint
|
||||||
|
String get collapsedHint => "collapsedHint";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement expandedHint
|
||||||
|
String get expandedHint => "expandedHint";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement expansionTileCollapsedHint
|
||||||
|
String get expansionTileCollapsedHint => "expansionTileCollapsedHint";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement expansionTileCollapsedTapHint
|
||||||
|
String get expansionTileCollapsedTapHint => "expansionTileCollapsedTapHint";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement expansionTileExpandedHint
|
||||||
|
String get expansionTileExpandedHint => "expansionTileExpandedHint";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement expansionTileExpandedTapHint
|
||||||
|
String get expansionTileExpandedTapHint => "expansionTileExpandedTapHint";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement scanTextButtonLabel
|
||||||
|
String get scanTextButtonLabel => "scanTextButtonLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement lookUpButtonLabel
|
||||||
|
String get lookUpButtonLabel => "lookUpButtonLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement menuDismissLabel
|
||||||
|
String get menuDismissLabel => "menuDismissLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement searchWebButtonLabel
|
||||||
|
String get searchWebButtonLabel => "searchWebButtonLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement shareButtonLabel
|
||||||
|
String get shareButtonLabel => "shareButtonLabel";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cupertino Support
|
/// Cupertino Support
|
||||||
|
@ -955,4 +999,24 @@ class HaCupertinoLocalizations extends GlobalCupertinoLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get noSpellCheckReplacementsLabel => "";
|
String get noSpellCheckReplacementsLabel => "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement clearButtonLabel
|
||||||
|
String get clearButtonLabel => "clearButtonLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement lookUpButtonLabel
|
||||||
|
String get lookUpButtonLabel => "lookUpButtonLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement menuDismissLabel
|
||||||
|
String get menuDismissLabel => "menuDismissLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement searchWebButtonLabel
|
||||||
|
String get searchWebButtonLabel => "searchWebButtonLabel";
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement shareButtonLabel
|
||||||
|
String get shareButtonLabel => "shareButtonLabel";
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,6 +7,7 @@ import 'package:cake_wallet/locales/locale.dart';
|
||||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||||
|
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||||
import 'package:cw_core/address_info.dart';
|
import 'package:cw_core/address_info.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cw_core/hive_type_ids.dart';
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
|
@ -41,6 +42,7 @@ import 'package:uni_links/uni_links.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
import 'package:cw_core/cake_hive.dart';
|
import 'package:cw_core/cake_hive.dart';
|
||||||
|
import 'package:cw_core/window_size.dart';
|
||||||
|
|
||||||
final navigatorKey = GlobalKey<NavigatorState>();
|
final navigatorKey = GlobalKey<NavigatorState>();
|
||||||
final rootKey = GlobalKey<RootState>();
|
final rootKey = GlobalKey<RootState>();
|
||||||
|
@ -60,6 +62,8 @@ Future<void> main() async {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
await setDefaultMinimumWindowSize();
|
||||||
|
|
||||||
await CakeHive.close();
|
await CakeHive.close();
|
||||||
|
|
||||||
await initializeAppConfigs();
|
await initializeAppConfigs();
|
||||||
|
@ -202,18 +206,20 @@ Future<void> initialSetup(
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
powNodes: powNodes);
|
powNodes: powNodes);
|
||||||
await setup(
|
await setup(
|
||||||
walletInfoSource: walletInfoSource,
|
walletInfoSource: walletInfoSource,
|
||||||
nodeSource: nodes,
|
nodeSource: nodes,
|
||||||
powNodeSource: powNodes,
|
powNodeSource: powNodes,
|
||||||
contactSource: contactSource,
|
contactSource: contactSource,
|
||||||
tradesSource: tradesSource,
|
tradesSource: tradesSource,
|
||||||
templates: templates,
|
templates: templates,
|
||||||
exchangeTemplates: exchangeTemplates,
|
exchangeTemplates: exchangeTemplates,
|
||||||
transactionDescriptionBox: transactionDescriptions,
|
transactionDescriptionBox: transactionDescriptions,
|
||||||
ordersSource: ordersSource,
|
ordersSource: ordersSource,
|
||||||
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
|
anonpayInvoiceInfoSource: anonpayInvoiceInfo,
|
||||||
unspentCoinsInfoSource: unspentCoinsInfoSource,
|
unspentCoinsInfoSource: unspentCoinsInfoSource,
|
||||||
secureStorage: secureStorage);
|
secureStorage: secureStorage,
|
||||||
|
navigatorKey: navigatorKey,
|
||||||
|
);
|
||||||
await bootstrap(navigatorKey);
|
await bootstrap(navigatorKey);
|
||||||
monero?.onStartup();
|
monero?.onStartup();
|
||||||
}
|
}
|
||||||
|
@ -284,6 +290,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
||||||
return Observer(builder: (BuildContext context) {
|
return Observer(builder: (BuildContext context) {
|
||||||
final appStore = getIt.get<AppStore>();
|
final appStore = getIt.get<AppStore>();
|
||||||
final authService = getIt.get<AuthService>();
|
final authService = getIt.get<AuthService>();
|
||||||
|
final linkViewModel = getIt.get<LinkViewModel>();
|
||||||
final settingsStore = appStore.settingsStore;
|
final settingsStore = appStore.settingsStore;
|
||||||
final statusBarColor = Colors.transparent;
|
final statusBarColor = Colors.transparent;
|
||||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||||
|
@ -306,6 +313,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
||||||
authenticationStore: authenticationStore,
|
authenticationStore: authenticationStore,
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
authService: authService,
|
authService: authService,
|
||||||
|
linkViewModel: linkViewModel,
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
navigatorObservers: [routeObserver],
|
navigatorObservers: [routeObserver],
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
|
|
|
@ -221,7 +221,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<SetupPinCodePage>(
|
builder: (_) => getIt.get<SetupPinCodePage>(
|
||||||
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
|
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
|
||||||
Navigator.of(context.context).pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
|
Navigator.of(context.context)
|
||||||
|
.pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
|
||||||
),
|
),
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
);
|
);
|
||||||
|
@ -231,9 +232,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
builder: (_) => ConnectDevicePage(
|
builder: (_) => ConnectDevicePage(
|
||||||
ConnectDevicePageParams(
|
ConnectDevicePageParams(
|
||||||
walletType: availableWalletTypes.first,
|
walletType: availableWalletTypes.first,
|
||||||
onConnectDevice: (BuildContext context, _) =>
|
onConnectDevice: (BuildContext context, _) => Navigator.of(context).pushNamed(
|
||||||
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
|
Routes.chooseHardwareWalletAccount,
|
||||||
arguments: [availableWalletTypes.first]),
|
arguments: [availableWalletTypes.first]),
|
||||||
),
|
),
|
||||||
getIt.get<LedgerViewModel>(),
|
getIt.get<LedgerViewModel>(),
|
||||||
));
|
));
|
||||||
|
@ -243,9 +244,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
param1: (BuildContext context, WalletType type) {
|
param1: (BuildContext context, WalletType type) {
|
||||||
final arguments = ConnectDevicePageParams(
|
final arguments = ConnectDevicePageParams(
|
||||||
walletType: type,
|
walletType: type,
|
||||||
onConnectDevice: (BuildContext context, _) =>
|
onConnectDevice: (BuildContext context, _) => Navigator.of(context)
|
||||||
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
|
.pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
|
||||||
arguments: [type]),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
||||||
|
@ -308,8 +308,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.bumpFeePage:
|
case Routes.bumpFeePage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
|
||||||
getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
|
|
||||||
|
|
||||||
case Routes.newSubaddress:
|
case Routes.newSubaddress:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -461,7 +460,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.exchange:
|
case Routes.exchange:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true, builder: (_) => getIt.get<ExchangePage>());
|
fullscreenDialog: true,
|
||||||
|
builder: (_) => getIt.get<ExchangePage>(param1: settings.arguments as PaymentRequest?),
|
||||||
|
);
|
||||||
|
|
||||||
case Routes.exchangeTemplate:
|
case Routes.exchangeTemplate:
|
||||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>());
|
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ExchangeTemplatePage>());
|
||||||
|
|
|
@ -22,7 +22,7 @@ class DropDownItemWidget extends StatelessWidget {
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
),
|
),
|
||||||
|
|
|
@ -59,12 +59,15 @@ class MarketPlacePage extends StatelessWidget {
|
||||||
// ),
|
// ),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
DashBoardRoundedCardWidget(
|
DashBoardRoundedCardWidget(
|
||||||
onTap: () => launchUrl(
|
|
||||||
Uri.https("buy.cakepay.com"),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
),
|
|
||||||
title: S.of(context).cake_pay_web_cards_title,
|
title: S.of(context).cake_pay_web_cards_title,
|
||||||
subTitle: S.of(context).cake_pay_web_cards_subtitle,
|
subTitle: S.of(context).cake_pay_web_cards_subtitle,
|
||||||
|
onTap: () => _launchMarketPlaceUrl("buy.cakepay.com"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
DashBoardRoundedCardWidget(
|
||||||
|
title: "NanoGPT",
|
||||||
|
subTitle: S.of(context).nanogpt_subtitle,
|
||||||
|
onTap: () => _launchMarketPlaceUrl("cake.nano-gpt.com"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -76,6 +79,17 @@ class MarketPlacePage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _launchMarketPlaceUrl(String url) async {
|
||||||
|
try {
|
||||||
|
launchUrl(
|
||||||
|
Uri.https(url),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Remove ionia flow/files if we will discard it
|
// TODO: Remove ionia flow/files if we will discard it
|
||||||
void _navigatorToGiftCardsPage(BuildContext context) {
|
void _navigatorToGiftCardsPage(BuildContext context) {
|
||||||
final walletType = dashboardViewModel.type;
|
final walletType = dashboardViewModel.type;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/add_template_button.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/debounce.dart';
|
import 'package:cake_wallet/utils/debounce.dart';
|
||||||
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cw_core/sync_status.dart';
|
import 'package:cw_core/sync_status.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -43,7 +44,7 @@ import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
||||||
|
|
||||||
class ExchangePage extends BasePage {
|
class ExchangePage extends BasePage {
|
||||||
ExchangePage(this.exchangeViewModel, this.authService) {
|
ExchangePage(this.exchangeViewModel, this.authService, this.initialPaymentRequest) {
|
||||||
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
|
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
|
||||||
? exchangeViewModel.wallet.name
|
? exchangeViewModel.wallet.name
|
||||||
: null;
|
: null;
|
||||||
|
@ -54,6 +55,7 @@ class ExchangePage extends BasePage {
|
||||||
|
|
||||||
final ExchangeViewModel exchangeViewModel;
|
final ExchangeViewModel exchangeViewModel;
|
||||||
final AuthService authService;
|
final AuthService authService;
|
||||||
|
final PaymentRequest? initialPaymentRequest;
|
||||||
final depositKey = GlobalKey<ExchangeCardState>();
|
final depositKey = GlobalKey<ExchangeCardState>();
|
||||||
final receiveKey = GlobalKey<ExchangeCardState>();
|
final receiveKey = GlobalKey<ExchangeCardState>();
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
@ -330,10 +332,12 @@ class ExchangePage extends BasePage {
|
||||||
|
|
||||||
void applyTemplate(
|
void applyTemplate(
|
||||||
BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
|
BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
|
||||||
exchangeViewModel.changeDepositCurrency(
|
|
||||||
currency: CryptoCurrency.fromString(template.depositCurrency));
|
final depositCryptoCurrency = CryptoCurrency.fromString(template.depositCurrency);
|
||||||
exchangeViewModel.changeReceiveCurrency(
|
final receiveCryptoCurrency = CryptoCurrency.fromString(template.receiveCurrency);
|
||||||
currency: CryptoCurrency.fromString(template.receiveCurrency));
|
|
||||||
|
exchangeViewModel.changeDepositCurrency(currency: depositCryptoCurrency);
|
||||||
|
exchangeViewModel.changeReceiveCurrency(currency: receiveCryptoCurrency);
|
||||||
|
|
||||||
exchangeViewModel.changeDepositAmount(amount: template.amount);
|
exchangeViewModel.changeDepositAmount(amount: template.amount);
|
||||||
exchangeViewModel.depositAddress = template.depositAddress;
|
exchangeViewModel.depositAddress = template.depositAddress;
|
||||||
|
@ -342,12 +346,10 @@ class ExchangePage extends BasePage {
|
||||||
exchangeViewModel.isFixedRateMode = false;
|
exchangeViewModel.isFixedRateMode = false;
|
||||||
|
|
||||||
var domain = template.depositAddress;
|
var domain = template.depositAddress;
|
||||||
var ticker = template.depositCurrency.toLowerCase();
|
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, depositCryptoCurrency);
|
||||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
|
|
||||||
domain = template.receiveAddress;
|
domain = template.receiveAddress;
|
||||||
ticker = template.receiveCurrency.toLowerCase();
|
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, receiveCryptoCurrency);
|
||||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
|
void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
|
||||||
|
@ -519,16 +521,14 @@ class ExchangePage extends BasePage {
|
||||||
_depositAddressFocus.addListener(() async {
|
_depositAddressFocus.addListener(() async {
|
||||||
if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
|
if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
|
||||||
final domain = depositAddressController.text;
|
final domain = depositAddressController.text;
|
||||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||||
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_receiveAddressFocus.addListener(() async {
|
_receiveAddressFocus.addListener(() async {
|
||||||
if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
|
if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
|
||||||
final domain = receiveAddressController.text;
|
final domain = receiveAddressController.text;
|
||||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||||
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -545,6 +545,12 @@ class ExchangePage extends BasePage {
|
||||||
// amount: depositAmountController.text);
|
// amount: depositAmountController.text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (initialPaymentRequest != null) {
|
||||||
|
exchangeViewModel.receiveCurrency = CryptoCurrency.fromString(initialPaymentRequest!.scheme);
|
||||||
|
exchangeViewModel.depositAmount = initialPaymentRequest!.amount;
|
||||||
|
exchangeViewModel.receiveAddress = initialPaymentRequest!.address;
|
||||||
|
}
|
||||||
|
|
||||||
_isReactionsSet = true;
|
_isReactionsSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,8 +581,8 @@ class ExchangePage extends BasePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> fetchParsedAddress(BuildContext context, String domain, String ticker) async {
|
Future<String> fetchParsedAddress(BuildContext context, String domain, CryptoCurrency currency) async {
|
||||||
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||||
final address = await extractAddressFromParsed(context, parsedAddress);
|
final address = await extractAddressFromParsed(context, parsedAddress);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
@ -663,15 +669,13 @@ class ExchangePage extends BasePage {
|
||||||
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
|
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
|
||||||
onPushPasteButton: (context) async {
|
onPushPasteButton: (context) async {
|
||||||
final domain = exchangeViewModel.depositAddress;
|
final domain = exchangeViewModel.depositAddress;
|
||||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.depositAddress =
|
exchangeViewModel.depositAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||||
},
|
},
|
||||||
onPushAddressBookButton: (context) async {
|
onPushAddressBookButton: (context) async {
|
||||||
final domain = exchangeViewModel.depositAddress;
|
final domain = exchangeViewModel.depositAddress;
|
||||||
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.depositAddress =
|
exchangeViewModel.depositAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -712,15 +716,13 @@ class ExchangePage extends BasePage {
|
||||||
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
|
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
|
||||||
onPushPasteButton: (context) async {
|
onPushPasteButton: (context) async {
|
||||||
final domain = exchangeViewModel.receiveAddress;
|
final domain = exchangeViewModel.receiveAddress;
|
||||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.receiveAddress =
|
exchangeViewModel.receiveAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||||
},
|
},
|
||||||
onPushAddressBookButton: (context) async {
|
onPushAddressBookButton: (context) async {
|
||||||
final domain = exchangeViewModel.receiveAddress;
|
final domain = exchangeViewModel.receiveAddress;
|
||||||
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
|
|
||||||
exchangeViewModel.receiveAddress =
|
exchangeViewModel.receiveAddress =
|
||||||
await fetchParsedAddress(context, domain, ticker);
|
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
|
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -25,6 +26,7 @@ class Root extends StatefulWidget {
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.navigatorKey,
|
required this.navigatorKey,
|
||||||
required this.authService,
|
required this.authService,
|
||||||
|
required this.linkViewModel,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final AuthenticationStore authenticationStore;
|
final AuthenticationStore authenticationStore;
|
||||||
|
@ -32,6 +34,7 @@ class Root extends StatefulWidget {
|
||||||
final GlobalKey<NavigatorState> navigatorKey;
|
final GlobalKey<NavigatorState> navigatorKey;
|
||||||
final AuthService authService;
|
final AuthService authService;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
final LinkViewModel linkViewModel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RootState createState() => RootState();
|
RootState createState() => RootState();
|
||||||
|
@ -53,7 +56,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
StreamSubscription<Uri?>? stream;
|
StreamSubscription<Uri?>? stream;
|
||||||
ReactionDisposer? _walletReactionDisposer;
|
ReactionDisposer? _walletReactionDisposer;
|
||||||
ReactionDisposer? _deepLinksReactionDisposer;
|
ReactionDisposer? _deepLinksReactionDisposer;
|
||||||
Uri? launchUri;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -98,7 +100,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
void handleDeepLinking(Uri? uri) async {
|
void handleDeepLinking(Uri? uri) async {
|
||||||
if (uri == null || !mounted) return;
|
if (uri == null || !mounted) return;
|
||||||
|
|
||||||
launchUri = uri;
|
widget.linkViewModel.currentLink = uri;
|
||||||
|
|
||||||
bool requireAuth = await widget.authService.requireAuth();
|
bool requireAuth = await widget.authService.requireAuth();
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
(AuthenticationState state) {
|
(AuthenticationState state) {
|
||||||
if (state == AuthenticationState.allowed) {
|
if (state == AuthenticationState.allowed) {
|
||||||
if (widget.appStore.wallet == null) {
|
if (widget.appStore.wallet == null) {
|
||||||
waitForWalletInstance(context, launchUri!);
|
waitForWalletInstance(context);
|
||||||
} else {
|
} else {
|
||||||
_navigateToDeepLinkScreen();
|
_navigateToDeepLinkScreen();
|
||||||
}
|
}
|
||||||
|
@ -150,6 +152,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// this only happens when the app has been in the background for some time
|
||||||
|
// this does NOT trigger when the app is started from the "closed" state!
|
||||||
if (_isInactive && !_postFrameCallback && _requestAuth) {
|
if (_isInactive && !_postFrameCallback && _requestAuth) {
|
||||||
_postFrameCallback = true;
|
_postFrameCallback = true;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
@ -158,40 +162,38 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||||
if (!isAuthenticatedSuccessfully) {
|
if (!isAuthenticatedSuccessfully) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
final useTotp = widget.appStore.settingsStore.useTOTP2FA;
|
||||||
|
final shouldUseTotp2FAToAccessWallets =
|
||||||
|
widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
||||||
|
if (useTotp && shouldUseTotp2FAToAccessWallets) {
|
||||||
|
_reset();
|
||||||
|
auth.close(
|
||||||
|
route: Routes.totpAuthCodePage,
|
||||||
|
arguments: TotpAuthArgumentsModel(
|
||||||
|
onTotpAuthenticationFinished:
|
||||||
|
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
|
||||||
|
if (!isAuthenticatedSuccessfully) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_reset();
|
||||||
|
totpAuth.close(
|
||||||
|
route: widget.linkViewModel.getRouteToGo(),
|
||||||
|
arguments: widget.linkViewModel.getRouteArgs(),
|
||||||
|
);
|
||||||
|
widget.linkViewModel.currentLink = null;
|
||||||
|
},
|
||||||
|
isForSetup: false,
|
||||||
|
isClosable: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
final useTotp = widget.appStore.settingsStore.useTOTP2FA;
|
_reset();
|
||||||
final shouldUseTotp2FAToAccessWallets =
|
auth.close(
|
||||||
widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
route: widget.linkViewModel.getRouteToGo(),
|
||||||
if (useTotp && shouldUseTotp2FAToAccessWallets) {
|
arguments: widget.linkViewModel.getRouteArgs(),
|
||||||
_reset();
|
);
|
||||||
auth.close(
|
widget.linkViewModel.currentLink = null;
|
||||||
route: Routes.totpAuthCodePage,
|
|
||||||
arguments: TotpAuthArgumentsModel(
|
|
||||||
onTotpAuthenticationFinished:
|
|
||||||
(bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
|
|
||||||
if (!isAuthenticatedSuccessfully) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_reset();
|
|
||||||
totpAuth.close(
|
|
||||||
route: _getRouteToGo(),
|
|
||||||
arguments:
|
|
||||||
isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
|
|
||||||
);
|
|
||||||
launchUri = null;
|
|
||||||
},
|
|
||||||
isForSetup: false,
|
|
||||||
isClosable: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_reset();
|
|
||||||
auth.close(
|
|
||||||
route: _getRouteToGo(),
|
|
||||||
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
|
|
||||||
);
|
|
||||||
launchUri = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -216,36 +218,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
_isInactiveController.add(value);
|
_isInactiveController.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isValidPaymentUri() => launchUri?.path.isNotEmpty ?? false;
|
void waitForWalletInstance(BuildContext context) {
|
||||||
|
|
||||||
bool get isWalletConnectLink => launchUri?.authority == 'wc';
|
|
||||||
|
|
||||||
String? _getRouteToGo() {
|
|
||||||
if (isWalletConnectLink) {
|
|
||||||
if (isEVMCompatibleChain(widget.appStore.wallet!.type)) {
|
|
||||||
_nonETHWalletErrorToast(S.current.switchToEVMCompatibleWallet);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Routes.walletConnectConnectionsListing;
|
|
||||||
} else if (_isValidPaymentUri()) {
|
|
||||||
return Routes.send;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _nonETHWalletErrorToast(String message) async {
|
|
||||||
Fluttertoast.showToast(
|
|
||||||
msg: message,
|
|
||||||
toastLength: Toast.LENGTH_LONG,
|
|
||||||
gravity: ToastGravity.SNACKBAR,
|
|
||||||
backgroundColor: Colors.black,
|
|
||||||
textColor: Colors.white,
|
|
||||||
fontSize: 16.0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waitForWalletInstance(BuildContext context, Uri tempLaunchUri) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
_walletReactionDisposer = reaction(
|
_walletReactionDisposer = reaction(
|
||||||
|
@ -263,14 +236,6 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateToDeepLinkScreen() {
|
void _navigateToDeepLinkScreen() {
|
||||||
if (_getRouteToGo() != null) {
|
widget.linkViewModel.handleLink();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
widget.navigatorKey.currentState?.pushNamed(
|
|
||||||
_getRouteToGo()!,
|
|
||||||
arguments: isWalletConnectLink ? launchUri : PaymentRequest.fromUri(launchUri),
|
|
||||||
);
|
|
||||||
launchUri = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class SendPage extends BasePage {
|
class SendPage extends BasePage {
|
||||||
SendPage({
|
SendPage({
|
||||||
|
@ -420,12 +422,10 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
reaction((_) => sendViewModel.state, (ExecutionState state) {
|
reaction((_) => sendViewModel.state, (ExecutionState state) {
|
||||||
|
|
||||||
if (dialogContext != null && dialogContext?.mounted == true) {
|
if (dialogContext != null && dialogContext?.mounted == true) {
|
||||||
Navigator.of(dialogContext!).pop();
|
Navigator.of(dialogContext!).pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (state is FailureState) {
|
if (state is FailureState) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
|
@ -460,10 +460,10 @@ class SendPage extends BasePage {
|
||||||
outputs: sendViewModel.outputs,
|
outputs: sendViewModel.outputs,
|
||||||
rightButtonText: S.of(_dialogContext).send,
|
rightButtonText: S.of(_dialogContext).send,
|
||||||
leftButtonText: S.of(_dialogContext).cancel,
|
leftButtonText: S.of(_dialogContext).cancel,
|
||||||
actionRightButton: () {
|
actionRightButton: () async {
|
||||||
Navigator.of(_dialogContext).pop();
|
Navigator.of(_dialogContext).pop();
|
||||||
sendViewModel.commitTransaction();
|
sendViewModel.commitTransaction();
|
||||||
showPopUp<void>(
|
await showPopUp<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext _dialogContext) {
|
builder: (BuildContext _dialogContext) {
|
||||||
return Observer(builder: (_) {
|
return Observer(builder: (_) {
|
||||||
|
@ -481,12 +481,14 @@ class SendPage extends BasePage {
|
||||||
sendViewModel.selectedCryptoCurrency.toString());
|
sendViewModel.selectedCryptoCurrency.toString());
|
||||||
|
|
||||||
final waitMessage = sendViewModel.walletType == WalletType.solana
|
final waitMessage = sendViewModel.walletType == WalletType.solana
|
||||||
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}' : '';
|
? '. ${S.of(_dialogContext).waitFewSecondForTxUpdate}'
|
||||||
|
: '';
|
||||||
|
|
||||||
final newContactMessage = newContactAddress != null
|
final newContactMessage = newContactAddress != null
|
||||||
? '\n${S.of(_dialogContext).add_contact_to_address_book}' : '';
|
? '\n${S.of(_dialogContext).add_contact_to_address_book}'
|
||||||
|
: '';
|
||||||
|
|
||||||
final alertContent =
|
String alertContent =
|
||||||
"$successMessage$waitMessage$newContactMessage";
|
"$successMessage$waitMessage$newContactMessage";
|
||||||
|
|
||||||
if (newContactAddress != null) {
|
if (newContactAddress != null) {
|
||||||
|
@ -509,6 +511,10 @@ class SendPage extends BasePage {
|
||||||
newContactAddress = null;
|
newContactAddress = null;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
if (initialPaymentRequest?.callbackMessage?.isNotEmpty ??
|
||||||
|
false) {
|
||||||
|
alertContent = initialPaymentRequest!.callbackMessage!;
|
||||||
|
}
|
||||||
return AlertWithOneAction(
|
return AlertWithOneAction(
|
||||||
alertTitle: '',
|
alertTitle: '',
|
||||||
alertContent: alertContent,
|
alertContent: alertContent,
|
||||||
|
@ -523,6 +529,20 @@ class SendPage extends BasePage {
|
||||||
return Offstage();
|
return Offstage();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
if (state is TransactionCommitted) {
|
||||||
|
if (initialPaymentRequest?.callbackUrl?.isNotEmpty ?? false) {
|
||||||
|
// wait a second so it's not as jarring:
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
try {
|
||||||
|
launchUrl(
|
||||||
|
Uri.parse(initialPaymentRequest!.callbackUrl!),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actionLeftButton: () => Navigator.of(_dialogContext).pop());
|
actionLeftButton: () => Navigator.of(_dialogContext).pop());
|
||||||
});
|
});
|
||||||
|
|
|
@ -56,6 +56,11 @@ Future<String> extractAddressFromParsed(
|
||||||
profileImageUrl = parsedAddress.profileImageUrl;
|
profileImageUrl = parsedAddress.profileImageUrl;
|
||||||
profileName = parsedAddress.profileName;
|
profileName = parsedAddress.profileName;
|
||||||
break;
|
break;
|
||||||
|
case ParseFrom.thorChain:
|
||||||
|
title = S.of(context).address_detected;
|
||||||
|
content = S.of(context).extracted_address_content('${parsedAddress.name} (ThorChain)');
|
||||||
|
address = parsedAddress.addresses.first;
|
||||||
|
break;
|
||||||
case ParseFrom.yatRecord:
|
case ParseFrom.yatRecord:
|
||||||
if (parsedAddress.name.isEmpty) {
|
if (parsedAddress.name.isEmpty) {
|
||||||
title = S.of(context).yat_error;
|
title = S.of(context).yat_error;
|
||||||
|
|
|
@ -12,7 +12,6 @@ final _settingsNavigatorKey = GlobalKey<NavigatorState>();
|
||||||
class DesktopSettingsPage extends StatefulWidget {
|
class DesktopSettingsPage extends StatefulWidget {
|
||||||
const DesktopSettingsPage({super.key});
|
const DesktopSettingsPage({super.key});
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DesktopSettingsPage> createState() => _DesktopSettingsPageState();
|
State<DesktopSettingsPage> createState() => _DesktopSettingsPageState();
|
||||||
}
|
}
|
||||||
|
@ -33,22 +32,21 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Container(
|
body: Container(
|
||||||
height: MediaQuery.of(context).size.height,
|
height: MediaQuery.of(context).size.height,
|
||||||
child: Column(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Text(
|
|
||||||
S.current.settings,
|
|
||||||
style: textXLarge(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
flex: 1,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 24, 24, 4),
|
||||||
|
child: Text(
|
||||||
|
S.current.settings,
|
||||||
|
style: textXLarge(),
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
padding: EdgeInsets.only(top: 0),
|
padding: EdgeInsets.only(top: 0),
|
||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
|
@ -78,27 +76,27 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
|
||||||
itemCount: itemCount,
|
itemCount: itemCount,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
|
||||||
flex: 2,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(maxWidth: 500),
|
|
||||||
child: Navigator(
|
|
||||||
key: _settingsNavigatorKey,
|
|
||||||
initialRoute: Routes.empty_no_route,
|
|
||||||
onGenerateRoute: (settings) => Router.createRoute(settings),
|
|
||||||
onGenerateInitialRoutes:
|
|
||||||
(NavigatorState navigator, String initialRouteName) {
|
|
||||||
return [
|
|
||||||
navigator
|
|
||||||
.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Flexible(
|
||||||
|
flex: 2,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxWidth: 500),
|
||||||
|
child: Navigator(
|
||||||
|
key: _settingsNavigatorKey,
|
||||||
|
initialRoute: Routes.empty_no_route,
|
||||||
|
onGenerateRoute: (settings) => Router.createRoute(settings),
|
||||||
|
onGenerateInitialRoutes:
|
||||||
|
(NavigatorState navigator, String initialRouteName) {
|
||||||
|
return [
|
||||||
|
navigator
|
||||||
|
.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
|
||||||
|
];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cake_wallet/core/auth_service.dart';
|
import 'package:cake_wallet/core/auth_service.dart';
|
||||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -58,7 +60,7 @@ class SecurityBackupPage extends BasePage {
|
||||||
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (DeviceInfo.instance.isMobile)
|
if (DeviceInfo.instance.isMobile || Platform.isMacOS || Platform.isLinux)
|
||||||
Observer(builder: (_) {
|
Observer(builder: (_) {
|
||||||
return SettingsSwitcherCell(
|
return SettingsSwitcherCell(
|
||||||
title: S.current.settings_allow_biometrical_authentication,
|
title: S.current.settings_allow_biometrical_authentication,
|
||||||
|
|
|
@ -146,7 +146,7 @@ class ConnectScreen extends StatelessWidget {
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: connect,
|
onPressed: connect,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
primary: Colors.blue,
|
// primary: Colors.blue,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(30),
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
@ -211,7 +211,7 @@ class DisconnectScreen extends StatelessWidget {
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: disconnect,
|
onPressed: disconnect,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
primary: Colors.red,
|
// primary: Colors.red,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(30),
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
@ -58,7 +59,6 @@ class ChatwootWidgetState extends State<ChatwootWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeCookie(String value) async {
|
Future<void> storeCookie(String value) async {
|
||||||
await widget.secureStorage.delete(key: COOKIE_KEY);
|
await writeSecureStorage(widget.secureStorage, key: COOKIE_KEY, value: value);
|
||||||
await widget.secureStorage.write(key: COOKIE_KEY, value: value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: DeviceInfo.instance.isDesktop ? 18 : 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<CakeTextTheme>()!
|
.extension<CakeTextTheme>()!
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
import 'package:cake_wallet/core/seed_validator.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Annotation extends Comparable<Annotation> {
|
|
||||||
|
extension Compare<T> on Comparable<T> {
|
||||||
|
bool operator <=(T other) => compareTo(other) <= 0;
|
||||||
|
bool operator >=(T other) => compareTo(other) >= 0;
|
||||||
|
bool operator <(T other) => compareTo(other) < 0;
|
||||||
|
bool operator >(T other) => compareTo(other) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Annotation implements Comparable<Annotation> {
|
||||||
Annotation({required this.range, required this.style});
|
Annotation({required this.range, required this.style});
|
||||||
|
|
||||||
final TextRange range;
|
final TextRange range;
|
||||||
|
@ -12,7 +18,7 @@ class Annotation extends Comparable<Annotation> {
|
||||||
int compareTo(Annotation other) => range.start.compareTo(other.range.start);
|
int compareTo(Annotation other) => range.start.compareTo(other.range.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextAnnotation extends Comparable<TextAnnotation> {
|
class TextAnnotation implements Comparable<TextAnnotation> {
|
||||||
TextAnnotation({required this.text, required this.style});
|
TextAnnotation({required this.text, required this.style});
|
||||||
|
|
||||||
final TextStyle style;
|
final TextStyle style;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||||
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||||
import 'package:cake_wallet/entities/provider_types.dart';
|
import 'package:cake_wallet/entities/provider_types.dart';
|
||||||
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
|
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
|
||||||
|
@ -442,79 +443,83 @@ abstract class SettingsStoreBase with Store {
|
||||||
// secure storage keys:
|
// secure storage keys:
|
||||||
reaction(
|
reaction(
|
||||||
(_) => allowBiometricalAuthentication,
|
(_) => allowBiometricalAuthentication,
|
||||||
(bool biometricalAuthentication) => secureStorage.write(
|
(bool biometricalAuthentication) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.allowBiometricalAuthenticationKey,
|
key: SecureKey.allowBiometricalAuthenticationKey,
|
||||||
value: biometricalAuthentication.toString()));
|
value: biometricalAuthentication.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => selectedCake2FAPreset,
|
(_) => selectedCake2FAPreset,
|
||||||
(Cake2FAPresetsOptions selectedCake2FAPreset) => secureStorage.write(
|
(Cake2FAPresetsOptions selectedCake2FAPreset) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.selectedCake2FAPreset,
|
key: SecureKey.selectedCake2FAPreset,
|
||||||
value: selectedCake2FAPreset.serialize().toString()));
|
value: selectedCake2FAPreset.serialize().toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForAccessingWallet,
|
(_) => shouldRequireTOTP2FAForAccessingWallet,
|
||||||
(bool requireTOTP2FAForAccessingWallet) => secureStorage.write(
|
(bool requireTOTP2FAForAccessingWallet) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForAccessingWallet,
|
key: SecureKey.shouldRequireTOTP2FAForAccessingWallet,
|
||||||
value: requireTOTP2FAForAccessingWallet.toString()));
|
value: requireTOTP2FAForAccessingWallet.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForSendsToContact,
|
(_) => shouldRequireTOTP2FAForSendsToContact,
|
||||||
(bool requireTOTP2FAForSendsToContact) => secureStorage.write(
|
(bool requireTOTP2FAForSendsToContact) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForSendsToContact,
|
key: SecureKey.shouldRequireTOTP2FAForSendsToContact,
|
||||||
value: requireTOTP2FAForSendsToContact.toString()));
|
value: requireTOTP2FAForSendsToContact.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForSendsToNonContact,
|
(_) => shouldRequireTOTP2FAForSendsToNonContact,
|
||||||
(bool requireTOTP2FAForSendsToNonContact) => secureStorage.write(
|
(bool requireTOTP2FAForSendsToNonContact) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact,
|
key: SecureKey.shouldRequireTOTP2FAForSendsToNonContact,
|
||||||
value: requireTOTP2FAForSendsToNonContact.toString()));
|
value: requireTOTP2FAForSendsToNonContact.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForSendsToInternalWallets,
|
(_) => shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||||
(bool requireTOTP2FAForSendsToInternalWallets) => secureStorage.write(
|
(bool requireTOTP2FAForSendsToInternalWallets) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
key: SecureKey.shouldRequireTOTP2FAForSendsToInternalWallets,
|
||||||
value: requireTOTP2FAForSendsToInternalWallets.toString()));
|
value: requireTOTP2FAForSendsToInternalWallets.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForExchangesToInternalWallets,
|
(_) => shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||||
(bool requireTOTP2FAForExchangesToInternalWallets) => secureStorage.write(
|
(bool requireTOTP2FAForExchangesToInternalWallets) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
key: SecureKey.shouldRequireTOTP2FAForExchangesToInternalWallets,
|
||||||
value: requireTOTP2FAForExchangesToInternalWallets.toString()));
|
value: requireTOTP2FAForExchangesToInternalWallets.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForExchangesToExternalWallets,
|
(_) => shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||||
(bool requireTOTP2FAForExchangesToExternalWallets) => secureStorage.write(
|
(bool requireTOTP2FAForExchangesToExternalWallets) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
key: SecureKey.shouldRequireTOTP2FAForExchangesToExternalWallets,
|
||||||
value: requireTOTP2FAForExchangesToExternalWallets.toString()));
|
value: requireTOTP2FAForExchangesToExternalWallets.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForAddingContacts,
|
(_) => shouldRequireTOTP2FAForAddingContacts,
|
||||||
(bool requireTOTP2FAForAddingContacts) => secureStorage.write(
|
(bool requireTOTP2FAForAddingContacts) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForAddingContacts,
|
key: SecureKey.shouldRequireTOTP2FAForAddingContacts,
|
||||||
value: requireTOTP2FAForAddingContacts.toString()));
|
value: requireTOTP2FAForAddingContacts.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForCreatingNewWallets,
|
(_) => shouldRequireTOTP2FAForCreatingNewWallets,
|
||||||
(bool requireTOTP2FAForCreatingNewWallets) => secureStorage.write(
|
(bool requireTOTP2FAForCreatingNewWallets) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
key: SecureKey.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||||
value: requireTOTP2FAForCreatingNewWallets.toString()));
|
value: requireTOTP2FAForCreatingNewWallets.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
(_) => shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||||
(bool requireTOTP2FAForAllSecurityAndBackupSettings) => secureStorage.write(
|
(bool requireTOTP2FAForAllSecurityAndBackupSettings) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
key: SecureKey.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||||
value: requireTOTP2FAForAllSecurityAndBackupSettings.toString()));
|
value: requireTOTP2FAForAllSecurityAndBackupSettings.toString()));
|
||||||
|
|
||||||
reaction((_) => useTOTP2FA,
|
reaction(
|
||||||
(bool use) => secureStorage.write(key: SecureKey.useTOTP2FA, value: use.toString()));
|
(_) => useTOTP2FA,
|
||||||
|
(bool use) =>
|
||||||
|
writeSecureStorage(secureStorage, key: SecureKey.useTOTP2FA, value: use.toString()));
|
||||||
|
|
||||||
reaction((_) => totpSecretKey,
|
reaction(
|
||||||
(String totpKey) => secureStorage.write(key: SecureKey.totpSecretKey, value: totpKey));
|
(_) => totpSecretKey,
|
||||||
|
(String totpKey) =>
|
||||||
|
writeSecureStorage(secureStorage, key: SecureKey.totpSecretKey, value: totpKey));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
(_) => pinTimeOutDuration,
|
(_) => pinTimeOutDuration,
|
||||||
(PinCodeRequiredDuration pinCodeInterval) => secureStorage.write(
|
(PinCodeRequiredDuration pinCodeInterval) => writeSecureStorage(secureStorage,
|
||||||
key: SecureKey.pinTimeOutDuration, value: pinCodeInterval.value.toString()));
|
key: SecureKey.pinTimeOutDuration, value: pinCodeInterval.value.toString()));
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
|
|
|
@ -1,19 +1,29 @@
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/nano/nano.dart';
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
|
|
||||||
class PaymentRequest {
|
class PaymentRequest {
|
||||||
PaymentRequest(this.address, this.amount, this.note, this.scheme);
|
PaymentRequest(this.address, this.amount, this.note, this.scheme, {this.callbackUrl, this.callbackMessage});
|
||||||
|
|
||||||
factory PaymentRequest.fromUri(Uri? uri) {
|
factory PaymentRequest.fromUri(Uri? uri) {
|
||||||
var address = "";
|
var address = "";
|
||||||
var amount = "";
|
var amount = "";
|
||||||
var note = "";
|
var note = "";
|
||||||
var scheme = "";
|
var scheme = "";
|
||||||
|
String? callbackUrl;
|
||||||
|
String? callbackMessage;
|
||||||
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
address = uri.path;
|
address = uri.queryParameters['address'] ?? uri.path;
|
||||||
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
|
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
|
||||||
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
|
note = uri.queryParameters['tx_description'] ?? uri.queryParameters['message'] ?? "";
|
||||||
scheme = uri.scheme;
|
scheme = uri.scheme;
|
||||||
|
callbackUrl = uri.queryParameters['callback'];
|
||||||
|
callbackMessage = uri.queryParameters['callbackMessage'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scheme == "nano-gpt") {
|
||||||
|
// treat as nano so filling out the address works:
|
||||||
|
scheme = "nano";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nano != null) {
|
if (nano != null) {
|
||||||
|
@ -26,11 +36,20 @@ class PaymentRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PaymentRequest(address, amount, note, scheme);
|
return PaymentRequest(
|
||||||
|
address,
|
||||||
|
amount,
|
||||||
|
note,
|
||||||
|
scheme,
|
||||||
|
callbackUrl: callbackUrl,
|
||||||
|
callbackMessage: callbackMessage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
final String amount;
|
final String amount;
|
||||||
final String note;
|
final String note;
|
||||||
final String scheme;
|
final String scheme;
|
||||||
|
final String? callbackUrl;
|
||||||
|
final String? callbackMessage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,14 +106,10 @@ abstract class AuthViewModelBase with Store {
|
||||||
@action
|
@action
|
||||||
Future<void> biometricAuth() async {
|
Future<void> biometricAuth() async {
|
||||||
try {
|
try {
|
||||||
final canBiometricAuth = await _biometricAuth.canCheckBiometrics();
|
if (await _biometricAuth.canCheckBiometrics() && await _biometricAuth.isAuthenticated()) {
|
||||||
|
state = ExecutedSuccessfullyState();
|
||||||
if (canBiometricAuth) {
|
} else {
|
||||||
final isAuthenticated = await _biometricAuth.isAuthenticated();
|
throw Exception('Biometric authentication failed');
|
||||||
|
|
||||||
if (isAuthenticated) {
|
|
||||||
state = ExecutedSuccessfullyState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
state = FailureState(e.toString());
|
state = FailureState(e.toString());
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/core/secure_storage.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||||
|
@ -37,8 +38,7 @@ abstract class EditBackupPasswordViewModelBase with Store {
|
||||||
@action
|
@action
|
||||||
Future<void> save() async {
|
Future<void> save() async {
|
||||||
final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
final key = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
|
||||||
await secureStorage.delete(key: key);
|
await writeSecureStorage(secureStorage, key: key, value: backupPassword);
|
||||||
await secureStorage.write(key: key, value: backupPassword);
|
|
||||||
secretStore.write(key: key, value: backupPassword);
|
secretStore.write(key: key, value: backupPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
118
lib/view_model/link_view_model.dart
Normal file
118
lib/view_model/link_view_model.dart
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
|
import 'package:cake_wallet/store/authentication_store.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
part 'link_view_model.g.dart';
|
||||||
|
|
||||||
|
class LinkViewModel = LinkViewModelBase with _$LinkViewModel;
|
||||||
|
|
||||||
|
abstract class LinkViewModelBase with Store {
|
||||||
|
LinkViewModelBase({
|
||||||
|
required this.settingsStore,
|
||||||
|
required this.appStore,
|
||||||
|
required this.authenticationStore,
|
||||||
|
required this.navigatorKey,
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
final SettingsStore settingsStore;
|
||||||
|
final AppStore appStore;
|
||||||
|
final AuthenticationStore authenticationStore;
|
||||||
|
final GlobalKey<NavigatorState> navigatorKey;
|
||||||
|
Uri? currentLink;
|
||||||
|
|
||||||
|
bool get _isValidPaymentUri => currentLink?.path.isNotEmpty ?? false;
|
||||||
|
bool get isWalletConnectLink => currentLink?.authority == 'wc';
|
||||||
|
bool get isNanoGptLink => currentLink?.scheme == 'nano-gpt';
|
||||||
|
|
||||||
|
String? getRouteToGo() {
|
||||||
|
if (isWalletConnectLink) {
|
||||||
|
if (!isEVMCompatibleChain(appStore.wallet!.type)) {
|
||||||
|
_errorToast(S.current.switchToEVMCompatibleWallet);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Routes.walletConnectConnectionsListing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authenticationStore.state == AuthenticationState.uninitialized) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNanoGptLink) {
|
||||||
|
switch (currentLink?.authority ?? '') {
|
||||||
|
case "exchange":
|
||||||
|
return Routes.exchange;
|
||||||
|
case "send":
|
||||||
|
return Routes.send;
|
||||||
|
case "buy":
|
||||||
|
return Routes.buySellPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isValidPaymentUri) {
|
||||||
|
return Routes.send;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic getRouteArgs() {
|
||||||
|
if (isWalletConnectLink) {
|
||||||
|
return currentLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNanoGptLink) {
|
||||||
|
switch (currentLink?.authority ?? '') {
|
||||||
|
case "exchange":
|
||||||
|
case "send":
|
||||||
|
return PaymentRequest.fromUri(currentLink);
|
||||||
|
case "buy":
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isValidPaymentUri) {
|
||||||
|
return PaymentRequest.fromUri(currentLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _errorToast(String message, {double fontSize = 16}) async {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: message,
|
||||||
|
toastLength: Toast.LENGTH_LONG,
|
||||||
|
gravity: ToastGravity.SNACKBAR,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: fontSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleLink() async {
|
||||||
|
String? route = getRouteToGo();
|
||||||
|
dynamic args = getRouteArgs();
|
||||||
|
if (route != null) {
|
||||||
|
if (appStore.wallet == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNanoGptLink) {
|
||||||
|
if (route == Routes.buySellPage || route == Routes.exchange) {
|
||||||
|
await _errorToast(S.current.nano_gpt_thanks_message, fontSize: 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentLink = null;
|
||||||
|
navigatorKey.currentState?.pushNamed(
|
||||||
|
route,
|
||||||
|
arguments: args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -296,8 +296,8 @@ abstract class OutputBase with Store {
|
||||||
|
|
||||||
Future<void> fetchParsedAddress(BuildContext context) async {
|
Future<void> fetchParsedAddress(BuildContext context) async {
|
||||||
final domain = address;
|
final domain = address;
|
||||||
final ticker = cryptoCurrencyHandler().title.toLowerCase();
|
final currency = cryptoCurrencyHandler();
|
||||||
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
|
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
|
||||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||||
note = parsedAddress.description;
|
note = parsedAddress.description;
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
wallet.type != WalletType.banano &&
|
wallet.type != WalletType.banano &&
|
||||||
wallet.type != WalletType.solana &&
|
wallet.type != WalletType.solana &&
|
||||||
wallet.type != WalletType.tron;
|
wallet.type != WalletType.tron;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
CryptoCurrency selectedCryptoCurrency;
|
CryptoCurrency selectedCryptoCurrency;
|
||||||
|
|
||||||
|
@ -363,7 +363,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is LedgerException) {
|
if (e is LedgerException) {
|
||||||
final errorCode = e.errorCode.toRadixString(16);
|
final errorCode = e.errorCode.toRadixString(16);
|
||||||
final fallbackMsg = e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
|
final fallbackMsg =
|
||||||
|
e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
|
||||||
final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
|
final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
|
||||||
|
|
||||||
state = FailureState(errorMsg);
|
state = FailureState(errorMsg);
|
||||||
|
@ -444,7 +445,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
Object _credentials() {
|
Object _credentials() {
|
||||||
final priority = _settingsStore.priority[wallet.type];
|
final priority = _settingsStore.priority[wallet.type];
|
||||||
|
|
||||||
if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.banano && wallet.type != WalletType.solana &&
|
if (priority == null &&
|
||||||
|
wallet.type != WalletType.nano &&
|
||||||
|
wallet.type != WalletType.banano &&
|
||||||
|
wallet.type != WalletType.solana &&
|
||||||
wallet.type != WalletType.tron) {
|
wallet.type != WalletType.tron) {
|
||||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||||
}
|
}
|
||||||
|
@ -570,6 +574,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (walletType == WalletType.tron) {
|
||||||
|
if (errorMessage.contains('balance is not sufficient')) {
|
||||||
|
return S.current.do_not_have_enough_gas_asset(currency.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage.contains('Transaction expired')) {
|
||||||
|
return 'An error occurred while processing the transaction. Kindly retry the transaction';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (walletType == WalletType.bitcoin ||
|
if (walletType == WalletType.bitcoin ||
|
||||||
walletType == WalletType.litecoin ||
|
walletType == WalletType.litecoin ||
|
||||||
walletType == WalletType.bitcoinCash) {
|
walletType == WalletType.bitcoinCash) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import cw_monero
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import devicelocale
|
import devicelocale
|
||||||
import flutter_inappwebview_macos
|
import flutter_inappwebview_macos
|
||||||
|
import flutter_local_authentication
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import in_app_review
|
import in_app_review
|
||||||
import package_info
|
import package_info
|
||||||
|
@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
||||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
|
FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
|
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
|
||||||
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))
|
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))
|
||||||
|
|
|
@ -26,6 +26,8 @@ PODS:
|
||||||
- flutter_inappwebview_macos (0.0.1):
|
- flutter_inappwebview_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- OrderedSet (~> 5.0)
|
- OrderedSet (~> 5.0)
|
||||||
|
- flutter_local_authentication (1.2.0):
|
||||||
|
- FlutterMacOS
|
||||||
- flutter_secure_storage_macos (6.1.1):
|
- flutter_secure_storage_macos (6.1.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
|
@ -56,6 +58,7 @@ DEPENDENCIES:
|
||||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||||
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
|
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
|
||||||
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
|
||||||
|
- flutter_local_authentication (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos`)
|
||||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
||||||
|
@ -83,6 +86,8 @@ EXTERNAL SOURCES:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
|
||||||
flutter_inappwebview_macos:
|
flutter_inappwebview_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
|
||||||
|
flutter_local_authentication:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos
|
||||||
flutter_secure_storage_macos:
|
flutter_secure_storage_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
|
||||||
FlutterMacOS:
|
FlutterMacOS:
|
||||||
|
@ -110,6 +115,7 @@ SPEC CHECKSUMS:
|
||||||
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||||
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
|
||||||
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
|
||||||
|
flutter_local_authentication: 85674893931e1c9cfa7c9e4f5973cb8c56b018b0
|
||||||
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0
|
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0
|
||||||
|
|
|
@ -24,7 +24,21 @@ class AppDelegate: FlutterAppDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
result(secRandom(count: count))
|
result(secRandom(count: count))
|
||||||
|
case "setMinWindowSize":
|
||||||
|
guard let self = self else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let arguments = call.arguments as? [String: Any],
|
||||||
|
let width = arguments["width"] as? Double,
|
||||||
|
let height = arguments["height"] as? Double {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.mainFlutterWindow?.minSize = CGSize(width: width, height: height)
|
||||||
|
}
|
||||||
|
result(true)
|
||||||
|
} else {
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,17 @@ dependencies:
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
mobx: ^2.1.4
|
mobx: ^2.1.4
|
||||||
flutter_mobx: ^2.0.6+5
|
flutter_mobx: ^2.0.6+5
|
||||||
flutter_slidable: ^2.0.0
|
flutter_slidable: ^3.0.1
|
||||||
share_plus: ^4.0.10
|
share_plus: ^4.0.10
|
||||||
# date_range_picker: ^1.0.6
|
# date_range_picker: ^1.0.6
|
||||||
#https://api.flutter.dev/flutter/material/showDateRangePicker.html
|
#https://api.flutter.dev/flutter/material/showDateRangePicker.html
|
||||||
dio: ^4.0.6
|
dio: ^4.0.6
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
hive_flutter: ^1.1.0
|
hive_flutter: ^1.1.0
|
||||||
local_auth: ^2.1.0
|
|
||||||
local_auth_android: 1.0.21
|
local_auth_android: 1.0.21
|
||||||
|
flutter_local_authentication:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/flutter_local_authentication
|
||||||
package_info: ^2.0.0
|
package_info: ^2.0.0
|
||||||
#package_info_plus: ^1.4.2
|
#package_info_plus: ^1.4.2
|
||||||
devicelocale:
|
devicelocale:
|
||||||
|
|
|
@ -4,4 +4,4 @@ version: 0.0.0
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.5 <3.0.0"
|
sdk: ">=3.1.0 <4.0.0"
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "المزيد من الخيارات",
|
"more_options": "المزيد من الخيارات",
|
||||||
"name": "ﻢﺳﺍ",
|
"name": "ﻢﺳﺍ",
|
||||||
|
"nano_gpt_thanks_message": "شكرا لاستخدام nanogpt! تذكر أن تعود إلى المتصفح بعد اكتمال معاملتك!",
|
||||||
|
"nanogpt_subtitle": "جميع النماذج الأحدث (GPT-4 ، Claude). \\ nno اشتراك ، ادفع مع Crypto.",
|
||||||
"nano_current_rep": "الممثل الحالي",
|
"nano_current_rep": "الممثل الحالي",
|
||||||
"nano_pick_new_rep": "اختر ممثلًا جديدًا",
|
"nano_pick_new_rep": "اختر ممثلًا جديدًا",
|
||||||
"narrow": "ضيق",
|
"narrow": "ضيق",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Още настройки",
|
"more_options": "Още настройки",
|
||||||
"name": "Име",
|
"name": "Име",
|
||||||
|
"nano_gpt_thanks_message": "Благодаря, че използвахте Nanogpt! Не забравяйте да се върнете обратно към браузъра, след като транзакцията ви приключи!",
|
||||||
|
"nanogpt_subtitle": "Всички най-нови модели (GPT-4, Claude). \\ Nno абонамент, платете с Crypto.",
|
||||||
"nano_current_rep": "Настоящ представител",
|
"nano_current_rep": "Настоящ представител",
|
||||||
"nano_pick_new_rep": "Изберете нов представител",
|
"nano_pick_new_rep": "Изберете нов представител",
|
||||||
"narrow": "Тесен",
|
"narrow": "Тесен",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Více možností",
|
"more_options": "Více možností",
|
||||||
"name": "název",
|
"name": "název",
|
||||||
|
"nano_gpt_thanks_message": "Děkujeme za používání Nanogpt! Nezapomeňte se po dokončení transakce vydat zpět do prohlížeče!",
|
||||||
|
"nanogpt_subtitle": "Všechny nejnovější modely (GPT-4, Claude). \\ Nno předplatné, plaťte krypto.",
|
||||||
"nano_current_rep": "Současný zástupce",
|
"nano_current_rep": "Současný zástupce",
|
||||||
"nano_pick_new_rep": "Vyberte nového zástupce",
|
"nano_pick_new_rep": "Vyberte nového zástupce",
|
||||||
"narrow": "Úzký",
|
"narrow": "Úzký",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein",
|
"moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein",
|
||||||
"more_options": "Weitere Optionen",
|
"more_options": "Weitere Optionen",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
"nano_gpt_thanks_message": "Danke, dass du Nanogpt benutzt hast! Denken Sie daran, nach Abschluss Ihrer Transaktion zurück zum Browser zu gehen!",
|
||||||
|
"nanogpt_subtitle": "Alle neuesten Modelle (GPT-4, Claude).",
|
||||||
"nano_current_rep": "Aktueller Vertreter",
|
"nano_current_rep": "Aktueller Vertreter",
|
||||||
"nano_pick_new_rep": "Wählen Sie einen neuen Vertreter aus",
|
"nano_pick_new_rep": "Wählen Sie einen neuen Vertreter aus",
|
||||||
"narrow": "Eng",
|
"narrow": "Eng",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "More Options",
|
"more_options": "More Options",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
"nano_gpt_thanks_message": "Thanks for using NanoGPT! Remember to head back to the browser after your transaction completes!",
|
||||||
|
"nanogpt_subtitle": "All the newest models (GPT-4, Claude).\\nNo subscription, pay with crypto.",
|
||||||
"nano_current_rep": "Current Representative",
|
"nano_current_rep": "Current Representative",
|
||||||
"nano_pick_new_rep": "Pick a new representative",
|
"nano_pick_new_rep": "Pick a new representative",
|
||||||
"narrow": "Narrow",
|
"narrow": "Narrow",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Más Opciones",
|
"more_options": "Más Opciones",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
|
"nano_gpt_thanks_message": "¡Gracias por usar nanogpt! ¡Recuerde regresar al navegador después de que se complete su transacción!",
|
||||||
|
"nanogpt_subtitle": "Todos los modelos más nuevos (GPT-4, Claude). \\ Nno suscripción, pague con cripto.",
|
||||||
"nano_current_rep": "Representante actual",
|
"nano_current_rep": "Representante actual",
|
||||||
"nano_pick_new_rep": "Elija un nuevo representante",
|
"nano_pick_new_rep": "Elija un nuevo representante",
|
||||||
"narrow": "Angosto",
|
"narrow": "Angosto",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Plus d'options",
|
"more_options": "Plus d'options",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
|
"nano_gpt_thanks_message": "Merci d'avoir utilisé Nanogpt! N'oubliez pas de retourner au navigateur une fois votre transaction terminée!",
|
||||||
|
"nanogpt_subtitle": "Tous les modèles les plus récents (GPT-4, Claude). \\ NNO abonnement, payez avec crypto.",
|
||||||
"nano_current_rep": "Représentant actuel",
|
"nano_current_rep": "Représentant actuel",
|
||||||
"nano_pick_new_rep": "Choisissez un nouveau représentant",
|
"nano_pick_new_rep": "Choisissez un nouveau représentant",
|
||||||
"narrow": "Étroit",
|
"narrow": "Étroit",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Ƙarin Zaɓuɓɓuka",
|
"more_options": "Ƙarin Zaɓuɓɓuka",
|
||||||
"name": "Suna",
|
"name": "Suna",
|
||||||
|
"nano_gpt_thanks_message": "Na gode da amfani da Nanogpt! Ka tuna da komawa zuwa mai bincike bayan ma'amalar ka ta cika!",
|
||||||
|
"nanogpt_subtitle": "Duk sabbin samfuran (GPT-4, CLODE). \\ NNO biyan kuɗi, biya tare da crypto.",
|
||||||
"nano_current_rep": "Wakilin Yanzu",
|
"nano_current_rep": "Wakilin Yanzu",
|
||||||
"nano_pick_new_rep": "Dauki sabon wakili",
|
"nano_pick_new_rep": "Dauki sabon wakili",
|
||||||
"narrow": "kunkuntar",
|
"narrow": "kunkuntar",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "और विकल्प",
|
"more_options": "और विकल्प",
|
||||||
"name": "नाम",
|
"name": "नाम",
|
||||||
|
"nano_gpt_thanks_message": "Nanogpt का उपयोग करने के लिए धन्यवाद! अपने लेन -देन के पूरा होने के बाद ब्राउज़र पर वापस जाना याद रखें!",
|
||||||
|
"nanogpt_subtitle": "सभी नवीनतम मॉडल (GPT-4, क्लाउड)। \\ nno सदस्यता, क्रिप्टो के साथ भुगतान करें।",
|
||||||
"nano_current_rep": "वर्तमान प्रतिनिधि",
|
"nano_current_rep": "वर्तमान प्रतिनिधि",
|
||||||
"nano_pick_new_rep": "एक नया प्रतिनिधि चुनें",
|
"nano_pick_new_rep": "एक नया प्रतिनिधि चुनें",
|
||||||
"narrow": "सँकरा",
|
"narrow": "सँकरा",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Više opcija",
|
"more_options": "Više opcija",
|
||||||
"name": "Ime",
|
"name": "Ime",
|
||||||
|
"nano_gpt_thanks_message": "Hvala što ste koristili nanogpt! Ne zaboravite da se vratite u preglednik nakon što vam se transakcija završi!",
|
||||||
|
"nanogpt_subtitle": "Svi najnoviji modeli (GPT-4, Claude). \\ NNO pretplata, plaćajte kripto.",
|
||||||
"nano_current_rep": "Trenutni predstavnik",
|
"nano_current_rep": "Trenutni predstavnik",
|
||||||
"nano_pick_new_rep": "Odaberite novog predstavnika",
|
"nano_pick_new_rep": "Odaberite novog predstavnika",
|
||||||
"narrow": "Usko",
|
"narrow": "Usko",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Opsi Lainnya",
|
"more_options": "Opsi Lainnya",
|
||||||
"name": "Nama",
|
"name": "Nama",
|
||||||
|
"nano_gpt_thanks_message": "Terima kasih telah menggunakan Nanogpt! Ingatlah untuk kembali ke browser setelah transaksi Anda selesai!",
|
||||||
|
"nanogpt_subtitle": "Semua model terbaru (GPT-4, Claude). \\ Nno langganan, bayar dengan crypto.",
|
||||||
"nano_current_rep": "Perwakilan saat ini",
|
"nano_current_rep": "Perwakilan saat ini",
|
||||||
"nano_pick_new_rep": "Pilih perwakilan baru",
|
"nano_pick_new_rep": "Pilih perwakilan baru",
|
||||||
"narrow": "Sempit",
|
"narrow": "Sempit",
|
||||||
|
|
|
@ -369,6 +369,8 @@
|
||||||
"moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Altre opzioni",
|
"more_options": "Altre opzioni",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
|
"nano_gpt_thanks_message": "Grazie per aver usato il nanogpt! Ricorda di tornare al browser dopo il completamento della transazione!",
|
||||||
|
"nanogpt_subtitle": "Tutti i modelli più recenti (GPT-4, Claude). Abbonamento nno, paga con cripto.",
|
||||||
"nano_current_rep": "Rappresentante attuale",
|
"nano_current_rep": "Rappresentante attuale",
|
||||||
"nano_pick_new_rep": "Scegli un nuovo rappresentante",
|
"nano_pick_new_rep": "Scegli un nuovo rappresentante",
|
||||||
"narrow": "Stretto",
|
"narrow": "Stretto",
|
||||||
|
|
|
@ -369,6 +369,8 @@
|
||||||
"moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "その他のオプション",
|
"more_options": "その他のオプション",
|
||||||
"name": "名前",
|
"name": "名前",
|
||||||
|
"nano_gpt_thanks_message": "NanoGptを使用してくれてありがとう!トランザクションが完了したら、ブラウザに戻ることを忘れないでください!",
|
||||||
|
"nanogpt_subtitle": "すべての最新モデル(GPT-4、Claude)。\\ nnoサブスクリプション、暗号で支払います。",
|
||||||
"nano_current_rep": "現在の代表",
|
"nano_current_rep": "現在の代表",
|
||||||
"nano_pick_new_rep": "新しい代表者を選びます",
|
"nano_pick_new_rep": "新しい代表者を選びます",
|
||||||
"narrow": "狭い",
|
"narrow": "狭い",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "추가 옵션",
|
"more_options": "추가 옵션",
|
||||||
"name": "이름",
|
"name": "이름",
|
||||||
|
"nano_gpt_thanks_message": "Nanogpt를 사용해 주셔서 감사합니다! 거래가 완료된 후 브라우저로 돌아가는 것을 잊지 마십시오!",
|
||||||
|
"nanogpt_subtitle": "모든 최신 모델 (GPT-4, Claude). \\ nno 구독, Crypto로 지불하십시오.",
|
||||||
"nano_current_rep": "현재 대표",
|
"nano_current_rep": "현재 대표",
|
||||||
"nano_pick_new_rep": "새로운 담당자를 선택하십시오",
|
"nano_pick_new_rep": "새로운 담당자를 선택하십시오",
|
||||||
"narrow": "좁은",
|
"narrow": "좁은",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်",
|
"moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်",
|
||||||
"more_options": "နောက်ထပ် ရွေးချယ်စရာများ",
|
"more_options": "နောက်ထပ် ရွေးချယ်စရာများ",
|
||||||
"name": "နာမည်",
|
"name": "နာမည်",
|
||||||
|
"nano_gpt_thanks_message": "nanogpt ကိုသုံးပြီးကျေးဇူးတင်ပါတယ် သင်၏ငွေပေးငွေယူပြီးနောက် browser သို့ပြန်သွားရန်သတိရပါ။",
|
||||||
|
"nanogpt_subtitle": "အားလုံးနောက်ဆုံးပေါ်မော်ဒယ်များ (GPT-4, Claude) ။ \\ nno subscription, crypto နှင့်အတူပေးဆောင်။",
|
||||||
"nano_current_rep": "လက်ရှိကိုယ်စားလှယ်",
|
"nano_current_rep": "လက်ရှိကိုယ်စားလှယ်",
|
||||||
"nano_pick_new_rep": "အသစ်တစ်ခုကိုရွေးပါ",
|
"nano_pick_new_rep": "အသစ်တစ်ခုကိုရွေးပါ",
|
||||||
"narrow": "ကျဉ်းသော",
|
"narrow": "ကျဉ်းသော",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Meer opties",
|
"more_options": "Meer opties",
|
||||||
"name": "Naam",
|
"name": "Naam",
|
||||||
|
"nano_gpt_thanks_message": "Bedankt voor het gebruik van Nanogpt! Vergeet niet om terug te gaan naar de browser nadat uw transactie is voltooid!",
|
||||||
|
"nanogpt_subtitle": "Alle nieuwste modellen (GPT-4, Claude). \\ Nno-abonnement, betalen met crypto.",
|
||||||
"nano_current_rep": "Huidige vertegenwoordiger",
|
"nano_current_rep": "Huidige vertegenwoordiger",
|
||||||
"nano_pick_new_rep": "Kies een nieuwe vertegenwoordiger",
|
"nano_pick_new_rep": "Kies een nieuwe vertegenwoordiger",
|
||||||
"narrow": "Smal",
|
"narrow": "Smal",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Więcej opcji",
|
"more_options": "Więcej opcji",
|
||||||
"name": "Nazwa",
|
"name": "Nazwa",
|
||||||
|
"nano_gpt_thanks_message": "Dzięki za użycie Nanogpt! Pamiętaj, aby wrócić do przeglądarki po zakończeniu transakcji!",
|
||||||
|
"nanogpt_subtitle": "Wszystkie najnowsze modele (GPT-4, Claude). \\ Nno subskrypcja, płacą za pomocą kryptografii.",
|
||||||
"nano_current_rep": "Obecny przedstawiciel",
|
"nano_current_rep": "Obecny przedstawiciel",
|
||||||
"nano_pick_new_rep": "Wybierz nowego przedstawiciela",
|
"nano_pick_new_rep": "Wybierz nowego przedstawiciela",
|
||||||
"narrow": "Wąski",
|
"narrow": "Wąski",
|
||||||
|
|
|
@ -369,6 +369,8 @@
|
||||||
"moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Mais opções",
|
"more_options": "Mais opções",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
|
"nano_gpt_thanks_message": "Obrigado por usar o Nanogpt! Lembre -se de voltar para o navegador após a conclusão da transação!",
|
||||||
|
"nanogpt_subtitle": "Todos os modelos mais recentes (GPT-4, Claude). \\ Nno assinatura, pagam com criptografia.",
|
||||||
"nano_current_rep": "Representante atual",
|
"nano_current_rep": "Representante atual",
|
||||||
"nano_pick_new_rep": "Escolha um novo representante",
|
"nano_pick_new_rep": "Escolha um novo representante",
|
||||||
"narrow": "Estreito",
|
"narrow": "Estreito",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Дополнительные параметры",
|
"more_options": "Дополнительные параметры",
|
||||||
"name": "Имя",
|
"name": "Имя",
|
||||||
|
"nano_gpt_thanks_message": "Спасибо за использование Nanogpt! Не забудьте вернуться в браузер после завершения транзакции!",
|
||||||
|
"nanogpt_subtitle": "Все новейшие модели (GPT-4, Claude). \\ Nno Подписка, платите с крипто.",
|
||||||
"nano_current_rep": "Нынешний представитель",
|
"nano_current_rep": "Нынешний представитель",
|
||||||
"nano_pick_new_rep": "Выберите нового представителя",
|
"nano_pick_new_rep": "Выберите нового представителя",
|
||||||
"narrow": "Узкий",
|
"narrow": "Узкий",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "ตัวเลือกเพิ่มเติม",
|
"more_options": "ตัวเลือกเพิ่มเติม",
|
||||||
"name": "ชื่อ",
|
"name": "ชื่อ",
|
||||||
|
"nano_gpt_thanks_message": "ขอบคุณที่ใช้ Nanogpt! อย่าลืมกลับไปที่เบราว์เซอร์หลังจากการทำธุรกรรมของคุณเสร็จสิ้น!",
|
||||||
|
"nanogpt_subtitle": "รุ่นใหม่ล่าสุดทั้งหมด (GPT-4, Claude). การสมัครสมาชิก \\ nno, จ่ายด้วย crypto",
|
||||||
"nano_current_rep": "ตัวแทนปัจจุบัน",
|
"nano_current_rep": "ตัวแทนปัจจุบัน",
|
||||||
"nano_pick_new_rep": "เลือกตัวแทนใหม่",
|
"nano_pick_new_rep": "เลือกตัวแทนใหม่",
|
||||||
"narrow": "แคบ",
|
"narrow": "แคบ",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Ang halaga ng halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Ang halaga ng halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Higit pang mga pagpipilian",
|
"more_options": "Higit pang mga pagpipilian",
|
||||||
"name": "Pangalan",
|
"name": "Pangalan",
|
||||||
|
"nano_gpt_thanks_message": "Salamat sa paggamit ng nanogpt! Tandaan na bumalik sa browser matapos makumpleto ang iyong transaksyon!",
|
||||||
|
"nanogpt_subtitle": "Ang lahat ng mga pinakabagong modelo (GPT-4, Claude). \\ Nno subscription, magbayad gamit ang crypto.",
|
||||||
"nano_current_rep": "Kasalukuyang kinatawan",
|
"nano_current_rep": "Kasalukuyang kinatawan",
|
||||||
"nano_pick_new_rep": "Pumili ng isang bagong kinatawan",
|
"nano_pick_new_rep": "Pumili ng isang bagong kinatawan",
|
||||||
"narrow": "Makitid",
|
"narrow": "Makitid",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır",
|
"moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır",
|
||||||
"more_options": "Daha Fazla Seçenek",
|
"more_options": "Daha Fazla Seçenek",
|
||||||
"name": "İsim",
|
"name": "İsim",
|
||||||
|
"nano_gpt_thanks_message": "Nanogpt kullandığınız için teşekkürler! İşleminiz tamamlandıktan sonra tarayıcıya geri dönmeyi unutmayın!",
|
||||||
|
"nanogpt_subtitle": "En yeni modeller (GPT-4, Claude). \\ Nno aboneliği, kripto ile ödeme yapın.",
|
||||||
"nano_current_rep": "Mevcut temsilci",
|
"nano_current_rep": "Mevcut temsilci",
|
||||||
"nano_pick_new_rep": "Yeni bir temsilci seçin",
|
"nano_pick_new_rep": "Yeni bir temsilci seçin",
|
||||||
"narrow": "Dar",
|
"narrow": "Dar",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Більше параметрів",
|
"more_options": "Більше параметрів",
|
||||||
"name": "Ім'я",
|
"name": "Ім'я",
|
||||||
|
"nano_gpt_thanks_message": "Дякуємо за використання наногпта! Не забудьте повернутися до браузера після завершення транзакції!",
|
||||||
|
"nanogpt_subtitle": "Усі найновіші моделі (GPT-4, Claude). \\ Nno підписка, оплата криптовалютою.",
|
||||||
"nano_current_rep": "Поточний представник",
|
"nano_current_rep": "Поточний представник",
|
||||||
"nano_pick_new_rep": "Виберіть нового представника",
|
"nano_pick_new_rep": "Виберіть нового представника",
|
||||||
"narrow": "вузькі",
|
"narrow": "вузькі",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔",
|
"moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔",
|
||||||
"more_options": "مزید زرائے",
|
"more_options": "مزید زرائے",
|
||||||
"name": "ﻡﺎﻧ",
|
"name": "ﻡﺎﻧ",
|
||||||
|
"nano_gpt_thanks_message": "نانوگپٹ استعمال کرنے کا شکریہ! اپنے لین دین کی تکمیل کے بعد براؤزر کی طرف واپس جانا یاد رکھیں!",
|
||||||
|
"nanogpt_subtitle": "تمام تازہ ترین ماڈل (GPT-4 ، کلاڈ)۔ n n no سبسکرپشن ، کریپٹو کے ساتھ ادائیگی کریں۔",
|
||||||
"nano_current_rep": "موجودہ نمائندہ",
|
"nano_current_rep": "موجودہ نمائندہ",
|
||||||
"nano_pick_new_rep": "ایک نیا نمائندہ منتخب کریں",
|
"nano_pick_new_rep": "ایک نیا نمائندہ منتخب کریں",
|
||||||
"narrow": "تنگ",
|
"narrow": "تنگ",
|
||||||
|
|
|
@ -369,6 +369,8 @@
|
||||||
"moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "Ìyàn àfikún",
|
"more_options": "Ìyàn àfikún",
|
||||||
"name": "Oruko",
|
"name": "Oruko",
|
||||||
|
"nano_gpt_thanks_message": "O ṣeun fun lilo Nonnogt! Ranti lati tẹle pada si ẹrọ lilọ kiri ayelujara lẹhin iṣowo rẹ pari!",
|
||||||
|
"nanogpt_subtitle": "Gbogbo awọn awoṣe tuntun (GPT-4, Claude). \\ Nno alabapin kan, sanwo pẹlu Crypto.",
|
||||||
"nano_current_rep": "Aṣoju lọwọlọwọ",
|
"nano_current_rep": "Aṣoju lọwọlọwọ",
|
||||||
"nano_pick_new_rep": "Mu aṣoju tuntun kan",
|
"nano_pick_new_rep": "Mu aṣoju tuntun kan",
|
||||||
"narrow": "Taara",
|
"narrow": "Taara",
|
||||||
|
|
|
@ -368,6 +368,8 @@
|
||||||
"moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}",
|
"moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}",
|
||||||
"more_options": "更多选项",
|
"more_options": "更多选项",
|
||||||
"name": "姓名",
|
"name": "姓名",
|
||||||
|
"nano_gpt_thanks_message": "感谢您使用Nanogpt!事务完成后,请记住回到浏览器!",
|
||||||
|
"nanogpt_subtitle": "所有最新型号(GPT-4,Claude)。\\ nno订阅,用加密货币付款。",
|
||||||
"nano_current_rep": "当前代表",
|
"nano_current_rep": "当前代表",
|
||||||
"nano_pick_new_rep": "选择新代表",
|
"nano_pick_new_rep": "选择新代表",
|
||||||
"narrow": "狭窄的",
|
"narrow": "狭窄的",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'utils/utils.dart';
|
||||||
const configPath = 'tool/.secrets-config.json';
|
const configPath = 'tool/.secrets-config.json';
|
||||||
const evmChainsConfigPath = 'tool/.evm-secrets-config.json';
|
const evmChainsConfigPath = 'tool/.evm-secrets-config.json';
|
||||||
const solanaConfigPath = 'tool/.solana-secrets-config.json';
|
const solanaConfigPath = 'tool/.solana-secrets-config.json';
|
||||||
|
const nanoConfigPath = 'tool/.nano-secrets-config.json';
|
||||||
const tronConfigPath = 'tool/.tron-secrets-config.json';
|
const tronConfigPath = 'tool/.tron-secrets-config.json';
|
||||||
|
|
||||||
Future<void> main(List<String> args) async => generateSecretsConfig(args);
|
Future<void> main(List<String> args) async => generateSecretsConfig(args);
|
||||||
|
@ -21,6 +22,7 @@ Future<void> generateSecretsConfig(List<String> args) async {
|
||||||
final configFile = File(configPath);
|
final configFile = File(configPath);
|
||||||
final evmChainsConfigFile = File(evmChainsConfigPath);
|
final evmChainsConfigFile = File(evmChainsConfigPath);
|
||||||
final solanaConfigFile = File(solanaConfigPath);
|
final solanaConfigFile = File(solanaConfigPath);
|
||||||
|
final nanoConfigFile = File(nanoConfigPath);
|
||||||
final tronConfigFile = File(tronConfigPath);
|
final tronConfigFile = File(tronConfigPath);
|
||||||
|
|
||||||
final secrets = <String, dynamic>{};
|
final secrets = <String, dynamic>{};
|
||||||
|
@ -42,45 +44,48 @@ Future<void> generateSecretsConfig(List<String> args) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// base:
|
||||||
SecretKey.base.forEach((sec) {
|
SecretKey.base.forEach((sec) {
|
||||||
if (secrets[sec.name] != null) {
|
if (secrets[sec.name] != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets[sec.name] = sec.generate();
|
secrets[sec.name] = sec.generate();
|
||||||
});
|
});
|
||||||
|
|
||||||
var secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
var secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
||||||
await configFile.writeAsString(secretsJson);
|
await configFile.writeAsString(secretsJson);
|
||||||
|
|
||||||
secrets.clear();
|
secrets.clear();
|
||||||
|
|
||||||
|
// evm chains:
|
||||||
SecretKey.evmChainsSecrets.forEach((sec) {
|
SecretKey.evmChainsSecrets.forEach((sec) {
|
||||||
if (secrets[sec.name] != null) {
|
if (secrets[sec.name] != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets[sec.name] = sec.generate();
|
secrets[sec.name] = sec.generate();
|
||||||
});
|
});
|
||||||
|
|
||||||
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
||||||
|
|
||||||
await evmChainsConfigFile.writeAsString(secretsJson);
|
await evmChainsConfigFile.writeAsString(secretsJson);
|
||||||
|
|
||||||
secrets.clear();
|
secrets.clear();
|
||||||
|
|
||||||
|
// solana:
|
||||||
SecretKey.solanaSecrets.forEach((sec) {
|
SecretKey.solanaSecrets.forEach((sec) {
|
||||||
if (secrets[sec.name] != null) {
|
if (secrets[sec.name] != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets[sec.name] = sec.generate();
|
secrets[sec.name] = sec.generate();
|
||||||
});
|
});
|
||||||
|
|
||||||
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
||||||
|
|
||||||
await solanaConfigFile.writeAsString(secretsJson);
|
await solanaConfigFile.writeAsString(secretsJson);
|
||||||
|
secrets.clear();
|
||||||
|
|
||||||
|
// nano:
|
||||||
|
SecretKey.nanoSecrets.forEach((sec) {
|
||||||
|
if (secrets[sec.name] != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
secrets[sec.name] = sec.generate();
|
||||||
|
});
|
||||||
|
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
||||||
|
await nanoConfigFile.writeAsString(secretsJson);
|
||||||
secrets.clear();
|
secrets.clear();
|
||||||
|
|
||||||
SecretKey.tronSecrets.forEach((sec) {
|
SecretKey.tronSecrets.forEach((sec) {
|
||||||
|
@ -90,8 +95,7 @@ Future<void> generateSecretsConfig(List<String> args) async {
|
||||||
|
|
||||||
secrets[sec.name] = sec.generate();
|
secrets[sec.name] = sec.generate();
|
||||||
});
|
});
|
||||||
|
|
||||||
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
secretsJson = JsonEncoder.withIndent(' ').convert(secrets);
|
||||||
|
|
||||||
await tronConfigFile.writeAsString(secretsJson);
|
await tronConfigFile.writeAsString(secretsJson);
|
||||||
|
secrets.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,10 @@ class SecretKey {
|
||||||
SecretKey('ankrApiKey', () => ''),
|
SecretKey('ankrApiKey', () => ''),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static final nanoSecrets = [
|
||||||
|
SecretKey('nano2ApiKey', () => ''),
|
||||||
|
];
|
||||||
|
|
||||||
static final tronSecrets = [
|
static final tronSecrets = [
|
||||||
SecretKey('tronGridApiKey', () => ''),
|
SecretKey('tronGridApiKey', () => ''),
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue