mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-02-02 03:06:29 +00:00
Merge branch 'staging' into docs
This commit is contained in:
commit
dc2a341e6b
52 changed files with 925 additions and 462 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit adc7bf50abe4bbe90d5050b82fb5751937cbae4e
|
Subproject commit 982f5ab19fe0dd3dd3f6be2c46f8dff13d49027c
|
|
@ -12,6 +12,7 @@ import '../../electrumx_rpc/electrumx_client.dart';
|
||||||
import '../../utilities/extensions/extensions.dart';
|
import '../../utilities/extensions/extensions.dart';
|
||||||
import '../../utilities/logger.dart';
|
import '../../utilities/logger.dart';
|
||||||
import '../../utilities/stack_file_system.dart';
|
import '../../utilities/stack_file_system.dart';
|
||||||
|
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
|
||||||
part 'firo_cache_coordinator.dart';
|
part 'firo_cache_coordinator.dart';
|
||||||
part 'firo_cache_reader.dart';
|
part 'firo_cache_reader.dart';
|
||||||
|
@ -31,29 +32,39 @@ void _debugLog(Object? object) {
|
||||||
abstract class _FiroCache {
|
abstract class _FiroCache {
|
||||||
static const int _setCacheVersion = 1;
|
static const int _setCacheVersion = 1;
|
||||||
static const int _tagsCacheVersion = 2;
|
static const int _tagsCacheVersion = 2;
|
||||||
static const String sparkSetCacheFileName =
|
|
||||||
"spark_set_v$_setCacheVersion.sqlite3";
|
|
||||||
static const String sparkUsedTagsCacheFileName =
|
|
||||||
"spark_tags_v$_tagsCacheVersion.sqlite3";
|
|
||||||
|
|
||||||
static Database? _setCacheDB;
|
static final networks = [
|
||||||
static Database? _usedTagsCacheDB;
|
CryptoCurrencyNetwork.main,
|
||||||
static Database get setCacheDB {
|
CryptoCurrencyNetwork.test,
|
||||||
if (_setCacheDB == null) {
|
];
|
||||||
|
|
||||||
|
static String sparkSetCacheFileName(CryptoCurrencyNetwork network) =>
|
||||||
|
network == CryptoCurrencyNetwork.main
|
||||||
|
? "spark_set_v$_setCacheVersion.sqlite3"
|
||||||
|
: "spark_set_v${_setCacheVersion}_${network.name}.sqlite3";
|
||||||
|
static String sparkUsedTagsCacheFileName(CryptoCurrencyNetwork network) =>
|
||||||
|
network == CryptoCurrencyNetwork.main
|
||||||
|
? "spark_tags_v$_tagsCacheVersion.sqlite3"
|
||||||
|
: "spark_tags_v${_tagsCacheVersion}_${network.name}.sqlite3";
|
||||||
|
|
||||||
|
static final Map<CryptoCurrencyNetwork, Database> _setCacheDB = {};
|
||||||
|
static final Map<CryptoCurrencyNetwork, Database> _usedTagsCacheDB = {};
|
||||||
|
static Database setCacheDB(CryptoCurrencyNetwork network) {
|
||||||
|
if (_setCacheDB[network] == null) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"FiroCache.init() must be called before accessing FiroCache.db!",
|
"FiroCache.init() must be called before accessing FiroCache.db!",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _setCacheDB!;
|
return _setCacheDB[network]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Database get usedTagsCacheDB {
|
static Database usedTagsCacheDB(CryptoCurrencyNetwork network) {
|
||||||
if (_usedTagsCacheDB == null) {
|
if (_usedTagsCacheDB[network] == null) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"FiroCache.init() must be called before accessing FiroCache.db!",
|
"FiroCache.init() must be called before accessing FiroCache.db!",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _usedTagsCacheDB!;
|
return _usedTagsCacheDB[network]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void>? _initFuture;
|
static Future<void>? _initFuture;
|
||||||
|
@ -63,30 +74,34 @@ abstract class _FiroCache {
|
||||||
final sqliteDir =
|
final sqliteDir =
|
||||||
await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||||
|
|
||||||
final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName");
|
for (final network in networks) {
|
||||||
final sparkUsedTagsCacheFile =
|
final sparkSetCacheFile =
|
||||||
File("${sqliteDir.path}/$sparkUsedTagsCacheFileName");
|
File("${sqliteDir.path}/${sparkSetCacheFileName(network)}");
|
||||||
|
|
||||||
if (!(await sparkSetCacheFile.exists())) {
|
final sparkUsedTagsCacheFile =
|
||||||
await _createSparkSetCacheDb(sparkSetCacheFile.path);
|
File("${sqliteDir.path}/${sparkUsedTagsCacheFileName(network)}");
|
||||||
}
|
|
||||||
if (!(await sparkUsedTagsCacheFile.exists())) {
|
|
||||||
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
_setCacheDB = sqlite3.open(
|
if (!(await sparkSetCacheFile.exists())) {
|
||||||
sparkSetCacheFile.path,
|
await _createSparkSetCacheDb(sparkSetCacheFile.path);
|
||||||
mode: OpenMode.readWrite,
|
}
|
||||||
);
|
if (!(await sparkUsedTagsCacheFile.exists())) {
|
||||||
_usedTagsCacheDB = sqlite3.open(
|
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
|
||||||
sparkUsedTagsCacheFile.path,
|
}
|
||||||
mode: OpenMode.readWrite,
|
|
||||||
);
|
_setCacheDB[network] = sqlite3.open(
|
||||||
|
sparkSetCacheFile.path,
|
||||||
|
mode: OpenMode.readWrite,
|
||||||
|
);
|
||||||
|
_usedTagsCacheDB[network] = sqlite3.open(
|
||||||
|
sparkUsedTagsCacheFile.path,
|
||||||
|
mode: OpenMode.readWrite,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> _deleteAllCache() async {
|
static Future<void> _deleteAllCache(CryptoCurrencyNetwork network) async {
|
||||||
final start = DateTime.now();
|
final start = DateTime.now();
|
||||||
setCacheDB.execute(
|
setCacheDB(network).execute(
|
||||||
"""
|
"""
|
||||||
DELETE FROM SparkSet;
|
DELETE FROM SparkSet;
|
||||||
DELETE FROM SparkCoin;
|
DELETE FROM SparkCoin;
|
||||||
|
@ -94,7 +109,7 @@ abstract class _FiroCache {
|
||||||
VACUUM;
|
VACUUM;
|
||||||
""",
|
""",
|
||||||
);
|
);
|
||||||
usedTagsCacheDB.execute(
|
usedTagsCacheDB(network).execute(
|
||||||
"""
|
"""
|
||||||
DELETE FROM SparkUsedCoinTags;
|
DELETE FROM SparkUsedCoinTags;
|
||||||
VACUUM;
|
VACUUM;
|
||||||
|
|
|
@ -5,7 +5,7 @@ typedef LTagPair = ({String tag, String txid});
|
||||||
/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a
|
/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a
|
||||||
/// background isolate and [FiroCacheCoordinator] should manage that isolate
|
/// background isolate and [FiroCacheCoordinator] should manage that isolate
|
||||||
abstract class FiroCacheCoordinator {
|
abstract class FiroCacheCoordinator {
|
||||||
static _FiroCacheWorker? _worker;
|
static final Map<CryptoCurrencyNetwork, _FiroCacheWorker> _workers = {};
|
||||||
|
|
||||||
static bool _init = false;
|
static bool _init = false;
|
||||||
static Future<void> init() async {
|
static Future<void> init() async {
|
||||||
|
@ -14,20 +14,22 @@ abstract class FiroCacheCoordinator {
|
||||||
}
|
}
|
||||||
_init = true;
|
_init = true;
|
||||||
await _FiroCache.init();
|
await _FiroCache.init();
|
||||||
_worker = await _FiroCacheWorker.spawn();
|
for (final network in _FiroCache.networks) {
|
||||||
|
_workers[network] = await _FiroCacheWorker.spawn(network);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> clearSharedCache() async {
|
static Future<void> clearSharedCache(CryptoCurrencyNetwork network) async {
|
||||||
return await _FiroCache._deleteAllCache();
|
return await _FiroCache._deleteAllCache(network);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String> getSparkCacheSize() async {
|
static Future<String> getSparkCacheSize(CryptoCurrencyNetwork network) async {
|
||||||
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||||
final setCacheFile = File(
|
final setCacheFile = File(
|
||||||
"${dir.path}/${_FiroCache.sparkSetCacheFileName}",
|
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}",
|
||||||
);
|
);
|
||||||
final usedTagsCacheFile = File(
|
final usedTagsCacheFile = File(
|
||||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}",
|
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}",
|
||||||
);
|
);
|
||||||
final int bytes =
|
final int bytes =
|
||||||
((await setCacheFile.exists()) ? await setCacheFile.length() : 0) +
|
((await setCacheFile.exists()) ? await setCacheFile.length() : 0) +
|
||||||
|
@ -51,13 +53,14 @@ abstract class FiroCacheCoordinator {
|
||||||
|
|
||||||
static Future<void> runFetchAndUpdateSparkUsedCoinTags(
|
static Future<void> runFetchAndUpdateSparkUsedCoinTags(
|
||||||
ElectrumXClient client,
|
ElectrumXClient client,
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
) async {
|
) async {
|
||||||
final count = await FiroCacheCoordinator.getUsedCoinTagsCount();
|
final count = await FiroCacheCoordinator.getUsedCoinTagsCount(network);
|
||||||
final unhashedTags = await client.getSparkUnhashedUsedCoinsTagsWithTxHashes(
|
final unhashedTags = await client.getSparkUnhashedUsedCoinsTagsWithTxHashes(
|
||||||
startNumber: count,
|
startNumber: count,
|
||||||
);
|
);
|
||||||
if (unhashedTags.isNotEmpty) {
|
if (unhashedTags.isNotEmpty) {
|
||||||
await _worker!.runTask(
|
await _workers[network]!.runTask(
|
||||||
FCTask(
|
FCTask(
|
||||||
func: FCFuncName._updateSparkUsedTagsWith,
|
func: FCFuncName._updateSparkUsedTagsWith,
|
||||||
data: unhashedTags,
|
data: unhashedTags,
|
||||||
|
@ -69,10 +72,12 @@ abstract class FiroCacheCoordinator {
|
||||||
static Future<void> runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
static Future<void> runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||||
int groupId,
|
int groupId,
|
||||||
ElectrumXClient client,
|
ElectrumXClient client,
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
) async {
|
) async {
|
||||||
final blockhashResult =
|
final blockhashResult =
|
||||||
await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||||
groupId,
|
groupId,
|
||||||
|
network,
|
||||||
);
|
);
|
||||||
final blockHash = blockhashResult?.blockHash ?? "";
|
final blockHash = blockhashResult?.blockHash ?? "";
|
||||||
|
|
||||||
|
@ -81,7 +86,7 @@ abstract class FiroCacheCoordinator {
|
||||||
startBlockHash: blockHash.toHexReversedFromBase64,
|
startBlockHash: blockHash.toHexReversedFromBase64,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _worker!.runTask(
|
await _workers[network]!.runTask(
|
||||||
FCTask(
|
FCTask(
|
||||||
func: FCFuncName._updateSparkAnonSetCoinsWith,
|
func: FCFuncName._updateSparkAnonSetCoinsWith,
|
||||||
data: (groupId, json),
|
data: (groupId, json),
|
||||||
|
@ -91,17 +96,22 @@ abstract class FiroCacheCoordinator {
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
static Future<Set<String>> getUsedCoinTags(int startNumber) async {
|
static Future<Set<String>> getUsedCoinTags(
|
||||||
|
int startNumber,
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
|
) async {
|
||||||
final result = await _Reader._getSparkUsedCoinTags(
|
final result = await _Reader._getSparkUsedCoinTags(
|
||||||
startNumber,
|
startNumber,
|
||||||
db: _FiroCache.usedTagsCacheDB,
|
db: _FiroCache.usedTagsCacheDB(network),
|
||||||
);
|
);
|
||||||
return result.map((e) => e["tag"] as String).toSet();
|
return result.map((e) => e["tag"] as String).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<int> getUsedCoinTagsCount() async {
|
static Future<int> getUsedCoinTagsCount(
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
|
) async {
|
||||||
final result = await _Reader._getUsedCoinTagsCount(
|
final result = await _Reader._getUsedCoinTagsCount(
|
||||||
db: _FiroCache.usedTagsCacheDB,
|
db: _FiroCache.usedTagsCacheDB(network),
|
||||||
);
|
);
|
||||||
if (result.isEmpty) {
|
if (result.isEmpty) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -111,13 +121,14 @@ abstract class FiroCacheCoordinator {
|
||||||
|
|
||||||
static Future<List<LTagPair>> getUsedCoinTxidsFor({
|
static Future<List<LTagPair>> getUsedCoinTxidsFor({
|
||||||
required List<String> tags,
|
required List<String> tags,
|
||||||
|
required CryptoCurrencyNetwork network,
|
||||||
}) async {
|
}) async {
|
||||||
if (tags.isEmpty) {
|
if (tags.isEmpty) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
final result = await _Reader._getUsedCoinTxidsFor(
|
final result = await _Reader._getUsedCoinTxidsFor(
|
||||||
tags,
|
tags,
|
||||||
db: _FiroCache.usedTagsCacheDB,
|
db: _FiroCache.usedTagsCacheDB(network),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.isEmpty) {
|
if (result.isEmpty) {
|
||||||
|
@ -135,20 +146,22 @@ abstract class FiroCacheCoordinator {
|
||||||
|
|
||||||
static Future<Set<String>> getUsedCoinTagsFor({
|
static Future<Set<String>> getUsedCoinTagsFor({
|
||||||
required String txid,
|
required String txid,
|
||||||
|
required CryptoCurrencyNetwork network,
|
||||||
}) async {
|
}) async {
|
||||||
final result = await _Reader._getUsedCoinTagsFor(
|
final result = await _Reader._getUsedCoinTagsFor(
|
||||||
txid,
|
txid,
|
||||||
db: _FiroCache.usedTagsCacheDB,
|
db: _FiroCache.usedTagsCacheDB(network),
|
||||||
);
|
);
|
||||||
return result.map((e) => e["tag"] as String).toSet();
|
return result.map((e) => e["tag"] as String).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> checkTagIsUsed(
|
static Future<bool> checkTagIsUsed(
|
||||||
String tag,
|
String tag,
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
) async {
|
) async {
|
||||||
return await _Reader._checkTagIsUsed(
|
return await _Reader._checkTagIsUsed(
|
||||||
tag,
|
tag,
|
||||||
db: _FiroCache.usedTagsCacheDB,
|
db: _FiroCache.usedTagsCacheDB(network),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,10 +174,11 @@ abstract class FiroCacheCoordinator {
|
||||||
})>> getSetCoinsForGroupId(
|
})>> getSetCoinsForGroupId(
|
||||||
int groupId, {
|
int groupId, {
|
||||||
int? newerThanTimeStamp,
|
int? newerThanTimeStamp,
|
||||||
|
required CryptoCurrencyNetwork network,
|
||||||
}) async {
|
}) async {
|
||||||
final resultSet = await _Reader._getSetCoinsForGroupId(
|
final resultSet = await _Reader._getSetCoinsForGroupId(
|
||||||
groupId,
|
groupId,
|
||||||
db: _FiroCache.setCacheDB,
|
db: _FiroCache.setCacheDB(network),
|
||||||
newerThanTimeStamp: newerThanTimeStamp,
|
newerThanTimeStamp: newerThanTimeStamp,
|
||||||
);
|
);
|
||||||
return resultSet
|
return resultSet
|
||||||
|
@ -187,10 +201,11 @@ abstract class FiroCacheCoordinator {
|
||||||
int timestampUTC,
|
int timestampUTC,
|
||||||
})?> getLatestSetInfoForGroupId(
|
})?> getLatestSetInfoForGroupId(
|
||||||
int groupId,
|
int groupId,
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
) async {
|
) async {
|
||||||
final result = await _Reader._getLatestSetInfoForGroupId(
|
final result = await _Reader._getLatestSetInfoForGroupId(
|
||||||
groupId,
|
groupId,
|
||||||
db: _FiroCache.setCacheDB,
|
db: _FiroCache.setCacheDB(network),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.isEmpty) {
|
if (result.isEmpty) {
|
||||||
|
@ -206,10 +221,11 @@ abstract class FiroCacheCoordinator {
|
||||||
|
|
||||||
static Future<bool> checkSetInfoForGroupIdExists(
|
static Future<bool> checkSetInfoForGroupIdExists(
|
||||||
int groupId,
|
int groupId,
|
||||||
|
CryptoCurrencyNetwork network,
|
||||||
) async {
|
) async {
|
||||||
return await _Reader._checkSetInfoForGroupIdExists(
|
return await _Reader._checkSetInfoForGroupIdExists(
|
||||||
groupId,
|
groupId,
|
||||||
db: _FiroCache.setCacheDB,
|
db: _FiroCache.setCacheDB(network),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,12 @@ class _FiroCacheWorker {
|
||||||
return await completer.future;
|
return await completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<_FiroCacheWorker> spawn() async {
|
static Future<_FiroCacheWorker> spawn(CryptoCurrencyNetwork network) async {
|
||||||
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||||
final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}";
|
final setCacheFilePath =
|
||||||
|
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}";
|
||||||
final usedTagsCacheFilePath =
|
final usedTagsCacheFilePath =
|
||||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}";
|
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}";
|
||||||
|
|
||||||
final initPort = RawReceivePort();
|
final initPort = RawReceivePort();
|
||||||
final connection = Completer<(ReceivePort, SendPort)>.sync();
|
final connection = Completer<(ReceivePort, SendPort)>.sync();
|
||||||
|
|
|
@ -1043,7 +1043,7 @@ class ElectrumXClient {
|
||||||
final start = DateTime.now();
|
final start = DateTime.now();
|
||||||
final response = await request(
|
final response = await request(
|
||||||
requestID: requestID,
|
requestID: requestID,
|
||||||
command: "spark.getmempooltxids",
|
command: "spark.getmempoolsparktxids",
|
||||||
);
|
);
|
||||||
|
|
||||||
final txids = List<String>.from(response as List)
|
final txids = List<String>.from(response as List)
|
||||||
|
@ -1072,7 +1072,7 @@ class ElectrumXClient {
|
||||||
final start = DateTime.now();
|
final start = DateTime.now();
|
||||||
final response = await request(
|
final response = await request(
|
||||||
requestID: requestID,
|
requestID: requestID,
|
||||||
command: "spark.getmempooltxs",
|
command: "spark.getmempoolsparktxs",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
"txids": txids,
|
"txids": txids,
|
||||||
|
@ -1087,10 +1087,10 @@ class ElectrumXClient {
|
||||||
(
|
(
|
||||||
txid: entry.key,
|
txid: entry.key,
|
||||||
serialContext:
|
serialContext:
|
||||||
List<String>.from(entry.value["Serial_context"] as List),
|
List<String>.from(entry.value["serial_context"] as List),
|
||||||
// the space after lTags is required lol
|
// the space after lTags is required lol
|
||||||
lTags: List<String>.from(entry.value["lTags "] as List),
|
lTags: List<String>.from(entry.value["lTags "] as List),
|
||||||
coins: List<String>.from(entry.value["Coins"] as List),
|
coins: List<String>.from(entry.value["coins"] as List),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1142,6 +1142,38 @@ class ElectrumXClient {
|
||||||
}
|
}
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
|
Future<bool> isMasterNodeCollateral({
|
||||||
|
String? requestID,
|
||||||
|
required String txid,
|
||||||
|
required int index,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final start = DateTime.now();
|
||||||
|
final response = await request(
|
||||||
|
requestID: requestID,
|
||||||
|
command: "blockchain.checkifmncollateral",
|
||||||
|
args: [
|
||||||
|
txid,
|
||||||
|
index.toString(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Logging.instance.log(
|
||||||
|
"Finished ElectrumXClient.isMasterNodeCollateral, "
|
||||||
|
"response: $response, "
|
||||||
|
"Duration=${DateTime.now().difference(start)}",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
|
||||||
|
return response as bool;
|
||||||
|
} catch (e) {
|
||||||
|
Logging.instance.log(e, level: LogLevel.Error);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
/// Get the current fee rate.
|
/// Get the current fee rate.
|
||||||
///
|
///
|
||||||
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
|
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
|
||||||
|
|
87
lib/models/coinlib/exp2pkh_address.dart
Normal file
87
lib/models/coinlib/exp2pkh_address.dart
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||||
|
|
||||||
|
const OP_EXCHANGEADDR = 0xe0;
|
||||||
|
|
||||||
|
class EXP2PKHAddress implements coinlib.Address {
|
||||||
|
/// The 160bit public key or redeemScript hash for the base58 address
|
||||||
|
final Uint8List _hash;
|
||||||
|
|
||||||
|
/// The network and address type version of the address
|
||||||
|
final Uint8List version;
|
||||||
|
|
||||||
|
String? _encodedCache;
|
||||||
|
|
||||||
|
EXP2PKHAddress._(Uint8List hash, this.version) : _hash = hash {
|
||||||
|
if (version.length != 3) {
|
||||||
|
throw ArgumentError(
|
||||||
|
"version bytes length must be 3",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factory EXP2PKHAddress.fromString(String encoded, Uint8List versionBytes) {
|
||||||
|
if (versionBytes.length != 3) {
|
||||||
|
throw ArgumentError(
|
||||||
|
"version bytes length must be 3",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = coinlib.base58Decode(encoded);
|
||||||
|
if (data.length != 23) throw coinlib.InvalidAddress();
|
||||||
|
|
||||||
|
final version = data.sublist(0, 3);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (version[i] != versionBytes[i]) {
|
||||||
|
throw Exception("EX address version bytes do not match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final payload = data.sublist(3);
|
||||||
|
|
||||||
|
final addr = EXP2PKHAddress._(payload, version);
|
||||||
|
|
||||||
|
addr._encodedCache = encoded;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _encodedCache.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
coinlib.Program get program => EXP2PKH.fromHash(_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
class EXP2PKH implements coinlib.Program {
|
||||||
|
static const template =
|
||||||
|
"OP_EXCHANGEADDR OP_DUP OP_HASH160 <20-bytes> OP_EQUALVERIFY OP_CHECKSIG";
|
||||||
|
|
||||||
|
@override
|
||||||
|
final coinlib.Script script;
|
||||||
|
|
||||||
|
EXP2PKH.fromScript(this.script);
|
||||||
|
|
||||||
|
factory EXP2PKH.fromHash(Uint8List pkHash) {
|
||||||
|
final List<coinlib.ScriptOp> ops = [
|
||||||
|
coinlib.ScriptOpCode(OP_EXCHANGEADDR),
|
||||||
|
];
|
||||||
|
final parts = template.split(" ").sublist(1);
|
||||||
|
for (final name in parts) {
|
||||||
|
if (name.startsWith("OP_")) {
|
||||||
|
ops.add(
|
||||||
|
coinlib.ScriptOpCode(
|
||||||
|
coinlib.scriptOpNameToCode[name.substring(3)]!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (name == "<20-bytes>") {
|
||||||
|
ops.add(coinlib.ScriptPushData(pkHash));
|
||||||
|
} else {
|
||||||
|
throw Exception("Something went wrong in this hacked code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXP2PKH.fromScript(coinlib.Script(ops));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import '../../../../../frost_route_generator.dart';
|
import '../../../../../frost_route_generator.dart';
|
||||||
import '../../../../wallet_view/transaction_views/transaction_details_view.dart';
|
|
||||||
import '../../../../../providers/frost_wallet/frost_wallet_providers.dart';
|
import '../../../../../providers/frost_wallet/frost_wallet_providers.dart';
|
||||||
import '../../../../../services/frost.dart';
|
import '../../../../../services/frost.dart';
|
||||||
import '../../../../../themes/stack_colors.dart';
|
import '../../../../../themes/stack_colors.dart';
|
||||||
|
@ -17,6 +16,8 @@ import '../../../../../widgets/desktop/secondary_button.dart';
|
||||||
import '../../../../../widgets/detail_item.dart';
|
import '../../../../../widgets/detail_item.dart';
|
||||||
import '../../../../../widgets/dialogs/simple_mobile_dialog.dart';
|
import '../../../../../widgets/dialogs/simple_mobile_dialog.dart';
|
||||||
import '../../../../../widgets/frost_step_user_steps.dart';
|
import '../../../../../widgets/frost_step_user_steps.dart';
|
||||||
|
import '../../../../../widgets/qr.dart';
|
||||||
|
import '../../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||||
|
|
||||||
class FrostCreateStep1a extends ConsumerStatefulWidget {
|
class FrostCreateStep1a extends ConsumerStatefulWidget {
|
||||||
const FrostCreateStep1a({super.key});
|
const FrostCreateStep1a({super.key});
|
||||||
|
@ -162,14 +163,9 @@ class _FrostCreateStep1aState extends ConsumerState<FrostCreateStep1a> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
QrImageView(
|
QR(
|
||||||
data: ref.watch(pFrostMultisigConfig.state).state ?? "Error",
|
data: ref.watch(pFrostMultisigConfig.state).state ?? "Error",
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.background,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
|
|
||||||
import '../../../../frost_route_generator.dart';
|
import '../../../../frost_route_generator.dart';
|
||||||
import '../../../../providers/db/main_db_provider.dart';
|
import '../../../../providers/db/main_db_provider.dart';
|
||||||
|
@ -23,6 +22,7 @@ import '../../../../widgets/detail_item.dart';
|
||||||
import '../../../../widgets/dialogs/frost/frost_error_dialog.dart';
|
import '../../../../widgets/dialogs/frost/frost_error_dialog.dart';
|
||||||
import '../../../../widgets/dialogs/simple_mobile_dialog.dart';
|
import '../../../../widgets/dialogs/simple_mobile_dialog.dart';
|
||||||
import '../../../../widgets/frost_step_user_steps.dart';
|
import '../../../../widgets/frost_step_user_steps.dart';
|
||||||
|
import '../../../../widgets/qr.dart';
|
||||||
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||||
|
|
||||||
class FrostReshareStep1a extends ConsumerStatefulWidget {
|
class FrostReshareStep1a extends ConsumerStatefulWidget {
|
||||||
|
@ -239,14 +239,9 @@ class _FrostReshareStep1aState extends ConsumerState<FrostReshareStep1a> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
QrImageView(
|
QR(
|
||||||
data: ref.watch(pFrostResharingData).resharerRConfig!,
|
data: ref.watch(pFrostResharingData).resharerRConfig!,
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.background,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import '../../../app_config.dart';
|
import '../../../app_config.dart';
|
||||||
|
@ -37,6 +36,7 @@ import '../../../wallets/wallet/impl/firo_wallet.dart';
|
||||||
import '../../../widgets/background.dart';
|
import '../../../widgets/background.dart';
|
||||||
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../../widgets/desktop/secondary_button.dart';
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/rounded_container.dart';
|
import '../../../widgets/rounded_container.dart';
|
||||||
import '../../../widgets/rounded_white_container.dart';
|
import '../../../widgets/rounded_white_container.dart';
|
||||||
import '../../../widgets/stack_dialog.dart';
|
import '../../../widgets/stack_dialog.dart';
|
||||||
|
@ -751,7 +751,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
// TODO: grab coin uri scheme from somewhere
|
// TODO: grab coin uri scheme from somewhere
|
||||||
// data: "${coin.uriScheme}:$receivingAddress",
|
// data: "${coin.uriScheme}:$receivingAddress",
|
||||||
data: model.trade!.payInAddress,
|
data: model.trade!.payInAddress,
|
||||||
|
@ -759,9 +759,6 @@ class _Step4ViewState extends ConsumerState<Step4View> {
|
||||||
.size
|
.size
|
||||||
.width /
|
.width /
|
||||||
2,
|
2,
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
|
|
@ -16,7 +16,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
@ -50,6 +49,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../widgets/custom_buttons/blue_text_button.dart';
|
import '../../widgets/custom_buttons/blue_text_button.dart';
|
||||||
import '../../widgets/desktop/desktop_dialog.dart';
|
import '../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../widgets/desktop/secondary_button.dart';
|
import '../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../widgets/qr.dart';
|
||||||
import '../../widgets/rounded_container.dart';
|
import '../../widgets/rounded_container.dart';
|
||||||
import '../../widgets/rounded_white_container.dart';
|
import '../../widgets/rounded_white_container.dart';
|
||||||
import '../../widgets/stack_dialog.dart';
|
import '../../widgets/stack_dialog.dart';
|
||||||
|
@ -808,15 +808,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: width + 20,
|
width: width + 20,
|
||||||
height: width + 20,
|
height: width + 20,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: trade.payInAddress,
|
data: trade.payInAddress,
|
||||||
size: width,
|
size: width,
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.popupBG,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,15 +14,11 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import '../../../exceptions/wallet/insufficient_balance_exception.dart';
|
import '../../../exceptions/wallet/insufficient_balance_exception.dart';
|
||||||
import '../../../models/paynym/paynym_account_lite.dart';
|
import '../../../models/paynym/paynym_account_lite.dart';
|
||||||
import '../../../notifications/show_flush_bar.dart';
|
import '../../../notifications/show_flush_bar.dart';
|
||||||
import 'confirm_paynym_connect_dialog.dart';
|
|
||||||
import '../paynym_home_view.dart';
|
|
||||||
import '../subwidgets/paynym_bot.dart';
|
|
||||||
import '../../send_view/confirm_transaction_view.dart';
|
|
||||||
import '../../send_view/send_view.dart';
|
|
||||||
import '../../../providers/global/locale_provider.dart';
|
import '../../../providers/global/locale_provider.dart';
|
||||||
import '../../../providers/global/wallets_provider.dart';
|
import '../../../providers/global/wallets_provider.dart';
|
||||||
import '../../../route_generator.dart';
|
import '../../../route_generator.dart';
|
||||||
|
@ -37,9 +33,14 @@ import '../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../widgets/desktop/primary_button.dart';
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/desktop/secondary_button.dart';
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
import '../../../widgets/loading_indicator.dart';
|
import '../../../widgets/loading_indicator.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/rounded_container.dart';
|
import '../../../widgets/rounded_container.dart';
|
||||||
import '../../../widgets/stack_dialog.dart';
|
import '../../../widgets/stack_dialog.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import '../../send_view/confirm_transaction_view.dart';
|
||||||
|
import '../../send_view/send_view.dart';
|
||||||
|
import '../paynym_home_view.dart';
|
||||||
|
import '../subwidgets/paynym_bot.dart';
|
||||||
|
import 'confirm_paynym_connect_dialog.dart';
|
||||||
|
|
||||||
class PaynymDetailsPopup extends ConsumerStatefulWidget {
|
class PaynymDetailsPopup extends ConsumerStatefulWidget {
|
||||||
const PaynymDetailsPopup({
|
const PaynymDetailsPopup({
|
||||||
|
@ -365,12 +366,10 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
),
|
),
|
||||||
QrImageView(
|
QR(
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
size: 100,
|
size: 100,
|
||||||
data: widget.accountLite.code,
|
data: widget.accountLite.code,
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.textDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -12,10 +12,9 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import '../../../models/paynym/paynym_account.dart';
|
import '../../../models/paynym/paynym_account.dart';
|
||||||
import '../../../notifications/show_flush_bar.dart';
|
import '../../../notifications/show_flush_bar.dart';
|
||||||
import '../subwidgets/paynym_bot.dart';
|
|
||||||
import '../../../themes/stack_colors.dart';
|
import '../../../themes/stack_colors.dart';
|
||||||
import '../../../utilities/assets.dart';
|
import '../../../utilities/assets.dart';
|
||||||
import '../../../utilities/text_styles.dart';
|
import '../../../utilities/text_styles.dart';
|
||||||
|
@ -23,6 +22,8 @@ import '../../../utilities/util.dart';
|
||||||
import '../../../widgets/custom_buttons/blue_text_button.dart';
|
import '../../../widgets/custom_buttons/blue_text_button.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
|
import '../subwidgets/paynym_bot.dart';
|
||||||
|
|
||||||
class PaynymQrPopup extends StatelessWidget {
|
class PaynymQrPopup extends StatelessWidget {
|
||||||
const PaynymQrPopup({
|
const PaynymQrPopup({
|
||||||
|
@ -157,12 +158,10 @@ class PaynymQrPopup extends StatelessWidget {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
),
|
),
|
||||||
QrImageView(
|
QR(
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
size: 130,
|
size: 130,
|
||||||
data: paynymAccount.nonSegwitPaymentCode.code,
|
data: paynymAccount.nonSegwitPaymentCode.code,
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.textDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,19 +14,15 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import '../../../exceptions/wallet/insufficient_balance_exception.dart';
|
import '../../../exceptions/wallet/insufficient_balance_exception.dart';
|
||||||
import '../../../models/paynym/paynym_account_lite.dart';
|
import '../../../models/paynym/paynym_account_lite.dart';
|
||||||
import '../../../notifications/show_flush_bar.dart';
|
import '../../../notifications/show_flush_bar.dart';
|
||||||
import '../dialogs/confirm_paynym_connect_dialog.dart';
|
|
||||||
import 'paynym_bot.dart';
|
|
||||||
import '../../send_view/confirm_transaction_view.dart';
|
|
||||||
import '../../../pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart';
|
import '../../../pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart';
|
||||||
import '../../../providers/global/locale_provider.dart';
|
import '../../../providers/global/locale_provider.dart';
|
||||||
import '../../../providers/global/wallets_provider.dart';
|
import '../../../providers/global/wallets_provider.dart';
|
||||||
import '../../../themes/stack_colors.dart';
|
import '../../../themes/stack_colors.dart';
|
||||||
import '../../../utilities/assets.dart';
|
import '../../../utilities/assets.dart';
|
||||||
|
|
||||||
import '../../../utilities/text_styles.dart';
|
import '../../../utilities/text_styles.dart';
|
||||||
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import '../../../wallets/models/tx_data.dart';
|
import '../../../wallets/models/tx_data.dart';
|
||||||
|
@ -36,8 +32,12 @@ import '../../../widgets/custom_buttons/paynym_follow_toggle_button.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../widgets/desktop/primary_button.dart';
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/loading_indicator.dart';
|
import '../../../widgets/loading_indicator.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/rounded_container.dart';
|
import '../../../widgets/rounded_container.dart';
|
||||||
import '../../../widgets/rounded_white_container.dart';
|
import '../../../widgets/rounded_white_container.dart';
|
||||||
|
import '../../send_view/confirm_transaction_view.dart';
|
||||||
|
import '../dialogs/confirm_paynym_connect_dialog.dart';
|
||||||
|
import 'paynym_bot.dart';
|
||||||
|
|
||||||
class DesktopPaynymDetails extends ConsumerStatefulWidget {
|
class DesktopPaynymDetails extends ConsumerStatefulWidget {
|
||||||
const DesktopPaynymDetails({
|
const DesktopPaynymDetails({
|
||||||
|
@ -359,12 +359,10 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
),
|
),
|
||||||
QrImageView(
|
QR(
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
size: 100,
|
size: 100,
|
||||||
data: widget.accountLite.code,
|
data: widget.accountLite.code,
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.textDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -188,12 +188,14 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
_timeout = Duration.zero;
|
_timeout = Duration.zero;
|
||||||
|
|
||||||
_checkUseBiometrics();
|
_checkUseBiometrics();
|
||||||
|
_pinTextController.addListener(_onPinChanged);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dispose() {
|
dispose() {
|
||||||
// _shakeController.dispose();
|
// _shakeController.dispose();
|
||||||
|
_pinTextController.removeListener(_onPinChanged);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,13 +210,27 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final _pinTextController = TextEditingController();
|
|
||||||
final FocusNode _pinFocusNode = FocusNode();
|
final FocusNode _pinFocusNode = FocusNode();
|
||||||
|
|
||||||
late SecureStorageInterface _secureStore;
|
late SecureStorageInterface _secureStore;
|
||||||
late Biometrics biometrics;
|
late Biometrics biometrics;
|
||||||
int pinCount = 1;
|
int pinCount = 1;
|
||||||
|
|
||||||
|
final _pinTextController = TextEditingController();
|
||||||
|
|
||||||
|
void _onPinChanged() async {
|
||||||
|
String enteredPin = _pinTextController.text;
|
||||||
|
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||||
|
final autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||||
|
|
||||||
|
if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) {
|
||||||
|
await Future<void>.delayed(
|
||||||
|
const Duration(milliseconds: 200),
|
||||||
|
);
|
||||||
|
unawaited(_onUnlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget get _body => Background(
|
Widget get _body => Background(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
|
|
@ -20,7 +20,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import '../../../db/isar/main_db.dart';
|
import '../../../db/isar/main_db.dart';
|
||||||
|
@ -39,6 +38,7 @@ import '../../../widgets/custom_buttons/blue_text_button.dart';
|
||||||
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
||||||
import '../../../widgets/desktop/primary_button.dart';
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/desktop/secondary_button.dart';
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/rounded_white_container.dart';
|
import '../../../widgets/rounded_white_container.dart';
|
||||||
import '../../../widgets/stack_dialog.dart';
|
import '../../../widgets/stack_dialog.dart';
|
||||||
|
|
||||||
|
@ -302,19 +302,13 @@ class _AddressCardState extends ConsumerState<AddressCard> {
|
||||||
Center(
|
Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
key: _qrKey,
|
key: _qrKey,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
widget.coin,
|
widget.coin,
|
||||||
address.value,
|
address.value,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.popupBG,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
|
|
||||||
import '../../../db/isar/main_db.dart';
|
import '../../../db/isar/main_db.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
|
@ -31,6 +30,7 @@ import '../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||||
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/rounded_white_container.dart';
|
import '../../../widgets/rounded_white_container.dart';
|
||||||
import '../../../widgets/transaction_card.dart';
|
import '../../../widgets/transaction_card.dart';
|
||||||
import '../../wallet_view/sub_widgets/no_transactions_found.dart';
|
import '../../wallet_view/sub_widgets/no_transactions_found.dart';
|
||||||
|
@ -92,18 +92,13 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
Center(
|
Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
key: _qrKey,
|
key: _qrKey,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
ref.watch(pWalletCoin(widget.walletId)),
|
ref.watch(pWalletCoin(widget.walletId)),
|
||||||
address.value,
|
address.value,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -289,19 +284,13 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
Center(
|
Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
key: _qrKey,
|
key: _qrKey,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
coin,
|
coin,
|
||||||
address.value,
|
address.value,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.background,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,7 +18,6 @@ import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import '../../../notifications/show_flush_bar.dart';
|
import '../../../notifications/show_flush_bar.dart';
|
||||||
|
@ -31,6 +30,7 @@ import '../../../utilities/util.dart';
|
||||||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../../widgets/desktop/primary_button.dart';
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/desktop/secondary_button.dart';
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/stack_dialog.dart';
|
import '../../../widgets/stack_dialog.dart';
|
||||||
|
|
||||||
class AddressQrPopup extends StatefulWidget {
|
class AddressQrPopup extends StatefulWidget {
|
||||||
|
@ -140,17 +140,13 @@ class _AddressQrPopupState extends State<AddressQrPopup> {
|
||||||
Center(
|
Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
key: _qrKey,
|
key: _qrKey,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
widget.coin,
|
widget.coin,
|
||||||
widget.addressString,
|
widget.addressString,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -249,17 +249,15 @@ class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
addressId: snapshot.data![index],
|
addressId: snapshot.data![index],
|
||||||
coin: coin,
|
coin: coin,
|
||||||
onPressed: !isDesktop
|
onPressed: () {
|
||||||
? null
|
Navigator.of(context).pushNamed(
|
||||||
: () {
|
AddressDetailsView.routeName,
|
||||||
Navigator.of(context).pushNamed(
|
arguments: Tuple2(
|
||||||
AddressDetailsView.routeName,
|
snapshot.data![index],
|
||||||
arguments: Tuple2(
|
widget.walletId,
|
||||||
snapshot.data![index],
|
),
|
||||||
widget.walletId,
|
);
|
||||||
),
|
},
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import '../../notifications/show_flush_bar.dart';
|
import '../../notifications/show_flush_bar.dart';
|
||||||
|
@ -39,6 +38,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../widgets/desktop/primary_button.dart';
|
import '../../widgets/desktop/primary_button.dart';
|
||||||
import '../../widgets/desktop/secondary_button.dart';
|
import '../../widgets/desktop/secondary_button.dart';
|
||||||
import '../../widgets/icon_widgets/x_icon.dart';
|
import '../../widgets/icon_widgets/x_icon.dart';
|
||||||
|
import '../../widgets/qr.dart';
|
||||||
import '../../widgets/rounded_white_container.dart';
|
import '../../widgets/rounded_white_container.dart';
|
||||||
import '../../widgets/stack_dialog.dart';
|
import '../../widgets/stack_dialog.dart';
|
||||||
import '../../widgets/stack_text_field.dart';
|
import '../../widgets/stack_text_field.dart';
|
||||||
|
@ -215,14 +215,9 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: width + 20,
|
width: width + 20,
|
||||||
height: width + 20,
|
height: width + 20,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: uriString,
|
data: uriString,
|
||||||
size: width,
|
size: width,
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -556,15 +551,9 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 234,
|
width: 234,
|
||||||
height: 234,
|
height: 234,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: _uriString,
|
data: _uriString,
|
||||||
size: 220,
|
size: 220,
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.popupBG,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -16,7 +16,6 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
|
|
||||||
import '../../models/isar/models/isar_models.dart';
|
import '../../models/isar/models/isar_models.dart';
|
||||||
import '../../notifications/show_flush_bar.dart';
|
import '../../notifications/show_flush_bar.dart';
|
||||||
|
@ -44,6 +43,7 @@ import '../../widgets/custom_buttons/blue_text_button.dart';
|
||||||
import '../../widgets/custom_loading_overlay.dart';
|
import '../../widgets/custom_loading_overlay.dart';
|
||||||
import '../../widgets/desktop/primary_button.dart';
|
import '../../widgets/desktop/primary_button.dart';
|
||||||
import '../../widgets/desktop/secondary_button.dart';
|
import '../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../widgets/qr.dart';
|
||||||
import '../../widgets/rounded_white_container.dart';
|
import '../../widgets/rounded_white_container.dart';
|
||||||
import 'addresses/wallet_addresses_view.dart';
|
import 'addresses/wallet_addresses_view.dart';
|
||||||
import 'generate_receiving_uri_qr_code_view.dart';
|
import 'generate_receiving_uri_qr_code_view.dart';
|
||||||
|
@ -575,16 +575,13 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
QrImageView(
|
QR(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
coin,
|
coin,
|
||||||
address,
|
address,
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: MediaQuery.of(context).size.width / 2,
|
size: MediaQuery.of(context).size.width / 2,
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import '../../../../frost_route_generator.dart';
|
import '../../../../frost_route_generator.dart';
|
||||||
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
|
||||||
import '../../../../providers/frost_wallet/frost_wallet_providers.dart';
|
import '../../../../providers/frost_wallet/frost_wallet_providers.dart';
|
||||||
import '../../../../providers/global/wallets_provider.dart';
|
import '../../../../providers/global/wallets_provider.dart';
|
||||||
import '../../../../themes/stack_colors.dart';
|
import '../../../../themes/stack_colors.dart';
|
||||||
|
@ -14,7 +13,9 @@ import '../../../../widgets/custom_buttons/checkbox_text_button.dart';
|
||||||
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||||
import '../../../../widgets/desktop/primary_button.dart';
|
import '../../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../../widgets/detail_item.dart';
|
import '../../../../widgets/detail_item.dart';
|
||||||
|
import '../../../../widgets/qr.dart';
|
||||||
import '../../../../widgets/rounded_white_container.dart';
|
import '../../../../widgets/rounded_white_container.dart';
|
||||||
|
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||||
|
|
||||||
class FrostSendStep1a extends ConsumerStatefulWidget {
|
class FrostSendStep1a extends ConsumerStatefulWidget {
|
||||||
const FrostSendStep1a({super.key});
|
const FrostSendStep1a({super.key});
|
||||||
|
@ -169,14 +170,9 @@ class _FrostSendStep1aState extends ConsumerState<FrostSendStep1a> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
QrImageView(
|
QR(
|
||||||
data: ref.watch(pFrostTxData.state).state!.frostMSConfig!,
|
data: ref.watch(pFrostTxData.state).state!.frostMSConfig!,
|
||||||
size: qrImageSize,
|
size: qrImageSize,
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.background,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -56,6 +56,7 @@ import '../../widgets/animated_text.dart';
|
||||||
import '../../widgets/background.dart';
|
import '../../widgets/background.dart';
|
||||||
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../widgets/custom_buttons/blue_text_button.dart';
|
import '../../widgets/custom_buttons/blue_text_button.dart';
|
||||||
|
import '../../widgets/dialogs/firo_exchange_address_dialog.dart';
|
||||||
import '../../widgets/fee_slider.dart';
|
import '../../widgets/fee_slider.dart';
|
||||||
import '../../widgets/icon_widgets/addressbook_icon.dart';
|
import '../../widgets/icon_widgets/addressbook_icon.dart';
|
||||||
import '../../widgets/icon_widgets/clipboard_icon.dart';
|
import '../../widgets/icon_widgets/clipboard_icon.dart';
|
||||||
|
@ -127,6 +128,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
|
|
||||||
bool _addressToggleFlag = false;
|
bool _addressToggleFlag = false;
|
||||||
|
|
||||||
|
bool _isFiroExWarningDisplayed = false;
|
||||||
|
|
||||||
bool _cryptoAmountChangeLock = false;
|
bool _cryptoAmountChangeLock = false;
|
||||||
late VoidCallback onCryptoAmountChanged;
|
late VoidCallback onCryptoAmountChanged;
|
||||||
|
|
||||||
|
@ -394,6 +397,19 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
address: address ?? "",
|
address: address ?? "",
|
||||||
isTestNet: wallet.cryptoCurrency.network.isTestNet,
|
isTestNet: wallet.cryptoCurrency.network.isTestNet,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ref.read(pIsExchangeAddress.state).state =
|
||||||
|
(coin as Firo).isExchangeAddress(address ?? "");
|
||||||
|
|
||||||
|
if (ref.read(publicPrivateBalanceStateProvider) == FiroType.spark &&
|
||||||
|
ref.read(pIsExchangeAddress) &&
|
||||||
|
!_isFiroExWarningDisplayed) {
|
||||||
|
_isFiroExWarningDisplayed = true;
|
||||||
|
showFiroExchangeAddressWarning(
|
||||||
|
context,
|
||||||
|
() => _isFiroExWarningDisplayed = false,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.read(pValidSendToAddress.notifier).state =
|
ref.read(pValidSendToAddress.notifier).state =
|
||||||
|
@ -875,7 +891,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
coin = widget.coin;
|
coin = widget.coin;
|
||||||
ref.refresh(feeSheetSessionCacheProvider);
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
ref.refresh(feeSheetSessionCacheProvider);
|
||||||
|
ref.refresh(pIsExchangeAddress);
|
||||||
|
});
|
||||||
_currentFee = 0.toAmountAsRaw(fractionDigits: coin.fractionDigits);
|
_currentFee = 0.toAmountAsRaw(fractionDigits: coin.fractionDigits);
|
||||||
|
|
||||||
_calculateFeesFuture =
|
_calculateFeesFuture =
|
||||||
|
@ -1003,6 +1022,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
: true);
|
: true);
|
||||||
|
|
||||||
if (isFiro) {
|
if (isFiro) {
|
||||||
|
final isExchangeAddress = ref.watch(pIsExchangeAddress);
|
||||||
|
|
||||||
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
||||||
selectedUTXOs = {};
|
selectedUTXOs = {};
|
||||||
|
|
||||||
|
@ -1019,6 +1040,19 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previous != next &&
|
||||||
|
next == FiroType.spark &&
|
||||||
|
isExchangeAddress &&
|
||||||
|
!_isFiroExWarningDisplayed) {
|
||||||
|
_isFiroExWarningDisplayed = true;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
|
(_) => showFiroExchangeAddressWarning(
|
||||||
|
context,
|
||||||
|
() => _isFiroExWarningDisplayed = false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,9 +61,12 @@ class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
||||||
|
|
||||||
int pinCount = 1;
|
int pinCount = 1;
|
||||||
|
|
||||||
|
final TextEditingController _pinTextController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_secureStore = ref.read(secureStoreProvider);
|
_secureStore = ref.read(secureStoreProvider);
|
||||||
|
_pinTextController.addListener(_onPinChanged);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,9 +77,23 @@ class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
||||||
_pinPutController2.dispose();
|
_pinPutController2.dispose();
|
||||||
_pinPutFocusNode1.dispose();
|
_pinPutFocusNode1.dispose();
|
||||||
_pinPutFocusNode2.dispose();
|
_pinPutFocusNode2.dispose();
|
||||||
|
_pinTextController.removeListener(_onPinChanged);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onPinChanged() async {
|
||||||
|
String enteredPin = _pinTextController.text;
|
||||||
|
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||||
|
final autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||||
|
|
||||||
|
if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) {
|
||||||
|
await _pageController.nextPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.linear,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Background(
|
return Background(
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../../../pinpad_views/lock_screen_view.dart';
|
|
||||||
import 'change_pin_view/change_pin_view.dart';
|
|
||||||
import '../../../../providers/global/prefs_provider.dart';
|
import '../../../../providers/global/prefs_provider.dart';
|
||||||
import '../../../../route_generator.dart';
|
import '../../../../route_generator.dart';
|
||||||
import '../../../../themes/stack_colors.dart';
|
import '../../../../themes/stack_colors.dart';
|
||||||
|
@ -21,6 +20,8 @@ import '../../../../widgets/background.dart';
|
||||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../../../widgets/custom_buttons/draggable_switch_button.dart';
|
import '../../../../widgets/custom_buttons/draggable_switch_button.dart';
|
||||||
import '../../../../widgets/rounded_white_container.dart';
|
import '../../../../widgets/rounded_white_container.dart';
|
||||||
|
import '../../../pinpad_views/lock_screen_view.dart';
|
||||||
|
import 'change_pin_view/change_pin_view.dart';
|
||||||
|
|
||||||
class SecurityView extends StatelessWidget {
|
class SecurityView extends StatelessWidget {
|
||||||
const SecurityView({
|
const SecurityView({
|
||||||
|
@ -203,6 +204,54 @@ class SecurityView extends StatelessWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// The "autoPin" preference (whether to automatically accept a correct PIN).
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
child: Consumer(
|
||||||
|
builder: (_, ref, __) {
|
||||||
|
return RawMaterialButton(
|
||||||
|
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: null,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Auto-accept correct PIN",
|
||||||
|
style: STextStyles.titleBold12(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 40,
|
||||||
|
child: DraggableSwitchButton(
|
||||||
|
isOn: ref.watch(
|
||||||
|
prefsChangeNotifierProvider
|
||||||
|
.select((value) => value.autoPin),
|
||||||
|
),
|
||||||
|
onValueChanged: (newValue) {
|
||||||
|
ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.autoPin = newValue;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
|
|
||||||
import '../../../../app_config.dart';
|
import '../../../../app_config.dart';
|
||||||
import '../../../../notifications/show_flush_bar.dart';
|
import '../../../../notifications/show_flush_bar.dart';
|
||||||
|
@ -30,6 +29,7 @@ import '../../../../widgets/background.dart';
|
||||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||||
import '../../../../widgets/detail_item.dart';
|
import '../../../../widgets/detail_item.dart';
|
||||||
|
import '../../../../widgets/qr.dart';
|
||||||
import '../../../../widgets/rounded_white_container.dart';
|
import '../../../../widgets/rounded_white_container.dart';
|
||||||
import '../../../../widgets/stack_dialog.dart';
|
import '../../../../widgets/stack_dialog.dart';
|
||||||
import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
|
import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
|
||||||
|
@ -317,15 +317,9 @@ class WalletBackupView extends ConsumerWidget {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: width + 20,
|
width: width + 20,
|
||||||
height: width + 20,
|
height: width + 20,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: data,
|
data: data,
|
||||||
size: width,
|
size: width,
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.popupBG,
|
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -759,6 +759,32 @@ class _WalletNetworkSettingsViewState
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: isDesktop ? 12 : 9,
|
||||||
|
),
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
borderColor: isDesktop
|
||||||
|
? Theme.of(context).extension<StackColors>()!.background
|
||||||
|
: null,
|
||||||
|
padding:
|
||||||
|
isDesktop ? const EdgeInsets.all(16) : const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Current height",
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
style: isDesktop
|
||||||
|
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||||
|
: STextStyles.smallMed12(context),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
ref.watch(pWalletChainHeight(widget.walletId)).toString(),
|
||||||
|
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: isDesktop ? 32 : 20,
|
height: isDesktop ? 32 : 20,
|
||||||
),
|
),
|
||||||
|
|
|
@ -430,7 +430,9 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
|
||||||
),
|
),
|
||||||
if (coin is Firo)
|
if (coin is Firo)
|
||||||
FiroCacheCoordinator
|
FiroCacheCoordinator
|
||||||
.clearSharedCache(),
|
.clearSharedCache(
|
||||||
|
coin.network,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../../../../db/sqlite/firo_cache.dart';
|
import '../../../../db/sqlite/firo_cache.dart';
|
||||||
import '../../../../themes/stack_colors.dart';
|
import '../../../../themes/stack_colors.dart';
|
||||||
import '../../../../utilities/text_styles.dart';
|
import '../../../../utilities/text_styles.dart';
|
||||||
|
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import '../../../../widgets/background.dart';
|
import '../../../../widgets/background.dart';
|
||||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../../../widgets/detail_item.dart';
|
import '../../../../widgets/detail_item.dart';
|
||||||
|
@ -11,10 +12,13 @@ import '../../../../widgets/detail_item.dart';
|
||||||
class SparkInfoView extends ConsumerWidget {
|
class SparkInfoView extends ConsumerWidget {
|
||||||
const SparkInfoView({
|
const SparkInfoView({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.walletId,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const String routeName = "/sparkInfo";
|
static const String routeName = "/sparkInfo";
|
||||||
|
|
||||||
|
final String walletId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return Background(
|
return Background(
|
||||||
|
@ -37,7 +41,9 @@ class SparkInfoView extends ConsumerWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: FiroCacheCoordinator.getSparkCacheSize(),
|
future: FiroCacheCoordinator.getSparkCacheSize(
|
||||||
|
ref.watch(pWalletCoin(walletId)).network,
|
||||||
|
),
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
String detail = "Loading...";
|
String detail = "Loading...";
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
|
|
@ -243,6 +243,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
SparkInfoView.routeName,
|
SparkInfoView.routeName,
|
||||||
|
arguments: walletId,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
|
|
||||||
import '../../../../notifications/show_flush_bar.dart';
|
import '../../../../notifications/show_flush_bar.dart';
|
||||||
import '../../../../providers/global/wallets_provider.dart';
|
import '../../../../providers/global/wallets_provider.dart';
|
||||||
|
@ -32,6 +31,7 @@ import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
import '../../../../widgets/desktop/primary_button.dart';
|
import '../../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../../widgets/desktop/secondary_button.dart';
|
import '../../../../widgets/desktop/secondary_button.dart';
|
||||||
import '../../../../widgets/loading_indicator.dart';
|
import '../../../../widgets/loading_indicator.dart';
|
||||||
|
import '../../../../widgets/qr.dart';
|
||||||
import '../../../../widgets/rounded_white_container.dart';
|
import '../../../../widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class XPubView extends ConsumerStatefulWidget {
|
class XPubView extends ConsumerStatefulWidget {
|
||||||
|
@ -256,11 +256,9 @@ class _XPub extends StatelessWidget {
|
||||||
builder: (child) => RoundedWhiteContainer(
|
builder: (child) => RoundedWhiteContainer(
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: xpub,
|
data: xpub,
|
||||||
size: isDesktop ? 280 : MediaQuery.of(context).size.width / 1.5,
|
size: isDesktop ? 280 : MediaQuery.of(context).size.width / 1.5,
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
|
|
|
@ -13,6 +13,7 @@ import '../../../../utilities/text_styles.dart';
|
||||||
import '../../../../utilities/util.dart';
|
import '../../../../utilities/util.dart';
|
||||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||||
|
import '../../../../widgets/breathing.dart';
|
||||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
import '../../../../widgets/trade_card.dart';
|
import '../../../../widgets/trade_card.dart';
|
||||||
|
@ -49,98 +50,100 @@ class TxListItem extends ConsumerWidget {
|
||||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Breathing(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
TransactionCardV2(
|
children: [
|
||||||
key: UniqueKey(),
|
TransactionCardV2(
|
||||||
transaction: _tx,
|
key: UniqueKey(),
|
||||||
),
|
transaction: _tx,
|
||||||
TradeCard(
|
),
|
||||||
key: Key(
|
TradeCard(
|
||||||
_tx.txid +
|
key: Key(
|
||||||
_tx.type.name +
|
_tx.txid +
|
||||||
_tx.hashCode.toString() +
|
_tx.type.name +
|
||||||
trade.uuid,
|
_tx.hashCode.toString() +
|
||||||
), //
|
trade.uuid,
|
||||||
trade: trade,
|
), //
|
||||||
onTap: () async {
|
trade: trade,
|
||||||
if (Util.isDesktop) {
|
onTap: () async {
|
||||||
await showDialog<void>(
|
if (Util.isDesktop) {
|
||||||
context: context,
|
await showDialog<void>(
|
||||||
builder: (context) => Navigator(
|
context: context,
|
||||||
initialRoute: TradeDetailsView.routeName,
|
builder: (context) => Navigator(
|
||||||
onGenerateRoute: RouteGenerator.generateRoute,
|
initialRoute: TradeDetailsView.routeName,
|
||||||
onGenerateInitialRoutes: (_, __) {
|
onGenerateRoute: RouteGenerator.generateRoute,
|
||||||
return [
|
onGenerateInitialRoutes: (_, __) {
|
||||||
FadePageRoute(
|
return [
|
||||||
DesktopDialog(
|
FadePageRoute(
|
||||||
maxHeight: null,
|
DesktopDialog(
|
||||||
maxWidth: 580,
|
maxHeight: null,
|
||||||
child: Column(
|
maxWidth: 580,
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.only(
|
Padding(
|
||||||
left: 32,
|
padding: const EdgeInsets.only(
|
||||||
bottom: 16,
|
left: 32,
|
||||||
|
bottom: 16,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Trade details",
|
||||||
|
style: STextStyles.desktopH3(
|
||||||
|
context),
|
||||||
|
),
|
||||||
|
DesktopDialogCloseButton(
|
||||||
|
onPressedOverride: Navigator.of(
|
||||||
|
context,
|
||||||
|
rootNavigator: true,
|
||||||
|
).pop,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
Flexible(
|
||||||
mainAxisAlignment:
|
child: TradeDetailsView(
|
||||||
MainAxisAlignment.spaceBetween,
|
tradeId: trade.tradeId,
|
||||||
children: [
|
// TODO: [prio:med]
|
||||||
Text(
|
// transactionIfSentFromStack: tx,
|
||||||
"Trade details",
|
transactionIfSentFromStack: null,
|
||||||
style:
|
walletName: ref
|
||||||
STextStyles.desktopH3(context),
|
.watch(pWalletName(_tx.walletId)),
|
||||||
),
|
walletId: _tx.walletId,
|
||||||
DesktopDialogCloseButton(
|
),
|
||||||
onPressedOverride: Navigator.of(
|
|
||||||
context,
|
|
||||||
rootNavigator: true,
|
|
||||||
).pop,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Flexible(
|
),
|
||||||
child: TradeDetailsView(
|
),
|
||||||
tradeId: trade.tradeId,
|
const RouteSettings(
|
||||||
// TODO: [prio:med]
|
name: TradeDetailsView.routeName,
|
||||||
// transactionIfSentFromStack: tx,
|
|
||||||
transactionIfSentFromStack: null,
|
|
||||||
walletName: ref
|
|
||||||
.watch(pWalletName(_tx.walletId)),
|
|
||||||
walletId: _tx.walletId,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const RouteSettings(
|
];
|
||||||
name: TradeDetailsView.routeName,
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unawaited(
|
|
||||||
Navigator.of(context).pushNamed(
|
|
||||||
TradeDetailsView.routeName,
|
|
||||||
arguments: Tuple4(
|
|
||||||
trade.tradeId,
|
|
||||||
_tx,
|
|
||||||
_tx.walletId,
|
|
||||||
ref.read(pWalletName(_tx.walletId)),
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
} else {
|
||||||
}
|
unawaited(
|
||||||
},
|
Navigator.of(context).pushNamed(
|
||||||
),
|
TradeDetailsView.routeName,
|
||||||
],
|
arguments: Tuple4(
|
||||||
|
trade.tradeId,
|
||||||
|
_tx,
|
||||||
|
_tx.walletId,
|
||||||
|
ref.read(pWalletName(_tx.walletId)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,10 +152,12 @@ class TxListItem extends ConsumerWidget {
|
||||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
),
|
),
|
||||||
child: TransactionCardV2(
|
child: Breathing(
|
||||||
// this may mess with combined firo transactions
|
child: TransactionCardV2(
|
||||||
key: UniqueKey(),
|
// this may mess with combined firo transactions
|
||||||
transaction: _tx,
|
key: UniqueKey(),
|
||||||
|
transaction: _tx,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -165,9 +170,11 @@ class TxListItem extends ConsumerWidget {
|
||||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
),
|
),
|
||||||
child: FusionTxGroupCard(
|
child: Breathing(
|
||||||
key: UniqueKey(),
|
child: FusionTxGroupCard(
|
||||||
group: group,
|
key: UniqueKey(),
|
||||||
|
group: group,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,6 @@ import '../buy_view/buy_in_wallet_view.dart';
|
||||||
import '../cashfusion/cashfusion_view.dart';
|
import '../cashfusion/cashfusion_view.dart';
|
||||||
import '../coin_control/coin_control_view.dart';
|
import '../coin_control/coin_control_view.dart';
|
||||||
import '../exchange_view/wallet_initiated_exchange_view.dart';
|
import '../exchange_view/wallet_initiated_exchange_view.dart';
|
||||||
import '../home_view/home_view.dart';
|
|
||||||
import '../monkey/monkey_view.dart';
|
import '../monkey/monkey_view.dart';
|
||||||
import '../notification_views/notifications_view.dart';
|
import '../notification_views/notifications_view.dart';
|
||||||
import '../ordinals/ordinals_view.dart';
|
import '../ordinals/ordinals_view.dart';
|
||||||
|
@ -257,40 +256,43 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime? _cachedTime;
|
// DateTime? _cachedTime;
|
||||||
|
|
||||||
Future<bool> _onWillPop() async {
|
Future<bool> _onWillPop() async {
|
||||||
if (_rescanningOnOpen || _lelantusRescanRecovery) {
|
if (_rescanningOnOpen || _lelantusRescanRecovery) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final now = DateTime.now();
|
_logout();
|
||||||
const timeout = Duration(milliseconds: 1500);
|
|
||||||
if (_cachedTime == null || now.difference(_cachedTime!) > timeout) {
|
return true;
|
||||||
_cachedTime = now;
|
// final now = DateTime.now();
|
||||||
unawaited(
|
// const timeout = Duration(milliseconds: 1500);
|
||||||
showDialog<dynamic>(
|
// if (_cachedTime == null || now.difference(_cachedTime!) > timeout) {
|
||||||
context: context,
|
// _cachedTime = now;
|
||||||
barrierDismissible: false,
|
// unawaited(
|
||||||
builder: (_) => WillPopScope(
|
// showDialog<dynamic>(
|
||||||
onWillPop: () async {
|
// context: context,
|
||||||
Navigator.of(context).popUntil(
|
// barrierDismissible: false,
|
||||||
ModalRoute.withName(HomeView.routeName),
|
// builder: (_) => WillPopScope(
|
||||||
);
|
// onWillPop: () async {
|
||||||
_logout();
|
// Navigator.of(context).popUntil(
|
||||||
return false;
|
// ModalRoute.withName(HomeView.routeName),
|
||||||
},
|
// );
|
||||||
child: const StackDialog(title: "Tap back again to exit wallet"),
|
// _logout();
|
||||||
),
|
// return false;
|
||||||
).timeout(
|
// },
|
||||||
timeout,
|
// child: const StackDialog(title: "Tap back again to exit wallet"),
|
||||||
onTimeout: () => Navigator.of(context).popUntil(
|
// ),
|
||||||
ModalRoute.withName(WalletView.routeName),
|
// ).timeout(
|
||||||
),
|
// timeout,
|
||||||
),
|
// onTimeout: () => Navigator.of(context).popUntil(
|
||||||
);
|
// ModalRoute.withName(WalletView.routeName),
|
||||||
}
|
// ),
|
||||||
return false;
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _logout() async {
|
void _logout() async {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'dart:async';
|
||||||
import 'package:decimal/decimal.dart';
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
|
|
||||||
import '../../../app_config.dart';
|
import '../../../app_config.dart';
|
||||||
import '../../../models/exchange/incomplete_exchange.dart';
|
import '../../../models/exchange/incomplete_exchange.dart';
|
||||||
|
@ -37,6 +36,7 @@ import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/desktop/secondary_button.dart';
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
import '../../../widgets/desktop/simple_desktop_dialog.dart';
|
import '../../../widgets/desktop/simple_desktop_dialog.dart';
|
||||||
import '../../../widgets/fade_stack.dart';
|
import '../../../widgets/fade_stack.dart';
|
||||||
|
import '../../../widgets/qr.dart';
|
||||||
import '../subwidgets/desktop_exchange_steps_indicator.dart';
|
import '../subwidgets/desktop_exchange_steps_indicator.dart';
|
||||||
import 'subwidgets/desktop_step_1.dart';
|
import 'subwidgets/desktop_step_1.dart';
|
||||||
import 'subwidgets/desktop_step_2.dart';
|
import 'subwidgets/desktop_step_2.dart';
|
||||||
|
@ -397,7 +397,7 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
|
||||||
height: 48,
|
height: 48,
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
// TODO: grab coin uri scheme from somewhere
|
// TODO: grab coin uri scheme from somewhere
|
||||||
// data: "${coin.uriScheme}:$receivingAddress",
|
// data: "${coin.uriScheme}:$receivingAddress",
|
||||||
data: ref.watch(
|
data: ref.watch(
|
||||||
|
@ -406,9 +406,6 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
size: 290,
|
size: 290,
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
|
|
@ -13,6 +13,7 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
import '../../pages/wallets_view/wallets_overview.dart';
|
import '../../pages/wallets_view/wallets_overview.dart';
|
||||||
import '../../providers/providers.dart';
|
import '../../providers/providers.dart';
|
||||||
import '../../themes/coin_icon_provider.dart';
|
import '../../themes/coin_icon_provider.dart';
|
||||||
|
@ -21,6 +22,7 @@ import '../../utilities/amount/amount.dart';
|
||||||
import '../../utilities/text_styles.dart';
|
import '../../utilities/text_styles.dart';
|
||||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../wallets/isar/providers/all_wallets_info_provider.dart';
|
import '../../wallets/isar/providers/all_wallets_info_provider.dart';
|
||||||
|
import '../../widgets/breathing.dart';
|
||||||
import '../../widgets/conditional_parent.dart';
|
import '../../widgets/conditional_parent.dart';
|
||||||
import '../../widgets/desktop/desktop_dialog.dart';
|
import '../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../widgets/desktop/desktop_dialog_close_button.dart';
|
import '../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
@ -146,71 +148,56 @@ class _DesktopWalletSummaryRowState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MouseRegion(
|
return Breathing(
|
||||||
onEnter: (_) => setState(
|
child: RoundedWhiteContainer(
|
||||||
() => _hovering = true,
|
padding: const EdgeInsets.all(20),
|
||||||
),
|
hoverColor: Colors.transparent,
|
||||||
onExit: (_) => setState(
|
onPressed: _onPressed,
|
||||||
() => _hovering = false,
|
child: Row(
|
||||||
),
|
children: [
|
||||||
child: AnimatedScale(
|
Expanded(
|
||||||
scale: _hovering ? 1.00 : 0.98,
|
flex: 4,
|
||||||
duration: const Duration(
|
child: Row(
|
||||||
milliseconds: 200,
|
children: [
|
||||||
),
|
SvgPicture.file(
|
||||||
child: RoundedWhiteContainer(
|
File(
|
||||||
padding: const EdgeInsets.all(20),
|
ref.watch(coinIconProvider(widget.coin)),
|
||||||
hoverColor: Colors.transparent,
|
|
||||||
onPressed: _onPressed,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 4,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
SvgPicture.file(
|
|
||||||
File(
|
|
||||||
ref.watch(coinIconProvider(widget.coin)),
|
|
||||||
),
|
|
||||||
width: 28,
|
|
||||||
height: 28,
|
|
||||||
),
|
),
|
||||||
const SizedBox(
|
width: 28,
|
||||||
width: 10,
|
height: 28,
|
||||||
),
|
|
||||||
Text(
|
|
||||||
widget.coin.prettyName,
|
|
||||||
style:
|
|
||||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textDark,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 4,
|
|
||||||
child: Text(
|
|
||||||
widget.walletCount == 1
|
|
||||||
? "${widget.walletCount} wallet"
|
|
||||||
: "${widget.walletCount} wallets",
|
|
||||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textSubtitle1,
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
widget.coin.prettyName,
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<StackColors>()!.textDark,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 4,
|
||||||
|
child: Text(
|
||||||
|
widget.walletCount == 1
|
||||||
|
? "${widget.walletCount} wallet"
|
||||||
|
: "${widget.walletCount} wallets",
|
||||||
|
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<StackColors>()!.textSubtitle1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
flex: 6,
|
Expanded(
|
||||||
child: TablePriceInfo(
|
flex: 6,
|
||||||
coin: widget.coin,
|
child: TablePriceInfo(
|
||||||
),
|
coin: widget.coin,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -294,7 +294,9 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: FiroCacheCoordinator.getSparkCacheSize(),
|
future: FiroCacheCoordinator.getSparkCacheSize(
|
||||||
|
wallet.cryptoCurrency.network,
|
||||||
|
),
|
||||||
builder: (_, snapshot) => Text(
|
builder: (_, snapshot) => Text(
|
||||||
snapshot.data ?? "",
|
snapshot.data ?? "",
|
||||||
),
|
),
|
||||||
|
|
|
@ -16,7 +16,6 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import '../../../../models/isar/models/isar_models.dart';
|
import '../../../../models/isar/models/isar_models.dart';
|
||||||
|
@ -42,6 +41,7 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../../../widgets/custom_loading_overlay.dart';
|
import '../../../../widgets/custom_loading_overlay.dart';
|
||||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../../widgets/desktop/secondary_button.dart';
|
import '../../../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../../../widgets/qr.dart';
|
||||||
import '../../../../widgets/rounded_white_container.dart';
|
import '../../../../widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
class DesktopReceive extends ConsumerStatefulWidget {
|
class DesktopReceive extends ConsumerStatefulWidget {
|
||||||
|
@ -476,15 +476,13 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
||||||
height: 32,
|
height: 32,
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: AddressUtils.buildUriString(
|
data: AddressUtils.buildUriString(
|
||||||
coin,
|
coin,
|
||||||
_qrcodeContent ?? "",
|
_qrcodeContent ?? "",
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
size: 200,
|
size: 200,
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
|
|
@ -60,6 +60,7 @@ import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
import '../../../../widgets/desktop/desktop_fee_dialog.dart';
|
import '../../../../widgets/desktop/desktop_fee_dialog.dart';
|
||||||
import '../../../../widgets/desktop/primary_button.dart';
|
import '../../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../../widgets/desktop/secondary_button.dart';
|
import '../../../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../../../widgets/dialogs/firo_exchange_address_dialog.dart';
|
||||||
import '../../../../widgets/fee_slider.dart';
|
import '../../../../widgets/fee_slider.dart';
|
||||||
import '../../../../widgets/icon_widgets/addressbook_icon.dart';
|
import '../../../../widgets/icon_widgets/addressbook_icon.dart';
|
||||||
import '../../../../widgets/icon_widgets/clipboard_icon.dart';
|
import '../../../../widgets/icon_widgets/clipboard_icon.dart';
|
||||||
|
@ -121,6 +122,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
|
|
||||||
bool _addressToggleFlag = false;
|
bool _addressToggleFlag = false;
|
||||||
|
|
||||||
|
bool _isFiroExWarningDisplayed = false;
|
||||||
|
|
||||||
bool _cryptoAmountChangeLock = false;
|
bool _cryptoAmountChangeLock = false;
|
||||||
late VoidCallback onCryptoAmountChanged;
|
late VoidCallback onCryptoAmountChanged;
|
||||||
|
|
||||||
|
@ -706,6 +709,19 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
address: address ?? "",
|
address: address ?? "",
|
||||||
isTestNet: wallet.cryptoCurrency.network.isTestNet,
|
isTestNet: wallet.cryptoCurrency.network.isTestNet,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ref.read(pIsExchangeAddress.state).state =
|
||||||
|
(coin as Firo).isExchangeAddress(address ?? "");
|
||||||
|
|
||||||
|
if (ref.read(publicPrivateBalanceStateProvider) == FiroType.spark &&
|
||||||
|
ref.read(pIsExchangeAddress) &&
|
||||||
|
!_isFiroExWarningDisplayed) {
|
||||||
|
_isFiroExWarningDisplayed = true;
|
||||||
|
showFiroExchangeAddressWarning(
|
||||||
|
context,
|
||||||
|
() => _isFiroExWarningDisplayed = false,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.read(pValidSendToAddress.notifier).state =
|
ref.read(pValidSendToAddress.notifier).state =
|
||||||
|
@ -842,6 +858,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
void initState() {
|
void initState() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
ref.refresh(feeSheetSessionCacheProvider);
|
ref.refresh(feeSheetSessionCacheProvider);
|
||||||
|
ref.refresh(pIsExchangeAddress);
|
||||||
ref.read(pValidSendToAddress.state).state = false;
|
ref.read(pValidSendToAddress.state).state = false;
|
||||||
ref.read(pValidSparkSendToAddress.state).state = false;
|
ref.read(pValidSparkSendToAddress.state).state = false;
|
||||||
});
|
});
|
||||||
|
@ -944,15 +961,31 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final firoType = ref.watch(publicPrivateBalanceStateProvider);
|
||||||
|
|
||||||
|
final isExchangeAddress = ref.watch(pIsExchangeAddress);
|
||||||
|
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
|
||||||
|
if (previous != next &&
|
||||||
|
next == FiroType.spark &&
|
||||||
|
isExchangeAddress &&
|
||||||
|
!_isFiroExWarningDisplayed) {
|
||||||
|
_isFiroExWarningDisplayed = true;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
|
(_) => showFiroExchangeAddressWarning(
|
||||||
|
context,
|
||||||
|
() => _isFiroExWarningDisplayed = false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
final showCoinControl = ref.watch(
|
final showCoinControl = ref.watch(
|
||||||
prefsChangeNotifierProvider.select(
|
prefsChangeNotifierProvider.select(
|
||||||
(value) => value.enableCoinControl,
|
(value) => value.enableCoinControl,
|
||||||
),
|
),
|
||||||
) &&
|
) &&
|
||||||
ref.watch(pWallets).getWallet(walletId) is CoinControlInterface &&
|
ref.watch(pWallets).getWallet(walletId) is CoinControlInterface &&
|
||||||
(coin is Firo
|
(coin is Firo ? firoType == FiroType.public : true);
|
||||||
? ref.watch(publicPrivateBalanceStateProvider) == FiroType.public
|
|
||||||
: true);
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -978,7 +1011,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
DropdownButtonHideUnderline(
|
DropdownButtonHideUnderline(
|
||||||
child: DropdownButton2(
|
child: DropdownButton2(
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
value: ref.watch(publicPrivateBalanceStateProvider.state).state,
|
value: firoType,
|
||||||
items: [
|
items: [
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: FiroType.spark,
|
value: FiroType.spark,
|
||||||
|
@ -1464,8 +1497,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
if (_address == null || _address!.isEmpty) {
|
if (_address == null || _address!.isEmpty) {
|
||||||
error = null;
|
error = null;
|
||||||
} else if (coin is Firo) {
|
} else if (coin is Firo) {
|
||||||
if (ref.watch(publicPrivateBalanceStateProvider) ==
|
if (firoType == FiroType.lelantus) {
|
||||||
FiroType.lelantus) {
|
|
||||||
if (_data != null && _data!.contactLabel == _address) {
|
if (_data != null && _data!.contactLabel == _address) {
|
||||||
error = SparkInterface.validateSparkAddress(
|
error = SparkInterface.validateSparkAddress(
|
||||||
address: _data!.address,
|
address: _data!.address,
|
||||||
|
@ -1526,15 +1558,13 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
if (isStellar ||
|
if (isStellar ||
|
||||||
(ref.watch(pValidSparkSendToAddress) &&
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
ref.watch(publicPrivateBalanceStateProvider) !=
|
firoType != FiroType.lelantus))
|
||||||
FiroType.lelantus))
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
if (isStellar ||
|
if (isStellar ||
|
||||||
(ref.watch(pValidSparkSendToAddress) &&
|
(ref.watch(pValidSparkSendToAddress) &&
|
||||||
ref.watch(publicPrivateBalanceStateProvider) !=
|
firoType != FiroType.lelantus))
|
||||||
FiroType.lelantus))
|
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
|
|
|
@ -19,8 +19,7 @@ import '../../../../../providers/global/wallets_provider.dart';
|
||||||
import '../../../../../themes/stack_colors.dart';
|
import '../../../../../themes/stack_colors.dart';
|
||||||
import '../../../../../utilities/assets.dart';
|
import '../../../../../utilities/assets.dart';
|
||||||
import '../../../../../utilities/text_styles.dart';
|
import '../../../../../utilities/text_styles.dart';
|
||||||
import '../../../../../wallets/crypto_currency/coins/banano.dart';
|
import '../../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
import '../../../../../wallets/crypto_currency/coins/firo.dart';
|
|
||||||
import '../../../../../wallets/isar/models/wallet_info.dart';
|
import '../../../../../wallets/isar/models/wallet_info.dart';
|
||||||
import '../../../../../wallets/isar/providers/wallet_info_provider.dart';
|
import '../../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||||
import '../../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
import '../../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
||||||
|
@ -187,7 +186,9 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
|
||||||
onPressed: () async => widget.onFusionPressed?.call(),
|
onPressed: () async => widget.onFusionPressed?.call(),
|
||||||
),
|
),
|
||||||
if (wallet is SparkInterface)
|
if (wallet is SparkInterface)
|
||||||
const _MoreFeaturesClearSparkCacheItem(),
|
_MoreFeaturesClearSparkCacheItem(
|
||||||
|
cryptoCurrency: wallet.cryptoCurrency,
|
||||||
|
),
|
||||||
if (wallet is LelantusInterface)
|
if (wallet is LelantusInterface)
|
||||||
_MoreFeaturesItemBase(
|
_MoreFeaturesItemBase(
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -371,10 +372,10 @@ class _MoreFeaturesItemBase extends StatelessWidget {
|
||||||
class _MoreFeaturesClearSparkCacheItem extends StatefulWidget {
|
class _MoreFeaturesClearSparkCacheItem extends StatefulWidget {
|
||||||
const _MoreFeaturesClearSparkCacheItem({
|
const _MoreFeaturesClearSparkCacheItem({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.cryptoCurrency,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const double iconSizeBG = 46;
|
final CryptoCurrency cryptoCurrency;
|
||||||
static const double iconSize = 24;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_MoreFeaturesClearSparkCacheItem> createState() =>
|
State<_MoreFeaturesClearSparkCacheItem> createState() =>
|
||||||
|
@ -396,7 +397,9 @@ class _MoreFeaturesClearSparkCacheItemState
|
||||||
}
|
}
|
||||||
_onPressedLock = true;
|
_onPressedLock = true;
|
||||||
try {
|
try {
|
||||||
await FiroCacheCoordinator.clearSharedCache();
|
await FiroCacheCoordinator.clearSharedCache(
|
||||||
|
widget.cryptoCurrency.network,
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
// trigger rebuild for cache size display
|
// trigger rebuild for cache size display
|
||||||
});
|
});
|
||||||
|
@ -434,7 +437,9 @@ class _MoreFeaturesClearSparkCacheItemState
|
||||||
style: STextStyles.w600_20(context),
|
style: STextStyles.w600_20(context),
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: FiroCacheCoordinator.getSparkCacheSize(),
|
future: FiroCacheCoordinator.getSparkCacheSize(
|
||||||
|
widget.cryptoCurrency.network,
|
||||||
|
),
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
return Text(
|
return Text(
|
||||||
snapshot.data ?? "",
|
snapshot.data ?? "",
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import '../../../../themes/stack_colors.dart';
|
|
||||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import '../../../../widgets/qr.dart';
|
||||||
|
|
||||||
class QRCodeDesktopPopupContent extends StatelessWidget {
|
class QRCodeDesktopPopupContent extends StatelessWidget {
|
||||||
const QRCodeDesktopPopupContent({
|
const QRCodeDesktopPopupContent({
|
||||||
|
@ -39,11 +39,9 @@ class QRCodeDesktopPopupContent extends StatelessWidget {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 14,
|
height: 14,
|
||||||
),
|
),
|
||||||
QrImageView(
|
QR(
|
||||||
data: value,
|
data: value,
|
||||||
size: 300,
|
size: 300,
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).extension<StackColors>()!.accentColorDark,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,27 +9,37 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../wallet/public_private_balance_state_provider.dart';
|
|
||||||
import '../../utilities/amount/amount.dart';
|
import '../../utilities/amount/amount.dart';
|
||||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
import '../wallet/public_private_balance_state_provider.dart';
|
||||||
|
|
||||||
final pSendAmount = StateProvider.autoDispose<Amount?>((_) => null);
|
final pSendAmount = StateProvider.autoDispose<Amount?>((_) => null);
|
||||||
final pValidSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
final pValidSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||||
final pValidSparkSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
final pValidSparkSendToAddress = StateProvider.autoDispose<bool>((_) => false);
|
||||||
|
|
||||||
|
final pIsExchangeAddress = StateProvider<bool>((_) => false);
|
||||||
|
|
||||||
final pPreviewTxButtonEnabled =
|
final pPreviewTxButtonEnabled =
|
||||||
Provider.autoDispose.family<bool, CryptoCurrency>((ref, coin) {
|
Provider.autoDispose.family<bool, CryptoCurrency>((ref, coin) {
|
||||||
final amount = ref.watch(pSendAmount) ?? Amount.zero;
|
final amount = ref.watch(pSendAmount) ?? Amount.zero;
|
||||||
|
|
||||||
if (coin is Firo) {
|
if (coin is Firo) {
|
||||||
if (ref.watch(publicPrivateBalanceStateProvider) == FiroType.lelantus) {
|
final firoType = ref.watch(publicPrivateBalanceStateProvider);
|
||||||
return ref.watch(pValidSendToAddress) &&
|
switch (firoType) {
|
||||||
!ref.watch(pValidSparkSendToAddress) &&
|
case FiroType.lelantus:
|
||||||
amount > Amount.zero;
|
return ref.watch(pValidSendToAddress) &&
|
||||||
} else {
|
!ref.watch(pValidSparkSendToAddress) &&
|
||||||
return (ref.watch(pValidSendToAddress) ||
|
amount > Amount.zero;
|
||||||
ref.watch(pValidSparkSendToAddress)) &&
|
|
||||||
amount > Amount.zero;
|
case FiroType.spark:
|
||||||
|
return (ref.watch(pValidSendToAddress) ||
|
||||||
|
ref.watch(pValidSparkSendToAddress)) &&
|
||||||
|
!ref.watch(pIsExchangeAddress) &&
|
||||||
|
amount > Amount.zero;
|
||||||
|
|
||||||
|
case FiroType.public:
|
||||||
|
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
return ref.watch(pValidSendToAddress) && amount > Amount.zero;
|
||||||
|
|
|
@ -1982,13 +1982,18 @@ class RouteGenerator {
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case SparkInfoView.routeName:
|
case SparkInfoView.routeName:
|
||||||
return getRoute(
|
if (args is String) {
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
return getRoute(
|
||||||
builder: (_) => const SparkInfoView(),
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
settings: RouteSettings(
|
builder: (_) => SparkInfoView(
|
||||||
name: settings.name,
|
walletId: args,
|
||||||
),
|
),
|
||||||
);
|
settings: RouteSettings(
|
||||||
|
name: settings.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
// == Desktop specific routes ============================================
|
// == Desktop specific routes ============================================
|
||||||
case CreatePasswordView.routeName:
|
case CreatePasswordView.routeName:
|
||||||
|
|
|
@ -11,18 +11,19 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import '../app_config.dart';
|
||||||
import '../db/hive/db.dart';
|
import '../db/hive/db.dart';
|
||||||
import '../services/event_bus/events/global/tor_status_changed_event.dart';
|
import '../services/event_bus/events/global/tor_status_changed_event.dart';
|
||||||
import '../services/event_bus/global_event_bus.dart';
|
import '../services/event_bus/global_event_bus.dart';
|
||||||
import '../app_config.dart';
|
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
import '../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
||||||
import 'amount/amount_unit.dart';
|
import 'amount/amount_unit.dart';
|
||||||
import 'constants.dart';
|
import 'constants.dart';
|
||||||
import 'enums/backup_frequency_type.dart';
|
import 'enums/backup_frequency_type.dart';
|
||||||
import 'enums/languages_enum.dart';
|
import 'enums/languages_enum.dart';
|
||||||
import 'enums/sync_type_enum.dart';
|
import 'enums/sync_type_enum.dart';
|
||||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
|
||||||
import '../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class Prefs extends ChangeNotifier {
|
class Prefs extends ChangeNotifier {
|
||||||
Prefs._();
|
Prefs._();
|
||||||
|
@ -1103,4 +1104,30 @@ class Prefs extends ChangeNotifier {
|
||||||
|
|
||||||
return actualMap;
|
return actualMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Automatic PIN entry.
|
||||||
|
|
||||||
|
bool _autoPin = false;
|
||||||
|
|
||||||
|
bool get autoPin => _autoPin;
|
||||||
|
|
||||||
|
set autoPin(bool autoPin) {
|
||||||
|
if (_autoPin != autoPin) {
|
||||||
|
DB.instance.put<dynamic>(
|
||||||
|
boxName: DB.boxNamePrefs,
|
||||||
|
key: "autoPin",
|
||||||
|
value: autoPin,
|
||||||
|
);
|
||||||
|
_autoPin = autoPin;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _getAutoPin() async {
|
||||||
|
return await DB.instance.get<dynamic>(
|
||||||
|
boxName: DB.boxNamePrefs,
|
||||||
|
key: "autoPin",
|
||||||
|
) as bool? ??
|
||||||
|
false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||||
|
|
||||||
|
import '../../../models/coinlib/exp2pkh_address.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||||
import '../../../models/node_model.dart';
|
import '../../../models/node_model.dart';
|
||||||
import '../../../utilities/amount/amount.dart';
|
import '../../../utilities/amount/amount.dart';
|
||||||
|
@ -77,6 +80,21 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
fractionDigits: fractionDigits,
|
fractionDigits: fractionDigits,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Uint8List get exAddressVersion {
|
||||||
|
switch (network) {
|
||||||
|
case CryptoCurrencyNetwork.main:
|
||||||
|
// https://github.com/firoorg/firo/blob/master/src/chainparams.cpp#L357
|
||||||
|
return Uint8List.fromList([0x01, 0xb9, 0xbb]);
|
||||||
|
|
||||||
|
case CryptoCurrencyNetwork.test:
|
||||||
|
// https://github.com/firoorg/firo/blob/master/src/chainparams.cpp#L669
|
||||||
|
return Uint8List.fromList([0x01, 0xb9, 0xb1]);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Exception("Unsupported network: $network");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
coinlib.Network get networkParams {
|
coinlib.Network get networkParams {
|
||||||
switch (network) {
|
switch (network) {
|
||||||
|
@ -169,7 +187,11 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
coinlib.Address.fromString(address, networkParams);
|
coinlib.Address.fromString(address, networkParams);
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return validateSparkAddress(address);
|
if (validateSparkAddress(address)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return isExchangeAddress(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +202,18 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isExchangeAddress(String address) {
|
||||||
|
try {
|
||||||
|
EXP2PKHAddress.fromString(
|
||||||
|
address,
|
||||||
|
exAddressVersion,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NodeModel get defaultNode {
|
NodeModel get defaultNode {
|
||||||
switch (network) {
|
switch (network) {
|
||||||
|
|
|
@ -387,6 +387,7 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
||||||
parseAnonFees();
|
parseAnonFees();
|
||||||
final tags = await FiroCacheCoordinator.getUsedCoinTagsFor(
|
final tags = await FiroCacheCoordinator.getUsedCoinTagsFor(
|
||||||
txid: txData["txid"] as String,
|
txid: txData["txid"] as String,
|
||||||
|
network: cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
spentSparkCoins = sparkCoinsInvolvedSpent
|
spentSparkCoins = sparkCoinsInvolvedSpent
|
||||||
.where(
|
.where(
|
||||||
|
@ -631,9 +632,23 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
||||||
BigInt.from(jsonUTXO["value"] as int);
|
BigInt.from(jsonUTXO["value"] as int);
|
||||||
|
|
||||||
if (blocked) {
|
if (blocked) {
|
||||||
blockedReason = "Possible masternode output. "
|
try {
|
||||||
|
blocked = await electrumXClient.isMasterNodeCollateral(
|
||||||
|
txid: jsonTX!["txid"] as String,
|
||||||
|
index: jsonUTXO["tx_pos"] as int,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// call failed, lock utxo just in case
|
||||||
|
// it should logically already be blocked
|
||||||
|
// but just in case
|
||||||
|
blocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blocked) {
|
||||||
|
blockedReason = "Possible masternode collateral. "
|
||||||
"Unlock and spend at your own risk.";
|
"Unlock and spend at your own risk.";
|
||||||
label = "Possible masternode";
|
label = "Possible masternode collateral";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,12 +713,14 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
||||||
FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||||
i,
|
i,
|
||||||
electrumXClient,
|
electrumXClient,
|
||||||
|
cryptoCurrency.network,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final sparkUsedCoinTagsFuture =
|
final sparkUsedCoinTagsFuture =
|
||||||
FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags(
|
FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags(
|
||||||
electrumXClient,
|
electrumXClient,
|
||||||
|
cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
|
|
||||||
// receiving addresses
|
// receiving addresses
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:isar/isar.dart';
|
||||||
import '../../../electrumx_rpc/cached_electrumx_client.dart';
|
import '../../../electrumx_rpc/cached_electrumx_client.dart';
|
||||||
import '../../../electrumx_rpc/client_manager.dart';
|
import '../../../electrumx_rpc/client_manager.dart';
|
||||||
import '../../../electrumx_rpc/electrumx_client.dart';
|
import '../../../electrumx_rpc/electrumx_client.dart';
|
||||||
|
import '../../../models/coinlib/exp2pkh_address.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/v2/input_v2.dart';
|
import '../../../models/isar/models/blockchain_data/v2/input_v2.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/v2/output_v2.dart';
|
import '../../../models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
|
@ -24,6 +25,7 @@ import '../../crypto_currency/coins/firo.dart';
|
||||||
import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
|
import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
import '../impl/bitcoin_wallet.dart';
|
import '../impl/bitcoin_wallet.dart';
|
||||||
|
import '../impl/firo_wallet.dart';
|
||||||
import '../impl/peercoin_wallet.dart';
|
import '../impl/peercoin_wallet.dart';
|
||||||
import '../intermediate/bip39_hd_wallet.dart';
|
import '../intermediate/bip39_hd_wallet.dart';
|
||||||
import 'cpfp_interface.dart';
|
import 'cpfp_interface.dart';
|
||||||
|
@ -725,11 +727,23 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
|
|
||||||
// Add transaction output
|
// Add transaction output
|
||||||
for (var i = 0; i < txData.recipients!.length; i++) {
|
for (var i = 0; i < txData.recipients!.length; i++) {
|
||||||
final address = coinlib.Address.fromString(
|
late final coinlib.Address address;
|
||||||
normalizeAddress(txData.recipients![i].address),
|
|
||||||
cryptoCurrency.networkParams,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
address = coinlib.Address.fromString(
|
||||||
|
normalizeAddress(txData.recipients![i].address),
|
||||||
|
cryptoCurrency.networkParams,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
if (this is FiroWallet) {
|
||||||
|
address = EXP2PKHAddress.fromString(
|
||||||
|
normalizeAddress(txData.recipients![i].address),
|
||||||
|
(cryptoCurrency as Firo).exAddressVersion,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
final output = coinlib.Output.fromAddress(
|
final output = coinlib.Output.fromAddress(
|
||||||
txData.recipients![i].amount.raw,
|
txData.recipients![i].amount.raw,
|
||||||
address,
|
address,
|
||||||
|
|
|
@ -278,13 +278,17 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
final List<Map<String, dynamic>> setMaps = [];
|
final List<Map<String, dynamic>> setMaps = [];
|
||||||
final List<({int groupId, String blockHash})> idAndBlockHashes = [];
|
final List<({int groupId, String blockHash})> idAndBlockHashes = [];
|
||||||
for (int i = 1; i <= currentId; i++) {
|
for (int i = 1; i <= currentId; i++) {
|
||||||
final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(i);
|
final resultSet = await FiroCacheCoordinator.getSetCoinsForGroupId(
|
||||||
|
i,
|
||||||
|
network: cryptoCurrency.network,
|
||||||
|
);
|
||||||
if (resultSet.isEmpty) {
|
if (resultSet.isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||||
i,
|
i,
|
||||||
|
cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
throw Exception("The `info` should never be null here");
|
throw Exception("The `info` should never be null here");
|
||||||
|
@ -716,13 +720,13 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"refreshSparkMempoolData() failed: $e",
|
"_refreshSparkCoinsMempoolCheck() failed: $e",
|
||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
);
|
);
|
||||||
return [];
|
return [];
|
||||||
} finally {
|
} finally {
|
||||||
Logging.instance.log(
|
Logging.instance.log(
|
||||||
"$walletId ${info.name} refreshSparkCoinsMempoolCheck() run "
|
"$walletId ${info.name} _refreshSparkCoinsMempoolCheck() run "
|
||||||
"duration: ${DateTime.now().difference(start)}",
|
"duration: ${DateTime.now().difference(start)}",
|
||||||
level: LogLevel.Debug,
|
level: LogLevel.Debug,
|
||||||
);
|
);
|
||||||
|
@ -741,6 +745,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
final setExists =
|
final setExists =
|
||||||
await FiroCacheCoordinator.checkSetInfoForGroupIdExists(
|
await FiroCacheCoordinator.checkSetInfoForGroupIdExists(
|
||||||
id,
|
id,
|
||||||
|
cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
if (!setExists) {
|
if (!setExists) {
|
||||||
groupIds.add(id);
|
groupIds.add(id);
|
||||||
|
@ -755,6 +760,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
FiroCacheCoordinator.runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||||
e,
|
e,
|
||||||
electrumXClient,
|
electrumXClient,
|
||||||
|
cryptoCurrency.network,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -763,6 +769,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
...possibleFutures,
|
...possibleFutures,
|
||||||
FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags(
|
FiroCacheCoordinator.runFetchAndUpdateSparkUsedCoinTags(
|
||||||
electrumXClient,
|
electrumXClient,
|
||||||
|
cryptoCurrency.network,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -782,11 +789,13 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
groupIdTimestampUTCMap[i.toString()] as int? ?? 0;
|
groupIdTimestampUTCMap[i.toString()] as int? ?? 0;
|
||||||
final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
final info = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||||
i,
|
i,
|
||||||
|
cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
final anonymitySetResult =
|
final anonymitySetResult =
|
||||||
await FiroCacheCoordinator.getSetCoinsForGroupId(
|
await FiroCacheCoordinator.getSetCoinsForGroupId(
|
||||||
i,
|
i,
|
||||||
newerThanTimeStamp: lastCheckedTimeStampUTC,
|
newerThanTimeStamp: lastCheckedTimeStampUTC,
|
||||||
|
network: cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
final coinsRaw = anonymitySetResult
|
final coinsRaw = anonymitySetResult
|
||||||
.map(
|
.map(
|
||||||
|
@ -882,7 +891,10 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
// only fetch tags from db if we need them to compare against any items
|
// only fetch tags from db if we need them to compare against any items
|
||||||
// in coinsToCheck
|
// in coinsToCheck
|
||||||
if (coinsToCheck.isNotEmpty) {
|
if (coinsToCheck.isNotEmpty) {
|
||||||
spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags(0);
|
spentCoinTags = await FiroCacheCoordinator.getUsedCoinTags(
|
||||||
|
0,
|
||||||
|
cryptoCurrency.network,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check and update coins if required
|
// check and update coins if required
|
||||||
|
@ -992,6 +1004,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
|
|
||||||
final pairs = await FiroCacheCoordinator.getUsedCoinTxidsFor(
|
final pairs = await FiroCacheCoordinator.getUsedCoinTxidsFor(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
network: cryptoCurrency.network,
|
||||||
);
|
);
|
||||||
|
|
||||||
pairs.removeWhere((e) => usedCoinTxidsFoundLocally.contains(e.txid));
|
pairs.removeWhere((e) => usedCoinTxidsFoundLocally.contains(e.txid));
|
||||||
|
|
32
lib/widgets/breathing.dart
Normal file
32
lib/widgets/breathing.dart
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Breathing extends StatefulWidget {
|
||||||
|
const Breathing({super.key, required this.child});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Breathing> createState() => _BreathingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BreathingState extends State<Breathing> {
|
||||||
|
bool _hovering = false;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MouseRegion(
|
||||||
|
onEnter: (_) => setState(
|
||||||
|
() => _hovering = true,
|
||||||
|
),
|
||||||
|
onExit: (_) => setState(
|
||||||
|
() => _hovering = false,
|
||||||
|
),
|
||||||
|
child: AnimatedScale(
|
||||||
|
scale: _hovering ? 1.00 : 0.98,
|
||||||
|
duration: const Duration(
|
||||||
|
milliseconds: 200,
|
||||||
|
),
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
30
lib/widgets/dialogs/firo_exchange_address_dialog.dart
Normal file
30
lib/widgets/dialogs/firo_exchange_address_dialog.dart
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../utilities/util.dart';
|
||||||
|
import '../stack_dialog.dart';
|
||||||
|
|
||||||
|
class FiroExchangeAddressDialog extends StatelessWidget {
|
||||||
|
const FiroExchangeAddressDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StackOkDialog(
|
||||||
|
title: "Firo exchange address detected",
|
||||||
|
message: "Sending to an exchange address from a Spark balance is not"
|
||||||
|
" allowed. Please send from your transparent balance.",
|
||||||
|
desktopPopRootNavigator: Util.isDesktop,
|
||||||
|
maxWidth: Util.isDesktop ? 500 : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showFiroExchangeAddressWarning(
|
||||||
|
BuildContext context,
|
||||||
|
VoidCallback onClosed,
|
||||||
|
) async {
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => const FiroExchangeAddressDialog(),
|
||||||
|
);
|
||||||
|
onClosed();
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import '../../../notifications/show_flush_bar.dart';
|
import '../../../notifications/show_flush_bar.dart';
|
||||||
|
@ -17,6 +17,7 @@ import '../../../utilities/text_styles.dart';
|
||||||
import '../../../utilities/util.dart';
|
import '../../../utilities/util.dart';
|
||||||
import '../../conditional_parent.dart';
|
import '../../conditional_parent.dart';
|
||||||
import '../../desktop/secondary_button.dart';
|
import '../../desktop/secondary_button.dart';
|
||||||
|
import '../../qr.dart';
|
||||||
import '../../rounded_container.dart';
|
import '../../rounded_container.dart';
|
||||||
import '../../rounded_white_container.dart';
|
import '../../rounded_white_container.dart';
|
||||||
import '../simple_mobile_dialog.dart';
|
import '../simple_mobile_dialog.dart';
|
||||||
|
@ -154,18 +155,9 @@ class _FrostStepQrDialogState extends State<FrostStepQrDialog> {
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: QrImageView(
|
child: QR(
|
||||||
data: widget.data,
|
data: widget.data,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
foregroundColor: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.accentColorDark,
|
|
||||||
// dataModuleStyle: QrDataModuleStyle(
|
|
||||||
// dataModuleShape: QrDataModuleShape.square,
|
|
||||||
// color: Theme.of(context)
|
|
||||||
// .extension<StackColors>()!
|
|
||||||
// .accentColorDark,
|
|
||||||
// ),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
27
lib/widgets/qr.dart
Normal file
27
lib/widgets/qr.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
|
||||||
|
/// Centralised Qr code image widget
|
||||||
|
class QR extends StatelessWidget {
|
||||||
|
const QR({super.key, required this.data, this.size, this.padding});
|
||||||
|
|
||||||
|
final String data;
|
||||||
|
final double? size;
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return QrImageView(
|
||||||
|
data: data,
|
||||||
|
size: size,
|
||||||
|
padding: padding ?? const EdgeInsets.all(10),
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
// backgroundColor:
|
||||||
|
// Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
// foregroundColor: Theme.of(context)
|
||||||
|
// .extension<StackColors>()!
|
||||||
|
// .accentColorDark,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -188,14 +188,19 @@ class StackOkDialog extends StatelessWidget {
|
||||||
height: 8,
|
height: 8,
|
||||||
),
|
),
|
||||||
if (message != null)
|
if (message != null)
|
||||||
Column(
|
ConstrainedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
constraints:
|
||||||
children: [
|
BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
||||||
Text(
|
child: Row(
|
||||||
message!,
|
children: [
|
||||||
style: STextStyles.smallMed14(context),
|
Flexible(
|
||||||
),
|
child: Text(
|
||||||
],
|
message!,
|
||||||
|
style: STextStyles.smallMed14(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
|
|
|
@ -1807,8 +1807,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: f1d02f7ad489df3119a540a7f31485db6d837843
|
ref: "647cadc3c82c276dc07915b02d24538fd610f220"
|
||||||
resolved-ref: f1d02f7ad489df3119a540a7f31485db6d837843
|
resolved-ref: "647cadc3c82c276dc07915b02d24538fd610f220"
|
||||||
url: "https://github.com/cypherstack/tor.git"
|
url: "https://github.com/cypherstack/tor.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
|
Loading…
Reference in a new issue