diff --git a/.metadata b/.metadata index d1551205d..675f4a740 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + revision: f92f44110e87bad5ff168335c36da6f6053036e6 channel: stable project_type: app @@ -13,17 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - - platform: linux - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 + base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 - platform: macos - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - - platform: windows - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 + base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 # User provided section diff --git a/assets/icon/macos-icon.png b/assets/icon/macos-icon.png new file mode 100644 index 000000000..e4a263022 Binary files /dev/null and b/assets/icon/macos-icon.png differ diff --git a/assets/svg/monkey.svg b/assets/svg/monkey.svg new file mode 100644 index 000000000..565ac4fdf --- /dev/null +++ b/assets/svg/monkey.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ordinal.svg b/assets/svg/ordinal.svg new file mode 100644 index 000000000..7ac863a84 --- /dev/null +++ b/assets/svg/ordinal.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/svg/send.svg b/assets/svg/send.svg new file mode 100644 index 000000000..61fe2a206 --- /dev/null +++ b/assets/svg/send.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/crypto_plugins/flutter_libepiccash b/crypto_plugins/flutter_libepiccash index cd12741de..f677dec0b 160000 --- a/crypto_plugins/flutter_libepiccash +++ b/crypto_plugins/flutter_libepiccash @@ -1 +1 @@ -Subproject commit cd12741de19e4faef39a23b7d543a2452524990a +Subproject commit f677dec0b34d3f9fe8fce2bc8ff5c508c3f3bb9a diff --git a/crypto_plugins/flutter_liblelantus b/crypto_plugins/flutter_liblelantus index ec3cf5e8e..9cd241b5e 160000 --- a/crypto_plugins/flutter_liblelantus +++ b/crypto_plugins/flutter_liblelantus @@ -1 +1 @@ -Subproject commit ec3cf5e8e1b90e006188aa8c323d4cd52dbfa9b9 +Subproject commit 9cd241b5ea142e21c01dd7639b42603281c43287 diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 26a152fea..407425c9f 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 26a152fea3ca4b8c3f1130392a02f579c2ff218c +Subproject commit 407425c9fcf7a30c81f1345246c7225bc18b5cd5 diff --git a/dockerfile.linux b/dockerfile.linux deleted file mode 100644 index 4a3867008..000000000 --- a/dockerfile.linux +++ /dev/null @@ -1,17 +0,0 @@ -FROM ubuntu:20.04 as base -COPY . /stack_wallet -WORKDIR /stack_wallet/scripts/linux -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y git=1:2.25.1-1ubuntu3.6 make=4.2.1-1.2 curl=7.68.0-1ubuntu2.14 cargo=0.62.0ubuntu0libgit2-0ubuntu0.20.04.1 \ - file=1:5.38-4 ca-certificates=20211016ubuntu0.20.04.1 cmake=3.16.3-1ubuntu1.20.04.1 cmake-data=3.16.3-1ubuntu1.20.04.1 g++=4:9.3.0-1ubuntu2 libgmp-dev=2:6.2.0+dfsg-4ubuntu0.1 libssl-dev=1.1.1f-1ubuntu2.16 \ - libclang-dev=1:10.0-50~exp1 unzip=6.0-25ubuntu1.1 python3=3.8.2-0ubuntu2 pkg-config=0.29.1-0ubuntu4 libglib2.0-dev=2.64.6-1~ubuntu20.04.4 libgcrypt20-dev=1.8.5-5ubuntu1.1 gettext-base=0.19.8.1-10build1 \ - libgirepository1.0-dev=1.64.1-1~ubuntu20.04.1 valac=0.48.6-0ubuntu1 xsltproc=1.1.34-4ubuntu0.20.04.1 docbook-xsl=1.79.1+dfsg-2 python3-pip=20.0.2-5ubuntu1.6 ninja-build=1.10.0-1build1 clang=1:10.0-50~exp1 \ - libgtk-3-dev=3.24.20-0ubuntu1.1 libunbound-dev=1.9.4-2ubuntu1.4 libzmq3-dev=4.3.2-2ubuntu1 libtool=2.4.6-14 autoconf=2.69-11.1 automake=1:1.16.1-4ubuntu6 bison=2:3.5.1+dfsg-1 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3.1.2 pygments==2.13.0 toml==0.10.2 typogrify==2.0.7 tomli==2.0.1 && cd .. && ./prebuild.sh && cd linux && ./build_all.sh -WORKDIR / -RUN git clone https://github.com/flutter/flutter.git -b 3.3.4 -ENV PATH "$PATH:/flutter/bin" -WORKDIR /stack_wallet -RUN flutter pub get Linux && flutter build linux -ENTRYPOINT ["/bin/bash"] diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 931a1cee4..2fb82c806 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -177,6 +177,9 @@ class DB { } Future> getTxCacheBox({required Coin coin}) async { + if (_txCacheBoxes[coin]?.isOpen != true) { + _txCacheBoxes.remove(coin); + } return _txCacheBoxes[coin] ??= await Hive.openBox(_boxNameTxCache(coin: coin)); } @@ -186,6 +189,9 @@ class DB { } Future> getAnonymitySetCacheBox({required Coin coin}) async { + if (_setCacheBoxes[coin]?.isOpen != true) { + _setCacheBoxes.remove(coin); + } return _setCacheBoxes[coin] ??= await Hive.openBox(_boxNameSetCache(coin: coin)); } @@ -195,6 +201,9 @@ class DB { } Future> getUsedSerialsCacheBox({required Coin coin}) async { + if (_usedSerialsCacheBoxes[coin]?.isOpen != true) { + _usedSerialsCacheBoxes.remove(coin); + } return _usedSerialsCacheBoxes[coin] ??= await Hive.openBox(_boxNameUsedSerialsCache(coin: coin)); } @@ -265,8 +274,12 @@ class DB { {required dynamic key, required String boxName}) async => await mutex.protect(() async => await Hive.box(boxName).delete(key)); - Future deleteAll({required String boxName}) async => - await mutex.protect(() async => await Hive.box(boxName).clear()); + Future deleteAll({required String boxName}) async { + await mutex.protect(() async { + final box = await Hive.openBox(boxName); + await box.clear(); + }); + } Future deleteBoxFromDisk({required String boxName}) async => await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName)); diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index bb6ea5e8d..9465de12b 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -15,6 +15,7 @@ import 'package:stackwallet/exceptions/main_db/main_db_exception.dart'; import 'package:stackwallet/models/isar/models/block_explorer.dart'; import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -54,6 +55,8 @@ class MainDB { TransactionBlockExplorerSchema, StackThemeSchema, ContactEntrySchema, + OrdinalSchema, + LelantusCoinSchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, @@ -246,7 +249,8 @@ class MainDB { await isar.utxos.putAll(utxos); }); - Future updateUTXOs(String walletId, List utxos) async { + Future updateUTXOs(String walletId, List utxos) async { + bool newUTXO = false; await isar.writeTxn(() async { final set = utxos.toSet(); for (final utxo in utxos) { @@ -268,12 +272,16 @@ class MainDB { blockHash: utxo.blockHash, ), ); + } else { + newUTXO = true; } } await isar.utxos.where().walletIdEqualTo(walletId).deleteAll(); await isar.utxos.putAll(set.toList()); }); + + return newUTXO; } Stream watchUTXO({ @@ -370,6 +378,8 @@ class MainDB { final transactionCount = await getTransactions(walletId).count(); final addressCount = await getAddresses(walletId).count(); final utxoCount = await getUTXOs(walletId).count(); + final lelantusCoinCount = + await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); await isar.writeTxn(() async { const paginateLimit = 50; @@ -403,6 +413,18 @@ class MainDB { .findAll(); await isar.utxos.deleteAll(utxoIds); } + + // lelantusCoins + for (int i = 0; i < lelantusCoinCount; i += paginateLimit) { + final lelantusCoinIds = await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .offset(i) + .limit(paginateLimit) + .idProperty() + .findAll(); + await isar.lelantusCoins.deleteAll(lelantusCoinIds); + } }); } @@ -497,4 +519,15 @@ class MainDB { isar.writeTxn(() async { await isar.ethContracts.putAll(contracts); }); + + // ========== Lelantus ======================================================= + + Future getHighestUsedMintIndex({required String walletId}) async { + return await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + } } diff --git a/lib/dto/ethereum/eth_token_tx_extra_dto.dart b/lib/dto/ethereum/eth_token_tx_extra_dto.dart index 144194914..aab41bb76 100644 --- a/lib/dto/ethereum/eth_token_tx_extra_dto.dart +++ b/lib/dto/ethereum/eth_token_tx_extra_dto.dart @@ -46,7 +46,7 @@ class EthTokenTxExtraDTO { ), gas: _amountFromJsonNum(map['gas']), gasPrice: _amountFromJsonNum(map['gasPrice']), - nonce: map['nonce'] as int, + nonce: map['nonce'] as int?, input: map['input'] as String, gasCost: _amountFromJsonNum(map['gasCost']), gasUsed: _amountFromJsonNum(map['gasUsed']), @@ -63,7 +63,7 @@ class EthTokenTxExtraDTO { final Amount gas; final Amount gasPrice; final String input; - final int nonce; + final int? nonce; final Amount gasCost; final Amount gasUsed; diff --git a/lib/dto/ethereum/eth_tx_dto.dart b/lib/dto/ethereum/eth_tx_dto.dart index 7289cda15..260675cf9 100644 --- a/lib/dto/ethereum/eth_tx_dto.dart +++ b/lib/dto/ethereum/eth_tx_dto.dart @@ -127,16 +127,16 @@ class EthTxDTO { map['timestamp'] = timestamp; map['from'] = from; map['to'] = to; - map['value'] = value; - map['gas'] = gas; - map['gasPrice'] = gasPrice; - map['maxFeePerGas'] = maxFeePerGas; - map['maxPriorityFeePerGas'] = maxPriorityFeePerGas; + map['value'] = value.toString(); + map['gas'] = gas.toString(); + map['gasPrice'] = gasPrice.toString(); + map['maxFeePerGas'] = maxFeePerGas.toString(); + map['maxPriorityFeePerGas'] = maxPriorityFeePerGas.toString(); map['isError'] = isError; map['hasToken'] = hasToken; map['compressedTx'] = compressedTx; - map['gasCost'] = gasCost; - map['gasUsed'] = gasUsed; + map['gasCost'] = gasCost.toString(); + map['gasUsed'] = gasUsed.toString(); return map; } diff --git a/lib/dto/ordinals/address_inscription_response.dart b/lib/dto/ordinals/address_inscription_response.dart new file mode 100644 index 000000000..240374284 --- /dev/null +++ b/lib/dto/ordinals/address_inscription_response.dart @@ -0,0 +1,39 @@ +import 'package:stackwallet/dto/ordinals/litescribe_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; + +class AddressInscriptionResponse extends LitescribeResponse { + final int status; + final String message; + final AddressInscriptionResult result; + + AddressInscriptionResponse({ + required this.status, + required this.message, + required this.result, + }); + + factory AddressInscriptionResponse.fromJson(Map json) { + return AddressInscriptionResponse( + status: json['status'] as int, + message: json['message'] as String, + result: AddressInscriptionResult.fromJson(json['result'] as Map), + ); + } +} + +class AddressInscriptionResult { + final List list; + final int total; + + AddressInscriptionResult({ + required this.list, + required this.total, + }); + + factory AddressInscriptionResult.fromJson(Map json) { + return AddressInscriptionResult( + list: (json['list'] as List).map((item) => InscriptionData.fromJson(item as Map)).toList(), + total: json['total'] as int, + ); + } +} diff --git a/lib/dto/ordinals/inscription_data.dart b/lib/dto/ordinals/inscription_data.dart new file mode 100644 index 000000000..2f12bd670 --- /dev/null +++ b/lib/dto/ordinals/inscription_data.dart @@ -0,0 +1,73 @@ +// inscription data from litescribe /address/inscriptions endpoint +class InscriptionData { + final String inscriptionId; + final int inscriptionNumber; + final String address; + final String preview; + final String content; + final int contentLength; + final String contentType; + final String contentBody; + final int timestamp; + final String genesisTransaction; + final String location; + final String output; + final int outputValue; + final int offset; + + InscriptionData({ + required this.inscriptionId, + required this.inscriptionNumber, + required this.address, + required this.preview, + required this.content, + required this.contentLength, + required this.contentType, + required this.contentBody, + required this.timestamp, + required this.genesisTransaction, + required this.location, + required this.output, + required this.outputValue, + required this.offset, + }); + + factory InscriptionData.fromJson(Map json) { + return InscriptionData( + inscriptionId: json['inscriptionId'] as String, + inscriptionNumber: json['inscriptionNumber'] as int, + address: json['address'] as String, + preview: json['preview'] as String, + content: json['content'] as String, + contentLength: json['contentLength'] as int, + contentType: json['contentType'] as String, + contentBody: json['contentBody'] as String, + timestamp: json['timestamp'] as int, + genesisTransaction: json['genesisTransaction'] as String, + location: json['location'] as String, + output: json['output'] as String, + outputValue: json['outputValue'] as int, + offset: json['offset'] as int, + ); + } + + @override + String toString() { + return 'InscriptionData {' + ' inscriptionId: $inscriptionId,' + ' inscriptionNumber: $inscriptionNumber,' + ' address: $address,' + ' preview: $preview,' + ' content: $content,' + ' contentLength: $contentLength,' + ' contentType: $contentType,' + ' contentBody: $contentBody,' + ' timestamp: $timestamp,' + ' genesisTransaction: $genesisTransaction,' + ' location: $location,' + ' output: $output,' + ' outputValue: $outputValue,' + ' offset: $offset' + ' }'; + } +} diff --git a/lib/dto/ordinals/litescribe_response.dart b/lib/dto/ordinals/litescribe_response.dart new file mode 100644 index 000000000..bebd5ce10 --- /dev/null +++ b/lib/dto/ordinals/litescribe_response.dart @@ -0,0 +1,6 @@ +class LitescribeResponse { + final T? data; + final String? error; + + LitescribeResponse({this.data, this.error}); +} diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 67f170bb4..91b7d1bc8 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -164,14 +164,16 @@ class CachedElectrumX { final _list = box.get("serials") as List?; - List cachedSerials = - _list == null ? [] : List.from(_list); + Set cachedSerials = + _list == null ? {} : List.from(_list).toSet(); - final startNumber = cachedSerials.length; + // startNumber is broken currently + final startNumber = 0; // cachedSerials.length; - final serials = - await electrumXClient.getUsedCoinSerials(startNumber: startNumber); - List newSerials = []; + final serials = await electrumXClient.getUsedCoinSerials( + startNumber: startNumber, + ); + Set newSerials = {}; for (final element in (serials["serials"] as List)) { if (!isHexadecimal(element as String)) { @@ -182,12 +184,14 @@ class CachedElectrumX { } cachedSerials.addAll(newSerials); + final resultingList = cachedSerials.toList(); + await box.put( "serials", - cachedSerials, + resultingList, ); - return cachedSerials; + return resultingList; } catch (e, s) { Logging.instance.log( "Failed to process CachedElectrumX.getTransaction(): $e\n$s", diff --git a/lib/electrumx_rpc/electrumx.dart b/lib/electrumx_rpc/electrumx.dart index e0a3118eb..804a1364c 100644 --- a/lib/electrumx_rpc/electrumx.dart +++ b/lib/electrumx_rpc/electrumx.dart @@ -159,8 +159,8 @@ class ElectrumX { throw Exception( "JSONRPC response\n" " command: $command\n" - " args: $args\n" - " error: ${response.data}", + " error: ${response.data}" + " args: $args\n", ); } diff --git a/lib/electrumx_rpc/rpc.dart b/lib/electrumx_rpc/rpc.dart index 0c3834ae8..e865356d6 100644 --- a/lib/electrumx_rpc/rpc.dart +++ b/lib/electrumx_rpc/rpc.dart @@ -79,7 +79,7 @@ class JsonRPC { // TODO different timeout length? req.initiateTimeout( - const Duration(seconds: 10), + Duration(seconds: connectionTimeout.inSeconds ~/ 2), onTimedOut: () { _requestQueue.remove(req); }, diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart new file mode 100644 index 000000000..ca4c11919 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -0,0 +1,81 @@ +import 'package:isar/isar.dart'; + +part 'lelantus_coin.g.dart'; + +@collection +class LelantusCoin { + Id id = Isar.autoIncrement; + + @Index() + final String walletId; + + final String txid; + + final String value; // can't use BigInt in isar :shrug: + + @Index( + unique: true, + replace: false, + composite: [ + CompositeIndex("walletId"), + ], + ) + final int mintIndex; + + final int anonymitySetId; + + final bool isUsed; + + final bool isJMint; + + final String? otherData; + + LelantusCoin({ + required this.walletId, + required this.txid, + required this.value, + required this.mintIndex, + required this.anonymitySetId, + required this.isUsed, + required this.isJMint, + required this.otherData, + }); + + LelantusCoin copyWith({ + String? walletId, + String? publicCoin, + String? txid, + String? value, + int? mintIndex, + int? anonymitySetId, + bool? isUsed, + bool? isJMint, + String? otherData, + }) { + return LelantusCoin( + walletId: walletId ?? this.walletId, + txid: txid ?? this.txid, + value: value ?? this.value, + mintIndex: mintIndex ?? this.mintIndex, + anonymitySetId: anonymitySetId ?? this.anonymitySetId, + isUsed: isUsed ?? this.isUsed, + isJMint: isJMint ?? this.isJMint, + otherData: otherData ?? this.otherData, + ); + } + + @override + String toString() { + return 'LelantusCoin{' + 'id: $id, ' + 'walletId: $walletId, ' + 'txid: $txid, ' + 'value: $value, ' + 'mintIndex: $mintIndex, ' + 'anonymitySetId: $anonymitySetId, ' + 'otherData: $otherData, ' + 'isJMint: $isJMint, ' + 'isUsed: $isUsed' + '}'; + } +} diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart new file mode 100644 index 000000000..4b9214889 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -0,0 +1,1629 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'lelantus_coin.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetLelantusCoinCollection on Isar { + IsarCollection get lelantusCoins => this.collection(); +} + +const LelantusCoinSchema = CollectionSchema( + name: r'LelantusCoin', + id: -6795633185033299066, + properties: { + r'anonymitySetId': PropertySchema( + id: 0, + name: r'anonymitySetId', + type: IsarType.long, + ), + r'isJMint': PropertySchema( + id: 1, + name: r'isJMint', + type: IsarType.bool, + ), + r'isUsed': PropertySchema( + id: 2, + name: r'isUsed', + type: IsarType.bool, + ), + r'mintIndex': PropertySchema( + id: 3, + name: r'mintIndex', + type: IsarType.long, + ), + r'otherData': PropertySchema( + id: 4, + name: r'otherData', + type: IsarType.string, + ), + r'txid': PropertySchema( + id: 5, + name: r'txid', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 6, + name: r'value', + type: IsarType.string, + ), + r'walletId': PropertySchema( + id: 7, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _lelantusCoinEstimateSize, + serialize: _lelantusCoinSerialize, + deserialize: _lelantusCoinDeserialize, + deserializeProp: _lelantusCoinDeserializeProp, + idName: r'id', + indexes: { + r'walletId': IndexSchema( + id: -1783113319798776304, + name: r'walletId', + unique: false, + replace: false, + properties: [ + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ), + r'mintIndex_walletId': IndexSchema( + id: -9147309777276196770, + name: r'mintIndex_walletId', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'mintIndex', + type: IndexType.value, + caseSensitive: false, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _lelantusCoinGetId, + getLinks: _lelantusCoinGetLinks, + attach: _lelantusCoinAttach, + version: '3.0.5', +); + +int _lelantusCoinEstimateSize( + LelantusCoin object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.otherData; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + bytesCount += 3 + object.txid.length * 3; + bytesCount += 3 + object.value.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _lelantusCoinSerialize( + LelantusCoin object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeLong(offsets[0], object.anonymitySetId); + writer.writeBool(offsets[1], object.isJMint); + writer.writeBool(offsets[2], object.isUsed); + writer.writeLong(offsets[3], object.mintIndex); + writer.writeString(offsets[4], object.otherData); + writer.writeString(offsets[5], object.txid); + writer.writeString(offsets[6], object.value); + writer.writeString(offsets[7], object.walletId); +} + +LelantusCoin _lelantusCoinDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LelantusCoin( + anonymitySetId: reader.readLong(offsets[0]), + isJMint: reader.readBool(offsets[1]), + isUsed: reader.readBool(offsets[2]), + mintIndex: reader.readLong(offsets[3]), + otherData: reader.readStringOrNull(offsets[4]), + txid: reader.readString(offsets[5]), + value: reader.readString(offsets[6]), + walletId: reader.readString(offsets[7]), + ); + object.id = id; + return object; +} + +P _lelantusCoinDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLong(offset)) as P; + case 1: + return (reader.readBool(offset)) as P; + case 2: + return (reader.readBool(offset)) as P; + case 3: + return (reader.readLong(offset)) as P; + case 4: + return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + case 6: + return (reader.readString(offset)) as P; + case 7: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _lelantusCoinGetId(LelantusCoin object) { + return object.id; +} + +List> _lelantusCoinGetLinks(LelantusCoin object) { + return []; +} + +void _lelantusCoinAttach( + IsarCollection col, Id id, LelantusCoin object) { + object.id = id; +} + +extension LelantusCoinByIndex on IsarCollection { + Future getByMintIndexWalletId(int mintIndex, String walletId) { + return getByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + LelantusCoin? getByMintIndexWalletIdSync(int mintIndex, String walletId) { + return getByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future deleteByMintIndexWalletId(int mintIndex, String walletId) { + return deleteByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + bool deleteByMintIndexWalletIdSync(int mintIndex, String walletId) { + return deleteByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future> getAllByMintIndexWalletId( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndex(r'mintIndex_walletId', values); + } + + List getAllByMintIndexWalletIdSync( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndexSync(r'mintIndex_walletId', values); + } + + Future deleteAllByMintIndexWalletId( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndex(r'mintIndex_walletId', values); + } + + int deleteAllByMintIndexWalletIdSync( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndexSync(r'mintIndex_walletId', values); + } + + Future putByMintIndexWalletId(LelantusCoin object) { + return putByIndex(r'mintIndex_walletId', object); + } + + Id putByMintIndexWalletIdSync(LelantusCoin object, {bool saveLinks = true}) { + return putByIndexSync(r'mintIndex_walletId', object, saveLinks: saveLinks); + } + + Future> putAllByMintIndexWalletId(List objects) { + return putAllByIndex(r'mintIndex_walletId', objects); + } + + List putAllByMintIndexWalletIdSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'mintIndex_walletId', objects, + saveLinks: saveLinks); + } +} + +extension LelantusCoinQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension LelantusCoinQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder walletIdEqualTo( + String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'walletId', + value: [walletId], + )); + }); + } + + QueryBuilder + walletIdNotEqualTo(String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + mintIndexEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex], + )); + }); + } + + QueryBuilder + mintIndexNotEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + mintIndexGreaterThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: include, + upper: [], + )); + }); + } + + QueryBuilder + mintIndexLessThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: include, + )); + }); + } + + QueryBuilder + mintIndexBetweenAnyWalletId( + int lowerMintIndex, + int upperMintIndex, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [lowerMintIndex], + includeLower: includeLower, + upper: [upperMintIndex], + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + mintIndexWalletIdEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex, walletId], + )); + }); + } + + QueryBuilder + mintIndexEqualToWalletIdNotEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )); + } + }); + } +} + +extension LelantusCoinQueryFilter + on QueryBuilder { + QueryBuilder + anonymitySetIdEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'anonymitySetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + isJMintEqualTo(bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isJMint', + value: value, + )); + }); + } + + QueryBuilder isUsedEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isUsed', + value: value, + )); + }); + } + + QueryBuilder + mintIndexEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'mintIndex', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + otherDataIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'otherData', + )); + }); + } + + QueryBuilder + otherDataIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'otherData', + )); + }); + } + + QueryBuilder + otherDataEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'otherData', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'otherData', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder + otherDataIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder txidEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'txid', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'txid', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder + txidIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder valueEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'value', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'value', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + valueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder + walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension LelantusCoinQueryObject + on QueryBuilder {} + +extension LelantusCoinQueryLinks + on QueryBuilder {} + +extension LelantusCoinQuerySortBy + on QueryBuilder { + QueryBuilder + sortByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder + sortByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder sortByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.asc); + }); + } + + QueryBuilder sortByIsJMintDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.desc); + }); + } + + QueryBuilder sortByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder sortByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder sortByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder sortByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder sortByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder sortByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + + QueryBuilder sortByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder sortByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder sortByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder sortByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQuerySortThenBy + on QueryBuilder { + QueryBuilder + thenByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder + thenByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.asc); + }); + } + + QueryBuilder thenByIsJMintDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.desc); + }); + } + + QueryBuilder thenByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder thenByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder thenByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder thenByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder thenByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder thenByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + + QueryBuilder thenByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder thenByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder thenByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder thenByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQueryWhereDistinct + on QueryBuilder { + QueryBuilder + distinctByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'anonymitySetId'); + }); + } + + QueryBuilder distinctByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isJMint'); + }); + } + + QueryBuilder distinctByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isUsed'); + }); + } + + QueryBuilder distinctByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'mintIndex'); + }); + } + + QueryBuilder distinctByOtherData( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'otherData', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTxid( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'txid', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByValue( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'value', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension LelantusCoinQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder anonymitySetIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'anonymitySetId'); + }); + } + + QueryBuilder isJMintProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isJMint'); + }); + } + + QueryBuilder isUsedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isUsed'); + }); + } + + QueryBuilder mintIndexProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'mintIndex'); + }); + } + + QueryBuilder otherDataProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'otherData'); + }); + } + + QueryBuilder txidProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'txid'); + }); + } + + QueryBuilder valueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'value'); + }); + } + + QueryBuilder walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} diff --git a/lib/models/isar/models/isar_models.dart b/lib/models/isar/models/isar_models.dart index ce7652a46..9de91fc84 100644 --- a/lib/models/isar/models/isar_models.dart +++ b/lib/models/isar/models/isar_models.dart @@ -15,5 +15,6 @@ export 'blockchain_data/output.dart'; export 'blockchain_data/transaction.dart'; export 'blockchain_data/utxo.dart'; export 'ethereum/eth_contract.dart'; +export 'firo_specific/lelantus_coin.dart'; export 'log.dart'; export 'transaction_note.dart'; diff --git a/lib/models/isar/ordinal.dart b/lib/models/isar/ordinal.dart new file mode 100644 index 000000000..06ba52ef5 --- /dev/null +++ b/lib/models/isar/ordinal.dart @@ -0,0 +1,89 @@ +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; + +part 'ordinal.g.dart'; + +@collection +class Ordinal { + Id id = Isar.autoIncrement; + + final String walletId; + + @Index(unique: true, replace: true, composite: [ + CompositeIndex("utxoTXID"), + CompositeIndex("utxoVOUT"), + ]) + final String inscriptionId; + + final int inscriptionNumber; + + final String content; + + // following two are used to look up the UTXO object in isar combined w/ walletId + final String utxoTXID; + final int utxoVOUT; + + Ordinal({ + required this.walletId, + required this.inscriptionId, + required this.inscriptionNumber, + required this.content, + required this.utxoTXID, + required this.utxoVOUT, + }); + + factory Ordinal.fromInscriptionData(InscriptionData data, String walletId) { + return Ordinal( + walletId: walletId, + inscriptionId: data.inscriptionId, + inscriptionNumber: data.inscriptionNumber, + content: data.content, + utxoTXID: data.output.split(':')[ + 0], // "output": "062f32e21aa04246b8873b5d9a929576addd0339881e1ea478b406795d6b6c47:0" + utxoVOUT: int.parse(data.output.split(':')[1]), + ); + } + + Ordinal copyWith({ + String? walletId, + String? inscriptionId, + int? inscriptionNumber, + String? content, + String? utxoTXID, + int? utxoVOUT, + }) { + return Ordinal( + walletId: walletId ?? this.walletId, + inscriptionId: inscriptionId ?? this.inscriptionId, + inscriptionNumber: inscriptionNumber ?? this.inscriptionNumber, + content: content ?? this.content, + utxoTXID: utxoTXID ?? this.utxoTXID, + utxoVOUT: utxoVOUT ?? this.utxoVOUT, + ); + } + + UTXO? getUTXO(MainDB db) { + return db.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(utxoTXID) + .and() + .voutEqualTo(utxoVOUT) + .findFirstSync(); + } + + @override + String toString() { + return 'Ordinal {' + ' walletId: $walletId,' + ' inscriptionId: $inscriptionId,' + ' inscriptionNumber: $inscriptionNumber,' + ' content: $content,' + ' utxoTXID: $utxoTXID,' + ' utxoVOUT: $utxoVOUT' + ' }'; + } +} diff --git a/lib/models/isar/ordinal.g.dart b/lib/models/isar/ordinal.g.dart new file mode 100644 index 000000000..89c967cb0 --- /dev/null +++ b/lib/models/isar/ordinal.g.dart @@ -0,0 +1,1489 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'ordinal.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetOrdinalCollection on Isar { + IsarCollection get ordinals => this.collection(); +} + +const OrdinalSchema = CollectionSchema( + name: r'Ordinal', + id: -7772149326141951436, + properties: { + r'content': PropertySchema( + id: 0, + name: r'content', + type: IsarType.string, + ), + r'inscriptionId': PropertySchema( + id: 1, + name: r'inscriptionId', + type: IsarType.string, + ), + r'inscriptionNumber': PropertySchema( + id: 2, + name: r'inscriptionNumber', + type: IsarType.long, + ), + r'utxoTXID': PropertySchema( + id: 3, + name: r'utxoTXID', + type: IsarType.string, + ), + r'utxoVOUT': PropertySchema( + id: 4, + name: r'utxoVOUT', + type: IsarType.long, + ), + r'walletId': PropertySchema( + id: 5, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _ordinalEstimateSize, + serialize: _ordinalSerialize, + deserialize: _ordinalDeserialize, + deserializeProp: _ordinalDeserializeProp, + idName: r'id', + indexes: { + r'inscriptionId_utxoTXID_utxoVOUT': IndexSchema( + id: 2138008085066605381, + name: r'inscriptionId_utxoTXID_utxoVOUT', + unique: true, + replace: true, + properties: [ + IndexPropertySchema( + name: r'inscriptionId', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'utxoTXID', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'utxoVOUT', + type: IndexType.value, + caseSensitive: false, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _ordinalGetId, + getLinks: _ordinalGetLinks, + attach: _ordinalAttach, + version: '3.0.5', +); + +int _ordinalEstimateSize( + Ordinal object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.content.length * 3; + bytesCount += 3 + object.inscriptionId.length * 3; + bytesCount += 3 + object.utxoTXID.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _ordinalSerialize( + Ordinal object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeString(offsets[0], object.content); + writer.writeString(offsets[1], object.inscriptionId); + writer.writeLong(offsets[2], object.inscriptionNumber); + writer.writeString(offsets[3], object.utxoTXID); + writer.writeLong(offsets[4], object.utxoVOUT); + writer.writeString(offsets[5], object.walletId); +} + +Ordinal _ordinalDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = Ordinal( + content: reader.readString(offsets[0]), + inscriptionId: reader.readString(offsets[1]), + inscriptionNumber: reader.readLong(offsets[2]), + utxoTXID: reader.readString(offsets[3]), + utxoVOUT: reader.readLong(offsets[4]), + walletId: reader.readString(offsets[5]), + ); + object.id = id; + return object; +} + +P _ordinalDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readString(offset)) as P; + case 1: + return (reader.readString(offset)) as P; + case 2: + return (reader.readLong(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + case 4: + return (reader.readLong(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _ordinalGetId(Ordinal object) { + return object.id; +} + +List> _ordinalGetLinks(Ordinal object) { + return []; +} + +void _ordinalAttach(IsarCollection col, Id id, Ordinal object) { + object.id = id; +} + +extension OrdinalByIndex on IsarCollection { + Future getByInscriptionIdUtxoTXIDUtxoVOUT( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return getByIndex(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + Ordinal? getByInscriptionIdUtxoTXIDUtxoVOUTSync( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return getByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + Future deleteByInscriptionIdUtxoTXIDUtxoVOUT( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return deleteByIndex(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + bool deleteByInscriptionIdUtxoTXIDUtxoVOUTSync( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return deleteByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', + [inscriptionId, utxoTXID, utxoVOUT]); + } + + Future> getAllByInscriptionIdUtxoTXIDUtxoVOUT( + List inscriptionIdValues, + List utxoTXIDValues, + List utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return getAllByIndex(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + List getAllByInscriptionIdUtxoTXIDUtxoVOUTSync( + List inscriptionIdValues, + List utxoTXIDValues, + List utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return getAllByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + Future deleteAllByInscriptionIdUtxoTXIDUtxoVOUT( + List inscriptionIdValues, + List utxoTXIDValues, + List utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return deleteAllByIndex(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + int deleteAllByInscriptionIdUtxoTXIDUtxoVOUTSync( + List inscriptionIdValues, + List utxoTXIDValues, + List utxoVOUTValues) { + final len = inscriptionIdValues.length; + assert(utxoTXIDValues.length == len && utxoVOUTValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values + .add([inscriptionIdValues[i], utxoTXIDValues[i], utxoVOUTValues[i]]); + } + + return deleteAllByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', values); + } + + Future putByInscriptionIdUtxoTXIDUtxoVOUT(Ordinal object) { + return putByIndex(r'inscriptionId_utxoTXID_utxoVOUT', object); + } + + Id putByInscriptionIdUtxoTXIDUtxoVOUTSync(Ordinal object, + {bool saveLinks = true}) { + return putByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', object, + saveLinks: saveLinks); + } + + Future> putAllByInscriptionIdUtxoTXIDUtxoVOUT( + List objects) { + return putAllByIndex(r'inscriptionId_utxoTXID_utxoVOUT', objects); + } + + List putAllByInscriptionIdUtxoTXIDUtxoVOUTSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'inscriptionId_utxoTXID_utxoVOUT', objects, + saveLinks: saveLinks); + } +} + +extension OrdinalQueryWhereSort on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension OrdinalQueryWhere on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + inscriptionIdEqualToAnyUtxoTXIDUtxoVOUT(String inscriptionId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + value: [inscriptionId], + )); + }); + } + + QueryBuilder + inscriptionIdNotEqualToAnyUtxoTXIDUtxoVOUT(String inscriptionId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [], + upper: [inscriptionId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [], + upper: [inscriptionId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + inscriptionIdUtxoTXIDEqualToAnyUtxoVOUT( + String inscriptionId, String utxoTXID) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + value: [inscriptionId, utxoTXID], + )); + }); + } + + QueryBuilder + inscriptionIdEqualToUtxoTXIDNotEqualToAnyUtxoVOUT( + String inscriptionId, String utxoTXID) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + upper: [inscriptionId, utxoTXID], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + includeLower: false, + upper: [inscriptionId], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + includeLower: false, + upper: [inscriptionId], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId], + upper: [inscriptionId, utxoTXID], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + inscriptionIdUtxoTXIDUtxoVOUTEqualTo( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + value: [inscriptionId, utxoTXID, utxoVOUT], + )); + }); + } + + QueryBuilder + inscriptionIdUtxoTXIDEqualToUtxoVOUTNotEqualTo( + String inscriptionId, String utxoTXID, int utxoVOUT) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + upper: [inscriptionId, utxoTXID, utxoVOUT], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, utxoVOUT], + includeLower: false, + upper: [inscriptionId, utxoTXID], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, utxoVOUT], + includeLower: false, + upper: [inscriptionId, utxoTXID], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + upper: [inscriptionId, utxoTXID, utxoVOUT], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + inscriptionIdUtxoTXIDEqualToUtxoVOUTGreaterThan( + String inscriptionId, + String utxoTXID, + int utxoVOUT, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, utxoVOUT], + includeLower: include, + upper: [inscriptionId, utxoTXID], + )); + }); + } + + QueryBuilder + inscriptionIdUtxoTXIDEqualToUtxoVOUTLessThan( + String inscriptionId, + String utxoTXID, + int utxoVOUT, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID], + upper: [inscriptionId, utxoTXID, utxoVOUT], + includeUpper: include, + )); + }); + } + + QueryBuilder + inscriptionIdUtxoTXIDEqualToUtxoVOUTBetween( + String inscriptionId, + String utxoTXID, + int lowerUtxoVOUT, + int upperUtxoVOUT, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'inscriptionId_utxoTXID_utxoVOUT', + lower: [inscriptionId, utxoTXID, lowerUtxoVOUT], + includeLower: includeLower, + upper: [inscriptionId, utxoTXID, upperUtxoVOUT], + includeUpper: includeUpper, + )); + }); + } +} + +extension OrdinalQueryFilter + on QueryBuilder { + QueryBuilder contentEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'content', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'content', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'content', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder contentIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'content', + value: '', + )); + }); + } + + QueryBuilder contentIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'content', + value: '', + )); + }); + } + + QueryBuilder idEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder inscriptionIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + inscriptionIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'inscriptionId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'inscriptionId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'inscriptionId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder inscriptionIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'inscriptionId', + value: '', + )); + }); + } + + QueryBuilder + inscriptionIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'inscriptionId', + value: '', + )); + }); + } + + QueryBuilder + inscriptionNumberEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'inscriptionNumber', + value: value, + )); + }); + } + + QueryBuilder + inscriptionNumberGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'inscriptionNumber', + value: value, + )); + }); + } + + QueryBuilder + inscriptionNumberLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'inscriptionNumber', + value: value, + )); + }); + } + + QueryBuilder + inscriptionNumberBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'inscriptionNumber', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder utxoTXIDEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'utxoTXID', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'utxoTXID', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'utxoTXID', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder utxoTXIDIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'utxoTXID', + value: '', + )); + }); + } + + QueryBuilder utxoTXIDIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'utxoTXID', + value: '', + )); + }); + } + + QueryBuilder utxoVOUTEqualTo( + int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'utxoVOUT', + value: value, + )); + }); + } + + QueryBuilder utxoVOUTGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'utxoVOUT', + value: value, + )); + }); + } + + QueryBuilder utxoVOUTLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'utxoVOUT', + value: value, + )); + }); + } + + QueryBuilder utxoVOUTBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'utxoVOUT', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension OrdinalQueryObject + on QueryBuilder {} + +extension OrdinalQueryLinks + on QueryBuilder {} + +extension OrdinalQuerySortBy on QueryBuilder { + QueryBuilder sortByContent() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.asc); + }); + } + + QueryBuilder sortByContentDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.desc); + }); + } + + QueryBuilder sortByInscriptionId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.asc); + }); + } + + QueryBuilder sortByInscriptionIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.desc); + }); + } + + QueryBuilder sortByInscriptionNumber() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.asc); + }); + } + + QueryBuilder sortByInscriptionNumberDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.desc); + }); + } + + QueryBuilder sortByUtxoTXID() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.asc); + }); + } + + QueryBuilder sortByUtxoTXIDDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.desc); + }); + } + + QueryBuilder sortByUtxoVOUT() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.asc); + }); + } + + QueryBuilder sortByUtxoVOUTDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.desc); + }); + } + + QueryBuilder sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension OrdinalQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByContent() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.asc); + }); + } + + QueryBuilder thenByContentDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'content', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByInscriptionId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.asc); + }); + } + + QueryBuilder thenByInscriptionIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionId', Sort.desc); + }); + } + + QueryBuilder thenByInscriptionNumber() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.asc); + }); + } + + QueryBuilder thenByInscriptionNumberDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'inscriptionNumber', Sort.desc); + }); + } + + QueryBuilder thenByUtxoTXID() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.asc); + }); + } + + QueryBuilder thenByUtxoTXIDDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoTXID', Sort.desc); + }); + } + + QueryBuilder thenByUtxoVOUT() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.asc); + }); + } + + QueryBuilder thenByUtxoVOUTDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'utxoVOUT', Sort.desc); + }); + } + + QueryBuilder thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension OrdinalQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByContent( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'content', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByInscriptionId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'inscriptionId', + caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByInscriptionNumber() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'inscriptionNumber'); + }); + } + + QueryBuilder distinctByUtxoTXID( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'utxoTXID', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByUtxoVOUT() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'utxoVOUT'); + }); + } + + QueryBuilder distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension OrdinalQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder contentProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'content'); + }); + } + + QueryBuilder inscriptionIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'inscriptionId'); + }); + } + + QueryBuilder inscriptionNumberProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'inscriptionNumber'); + }); + } + + QueryBuilder utxoTXIDProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'utxoTXID'); + }); + } + + QueryBuilder utxoVOUTProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'utxoVOUT'); + }); + } + + QueryBuilder walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} diff --git a/lib/models/isar/stack_theme.dart b/lib/models/isar/stack_theme.dart index f6f7c351d..ea449a497 100644 --- a/lib/models/isar/stack_theme.dart +++ b/lib/models/isar/stack_theme.dart @@ -2350,8 +2350,6 @@ class ThemeAssetsV3 implements IThemeAssets { // Added some future proof params in case we want to add anything else // This should provide some buffer in stead of creating assetsV4 etc - @Name("otherStringParam1") - late final String? dummy1; @Name("otherStringParam2") late final String? dummy2; @Name("otherStringParam3") @@ -2397,6 +2395,19 @@ class ThemeAssetsV3 implements IThemeAssets { Map? _coinCardImages; late final String? coinCardImagesString; + @ignore + Map? get coinCardFavoritesImages => + _coinCardFavoritesImages ??= coinCardFavoritesImagesString == null + ? null + : parseCoinAssetsString( + coinCardFavoritesImagesString!, + placeHolder: coinPlaceholder, + ); + @ignore + Map? _coinCardFavoritesImages; + @Name("otherStringParam1") + late final String? coinCardFavoritesImagesString; + ThemeAssetsV3(); factory ThemeAssetsV3.fromJson({ @@ -2452,13 +2463,18 @@ class ThemeAssetsV3 implements IThemeAssets { Map.from(json["coins"]["cards"] as Map), ) : null + ..coinCardFavoritesImagesString = json["coins"]["favoriteCards"] is Map + ? createCoinAssetsString( + "$themeId/assets", + Map.from(json["coins"]["favoriteCards"] as Map), + ) + : null ..loadingGifRelative = json["loading_gif"] is String ? "$themeId/assets/${json["loading_gif"] as String}" : null ..backgroundRelative = json["background"] is String ? "$themeId/assets/${json["background"] as String}" : null - ..dummy1 = null ..dummy2 = null ..dummy3 = null; } @@ -2537,6 +2553,7 @@ class ThemeAssetsV3 implements IThemeAssets { 'coinImages: $coinImages, ' 'coinSecondaryImages: $coinSecondaryImages, ' 'coinCardImages: $coinCardImages' + 'coinCardFavoritesImages: $coinCardFavoritesImages' ')'; } } diff --git a/lib/models/isar/stack_theme.g.dart b/lib/models/isar/stack_theme.g.dart index bf38c461d..03c543ff0 100644 --- a/lib/models/isar/stack_theme.g.dart +++ b/lib/models/isar/stack_theme.g.dart @@ -29626,7 +29626,7 @@ int _themeAssetsV3EstimateSize( } } { - final value = object.dummy1; + final value = object.coinCardFavoritesImagesString; if (value != null) { bytesCount += 3 + value.length * 3; } @@ -29677,7 +29677,7 @@ void _themeAssetsV3Serialize( writer.writeString(offsets[7], object.coinSecondaryImagesString); writer.writeString(offsets[8], object.exchangeRelative); writer.writeString(offsets[9], object.loadingGifRelative); - writer.writeString(offsets[10], object.dummy1); + writer.writeString(offsets[10], object.coinCardFavoritesImagesString); writer.writeString(offsets[11], object.dummy2); writer.writeString(offsets[12], object.dummy3); writer.writeString(offsets[13], object.personaEasyRelative); @@ -29714,7 +29714,7 @@ ThemeAssetsV3 _themeAssetsV3Deserialize( object.coinSecondaryImagesString = reader.readString(offsets[7]); object.exchangeRelative = reader.readString(offsets[8]); object.loadingGifRelative = reader.readStringOrNull(offsets[9]); - object.dummy1 = reader.readStringOrNull(offsets[10]); + object.coinCardFavoritesImagesString = reader.readStringOrNull(offsets[10]); object.dummy2 = reader.readStringOrNull(offsets[11]); object.dummy3 = reader.readStringOrNull(offsets[12]); object.personaEasyRelative = reader.readString(offsets[13]); @@ -31224,7 +31224,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsNull() { + coinCardFavoritesImagesStringIsNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNull( property: r'otherStringParam1', @@ -31233,7 +31233,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsNotNull() { + coinCardFavoritesImagesStringIsNotNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNotNull( property: r'otherStringParam1', @@ -31242,7 +31242,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1EqualTo( + coinCardFavoritesImagesStringEqualTo( String? value, { bool caseSensitive = true, }) { @@ -31256,7 +31256,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1GreaterThan( + coinCardFavoritesImagesStringGreaterThan( String? value, { bool include = false, bool caseSensitive = true, @@ -31272,7 +31272,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1LessThan( + coinCardFavoritesImagesStringLessThan( String? value, { bool include = false, bool caseSensitive = true, @@ -31288,7 +31288,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1Between( + coinCardFavoritesImagesStringBetween( String? lower, String? upper, { bool includeLower = true, @@ -31308,7 +31308,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1StartsWith( + coinCardFavoritesImagesStringStartsWith( String value, { bool caseSensitive = true, }) { @@ -31322,7 +31322,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1EndsWith( + coinCardFavoritesImagesStringEndsWith( String value, { bool caseSensitive = true, }) { @@ -31336,7 +31336,8 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1Contains(String value, {bool caseSensitive = true}) { + coinCardFavoritesImagesStringContains(String value, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.contains( property: r'otherStringParam1', @@ -31347,7 +31348,8 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1Matches(String pattern, {bool caseSensitive = true}) { + coinCardFavoritesImagesStringMatches(String pattern, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.matches( property: r'otherStringParam1', @@ -31358,7 +31360,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsEmpty() { + coinCardFavoritesImagesStringIsEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'otherStringParam1', @@ -31368,7 +31370,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsNotEmpty() { + coinCardFavoritesImagesStringIsNotEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( property: r'otherStringParam1', diff --git a/lib/models/lelantus_coin.dart b/lib/models/lelantus_coin.dart index 0e32d33bf..56557c1cd 100644 --- a/lib/models/lelantus_coin.dart +++ b/lib/models/lelantus_coin.dart @@ -12,6 +12,7 @@ import 'package:hive/hive.dart'; part 'type_adaptors/lelantus_coin.g.dart'; +@Deprecated("Use Isar object instead") // @HiveType(typeId: 9) class LelantusCoin { // @HiveField(0) @@ -27,6 +28,7 @@ class LelantusCoin { // @HiveField(5) bool isUsed; + @Deprecated("Use Isar object instead") LelantusCoin( this.index, this.value, diff --git a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart index a3d297b8c..dd807976b 100644 --- a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart +++ b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart @@ -142,7 +142,7 @@ class _AddCustomTokenViewState extends ConsumerState { context: context, message: "Looking up contract", ); - currentToken = response.value; + currentToken = response!.value; if (currentToken != null) { nameController.text = currentToken!.name; symbolController.text = currentToken!.symbol; @@ -157,7 +157,7 @@ class _AddCustomTokenViewState extends ConsumerState { context: context, builder: (context) => StackOkDialog( title: "Failed to look up token", - message: response.exception?.message, + message: response!.exception?.message, ), ), ); diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart index e8c7c711c..36c914fd8 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart @@ -8,6 +8,8 @@ * */ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -32,35 +34,37 @@ class CreateWalletButtonGroup extends StatelessWidget { crossAxisAlignment: isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch, children: [ - ConstrainedBox( - constraints: BoxConstraints( - minHeight: isDesktop ? 70 : 0, - minWidth: isDesktop ? 480 : 0, - ), - child: TextButton( - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context), - onPressed: () { - Navigator.of(context).pushNamed( - NameYourWalletView.routeName, - arguments: Tuple2( - AddWalletType.New, - coin, - ), - ); - }, - child: Text( - "Create new wallet", - style: isDesktop - ? STextStyles.desktopButtonEnabled(context) - : STextStyles.button(context), + if (Platform.isAndroid || coin != Coin.wownero) + ConstrainedBox( + constraints: BoxConstraints( + minHeight: isDesktop ? 70 : 0, + minWidth: isDesktop ? 480 : 0, + ), + child: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + onPressed: () { + Navigator.of(context).pushNamed( + NameYourWalletView.routeName, + arguments: Tuple2( + AddWalletType.New, + coin, + ), + ); + }, + child: Text( + "Create new wallet", + style: isDesktop + ? STextStyles.desktopButtonEnabled(context) + : STextStyles.button(context), + ), ), ), - ), - SizedBox( - height: isDesktop ? 16 : 12, - ), + if (Platform.isAndroid || coin != Coin.wownero) + SizedBox( + height: isDesktop ? 16 : 12, + ), ConstrainedBox( constraints: BoxConstraints( minHeight: isDesktop ? 70 : 0, diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index e65242fbd..a15e13843 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -35,6 +35,7 @@ import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart'; import 'package:stackwallet/services/exchange/trocador/trocador_exchange.dart'; import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -160,26 +161,15 @@ class _ExchangeFormState extends ConsumerState { if (value == null) { return null; } - try { - // wtf Dart????? - // This turns "99999999999999999999" into 100000000000000000000.0 - // final numFromLocalised = NumberFormat.decimalPattern( - // ref.read(localeServiceChangeNotifierProvider).locale) - // .parse(value); - // return Decimal.tryParse(numFromLocalised.toString()); - try { - return Decimal.parse(value); - } catch (_) { - try { - return Decimal.parse(value.replaceAll(",", ".")); - } catch (_) { - rethrow; - } - } - } catch (_) { - return null; - } + return AmountUnit.normal + .tryParse( + value, + locale: ref.read(localeServiceChangeNotifierProvider).locale, + coin: Coin.bitcoin, // dummy value (not used due to override) + overrideWithDecimalPlacesFromString: true, + ) + ?.decimal; } Future _getAggregateCurrency(Currency currency) async { @@ -809,6 +799,14 @@ class _ExchangeFormState extends ConsumerState { // if (_swapLock) { _sendController.text = ref.read(efSendAmountStringProvider); // } + + if (_sendFocusNode.hasFocus) { + _sendController.selection = TextSelection.fromPosition( + TextPosition( + offset: _sendController.text.length, + ), + ); + } } }); ref.listen(efSendAmountStringProvider, (previous, String next) { @@ -820,11 +818,19 @@ class _ExchangeFormState extends ConsumerState { ? "-" : ref.read(efReceiveAmountStringProvider); // } + + if (_receiveFocusNode.hasFocus) { + _receiveController.selection = TextSelection.fromPosition( + TextPosition( + offset: _receiveController.text.length, + ), + ); + } } }); ref.listen(efEstimateProvider.notifier, (previous, next) { - final estimate = (next as StateController).state; + final estimate = (next).state; if (ref.read(efReversedProvider)) { updateSend(estimate); } else { diff --git a/lib/pages/monkey/monkey_loaded_view.dart b/lib/pages/monkey/monkey_loaded_view.dart new file mode 100644 index 000000000..93c28b39a --- /dev/null +++ b/lib/pages/monkey/monkey_loaded_view.dart @@ -0,0 +1,275 @@ +// import 'dart:io'; +// import 'dart:typed_data'; +// +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:flutter_svg/svg.dart'; +// import 'package:http/http.dart' as http; +// import 'package:path_provider/path_provider.dart'; +// import 'package:permission_handler/permission_handler.dart'; +// import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; +// import 'package:stackwallet/providers/global/wallets_provider.dart'; +// import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +// import 'package:stackwallet/services/coins/manager.dart'; +// import 'package:stackwallet/themes/stack_colors.dart'; +// import 'package:stackwallet/utilities/assets.dart'; +// import 'package:stackwallet/utilities/enums/coin_enum.dart'; +// import 'package:stackwallet/utilities/text_styles.dart'; +// import 'package:stackwallet/widgets/background.dart'; +// import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +// import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +// +// class MonkeyLoadedView extends ConsumerStatefulWidget { +// const MonkeyLoadedView({ +// Key? key, +// required this.walletId, +// required this.managerProvider, +// }) : super(key: key); +// +// static const String routeName = "/hasMonkey"; +// static const double navBarHeight = 65.0; +// +// final String walletId; +// final ChangeNotifierProvider managerProvider; +// +// @override +// ConsumerState createState() => _MonkeyLoadedViewState(); +// } +// +// class _MonkeyLoadedViewState extends ConsumerState { +// late final String walletId; +// late final ChangeNotifierProvider managerProvider; +// +// String receivingAddress = ""; +// +// void getMonkeySVG(String address) async { +// if (address.isEmpty) { +// //address shouldn't be empty +// return; +// } +// +// final http.Response response = await http +// .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); +// +// if (response.statusCode == 200) { +// final decodedResponse = response.bodyBytes; +// Directory directory = await getApplicationDocumentsDirectory(); +// late Directory sampleFolder; +// +// if (Platform.isAndroid) { +// directory = Directory("/storage/emulated/0/"); +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isIOS) { +// sampleFolder = Directory(directory!.path); +// } else if (Platform.isLinux) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isWindows) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isMacOS) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } +// +// try { +// if (!sampleFolder.existsSync()) { +// sampleFolder.createSync(recursive: true); +// } +// } catch (e, s) { +// // todo: come back to this +// debugPrint("$e $s"); +// } +// +// final docPath = sampleFolder.path; +// final filePath = "$docPath/monkey.svg"; +// +// File imgFile = File(filePath); +// await imgFile.writeAsBytes(decodedResponse); +// } else { +// throw Exception("Failed to get MonKey"); +// } +// } +// +// void getMonkeyPNG(String address) async { +// if (address.isEmpty) { +// //address shouldn't be empty +// return; +// } +// +// final http.Response response = await http.get(Uri.parse( +// 'https://monkey.banano.cc/api/v1/monkey/${address}?format=png&size=512&background=false')); +// +// if (response.statusCode == 200) { +// if (Platform.isAndroid) { +// await Permission.storage.request(); +// } +// +// final decodedResponse = response.bodyBytes; +// Directory directory = await getApplicationDocumentsDirectory(); +// late Directory sampleFolder; +// +// if (Platform.isAndroid) { +// directory = Directory("/storage/emulated/0/"); +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isIOS) { +// sampleFolder = Directory(directory!.path); +// } else if (Platform.isLinux) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isWindows) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } else if (Platform.isMacOS) { +// sampleFolder = Directory('${directory!.path}Documents'); +// } +// +// try { +// if (!sampleFolder.existsSync()) { +// sampleFolder.createSync(recursive: true); +// } +// } catch (e, s) { +// // todo: come back to this +// debugPrint("$e $s"); +// } +// +// final docPath = sampleFolder.path; +// final filePath = "$docPath/monkey.png"; +// +// File imgFile = File(filePath); +// await imgFile.writeAsBytes(decodedResponse); +// } else { +// throw Exception("Failed to get MonKey"); +// } +// } +// +// @override +// void initState() { +// walletId = widget.walletId; +// managerProvider = widget.managerProvider; +// +// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { +// final address = await ref +// .read(walletsChangeNotifierProvider) +// .getManager(walletId) +// .currentReceivingAddress; +// setState(() { +// receivingAddress = address; +// }); +// }); +// +// super.initState(); +// } +// +// @override +// void dispose() { +// super.dispose(); +// } +// +// @override +// Widget build(BuildContext context) { +// final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); +// final manager = ref.watch(walletsChangeNotifierProvider +// .select((value) => value.getManager(widget.walletId))); +// +// List? imageBytes; +// imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); +// +// return Background( +// child: Stack( +// children: [ +// Scaffold( +// appBar: AppBar( +// leading: AppBarBackButton( +// onPressed: () { +// Navigator.of(context).popUntil( +// ModalRoute.withName(WalletView.routeName), +// ); +// }, +// ), +// title: Text( +// "MonKey", +// style: STextStyles.navBarTitle(context), +// ), +// actions: [ +// AspectRatio( +// aspectRatio: 1, +// child: AppBarIconButton( +// icon: SvgPicture.asset(Assets.svg.circleQuestion), +// onPressed: () { +// showDialog( +// context: context, +// useSafeArea: false, +// barrierDismissible: true, +// builder: (context) { +// return Dialog( +// child: Material( +// borderRadius: BorderRadius.circular( +// 20, +// ), +// child: Container( +// height: 200, +// decoration: BoxDecoration( +// color: Theme.of(context) +// .extension()! +// .popupBG, +// borderRadius: BorderRadius.circular( +// 20, +// ), +// ), +// child: Column( +// children: [ +// Center( +// child: Text( +// "Help", +// style: STextStyles.pageTitleH2( +// context), +// ), +// ) +// ], +// ), +// ), +// ), +// ); +// }); +// }), +// ) +// ], +// ), +// body: Column( +// children: [ +// const Spacer( +// flex: 1, +// ), +// if (imageBytes != null) +// Container( +// child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), +// width: 300, +// height: 300, +// ), +// const Spacer( +// flex: 1, +// ), +// Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// children: [ +// SecondaryButton( +// label: "Download as SVG", +// onPressed: () async { +// getMonkeySVG(receivingAddress); +// }, +// ), +// const SizedBox(height: 12), +// SecondaryButton( +// label: "Download as PNG", +// onPressed: () { +// getMonkeyPNG(receivingAddress); +// }, +// ), +// ], +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// ); +// } +// } diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart new file mode 100644 index 000000000..2ad4b18eb --- /dev/null +++ b/lib/pages/monkey/monkey_view.dart @@ -0,0 +1,497 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/themes/coin_icon_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class MonkeyView extends ConsumerStatefulWidget { + const MonkeyView({ + Key? key, + required this.walletId, + }) : super(key: key); + + static const String routeName = "/monkey"; + static const double navBarHeight = 65.0; + + final String walletId; + + @override + ConsumerState createState() => _MonkeyViewState(); +} + +class _MonkeyViewState extends ConsumerState { + late final String walletId; + List? imageBytes; + + String receivingAddress = ""; + + Future getMonkeyImage(String address) async { + if (address.isEmpty) { + //address shouldn't be empty + return; + } + + final http.Response response = await http + .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); + + if (response.statusCode == 200) { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(walletId); + final decodedResponse = response.bodyBytes; + await (manager.wallet as BananoWallet) + .updateMonkeyImageBytes(decodedResponse); + } else { + throw Exception("Failed to get MonKey"); + } + } + + // void getMonkeySVG(String address) async { + // if (address.isEmpty) { + // //address shouldn't be empty + // return; + // } + // + // final http.Response response = await http + // .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); + // + // if (response.statusCode == 200) { + // final decodedResponse = response.bodyBytes; + // Directory directory = await getApplicationDocumentsDirectory(); + // late Directory sampleFolder; + // + // if (Platform.isAndroid) { + // directory = Directory("/storage/emulated/0/"); + // sampleFolder = Directory('${directory!.path}Documents'); + // } else if (Platform.isIOS) { + // sampleFolder = Directory(directory!.path); + // } else if (Platform.isLinux) { + // sampleFolder = Directory('${directory!.path}Documents'); + // } else if (Platform.isWindows) { + // sampleFolder = Directory('${directory!.path}Documents'); + // } else if (Platform.isMacOS) { + // sampleFolder = Directory('${directory!.path}Documents'); + // } + // + // try { + // if (!sampleFolder.existsSync()) { + // sampleFolder.createSync(recursive: true); + // } + // } catch (e, s) { + // // todo: come back to this + // debugPrint("$e $s"); + // } + // + // final docPath = sampleFolder.path; + // final filePath = "$docPath/monkey.svg"; + // + // File imgFile = File(filePath); + // await imgFile.writeAsBytes(decodedResponse); + // } else { + // throw Exception("Failed to get MonKey"); + // } + // } + + Future getDocsDir() async { + try { + if (Platform.isAndroid) { + return Directory("/storage/emulated/0/Documents"); + } + + return await getApplicationDocumentsDirectory(); + } catch (_) { + return null; + } + } + + Future downloadMonkey(String address, bool isPNG) async { + if (address.isEmpty) { + //address shouldn't be empty + return; + } + + String url = "https://monkey.banano.cc/api/v1/monkey/$address"; + + if (isPNG) { + url += '?format=png&size=512&background=false'; + } + + final http.Response response = await http.get(Uri.parse(url)); + + if (response.statusCode == 200) { + if (Platform.isAndroid) { + await Permission.storage.request(); + } + + final decodedResponse = response.bodyBytes; + final Directory? sampleFolder = await getDocsDir(); + + print("PATH: ${sampleFolder?.path}"); + + if (sampleFolder == null) { + print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + return; + } + + // try { + // if (!sampleFolder.existsSync()) { + // sampleFolder.createSync(recursive: true); + // } + // } catch (e, s) { + // // todo: come back to this + // debugPrint("$e $s"); + // } + + final docPath = sampleFolder.path; + String filePath = "$docPath/monkey_$address"; + + filePath += isPNG ? ".png" : ".svg"; + + // todo check if monkey.png exists + + File imgFile = File(filePath); + await imgFile.writeAsBytes(decodedResponse); + } else { + throw Exception("Failed to get MonKey"); + } + } + + @override + void initState() { + walletId = widget.walletId; + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final address = await ref + .read(walletsChangeNotifierProvider) + .getManager(walletId) + .currentReceivingAddress; + setState(() { + receivingAddress = address; + }); + }); + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + final Coin coin = manager.coin; + + final bool isDesktop = Util.isDesktop; + + imageBytes ??= (manager.wallet as BananoWallet).getMonkeyImageBytes(); + + //edit for desktop + return Background( + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension()!.popupBG, + leading: Expanded( + child: Row( + children: [ + const SizedBox( + width: 32, + ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: () { + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + const SizedBox( + width: 15, + ), + SvgPicture.asset(Assets.svg.monkey), + const SizedBox( + width: 12, + ), + Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + ], + ), + ), + trailing: Padding( + padding: const EdgeInsets.all(8.0), + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackDialog( + title: "About MonKeys", + message: + "A MonKey is a visual representation of your Banano address.", + ); + }); + }, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.circleQuestion, + color: Colors.blue[800], + ), + const SizedBox( + width: 6, + ), + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + "What is MonKey?", + style: STextStyles.desktopTextSmall(context).copyWith( + color: Colors.blue[800], + ), + ), + ), + ], + ), + ), + ), + ), + useSpacers: false, + isCompactHeight: true, + ), + body: child, + ), + child: ConditionalParent( + condition: !isDesktop, + builder: (child) => Scaffold( + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + ), + onPressed: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackOkDialog( + title: "About MonKeys", + message: + "A MonKey is a visual representation of your Banano address.", + ); + }); + }), + ), + ], + ), + body: child, + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => SizedBox( + width: 318, + child: child, + ), + child: ConditionalParent( + condition: imageBytes != null, + builder: (_) => Column( + children: [ + isDesktop + ? const SizedBox( + height: 50, + ) + : const Spacer( + flex: 1, + ), + if (imageBytes != null) + SizedBox( + width: 300, + height: 300, + child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), + ), + isDesktop + ? const SizedBox( + height: 50, + ) + : const Spacer( + flex: 1, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + SecondaryButton( + label: "Save as SVG", + onPressed: () async { + await showLoading( + whileFuture: + downloadMonkey(receivingAddress, false), + context: context, + isDesktop: Util.isDesktop, + message: "Saving MonKey svg", + ); + }, + ), + const SizedBox(height: 12), + SecondaryButton( + label: "Download as PNG", + onPressed: () async { + await showLoading( + whileFuture: + downloadMonkey(receivingAddress, true), + context: context, + isDesktop: Util.isDesktop, + message: "Downloading MonKey png", + ); + }, + ), + ], + ), + ), + // child, + ], + ), + child: Column( + children: [ + isDesktop + ? const SizedBox( + height: 100, + ) + : const Spacer( + flex: 4, + ), + Center( + child: Column( + children: [ + Opacity( + opacity: 0.2, + child: SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 200, + height: 200, + ), + ), + const SizedBox( + height: 70, + ), + Text( + "You do not have a MonKey yet. \nFetch yours now!", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + isDesktop + ? const SizedBox( + height: 50, + ) + : const Spacer( + flex: 6, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () async { + final future = Future.wait([ + getMonkeyImage(receivingAddress), + Future.delayed(const Duration(seconds: 2)), + ]); + + await showLoading( + whileFuture: future, + context: context, + isDesktop: Util.isDesktop, + message: "Fetching MonKey", + subMessage: "We are fetching your MonKey", + ); + + imageBytes = (manager.wallet as BananoWallet) + .getMonkeyImageBytes(); + + if (imageBytes != null) { + setState(() {}); + } + + // if (isDesktop) { + // Navigator.of(context).popUntil( + // ModalRoute.withName( + // DesktopWalletView.routeName), + // ); + // } else { + // Navigator.of(context).popUntil( + // ModalRoute.withName(WalletView.routeName), + // ); + // } + }, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart b/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart new file mode 100644 index 000000000..94034fb78 --- /dev/null +++ b/lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart @@ -0,0 +1,135 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/material.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/animated_widgets/rotating_arrows.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class FetchMonkeyDialog extends StatefulWidget { + const FetchMonkeyDialog({ + Key? key, + required this.onCancel, + }) : super(key: key); + + final Future Function() onCancel; + + @override + State createState() => _FetchMonkeyDialogState(); +} + +class _FetchMonkeyDialogState extends State { + late final Future Function() onCancel; + @override + void initState() { + onCancel = widget.onCancel; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (Util.isDesktop) { + return DesktopDialog( + child: Column( + children: [ + DesktopDialogCloseButton( + onPressedOverride: () async { + await onCancel.call(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + const Spacer( + flex: 1, + ), + const RotatingArrows( + width: 40, + height: 40, + ), + const Spacer( + flex: 2, + ), + Text( + "Fetching MonKey", + style: STextStyles.desktopH2(context), + textAlign: TextAlign.center, + ), + const SizedBox( + height: 16, + ), + Text( + "We are fetching your MonKey", + style: STextStyles.desktopTextMedium(context).copyWith( + color: Theme.of(context).extension()!.textDark3, + ), + textAlign: TextAlign.center, + ), + const Spacer( + flex: 2, + ), + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: SecondaryButton( + label: "Cancel", + width: 272.5, + onPressed: () async { + await onCancel.call(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + ], + ), + ); + } else { + return WillPopScope( + onWillPop: () async { + return false; + }, + child: StackDialog( + title: "Fetching MonKey", + message: "We are fetching your MonKey", + icon: const RotatingArrows( + width: 24, + height: 24, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + child: Text( + "Cancel", + style: STextStyles.itemSubtitle12(context), + ), + onPressed: () async { + await onCancel.call(); + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + ); + } + } +} diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart new file mode 100644 index 000000000..ced80c42c --- /dev/null +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -0,0 +1,267 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalDetailsView extends StatefulWidget { + const OrdinalDetailsView({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const routeName = "/ordinalDetailsView"; + + @override + State createState() => _OrdinalDetailsViewState(); +} + +class _OrdinalDetailsViewState extends State { + static const _spacing = 12.0; + + @override + Widget build(BuildContext context) { + return Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension()!.background, + leading: const AppBarBackButton(), + title: Text( + "Ordinal details", + style: STextStyles.navBarTitle(context), + ), + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 39, + ), + child: _OrdinalImageGroup( + ordinal: widget.ordinal, + walletId: widget.walletId, + ), + ), + _DetailsItemWCopy( + title: "Inscription number", + data: widget.ordinal.inscriptionNumber.toString(), + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "ID", + data: widget.ordinal.inscriptionId, + ), + const SizedBox( + height: _spacing, + ), + // todo: add utxo status + const SizedBox( + height: _spacing, + ), + const _DetailsItemWCopy( + title: "Amount", + data: "TODO", // TODO infer from utxo utxoTXID:utxoVOUT + ), + const SizedBox( + height: _spacing, + ), + const _DetailsItemWCopy( + title: "Owner address", + data: "TODO", // infer from address associated w utxoTXID + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Transaction ID", + data: widget.ordinal.utxoTXID, + ), + const SizedBox( + height: _spacing, + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _DetailsItemWCopy extends StatelessWidget { + const _DetailsItemWCopy({ + Key? key, + required this.title, + required this.data, + }) : super(key: key); + + final String title; + final String data; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.itemSubtitle(context), + ), + GestureDetector( + onTap: () async { + await Clipboard.setData(ClipboardData(text: data)); + if (context.mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + context: context, + ), + ); + } + }, + child: SvgPicture.asset( + Assets.svg.copy, + color: + Theme.of(context).extension()!.infoItemIcons, + width: 12, + ), + ), + ], + ), + const SizedBox( + height: 4, + ), + SelectableText( + data, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), + ); + } +} + +class _OrdinalImageGroup extends StatelessWidget { + const _OrdinalImageGroup({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const _spacing = 12.0; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Text( + // "${ordinal.inscriptionId}", // Use any other property you want + // style: STextStyles.w600_16(context), + // ), + // const SizedBox( + // height: _spacing, + // ), + AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.transparent, + child: Image.network( + ordinal.content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: + FilterQuality.none, // Set the filter mode to nearest + ), + ), + ), + const SizedBox( + height: _spacing, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Download", + icon: SvgPicture.asset( + Assets.svg.arrowDown, + width: 10, + height: 12, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, + ), + buttonHeight: ButtonHeight.l, + iconSpacing: 4, + onPressed: () { + // TODO: save and download image to device + }, + ), + ), + // const SizedBox( + // width: _spacing, + // ), + // Expanded( + // child: PrimaryButton( + // label: "Send", + // icon: SvgPicture.asset( + // Assets.svg.send, + // width: 10, + // height: 10, + // color: Theme.of(context) + // .extension()! + // .buttonTextPrimary, + // ), + // buttonHeight: ButtonHeight.l, + // iconSpacing: 4, + // onPressed: () async { + // final response = await showDialog( + // context: context, + // builder: (_) => const SendOrdinalUnfreezeDialog(), + // ); + // if (response == "unfreeze") { + // // TODO: unfreeze and go to send ord screen + // } + // }, + // ), + // ), + ], + ), + ], + ); + } +} diff --git a/lib/pages/ordinals/ordinals_filter_view.dart b/lib/pages/ordinals/ordinals_filter_view.dart new file mode 100644 index 000000000..631a9833a --- /dev/null +++ b/lib/pages/ordinals/ordinals_filter_view.dart @@ -0,0 +1,889 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/themes/theme_providers.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/format.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class OrdinalFilter { + // final bool isMoonbird; + // final bool isPunk; + final DateTime? from; + final DateTime? to; + final String? inscription; + final String keyword; + + OrdinalFilter({ + // required this.isMoonbird, + // required this.isPunk, + required this.from, + required this.to, + required this.inscription, + required this.keyword, + }); + + OrdinalFilter copyWith({ + // bool? isMoonbird, + // bool? isPunk, + DateTime? from, + DateTime? to, + String? inscription, + String? keyword, + }) { + return OrdinalFilter( + // isMoonbird: isMoonbird ?? this.isMoonbird, + // isPunk: isPunk ?? this.isPunk, + from: from ?? this.from, + to: to ?? this.to, + inscription: inscription ?? this.inscription, + keyword: keyword ?? this.keyword, + ); + } +} + +final ordinalFilterProvider = StateProvider((_) => null); + +class OrdinalsFilterView extends ConsumerStatefulWidget { + const OrdinalsFilterView({ + Key? key, + }) : super(key: key); + + static const String routeName = "/ordinalsFilterView"; + + @override + ConsumerState createState() => _OrdinalsFilterViewState(); +} + +class _OrdinalsFilterViewState extends ConsumerState { + final _inscriptionTextEditingController = TextEditingController(); + final _keywordTextEditingController = TextEditingController(); + + // bool _isPunk = false; + // bool _isMoonbird = false; + + String _fromDateString = ""; + String _toDateString = ""; + + final keywordTextFieldFocusNode = FocusNode(); + final inscriptionTextFieldFocusNode = FocusNode(); + + late Color baseColor; + + @override + initState() { + baseColor = ref.read(themeProvider.state).state.textSubtitle2; + final filterState = ref.read(ordinalFilterProvider.state).state; + if (filterState != null) { + // _isMoonbird = filterState.isMoonbird; + // _isPunk = filterState.isPunk; + _selectedToDate = filterState.to; + _selectedFromDate = filterState.from; + _keywordTextEditingController.text = filterState.keyword; + _inscriptionTextEditingController.text = filterState.inscription ?? ""; + } + + super.initState(); + } + + @override + dispose() { + _inscriptionTextEditingController.dispose(); + _keywordTextEditingController.dispose(); + keywordTextFieldFocusNode.dispose(); + inscriptionTextFieldFocusNode.dispose(); + + super.dispose(); + } + + // The following two getters are not required if the + // date fields are to remain unclearable. + Widget get _dateFromText { + final isDateSelected = _fromDateString.isEmpty; + return Text( + isDateSelected ? "From..." : _fromDateString, + style: STextStyles.fieldLabel(context).copyWith( + color: isDateSelected + ? Theme.of(context).extension()!.textSubtitle2 + : Theme.of(context).extension()!.accentColorDark), + ); + } + + Widget get _dateToText { + final isDateSelected = _toDateString.isEmpty; + return Text( + isDateSelected ? "To..." : _toDateString, + style: STextStyles.fieldLabel(context).copyWith( + color: isDateSelected + ? Theme.of(context).extension()!.textSubtitle2 + : Theme.of(context).extension()!.accentColorDark), + ); + } + + DateTime? _selectedFromDate = DateTime(2007); + DateTime? _selectedToDate = DateTime.now(); + + MaterialRoundedDatePickerStyle _buildDatePickerStyle() { + return MaterialRoundedDatePickerStyle( + backgroundPicker: Theme.of(context).extension()!.popupBG, + // backgroundHeader: Theme.of(context).extension()!.textSubtitle2, + paddingMonthHeader: const EdgeInsets.only(top: 11), + colorArrowNext: Theme.of(context).extension()!.textSubtitle1, + colorArrowPrevious: + Theme.of(context).extension()!.textSubtitle1, + textStyleButtonNegative: STextStyles.datePicker600(context).copyWith( + color: baseColor, + ), + textStyleButtonPositive: STextStyles.datePicker600(context).copyWith( + color: baseColor, + ), + textStyleCurrentDayOnCalendar: STextStyles.datePicker400(context), + textStyleDayHeader: STextStyles.datePicker600(context), + textStyleDayOnCalendar: STextStyles.datePicker400(context).copyWith( + color: baseColor, + ), + textStyleDayOnCalendarDisabled: + STextStyles.datePicker400(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle3, + ), + textStyleDayOnCalendarSelected: + STextStyles.datePicker400(context).copyWith( + color: Theme.of(context).extension()!.textWhite, + ), + textStyleMonthYearHeader: STextStyles.datePicker600(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle1, + ), + textStyleYearButton: STextStyles.datePicker600(context).copyWith( + color: Theme.of(context).extension()!.textWhite, + ), + // textStyleButtonAction: GoogleFonts.inter(), + ); + } + + MaterialRoundedYearPickerStyle _buildYearPickerStyle() { + return MaterialRoundedYearPickerStyle( + backgroundPicker: Theme.of(context).extension()!.popupBG, + textStyleYear: STextStyles.datePicker600(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle2, + fontSize: 16, + ), + textStyleYearSelected: STextStyles.datePicker600(context).copyWith( + fontSize: 18, + ), + ); + } + + Widget _buildDateRangePicker() { + const middleSeparatorPadding = 2.0; + const middleSeparatorWidth = 12.0; + final isDesktop = Util.isDesktop; + + final width = isDesktop + ? null + : (MediaQuery.of(context).size.width - + (middleSeparatorWidth + + (2 * middleSeparatorPadding) + + (2 * Constants.size.standardPadding))) / + 2; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: GestureDetector( + key: const Key("OrdinalsViewFromDatePickerKey"), + onTap: () async { + final color = + Theme.of(context).extension()!.accentColorDark; + final height = MediaQuery.of(context).size.height; + // check and hide keyboard + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 125)); + } + + if (mounted) { + final date = await showRoundedDatePicker( + // This doesn't change statusbar color... + // background: CFColors.starryNight.withOpacity(0.8), + context: context, + initialDate: DateTime.now(), + height: height * 0.5, + theme: ThemeData( + primarySwatch: Util.createMaterialColor( + color, + ), + ), + //TODO pick a better initial date + // 2007 chosen as that is just before bitcoin launched + firstDate: DateTime(2007), + lastDate: DateTime.now(), + borderRadius: Constants.size.circularBorderRadius * 2, + + textPositiveButton: "SELECT", + + styleDatePicker: _buildDatePickerStyle(), + styleYearPicker: _buildYearPickerStyle(), + ); + if (date != null) { + _selectedFromDate = date; + + // flag to adjust date so from date is always before to date + final flag = _selectedToDate != null && + !_selectedFromDate!.isBefore(_selectedToDate!); + if (flag) { + _selectedToDate = DateTime.fromMillisecondsSinceEpoch( + _selectedFromDate!.millisecondsSinceEpoch); + } + + setState(() { + if (flag) { + _toDateString = _selectedToDate == null + ? "" + : Format.formatDate(_selectedToDate!); + } + _fromDateString = _selectedFromDate == null + ? "" + : Format.formatDate(_selectedFromDate!); + }); + } + } + }, + child: Container( + width: width, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: + BorderRadius.circular(Constants.size.circularBorderRadius), + border: Border.all( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, + vertical: isDesktop ? 17 : 12, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.calendar, + height: 20, + width: 20, + color: Theme.of(context) + .extension()! + .textSubtitle2, + ), + const SizedBox( + width: 10, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: _dateFromText, + ), + ) + ], + ), + ), + ), + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: middleSeparatorPadding), + child: Container( + width: middleSeparatorWidth, + // height: 1, + // color: CFColors.smoke, + ), + ), + Expanded( + child: GestureDetector( + key: const Key("OrdinalsViewToDatePickerKey"), + onTap: () async { + final color = + Theme.of(context).extension()!.accentColorDark; + final height = MediaQuery.of(context).size.height; + // check and hide keyboard + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 125)); + } + + if (mounted) { + final date = await showRoundedDatePicker( + // This doesn't change statusbar color... + // background: CFColors.starryNight.withOpacity(0.8), + context: context, + height: height * 0.5, + theme: ThemeData( + primarySwatch: Util.createMaterialColor( + color, + ), + ), + //TODO pick a better initial date + // 2007 chosen as that is just before bitcoin launched + initialDate: DateTime.now(), + firstDate: DateTime(2007), + lastDate: DateTime.now(), + borderRadius: Constants.size.circularBorderRadius * 2, + + textPositiveButton: "SELECT", + + styleDatePicker: _buildDatePickerStyle(), + styleYearPicker: _buildYearPickerStyle(), + ); + if (date != null) { + _selectedToDate = date; + + // flag to adjust date so from date is always before to date + final flag = _selectedFromDate != null && + !_selectedToDate!.isAfter(_selectedFromDate!); + if (flag) { + _selectedFromDate = DateTime.fromMillisecondsSinceEpoch( + _selectedToDate!.millisecondsSinceEpoch); + } + + setState(() { + if (flag) { + _fromDateString = _selectedFromDate == null + ? "" + : Format.formatDate(_selectedFromDate!); + } + _toDateString = _selectedToDate == null + ? "" + : Format.formatDate(_selectedToDate!); + }); + } + } + }, + child: Container( + width: width, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + borderRadius: + BorderRadius.circular(Constants.size.circularBorderRadius), + border: Border.all( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, + vertical: isDesktop ? 17 : 12, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.calendar, + height: 20, + width: 20, + color: Theme.of(context) + .extension()! + .textSubtitle2, + ), + const SizedBox( + width: 10, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: _dateToText, + ), + ) + ], + ), + ), + ), + ), + ), + if (isDesktop) + const SizedBox( + width: 24, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + if (Util.isDesktop) { + return DesktopDialog( + maxWidth: 576, + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.only( + left: 32, + bottom: 32, + ), + child: _buildContent(context), + ), + ); + } else { + return Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension()!.background, + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed(const Duration(milliseconds: 75)); + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + "Ordinals filter", + style: STextStyles.navBarTitle(context), + ), + ), + body: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constants.size.standardPadding, + ), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: _buildContent(context), + ), + ), + ); + }, + ), + ), + ), + ); + } + } + + Widget _buildContent(BuildContext context) { + final isDesktop = Util.isDesktop; + + return Column( + children: [ + if (isDesktop) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Ordinals filter", + style: STextStyles.desktopH3(context), + textAlign: TextAlign.center, + ), + const DesktopDialogCloseButton(), + ], + ), + SizedBox( + height: isDesktop ? 14 : 10, + ), + // if (!isDesktop) + // Align( + // alignment: Alignment.centerLeft, + // child: FittedBox( + // child: Text( + // "Collection", + // style: STextStyles.smallMed12(context), + // ), + // ), + // ), + // if (!isDesktop) + // const SizedBox( + // height: 12, + // ), + // RoundedWhiteContainer( + // padding: EdgeInsets.all(isDesktop ? 0 : 12), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // children: [ + // GestureDetector( + // onTap: () { + // setState(() { + // _isPunk = !_isPunk; + // }); + // }, + // child: Container( + // color: Colors.transparent, + // child: Row( + // children: [ + // SizedBox( + // height: 20, + // width: 20, + // child: Checkbox( + // key: const Key("OrdinalsPunkCheckboxKey"), + // materialTapTargetSize: + // MaterialTapTargetSize.shrinkWrap, + // value: _isPunk, + // onChanged: (newValue) { + // setState(() { + // _isPunk = newValue!; + // }); + // }, + // ), + // ), + // const SizedBox( + // width: 14, + // ), + // Align( + // alignment: Alignment.centerLeft, + // child: FittedBox( + // child: Column( + // children: [ + // Text( + // "Punks", + // style: isDesktop + // ? STextStyles.desktopTextSmall(context) + // : STextStyles.itemSubtitle12(context), + // ), + // if (isDesktop) + // const SizedBox( + // height: 4, + // ), + // ], + // ), + // ), + // ) + // ], + // ), + // ), + // ), + // ], + // ), + // SizedBox( + // height: isDesktop ? 4 : 10, + // ), + // Row( + // children: [ + // GestureDetector( + // onTap: () { + // setState(() { + // _isMoonbird = !_isMoonbird; + // }); + // }, + // child: Container( + // color: Colors.transparent, + // child: Row( + // children: [ + // SizedBox( + // height: 20, + // width: 20, + // child: Checkbox( + // key: const Key( + // "OrdinalsFilterMoonbirdCheckboxKey", + // ), + // materialTapTargetSize: + // MaterialTapTargetSize.shrinkWrap, + // value: _isMoonbird, + // onChanged: (newValue) { + // setState(() { + // _isMoonbird = newValue!; + // }); + // }, + // ), + // ), + // const SizedBox( + // width: 14, + // ), + // Align( + // alignment: Alignment.centerLeft, + // child: FittedBox( + // child: Column( + // children: [ + // Text( + // "Moonbirds", + // style: isDesktop + // ? STextStyles.desktopTextSmall(context) + // : STextStyles.itemSubtitle12(context), + // ), + // if (isDesktop) + // const SizedBox( + // height: 4, + // ), + // ], + // ), + // ), + // ) + // ], + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), + // SizedBox( + // height: isDesktop ? 32 : 24, + // ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: Text( + "Date", + style: isDesktop + ? STextStyles.labelExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + ), + ), + SizedBox( + height: isDesktop ? 10 : 8, + ), + _buildDateRangePicker(), + SizedBox( + height: isDesktop ? 32 : 24, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: Text( + "Inscription", + style: isDesktop + ? STextStyles.labelExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + ), + ), + SizedBox( + height: isDesktop ? 10 : 8, + ), + Padding( + padding: EdgeInsets.only(right: isDesktop ? 32 : 0), + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + key: const Key("OrdinalsInscriptionFieldKey"), + controller: _inscriptionTextEditingController, + focusNode: inscriptionTextFieldFocusNode, + onChanged: (_) => setState(() {}), + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark, + height: 1.8, + ) + : STextStyles.field(context), + decoration: standardInputDecoration( + "Enter inscription number...", + keywordTextFieldFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ) + : null, + suffixIcon: _inscriptionTextEditingController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _inscriptionTextEditingController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + SizedBox( + height: isDesktop ? 32 : 24, + ), + Align( + alignment: Alignment.centerLeft, + child: FittedBox( + child: Text( + "Keyword", + style: isDesktop + ? STextStyles.labelExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + ), + ), + SizedBox( + height: isDesktop ? 10 : 8, + ), + Padding( + padding: EdgeInsets.only(right: isDesktop ? 32 : 0), + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + key: const Key("OrdinalsViewKeywordFieldKey"), + controller: _keywordTextEditingController, + focusNode: keywordTextFieldFocusNode, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: + Theme.of(context).extension()!.textDark, + height: 1.8, + ) + : STextStyles.field(context), + onChanged: (_) => setState(() {}), + decoration: standardInputDecoration( + "Type keyword...", + keywordTextFieldFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ) + : null, + suffixIcon: _keywordTextEditingController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _keywordTextEditingController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + ), + if (!isDesktop) const Spacer(), + SizedBox( + height: isDesktop ? 32 : 20, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: () async { + if (!isDesktop) { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration( + milliseconds: 75, + ), + ); + } + } + if (mounted) { + Navigator.of(context).pop(); + } + }, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: () async { + await _onApplyPressed(); + }, + label: "Save", + ), + ), + if (isDesktop) + const SizedBox( + width: 32, + ), + ], + ), + if (!isDesktop) + const SizedBox( + height: 20, + ), + ], + ); + } + + Future _onApplyPressed() async { + final filter = OrdinalFilter( + // isPunk: _isPunk, + // isMoonbird: _isMoonbird, + from: _selectedFromDate, + to: _selectedToDate, + inscription: _inscriptionTextEditingController.text, + keyword: _keywordTextEditingController.text, + ); + + ref.read(ordinalFilterProvider.state).state = filter; + + Navigator.of(context).pop(); + } +} diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart new file mode 100644 index 000000000..45b30655a --- /dev/null +++ b/lib/pages/ordinals/ordinals_view.dart @@ -0,0 +1,202 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; + +class OrdinalsView extends ConsumerStatefulWidget { + const OrdinalsView({ + super.key, + required this.walletId, + }); + + static const routeName = "/ordinalsView"; + + final String walletId; + + @override + ConsumerState createState() => _OrdinalsViewState(); +} + +class _OrdinalsViewState extends ConsumerState { + late final TextEditingController searchController; + late final FocusNode searchFocus; + + String _searchTerm = ""; + + @override + void initState() { + searchController = TextEditingController(); + searchFocus = FocusNode(); + + super.initState(); + } + + @override + void dispose() { + searchController.dispose(); + searchFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: const AppBarBackButton(), + title: Text( + "Ordinals", + style: STextStyles.navBarTitle(context), + ), + titleSpacing: 0, + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + size: 36, + icon: SvgPicture.asset( + Assets.svg.arrowRotate, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: () async { + // show loading for a minimum of 2 seconds on refreshing + await showLoading( + whileFuture: Future.wait([ + Future.delayed(const Duration(seconds: 2)), + (ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as OrdinalsInterface) + .refreshInscriptions() + ]), + context: context, + message: "Refreshing...", + ); + }, + ), + ), + // AspectRatio( + // aspectRatio: 1, + // child: AppBarIconButton( + // size: 36, + // icon: SvgPicture.asset( + // Assets.svg.filter, + // width: 20, + // height: 20, + // color: Theme.of(context) + // .extension()! + // .topNavIconPrimary, + // ), + // onPressed: () { + // Navigator.of(context).pushNamed( + // OrdinalsFilterView.routeName, + // ); + // }, + // ), + // ), + ], + ), + body: Padding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + top: 8, + ), + child: Column( + children: [ + // ClipRRect( + // borderRadius: BorderRadius.circular( + // Constants.size.circularBorderRadius, + // ), + // child: TextField( + // autocorrect: Util.isDesktop ? false : true, + // enableSuggestions: Util.isDesktop ? false : true, + // controller: searchController, + // focusNode: searchFocus, + // onChanged: (value) { + // setState(() { + // _searchTerm = value; + // }); + // }, + // style: STextStyles.field(context), + // decoration: standardInputDecoration( + // "Search", + // searchFocus, + // context, + // ).copyWith( + // prefixIcon: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 16, + // ), + // child: SvgPicture.asset( + // Assets.svg.search, + // width: 16, + // height: 16, + // ), + // ), + // suffixIcon: searchController.text.isNotEmpty + // ? Padding( + // padding: const EdgeInsets.only(right: 0), + // child: UnconstrainedBox( + // child: Row( + // children: [ + // TextFieldIconButton( + // child: const XIcon(), + // onTap: () async { + // setState(() { + // searchController.text = ""; + // _searchTerm = ""; + // }); + // }, + // ), + // ], + // ), + // ), + // ) + // : null, + // ), + // ), + // ), + // const SizedBox( + // height: 16, + // ), + Expanded( + child: OrdinalsList( + walletId: widget.walletId, + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/dialogs.dart b/lib/pages/ordinals/widgets/dialogs.dart new file mode 100644 index 000000000..ee5b57d33 --- /dev/null +++ b/lib/pages/ordinals/widgets/dialogs.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class SendOrdinalUnfreezeDialog extends StatelessWidget { + const SendOrdinalUnfreezeDialog({super.key}); + + @override + Widget build(BuildContext context) { + return StackDialog( + title: "This ordinal is frozen", + icon: SvgPicture.asset( + Assets.svg.coinControl.blocked, + width: 24, + height: 24, + color: Theme.of(context).extension()!.textDark, + ), + message: "To send this ordinal, you must unfreeze it first.", + leftButton: SecondaryButton( + label: "Cancel", + onPressed: Navigator.of(context).pop, + ), + rightButton: PrimaryButton( + label: "Unfreeze", + onPressed: () { + Navigator.of(context).pop("unfreeze"); + }, + ), + ); + } +} + +class UnfreezeOrdinalDialog extends StatelessWidget { + const UnfreezeOrdinalDialog({super.key}); + + @override + Widget build(BuildContext context) { + return StackDialog( + title: "Are you sure you want to unfreeze this ordinal?", + icon: SvgPicture.asset( + Assets.svg.coinControl.blocked, + width: 24, + height: 24, + color: Theme.of(context).extension()!.textDark, + ), + leftButton: SecondaryButton( + label: "Cancel", + onPressed: Navigator.of(context).pop, + ), + rightButton: PrimaryButton( + label: "Unfreeze", + onPressed: () { + Navigator.of(context).pop("unfreeze"); + }, + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart new file mode 100644 index 000000000..c74366d74 --- /dev/null +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalCard extends StatelessWidget { + const OrdinalCard({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + radiusMultiplier: 2, + onPressed: () { + Navigator.of(context).pushNamed( + Util.isDesktop + ? DesktopOrdinalDetailsView.routeName + : OrdinalDetailsView.routeName, + arguments: (walletId: walletId, ordinal: ordinal), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AspectRatio( + aspectRatio: 1, + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: Image.network( + ordinal.content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: + FilterQuality.none, // Set the filter mode to nearest + ), + ), + ), + const Spacer(), + Text( + 'INSC. ${ordinal.inscriptionNumber}', // infer from address associated with utxoTXID + style: STextStyles.w500_12(context), + ), + // const Spacer(), + // Text( + // "ID ${ordinal.inscriptionId}", + // style: STextStyles.w500_8(context), + // ), + ], + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart new file mode 100644 index 000000000..481b0ef0a --- /dev/null +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -0,0 +1,119 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:isar/isar.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; +import 'package:stackwallet/providers/db/main_db_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalsList extends ConsumerStatefulWidget { + const OrdinalsList({ + Key? key, + required this.walletId, + }) : super(key: key); + + final String walletId; + + @override + ConsumerState createState() => _OrdinalsListState(); +} + +class _OrdinalsListState extends ConsumerState { + final double _spacing = Util.isDesktop ? 16 : 10; + + late List _data; + + late final Stream?> _stream; + + @override + void initState() { + _stream = ref + .read(mainDBProvider) + .isar + .ordinals + .where() + .filter() + .walletIdEqualTo(widget.walletId) + .watch(); + + _data = ref + .read(mainDBProvider) + .isar + .ordinals + .where() + .filter() + .walletIdEqualTo(widget.walletId) + .findAllSync(); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return StreamBuilder?>( + stream: _stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + _data = snapshot.data!; + } + + if (_data.isEmpty) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + child: Center( + child: Text( + "Your ordinals will appear here", + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1) + : STextStyles.label(context), + ), + ), + ), + ], + ); + } + + if (Util.isDesktop) { + return Wrap( + spacing: _spacing, + runSpacing: _spacing, + children: _data + .map((e) => SizedBox( + width: 220, + height: 270, + child: OrdinalCard( + walletId: widget.walletId, + ordinal: e, + ))) + .toList(), + ); + } else { + return GridView.builder( + shrinkWrap: true, + itemCount: _data.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: _spacing, + mainAxisSpacing: _spacing, + crossAxisCount: Util.isDesktop ? 4 : 2, + childAspectRatio: 6 / 7, // was 3/4, less data displayed now + ), + itemBuilder: (_, i) => OrdinalCard( + walletId: widget.walletId, + ordinal: _data[i], + ), + ); + } + }, + ); + } +} diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart index 3193e7bb7..a1fc66f21 100644 --- a/lib/pages/send_view/confirm_transaction_view.dart +++ b/lib/pages/send_view/confirm_transaction_view.dart @@ -493,51 +493,54 @@ class _ConfirmTransactionViewState ], ), ), - if (coin == Coin.epicCash) + if (coin == Coin.epicCash && + (transactionInfo["onChainNote"] as String).isNotEmpty) const SizedBox( height: 12, ), - if (coin == Coin.epicCash) + if (coin == Coin.epicCash && + (transactionInfo["onChainNote"] as String).isNotEmpty) RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "On chain note", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - transactionInfo["onChainNote"] as String, - style: STextStyles.itemSubtitle12(context), - ), - ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "On chain note", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + transactionInfo["onChainNote"] as String, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), ), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - (coin == Coin.epicCash) ? "Local Note" : - "Note", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - transactionInfo["note"] as String, - style: STextStyles.itemSubtitle12(context), - ), - ], + if ((transactionInfo["note"] as String).isNotEmpty) + const SizedBox( + height: 12, + ), + if ((transactionInfo["note"] as String).isNotEmpty) + RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + (coin == Coin.epicCash) ? "Local Note" : "Note", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + transactionInfo["note"] as String, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), ), - ), ], ), if (isDesktop) diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart index 235bb5e95..812fcc94b 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart @@ -164,7 +164,7 @@ class _InstallThemeFromFileDialogState ); if (mounted) { Navigator.of(context).pop(); - if (!result) { + if (!result!) { unawaited( showDialog( context: context, diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart index 0c45a68f7..003063bde 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart @@ -72,11 +72,11 @@ class _StackThemeCardState extends ConsumerState { } Future _downloadPressed() async { - final result = await showLoading( + final result = (await showLoading( whileFuture: _downloadAndInstall(), context: context, message: "Downloading and installing theme...", - ); + ))!; if (mounted) { final message = result diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 59f443db9..fcacc60d4 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -20,11 +20,12 @@ import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/advanced_views/debug_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/route_generator.dart'; @@ -231,7 +232,7 @@ class _WalletSettingsViewState extends ConsumerState { .mnemonic; if (mounted) { - Navigator.push( + await Navigator.push( context, RouteGenerator.getRoute( shouldUseMaterialRoute: @@ -305,6 +306,25 @@ class _WalletSettingsViewState extends ConsumerState { ); }, ), + if (coin == Coin.nano || coin == Coin.banano) + const SizedBox( + height: 8, + ), + if (coin == Coin.nano || coin == Coin.banano) + Consumer( + builder: (_, ref, __) { + return SettingsListButton( + iconAssetName: Assets.svg.eye, + title: "Change representative", + onPressed: () { + Navigator.of(context).pushNamed( + ChangeRepresentativeView.routeName, + arguments: widget.walletId, + ); + }, + ); + }, + ), const SizedBox( height: 8, ), @@ -434,18 +454,20 @@ class _EpiBoxInfoFormState extends ConsumerState { TextButton( onPressed: () async { try { - wallet.updateEpicboxConfig( + await wallet.updateEpicboxConfig( hostController.text, int.parse(portController.text), ); - showFloatingFlushBar( - context: context, - message: "Epicbox info saved!", - type: FlushBarType.success, - ); - wallet.refresh(); + if (mounted) { + await showFloatingFlushBar( + context: context, + message: "Epicbox info saved!", + type: FlushBarType.success, + ); + } + unawaited(wallet.refresh()); } catch (e) { - showFloatingFlushBar( + await showFloatingFlushBar( context: context, message: "Failed to save epicbox info: $e", type: FlushBarType.warning, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart new file mode 100644 index 000000000..a91b0cb4e --- /dev/null +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart @@ -0,0 +1,402 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/services/coins/nano/nano_wallet.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/clipboard_interface.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class ChangeRepresentativeView extends ConsumerStatefulWidget { + const ChangeRepresentativeView({ + Key? key, + required this.walletId, + this.clipboardInterface = const ClipboardWrapper(), + }) : super(key: key); + + final String walletId; + final ClipboardInterface clipboardInterface; + + static const String routeName = "/changeRepresentative"; + + @override + ConsumerState createState() => _XPubViewState(); +} + +class _XPubViewState extends ConsumerState { + final _textController = TextEditingController(); + final _textFocusNode = FocusNode(); + final bool isDesktop = Util.isDesktop; + + late ClipboardInterface _clipboardInterface; + + String? representative; + + Future loadRepresentative() async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(widget.walletId); + + if (manager.coin == Coin.nano) { + return (manager.wallet as NanoWallet).getCurrentRepresentative(); + } else if (manager.coin == Coin.banano) { + return (manager.wallet as BananoWallet).getCurrentRepresentative(); + } + throw Exception("Unsupported wallet attempted to show representative!"); + } + + Future _save() async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(widget.walletId); + + final changeFuture = manager.coin == Coin.nano + ? (manager.wallet as NanoWallet).changeRepresentative + : (manager.wallet as BananoWallet).changeRepresentative; + + final result = await showLoading( + whileFuture: changeFuture(_textController.text), + context: context, + message: "Updating representative...", + isDesktop: Util.isDesktop, + onException: (ex) { + String msg = ex.toString(); + while (msg.isNotEmpty && msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }); + + if (mounted) { + if (result != null && result) { + setState(() { + representative = _textController.text; + _textController.text = ""; + }); + await showFloatingFlushBar( + type: FlushBarType.success, + message: "Representative changed", + context: context, + ); + } + } + } + + @override + void initState() { + _clipboardInterface = widget.clipboardInterface; + + super.initState(); + } + + @override + void dispose() { + _textController.dispose(); + _textFocusNode.dispose(); + super.dispose(); + } + + Future _copy() async { + await _clipboardInterface + .setData(ClipboardData(text: representative ?? "")); + if (mounted) { + unawaited(showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + )); + } + } + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Wallet representative", + style: STextStyles.navBarTitle(context), + ), + actions: [ + Padding( + padding: const EdgeInsets.all(10), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + color: Theme.of(context) + .extension()! + .background, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.copy, + width: 24, + height: 24, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: () { + if (representative != null) { + _copy(); + } + }, + ), + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.only( + top: 12, + left: 16, + right: 16, + ), + child: child, + ), + ), + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopDialog( + maxWidth: 600, + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Change representative", + style: STextStyles.desktopH2(context), + ), + ), + DesktopDialogCloseButton( + onPressedOverride: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), + ], + ), + AnimatedSize( + duration: const Duration( + milliseconds: 150, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(32, 0, 32, 32), + child: child, + ), + ), + ], + ), + ), + child: Column( + children: [ + if (isDesktop) const SizedBox(height: 24), + ConditionalParent( + condition: !isDesktop, + builder: (child) => Expanded( + child: child, + ), + child: FutureBuilder( + future: loadRepresentative(), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + representative = snapshot.data!; + } + + const height = 600.0; + Widget child; + if (representative == null) { + child = const SizedBox( + key: Key("loadingRepresentative"), + height: height, + child: Center( + child: LoadingIndicator( + width: 100, + ), + ), + ); + } else { + child = Column( + children: [ + ConditionalParent( + condition: !isDesktop, + builder: (child) => RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + child, + ], + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Current representative", + style: STextStyles.desktopTextExtraExtraSmall( + context), + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + child, + ], + ), + ], + ), + child: SelectableText( + representative!, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle12(context), + ), + ), + ), + const SizedBox( + height: 24, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + controller: _textController, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveText, + height: 1.8, + ) + : STextStyles.field(context), + focusNode: _textFocusNode, + decoration: standardInputDecoration( + "Enter new representative", + _textFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.only( + left: 16, + top: 11, + bottom: 12, + right: 5, + ) + : null, + suffixIcon: _textController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _textController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + if (isDesktop) const SizedBox(height: 60), + if (!isDesktop) const Spacer(), + PrimaryButton( + label: "Save", + onPressed: _save, + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), + ], + ); + } + + return AnimatedSwitcher( + duration: const Duration( + milliseconds: 200, + ), + child: child, + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/global_settings_view/xpub_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart similarity index 100% rename from lib/pages/settings_views/global_settings_view/xpub_view.dart rename to lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index 31dc2b085..323ddcf8b 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -100,7 +100,7 @@ class _MyTokenSelectItemState extends ConsumerState { message: "Loading ${widget.token.name}", ); - if (!success) { + if (!success!) { return; } diff --git a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart index 4b2ee7a3b..029cd7aa3 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart @@ -121,7 +121,8 @@ class WalletBalanceToggleSheet extends ConsumerWidget { height: 24, ), BalanceSelector( - title: "Available balance", + title: + "Available${balanceSecondary != null ? " public" : ""} balance", coin: coin, balance: balance.spendable, onPressed: () { @@ -141,6 +142,31 @@ class WalletBalanceToggleSheet extends ConsumerWidget { value: _BalanceType.available, groupValue: _bal, ), + const SizedBox( + height: 12, + ), + BalanceSelector( + title: + "Full${balanceSecondary != null ? " public" : ""} balance", + coin: coin, + balance: balance.total, + onPressed: () { + ref.read(walletBalanceToggleStateProvider.state).state = + WalletBalanceToggleState.full; + ref.read(publicPrivateBalanceStateProvider.state).state = + "Public"; + Navigator.of(context).pop(); + }, + onChanged: (_) { + ref.read(walletBalanceToggleStateProvider.state).state = + WalletBalanceToggleState.full; + ref.read(publicPrivateBalanceStateProvider.state).state = + "Public"; + Navigator.of(context).pop(); + }, + value: _BalanceType.full, + groupValue: _bal, + ), if (balanceSecondary != null) const SizedBox( height: 12, @@ -167,30 +193,6 @@ class WalletBalanceToggleSheet extends ConsumerWidget { value: _BalanceType.privateAvailable, groupValue: _bal, ), - const SizedBox( - height: 12, - ), - BalanceSelector( - title: "Full balance", - coin: coin, - balance: balance.total, - onPressed: () { - ref.read(walletBalanceToggleStateProvider.state).state = - WalletBalanceToggleState.full; - ref.read(publicPrivateBalanceStateProvider.state).state = - "Public"; - Navigator.of(context).pop(); - }, - onChanged: (_) { - ref.read(walletBalanceToggleStateProvider.state).state = - WalletBalanceToggleState.full; - ref.read(publicPrivateBalanceStateProvider.state).state = - "Public"; - Navigator.of(context).pop(); - }, - value: _BalanceType.full, - groupValue: _bal, - ), if (balanceSecondary != null) const SizedBox( height: 12, diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary.dart index a82145817..08f5f22c8 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary.dart @@ -52,6 +52,7 @@ class WalletSummary extends StatelessWidget { walletId: walletId, width: constraints.maxWidth, height: constraints.maxHeight, + isFavorite: false, ), Positioned.fill( child: Padding( diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart index a4716d190..0c4e6fb08 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart @@ -10,6 +10,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -19,6 +20,7 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button. import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart'; import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/event_bus/events/global/balance_refreshed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; @@ -31,6 +33,7 @@ import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart'; import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; class WalletSummaryInfo extends ConsumerStatefulWidget { const WalletSummaryInfo({ @@ -49,6 +52,8 @@ class WalletSummaryInfo extends ConsumerStatefulWidget { class _WalletSummaryInfoState extends ConsumerState { late StreamSubscription _balanceUpdated; + String receivingAddress = ""; + void showSheet() { showModalBottomSheet( backgroundColor: Colors.transparent, @@ -72,6 +77,17 @@ class _WalletSummaryInfoState extends ConsumerState { } }, ); + + // managerProvider = widget.managerProvider; + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final address = await ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .currentReceivingAddress; + setState(() { + receivingAddress = address; + }); + }); super.initState(); } @@ -85,10 +101,14 @@ class _WalletSummaryInfoState extends ConsumerState { Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + bool isMonkey = true; + + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + final externalCalls = ref.watch( prefsChangeNotifierProvider.select((value) => value.externalCalls)); - final coin = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId).coin)); + final coin = manager.coin; final balance = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManager(widget.walletId).balance)); @@ -125,84 +145,104 @@ class _WalletSummaryInfoState extends ConsumerState { title = _showAvailable ? "Available balance" : "Full balance"; } - return Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onTap: showSheet, - child: Row( - children: [ - Text( - title, - style: STextStyles.subtitle500(context).copyWith( + List? imageBytes; + + if (coin == Coin.banano) { + imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); + } + + return ConditionalParent( + condition: imageBytes != null, + builder: (child) => Stack( + children: [ + Positioned.fill( + left: 150.0, + child: SvgPicture.memory( + Uint8List.fromList(imageBytes!), + ), + ), + child, + ], + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: showSheet, + child: Row( + children: [ + Text( + title, + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension()! + .textFavoriteCard, + ), + ), + const SizedBox( + width: 4, + ), + SvgPicture.asset( + Assets.svg.chevronDown, color: Theme.of(context) .extension()! .textFavoriteCard, + width: 8, + height: 4, ), - ), - const SizedBox( - width: 4, - ), - SvgPicture.asset( - Assets.svg.chevronDown, + ], + ), + ), + const Spacer(), + FittedBox( + fit: BoxFit.scaleDown, + child: SelectableText( + ref.watch(pAmountFormatter(coin)).format(balanceToShow), + style: STextStyles.pageTitleH1(context).copyWith( + fontSize: 24, color: Theme.of(context) .extension()! .textFavoriteCard, - width: 8, - height: 4, ), - ], + ), ), + if (externalCalls) + Text( + "${(priceTuple.item1 * balanceToShow.decimal).toAmount( + fractionDigits: 2, + ).fiatString( + locale: locale, + )} $baseCurrency", + style: STextStyles.subtitle500(context).copyWith( + color: Theme.of(context) + .extension()! + .textFavoriteCard, + ), + ), + ], + ), + ), + Column( + children: [ + SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 24, + height: 24, ), const Spacer(), - FittedBox( - fit: BoxFit.scaleDown, - child: SelectableText( - ref.watch(pAmountFormatter(coin)).format(balanceToShow), - style: STextStyles.pageTitleH1(context).copyWith( - fontSize: 24, - color: Theme.of(context) - .extension()! - .textFavoriteCard, - ), - ), + WalletRefreshButton( + walletId: widget.walletId, + initialSyncStatus: widget.initialSyncStatus, ), - if (externalCalls) - Text( - "${(priceTuple.item1 * balanceToShow.decimal).toAmount( - fractionDigits: 2, - ).fiatString( - locale: locale, - )} $baseCurrency", - style: STextStyles.subtitle500(context).copyWith( - color: Theme.of(context) - .extension()! - .textFavoriteCard, - ), - ), ], - ), - ), - Column( - children: [ - SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), - ), - width: 24, - height: 24, - ), - const Spacer(), - WalletRefreshButton( - walletId: widget.walletId, - initialSyncStatus: widget.initialSyncStatus, - ), - ], - ) - ], + ) + ], + ), ); } } diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index e8ccf98de..2b1ba62c4 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -358,7 +358,6 @@ class _TransactionDetailsViewState final currentHeight = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManager(walletId).currentHeight)); - print("THIS TRANSACTION IS $_transaction"); return ConditionalParent( @@ -474,7 +473,9 @@ class _TransactionDetailsViewState ), SelectableText( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction, currentHeight, @@ -585,7 +586,9 @@ class _TransactionDetailsViewState // child: SelectableText( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction, currentHeight, @@ -781,8 +784,8 @@ class _TransactionDetailsViewState isDesktop ? const _Divider() : const SizedBox( - height: 12, - ), + height: 12, + ), if (coin == Coin.epicCash) RoundedWhiteContainer( padding: isDesktop @@ -790,22 +793,22 @@ class _TransactionDetailsViewState : const EdgeInsets.all(12), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( "On chain note", style: isDesktop ? STextStyles - .desktopTextExtraExtraSmall( - context) + .desktopTextExtraExtraSmall( + context) : STextStyles.itemSubtitle( - context), + context), ), const SizedBox( height: 8, @@ -814,18 +817,16 @@ class _TransactionDetailsViewState _transaction.otherData ?? "", style: isDesktop ? STextStyles - .desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of( - context) - .extension< - StackColors>()! - .textDark, - ) - : STextStyles - .itemSubtitle12( - context), + .desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context), ), ], ), @@ -854,7 +855,9 @@ class _TransactionDetailsViewState MainAxisAlignment.spaceBetween, children: [ Text( - (coin == Coin.epicCash) ? "Local Note" : "Note ", + (coin == Coin.epicCash) + ? "Local Note" + : "Note ", style: isDesktop ? STextStyles .desktopTextExtraExtraSmall( @@ -923,7 +926,9 @@ class _TransactionDetailsViewState notesServiceChangeNotifierProvider( walletId) .select((value) => value.getNoteFor( - txid: (coin == Coin.epicCash)? _transaction.slateId! : _transaction.txid ))), + txid: (coin == Coin.epicCash) + ? _transaction.slateId! + : _transaction.txid))), builder: (builderContext, AsyncSnapshot snapshot) { if (snapshot.connectionState == diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 3e7fb37fa..5e9fc8eba 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -22,7 +22,9 @@ import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart'; import 'package:stackwallet/pages/coin_control/coin_control_view.dart'; import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; +import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; import 'package:stackwallet/pages/receive_view/receive_view.dart'; @@ -72,6 +74,7 @@ import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart'; import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart'; @@ -924,6 +927,22 @@ class _WalletViewState extends ConsumerState { ); }, ), + WalletNavigationBarItemData( + icon: SvgPicture.asset( + Assets.svg.monkey, + height: 20, + width: 20, + color: Theme.of(context) + .extension()! + .bottomNavIconIcon, + ), + label: "MonKey", + onTap: () { + Navigator.of(context).pushNamed( + MonkeyView.routeName, + arguments: widget.walletId, + ); + }), if (ref.watch( walletsChangeNotifierProvider.select( (value) => value @@ -1007,6 +1026,22 @@ class _WalletViewState extends ConsumerState { } }, ), + if (ref.watch( + walletsChangeNotifierProvider.select( + (value) => + value.getManager(widget.walletId).hasOrdinalsSupport, + ), + )) + WalletNavigationBarItemData( + label: "Ordinals", + icon: const OrdinalsNavIcon(), + onTap: () { + Navigator.of(context).pushNamed( + OrdinalsView.routeName, + arguments: widget.walletId, + ); + }, + ), ], ), ], diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index b3f5e97e5..107b323e0 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -149,6 +149,7 @@ class _FavoriteCardState extends ConsumerState { walletId: widget.walletId, width: widget.width, height: widget.height, + isFavorite: true, ), child: Padding( padding: const EdgeInsets.all(12.0), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index aa5922959..2a6cadf11 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -16,12 +16,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; import 'package:stackwallet/pages_desktop_specific/coin_control/desktop_coin_control_view.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_menu.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinals_view.dart'; import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart'; import 'package:stackwallet/providers/global/paynym_api_provider.dart'; import 'package:stackwallet/providers/providers.dart'; @@ -80,6 +82,8 @@ class _DesktopWalletFeaturesState extends ConsumerState { onCoinControlPressed: _onCoinControlPressed, onAnonymizeAllPressed: _onAnonymizeAllPressed, onWhirlpoolPressed: _onWhirlpoolPressed, + onOrdinalsPressed: _onOrdinalsPressed, + onMonkeyPressed: _onMonkeyPressed, ), ); } @@ -313,6 +317,24 @@ class _DesktopWalletFeaturesState extends ConsumerState { } } + Future _onMonkeyPressed() async { + Navigator.of(context, rootNavigator: true).pop(); + + await (Navigator.of(context).pushNamed( + MonkeyView.routeName, + arguments: widget.walletId, + )); + } + + void _onOrdinalsPressed() { + Navigator.of(context, rootNavigator: true).pop(); + + Navigator.of(context).pushNamed( + DesktopOrdinalsView.routeName, + arguments: widget.walletId, + ); + } + @override Widget build(BuildContext context) { final manager = ref.watch( @@ -330,8 +352,9 @@ class _DesktopWalletFeaturesState extends ConsumerState { )) || manager.coin == Coin.firo || manager.coin == Coin.firoTestNet || + manager.hasWhirlpoolSupport || + manager.coin == Coin.banano || manager.hasWhirlpoolSupport; - return Row( children: [ if (Constants.enableExchange) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 4c8f47c25..09c400324 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -29,6 +29,8 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { required this.onCoinControlPressed, required this.onAnonymizeAllPressed, required this.onWhirlpoolPressed, + required this.onOrdinalsPressed, + required this.onMonkeyPressed, }) : super(key: key); final String walletId; @@ -36,6 +38,8 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { final VoidCallback? onCoinControlPressed; final VoidCallback? onAnonymizeAllPressed; final VoidCallback? onWhirlpoolPressed; + final VoidCallback? onOrdinalsPressed; + final VoidCallback? onMonkeyPressed; @override ConsumerState createState() => _MoreFeaturesDialogState(); @@ -103,6 +107,20 @@ class _MoreFeaturesDialogState extends ConsumerState { iconAsset: Assets.svg.robotHead, onPressed: () => widget.onPaynymPressed?.call(), ), + if (manager.hasOrdinalsSupport) + _MoreFeaturesItem( + label: "Ordinals", + detail: "View and control your ordinals in Stack", + iconAsset: Assets.svg.ordinal, + onPressed: () => widget.onOrdinalsPressed?.call(), + ), + if (manager.coin == Coin.banano) + _MoreFeaturesItem( + label: "MonKey", + detail: "Generate Banano MonKey", + iconAsset: Assets.svg.monkey, + onPressed: () => widget.onMonkeyPressed?.call(), + ), const SizedBox( height: 28, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart index 68fe09aa8..61c61a807 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart @@ -13,7 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart'; import 'package:stackwallet/providers/providers.dart'; @@ -21,11 +22,13 @@ import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; enum _WalletOptions { addressList, deleteWallet, + changeRepresentative, showXpub; String get prettyName { @@ -34,6 +37,8 @@ enum _WalletOptions { return "Address list"; case _WalletOptions.deleteWallet: return "Delete wallet"; + case _WalletOptions.changeRepresentative: + return "Change representative"; case _WalletOptions.showXpub: return "Show xPub"; } @@ -70,6 +75,9 @@ class WalletOptionsButton extends StatelessWidget { onAddressListPressed: () async { Navigator.of(context).pop(_WalletOptions.addressList); }, + onChangeRepPressed: () async { + Navigator.of(context).pop(_WalletOptions.changeRepresentative); + }, onShowXpubPressed: () async { Navigator.of(context).pop(_WalletOptions.showXpub); }, @@ -134,6 +142,32 @@ class WalletOptionsButton extends StatelessWidget { ), ); + if (result == true) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + break; + case _WalletOptions.changeRepresentative: + final result = await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => Navigator( + initialRoute: ChangeRepresentativeView.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + RouteGenerator.generateRoute( + RouteSettings( + name: ChangeRepresentativeView.routeName, + arguments: walletId, + ), + ), + ]; + }, + ), + ); + if (result == true) { if (context.mounted) { Navigator.of(context).pop(); @@ -171,18 +205,24 @@ class WalletOptionsPopupMenu extends ConsumerWidget { required this.onDeletePressed, required this.onAddressListPressed, required this.onShowXpubPressed, + required this.onChangeRepPressed, required this.walletId, }) : super(key: key); final VoidCallback onDeletePressed; final VoidCallback onAddressListPressed; final VoidCallback onShowXpubPressed; + final VoidCallback onChangeRepPressed; final String walletId; @override Widget build(BuildContext context, WidgetRef ref) { - final bool xpubEnabled = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(walletId).hasXPub)); + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(walletId))); + final bool xpubEnabled = manager.hasXPub; + + final bool canChangeRep = + manager.coin == Coin.nano || manager.coin == Coin.banano; return Stack( children: [ @@ -237,6 +277,43 @@ class WalletOptionsPopupMenu extends ConsumerWidget { ), ), ), + if (canChangeRep) + const SizedBox( + height: 8, + ), + if (canChangeRep) + TransparentButton( + onPressed: onChangeRepPressed, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgPicture.asset( + Assets.svg.eye, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconLeft, + ), + const SizedBox(width: 14), + Expanded( + child: Text( + _WalletOptions.changeRepresentative.prettyName, + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ), + ], + ), + ), + ), if (xpubEnabled) const SizedBox( height: 8, diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart new file mode 100644 index 000000000..2f89b7cd6 --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -0,0 +1,314 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/providers/db/main_db_provider.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/amount/amount_formatter.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class DesktopOrdinalDetailsView extends ConsumerStatefulWidget { + const DesktopOrdinalDetailsView({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const routeName = "/desktopOrdinalDetailsView"; + + @override + ConsumerState createState() => + _DesktopOrdinalDetailsViewState(); +} + +class _DesktopOrdinalDetailsViewState + extends ConsumerState { + static const _spacing = 12.0; + + late final UTXO? utxo; + + @override + void initState() { + utxo = widget.ordinal.getUTXO(ref.read(mainDBProvider)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final coin = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId).coin)); + + return DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension()!.popupBG, + leading: Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + width: 32, + ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 18, + ), + Text( + "Ordinal details", + style: STextStyles.desktopH3(context), + ), + ], + ), + ), + useSpacers: false, + isCompactHeight: true, + ), + body: Padding( + padding: const EdgeInsets.only( + left: 24, + top: 24, + right: 24, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 300, + height: 300, + child: ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: Image.network( + widget.ordinal + .content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: + FilterQuality.none, // Set the filter mode to nearest + ), + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + RoundedWhiteContainer( + padding: const EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "INSC. ${widget.ordinal.inscriptionNumber}", + style: STextStyles.w600_20(context), + ), + ], + ), + ), + const SizedBox( + width: 16, + ), + // PrimaryButton( + // width: 150, + // label: "Send", + // icon: SvgPicture.asset( + // Assets.svg.send, + // width: 18, + // height: 18, + // color: Theme.of(context) + // .extension()! + // .buttonTextPrimary, + // ), + // buttonHeight: ButtonHeight.l, + // iconSpacing: 8, + // onPressed: () async { + // final response = await showDialog( + // context: context, + // builder: (_) => + // const SendOrdinalUnfreezeDialog(), + // ); + // if (response == "unfreeze") { + // // TODO: unfreeze and go to send ord screen + // } + // }, + // ), + // const SizedBox( + // width: 16, + // ), + // SecondaryButton( + // width: 150, + // label: "Download", + // icon: SvgPicture.asset( + // Assets.svg.arrowDown, + // width: 13, + // height: 18, + // color: Theme.of(context) + // .extension()! + // .buttonTextSecondary, + // ), + // buttonHeight: ButtonHeight.l, + // iconSpacing: 8, + // onPressed: () { + // // TODO: save and download image to device + // }, + // ), + ], + ), + ), + const SizedBox( + height: 16, + ), + _DetailsItemWCopy( + title: "Inscription number", + data: widget.ordinal.inscriptionNumber.toString(), + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Inscription ID", + data: widget.ordinal.inscriptionId, + ), + const SizedBox( + height: _spacing, + ), + // todo: add utxo status + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Amount", + data: utxo == null + ? "ERROR" + : ref.watch(pAmountFormatter(coin)).format( + Amount( + rawValue: BigInt.from(utxo!.value), + fractionDigits: coin.decimals, + ), + ), + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Owner address", + data: utxo?.address ?? "ERROR", + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Transaction ID", + data: widget.ordinal.utxoTXID, + ), + const SizedBox( + height: _spacing, + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +class _DetailsItemWCopy extends StatelessWidget { + const _DetailsItemWCopy({ + Key? key, + required this.title, + required this.data, + }) : super(key: key); + + final String title; + final String data; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.itemSubtitle(context), + ), + GestureDetector( + onTap: () async { + await Clipboard.setData(ClipboardData(text: data)); + if (context.mounted) { + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + context: context, + ), + ); + } + }, + child: SvgPicture.asset( + Assets.svg.copy, + color: + Theme.of(context).extension()!.infoItemIcons, + width: 12, + ), + ), + ], + ), + const SizedBox( + height: 4, + ), + SelectableText( + data, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), + ); + } +} diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart new file mode 100644 index 000000000..ec2748560 --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -0,0 +1,246 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class DesktopOrdinalsView extends ConsumerStatefulWidget { + const DesktopOrdinalsView({ + super.key, + required this.walletId, + }); + + static const String routeName = "/desktopOrdinalsView"; + + final String walletId; + + @override + ConsumerState createState() => _DesktopOrdinals(); +} + +class _DesktopOrdinals extends ConsumerState { + late final TextEditingController searchController; + late final FocusNode searchFocusNode; + + String _searchTerm = ""; + + @override + void initState() { + searchController = TextEditingController(); + searchFocusNode = FocusNode(); + + super.initState(); + } + + @override + void dispose() { + searchController.dispose(); + searchFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + debugPrint("BUILD: $runtimeType"); + + return DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension()!.popupBG, + isCompactHeight: true, + useSpacers: false, + leading: Expanded( + child: Row( + children: [ + const SizedBox( + width: 32, + ), + AppBarIconButton( + size: 32, + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 15, + ), + SvgPicture.asset( + Assets.svg.ordinal, + width: 32, + height: 32, + ), + const SizedBox( + width: 12, + ), + Text( + "Ordinals", + style: STextStyles.desktopH3(context), + ) + ], + ), + ), + ), + body: Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Spacer(), + // Expanded( + // child: ClipRRect( + // borderRadius: BorderRadius.circular( + // Constants.size.circularBorderRadius, + // ), + // child: TextField( + // autocorrect: Util.isDesktop ? false : true, + // enableSuggestions: Util.isDesktop ? false : true, + // controller: searchController, + // focusNode: searchFocusNode, + // onChanged: (value) { + // setState(() { + // _searchTerm = value; + // }); + // }, + // style: STextStyles.field(context), + // decoration: standardInputDecoration( + // "Search", + // searchFocusNode, + // context, + // ).copyWith( + // prefixIcon: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 10, + // vertical: 20, + // ), + // child: SvgPicture.asset( + // Assets.svg.search, + // width: 16, + // height: 16, + // ), + // ), + // suffixIcon: searchController.text.isNotEmpty + // ? Padding( + // padding: const EdgeInsets.only(right: 0), + // child: UnconstrainedBox( + // child: Row( + // children: [ + // TextFieldIconButton( + // child: const XIcon(), + // onTap: () async { + // setState(() { + // searchController.text = ""; + // _searchTerm = ""; + // }); + // }, + // ), + // ], + // ), + // ), + // ) + // : null, + // ), + // ), + // ), + // ), + // const SizedBox( + // width: 16, + // ), + // SecondaryButton( + // width: 184, + // label: "Filter", + // buttonHeight: ButtonHeight.l, + // icon: SvgPicture.asset( + // Assets.svg.filter, + // color: Theme.of(context) + // .extension()! + // .buttonTextSecondary, + // ), + // onPressed: () { + // Navigator.of(context).pushNamed( + // OrdinalsFilterView.routeName, + // ); + // }, + // ), + const SizedBox( + width: 16, + ), + SecondaryButton( + width: 184, + label: "Update", + buttonHeight: ButtonHeight.l, + icon: SvgPicture.asset( + Assets.svg.arrowRotate, + color: Theme.of(context) + .extension()! + .buttonTextSecondary, + ), + onPressed: () async { + // show loading for a minimum of 2 seconds on refreshing + await showLoading( + isDesktop: true, + whileFuture: Future.wait([ + Future.delayed(const Duration(seconds: 2)), + (ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as OrdinalsInterface) + .refreshInscriptions() + ]), + context: context, + message: "Refreshing..."); + }, + ), + ], + ), + const SizedBox( + height: 16, + ), + Expanded( + child: SingleChildScrollView( + child: OrdinalsList( + walletId: widget.walletId, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages_desktop_specific/password/create_password_view.dart b/lib/pages_desktop_specific/password/create_password_view.dart index 68205d077..2986cd0da 100644 --- a/lib/pages_desktop_specific/password/create_password_view.dart +++ b/lib/pages_desktop_specific/password/create_password_view.dart @@ -125,7 +125,7 @@ class _CreatePasswordViewState extends ConsumerState { } } - if (!widget.restoreFromSWB) { + if (!widget.restoreFromSWB && mounted) { unawaited(showFloatingFlushBar( type: FlushBarType.success, message: "Your password is set up", diff --git a/lib/pages_desktop_specific/password/desktop_login_view.dart b/lib/pages_desktop_specific/password/desktop_login_view.dart index 10e1477b4..471da2763 100644 --- a/lib/pages_desktop_specific/password/desktop_login_view.dart +++ b/lib/pages_desktop_specific/password/desktop_login_view.dart @@ -84,10 +84,10 @@ class _DesktopLoginViewState extends ConsumerState { unawaited( showDialog( context: context, - builder: (context) => Column( + builder: (context) => const Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - children: const [ + children: [ LoadingIndicator( width: 200, height: 200, diff --git a/lib/providers/exchange/exchange_form_state_provider.dart b/lib/providers/exchange/exchange_form_state_provider.dart index 07447f169..b4a923b78 100644 --- a/lib/providers/exchange/exchange_form_state_provider.dart +++ b/lib/providers/exchange/exchange_form_state_provider.dart @@ -13,8 +13,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/exchange/active_pair.dart'; import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; import 'package:stackwallet/models/exchange/response_objects/range.dart'; +import 'package:stackwallet/providers/global/locale_provider.dart'; import 'package:stackwallet/services/exchange/exchange.dart'; import 'package:stackwallet/services/exchange/exchange_response.dart'; +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart'; import 'package:tuple/tuple.dart'; @@ -44,7 +48,22 @@ final efSendAmountStringProvider = StateProvider((ref) { if (refreshing && reversed) { return "-"; } else { - return ref.watch(efSendAmountProvider)?.toString() ?? ""; + final decimal = ref.watch(efSendAmountProvider); + String string = ""; + if (decimal != null) { + final amount = Amount.fromDecimal(decimal, fractionDigits: decimal.scale); + final locale = ref.watch(localeServiceChangeNotifierProvider).locale; + string = AmountUnit.normal.displayAmount( + amount: amount, + locale: locale, + coin: Coin + .nano, // use nano just to ensure decimal.scale < Coin.value.decimals + withUnitName: false, + maxDecimalPlaces: decimal.scale, + ); + } + + return string; } }); final efReceiveAmountStringProvider = StateProvider((ref) { @@ -54,7 +73,22 @@ final efReceiveAmountStringProvider = StateProvider((ref) { if (refreshing && reversed == false) { return "-"; } else { - return ref.watch(efReceiveAmountProvider)?.toString() ?? ""; + final decimal = ref.watch(efReceiveAmountProvider); + String string = ""; + if (decimal != null) { + final amount = Amount.fromDecimal(decimal, fractionDigits: decimal.scale); + final locale = ref.watch(localeServiceChangeNotifierProvider).locale; + string = AmountUnit.normal.displayAmount( + amount: amount, + locale: locale, + coin: Coin + .nano, // use nano just to ensure decimal.scale < Coin.value.decimals + withUnitName: false, + maxDecimalPlaces: decimal.scale, + ); + } + + return string; } }); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7ccc378c9..8916bb008 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -17,7 +17,9 @@ import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_ import 'package:stackwallet/models/buy/response_objects/quote.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/send_view_auto_fill_data.dart'; import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_custom_token_view.dart'; @@ -56,7 +58,11 @@ import 'package:stackwallet/pages/generic/single_field_edit_view.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/intro_view.dart'; import 'package:stackwallet/pages/manage_favorites_view/manage_favorites_view.dart'; +import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_view.dart'; import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; @@ -103,14 +109,15 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/support_vi import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/pages/stack_privacy_calls.dart'; import 'package:stackwallet/pages/token_view/my_tokens_view.dart'; import 'package:stackwallet/pages/token_view/token_contract_details_view.dart'; @@ -140,6 +147,8 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart'; import 'package:stackwallet/pages_desktop_specific/notifications/desktop_notifications_view.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinals_view.dart'; import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart'; import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart'; import 'package:stackwallet/pages_desktop_specific/password/forgot_password_desktop_view.dart'; @@ -164,8 +173,6 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/widgets/choose_coin_view.dart'; import 'package:tuple/tuple.dart'; -import 'models/isar/models/contact_entry.dart'; - /* * This file contains all the routes for the app. * To add a new route, add it to the switch statement in the generateRoute method. @@ -375,6 +382,35 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case MonkeyView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => MonkeyView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + // case MonkeyLoadedView.routeName: + // if (args is Tuple2>) { + // return getRoute( + // shouldUseMaterialRoute: useMaterialPageRoute, + // builder: (_) => MonkeyLoadedView( + // walletId: args.item1, + // managerProvider: args.item2, + // ), + // settings: RouteSettings( + // name: settings.name, + // ), + // ); + // } + // return _routeError("${settings.name} invalid args: ${args.toString()}"); + case CoinControlView.routeName: if (args is Tuple2) { return getRoute( @@ -404,6 +440,70 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case OrdinalsView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => OrdinalsView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case DesktopOrdinalsView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => DesktopOrdinalsView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case OrdinalDetailsView.routeName: + if (args is ({Ordinal ordinal, String walletId})) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => OrdinalDetailsView( + walletId: args.walletId, + ordinal: args.ordinal, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case DesktopOrdinalDetailsView.routeName: + if (args is ({Ordinal ordinal, String walletId})) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => DesktopOrdinalDetailsView( + walletId: args.walletId, + ordinal: args.ordinal, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case OrdinalsFilterView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const OrdinalsFilterView(), + settings: RouteSettings(name: settings.name)); + case UtxoDetailsView.routeName: if (args is Tuple2) { return getRoute( @@ -564,6 +664,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case ChangeRepresentativeView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => ChangeRepresentativeView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case AppearanceSettingsView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/services/coins/banano/banano_wallet.dart b/lib/services/coins/banano/banano_wallet.dart index 8511f5599..e2032aa09 100644 --- a/lib/services/coins/banano/banano_wallet.dart +++ b/lib/services/coins/banano/banano_wallet.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:isar/isar.dart'; import 'package:nanodart/nanodart.dart'; +import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; @@ -14,9 +15,9 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/nano_api.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -32,8 +33,7 @@ const int MINIMUM_CONFIRMATIONS = 1; const String DEFAULT_REPRESENTATIVE = "ban_1ka1ium4pfue3uxtntqsrib8mumxgazsjf58gidh1xeo5te3whsq8z476goo"; -class BananoWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { +class BananoWallet extends CoinServiceAPI with WalletCache, WalletDB { BananoWallet({ required String walletId, required String walletName, @@ -925,4 +925,66 @@ class BananoWallet extends CoinServiceAPI ); await updateCachedChainHeight(height ?? 0); } + + Future updateMonkeyImageBytes(List bytes) async { + await DB.instance.put( + boxName: _walletId, + key: "monkeyImageBytesKey", + value: bytes, + ); + } + + List? getMonkeyImageBytes() { + return DB.instance.get( + boxName: _walletId, + key: "monkeyImageBytesKey", + ) as List?; + } + + Future getCurrentRepresentative() async { + final serverURI = Uri.parse(getCurrentNode().host); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + return response.accountInfo?.representative ?? DEFAULT_REPRESENTATIVE; + } + + Future changeRepresentative(String newRepresentative) async { + try { + final serverURI = Uri.parse(getCurrentNode().host); + final balance = this.balance.spendable.raw.toString(); + final String privateKey = await getPrivateKeyFromMnemonic(); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + if (response.accountInfo == null) { + throw response.exception ?? Exception("Failed to get account info"); + } + + final work = await requestWork(response.accountInfo!.frontier); + + return await NanoAPI.changeRepresentative( + server: serverURI, + accountType: NanoAccountType.BANANO, + account: address, + newRepresentative: newRepresentative, + previousBlock: response.accountInfo!.frontier, + balance: balance, + privateKey: privateKey, + work: work!, + ); + } catch (_) { + rethrow; + } + } } diff --git a/lib/services/coins/ethereum/ethereum_wallet.dart b/lib/services/coins/ethereum/ethereum_wallet.dart index 9990406d9..62f3020e0 100644 --- a/lib/services/coins/ethereum/ethereum_wallet.dart +++ b/lib/services/coins/ethereum/ethereum_wallet.dart @@ -1023,6 +1023,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB { final response = await EthereumAPI.getEthTransactions( address: thisAddress, firstBlock: isRescan ? 0 : firstBlock, + includeTokens: true, ); if (response.value == null) { @@ -1057,8 +1058,10 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB { txFailed = true; } isIncoming = false; - } else { + } else if (checksumEthereumAddress(element.to) == thisAddress) { isIncoming = true; + } else { + continue; } //Calculate fees (GasLimit * gasPrice) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index e62b7d069..573992318 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -26,7 +26,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; -import 'package:stackwallet/models/lelantus_coin.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/signing_data.dart'; @@ -36,7 +35,6 @@ import 'package:stackwallet/services/event_bus/events/global/refresh_percent_cha import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/firo_hive.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; @@ -60,8 +58,8 @@ import 'package:uuid/uuid.dart'; const DUST_LIMIT = 1000; const MINIMUM_CONFIRMATIONS = 1; -const MINT_LIMIT = 100100000000; -const int LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION = 5001 * 100000000; +const MINT_LIMIT = 5001 * 100000000; +const MINT_LIMIT_TESTNET = 1001 * 100000000; const JMINT_INDEX = 5; const MINT_INDEX = 2; @@ -161,6 +159,7 @@ Future executeNative(Map arguments) async { final mnemonicPassphrase = arguments['mnemonicPassphrase'] as String; final coin = arguments['coin'] as Coin; final network = arguments['network'] as NetworkType; + final walletId = arguments['walletId'] as String; final restoreData = await isolateRestore( mnemonic, @@ -170,6 +169,7 @@ Future executeNative(Map arguments) async { setDataMap, usedSerialNumbers, network, + walletId, ); sendPort.send(restoreData); return; @@ -206,13 +206,14 @@ Future> isolateRestore( Map _setDataMap, List _usedSerialNumbers, NetworkType network, + String walletId, ) async { List jindexes = []; - List> lelantusCoins = []; + List lelantusCoins = []; final List spendTxIds = []; - var lastFoundIndex = 0; - var currentIndex = 0; + int lastFoundIndex = 0; + int currentIndex = 0; try { Set usedSerialNumbersSet = _usedSerialNumbers.toSet(); @@ -239,7 +240,7 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); - for (var setId = 1; setId <= _latestSetId; setId++) { + for (int setId = 1; setId <= _latestSetId; setId++) { final setData = _setDataMap[setId] as Map; final foundCoin = (setData["coins"] as List).firstWhere( (e) => e[1] == mintTag, @@ -264,32 +265,25 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); final bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + mintIndex: currentIndex, + value: amount.toString(), + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + isJMint: false, + otherData: + publicCoin, // not really needed but saved just in case + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); Logging.instance.log( "amount $amount used $isUsed", level: LogLevel.Info, @@ -322,32 +316,24 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + mintIndex: currentIndex, + value: amount.toString(), + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + isJMint: true, + otherData: + publicCoin, // not really needed but saved just in case + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); jindexes.add(currentIndex); spendTxIds.add(txId); @@ -363,11 +349,6 @@ Future> isolateRestore( level: LogLevel.Warning, ); } - } else { - Logging.instance.log( - "Coin not found in data with the mint tag: $mintTag", - level: LogLevel.Warning, - ); } } @@ -384,8 +365,6 @@ Future> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['mintIndex'] = lastFoundIndex + 1; - result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; return result; @@ -396,73 +375,74 @@ Future> staticProcessRestore( Map result, int currentHeight, ) async { - List? _l = result['_lelantus_coins'] as List?; - final List> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } + List lelantusCoins = + result['_lelantus_coins'] as List; // Edit the receive transactions with the mint fees. - Map editedTransactions = - {}; - for (var item in lelantusCoins) { - item.forEach((key, value) { - String txid = value.txId; - isar_models.Transaction? tx; + List editedTransactions = []; + + for (final coin in lelantusCoins) { + String txid = coin.txid; + isar_models.Transaction? tx; + try { + tx = txns.firstWhere((e) => e.txid == txid); + } catch (_) { + tx = null; + } + + if (tx == null || tx.subType == isar_models.TransactionSubType.join) { + // This is a jmint. + continue; + } + + List inputTxns = []; + for (final input in tx.inputs) { + isar_models.Transaction? inputTx; try { - tx = txns.firstWhere((e) => e.txid == txid); + inputTx = txns.firstWhere((e) => e.txid == input.txid); } catch (_) { - tx = null; + inputTx = null; } + if (inputTx != null) { + inputTxns.add(inputTx); + } + } + if (inputTxns.isEmpty) { + //some error. + Logging.instance.log( + "cryptic \"//some error\" occurred in staticProcessRestore on lelantus coin: $coin", + level: LogLevel.Error, + ); + continue; + } - if (tx == null || tx.subType == isar_models.TransactionSubType.join) { - // This is a jmint. - return; - } - List inputs = []; - for (var element in tx.inputs) { - isar_models.Transaction? input; - try { - input = txns.firstWhere((e) => e.txid == element.txid); - } catch (_) { - input = null; - } - if (input != null) { - inputs.add(input); - } - } - if (inputs.isEmpty) { - //some error. - return; - } - - int mintFee = tx.fee; - int sharedFee = mintFee ~/ inputs.length; - for (var element in inputs) { - editedTransactions[element.txid] = isar_models.Transaction( - walletId: element.walletId, - txid: element.txid, - timestamp: element.timestamp, - type: element.type, - subType: isar_models.TransactionSubType.mint, - amount: element.amount, - amountString: Amount( - rawValue: BigInt.from(element.amount), - fractionDigits: Coin.firo.decimals, - ).toJsonString(), - fee: sharedFee, - height: element.height, - isCancelled: false, - isLelantus: true, - slateId: null, - otherData: txid, - nonce: null, - inputs: element.inputs, - outputs: element.outputs, - numberOfMessages: null, - )..address.value = element.address.value; - } - }); + int mintFee = tx.fee; + int sharedFee = mintFee ~/ inputTxns.length; + for (final inputTx in inputTxns) { + final edited = isar_models.Transaction( + walletId: inputTx.walletId, + txid: inputTx.txid, + timestamp: inputTx.timestamp, + type: inputTx.type, + subType: isar_models.TransactionSubType.mint, + amount: inputTx.amount, + amountString: Amount( + rawValue: BigInt.from(inputTx.amount), + fractionDigits: Coin.firo.decimals, + ).toJsonString(), + fee: sharedFee, + height: inputTx.height, + isCancelled: false, + isLelantus: true, + slateId: null, + otherData: txid, + nonce: null, + inputs: inputTx.inputs, + outputs: inputTx.outputs, + numberOfMessages: null, + )..address.value = inputTx.address.value; + editedTransactions.add(edited); + } } // Logging.instance.log(editedTransactions, addToDebugMessagesDB: false); @@ -472,12 +452,13 @@ Future> staticProcessRestore( } // Logging.instance.log(transactionMap, addToDebugMessagesDB: false); - editedTransactions.forEach((key, value) { - transactionMap.update(key, (_value) => value); - }); + // update with edited transactions + for (final tx in editedTransactions) { + transactionMap[tx.txid] = tx; + } transactionMap.removeWhere((key, value) => - lelantusCoins.any((element) => element.containsKey(key)) || + lelantusCoins.any((element) => element.txid == key) || ((value.height == -1 || value.height == null) && !value.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS))); @@ -694,7 +675,6 @@ Future isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -763,7 +743,7 @@ Future _setTestnetWrapper(bool isTestnet) async { /// Handles a single instance of a firo wallet class FiroWallet extends CoinServiceAPI - with WalletCache, WalletDB, FiroHive + with WalletCache, WalletDB implements XPubAble { // Constructor FiroWallet({ @@ -784,7 +764,6 @@ class FiroWallet extends CoinServiceAPI _cachedElectrumXClient = cachedClient; _secureStore = secureStore; initCache(walletId, coin); - initFiroHive(walletId); initWalletDB(mockableOverride: mockableOverride); Logging.instance.log("$walletName isolates length: ${isolates.length}", @@ -1151,7 +1130,8 @@ class FiroWallet extends CoinServiceAPI } } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1159,7 +1139,8 @@ class FiroWallet extends CoinServiceAPI throw ArgumentError("Invalid fee rate argument provided!"); } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1188,6 +1169,11 @@ class FiroWallet extends CoinServiceAPI required Amount amount, Map? args, }) async { + if (amount.raw > BigInt.from(MINT_LIMIT)) { + throw Exception( + "Lelantus sends of more than 5001 are currently disabled"); + } + try { // check for send all bool isSendAll = false; @@ -1237,18 +1223,6 @@ class FiroWallet extends CoinServiceAPI try { final txid = txData["txid"] as String; - // temporarily update apdate available balance until a full refresh is done - - // TODO: something here causes an exception to be thrown giving user false info that the tx failed - // Decimal sendTotal = - // Format.satoshisToAmount(txData["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txData["fees"].toString()); - - // TODO: is this needed? - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - return txid; } catch (e, s) { //todo: come back to this @@ -1264,53 +1238,6 @@ class FiroWallet extends CoinServiceAPI } } - // /// returns txid on successful send - // /// - // /// can throw - // @override - // Future send({ - // required String toAddress, - // required int amount, - // Map args = const {}, - // }) async { - // try { - // dynamic txHexOrError = - // await _createJoinSplitTransaction(amount, toAddress, false); - // Logging.instance.log("txHexOrError $txHexOrError", level: LogLevel.Error); - // if (txHexOrError is int) { - // // Here, we assume that transaction crafting returned an error - // switch (txHexOrError) { - // case 1: - // throw Exception("Insufficient balance!"); - // default: - // throw Exception("Error Creating Transaction!"); - // } - // } else { - // if (await _submitLelantusToNetwork( - // txHexOrError as Map)) { - // final txid = txHexOrError["txid"] as String; - // - // // temporarily update apdate available balance until a full refresh is done - // Decimal sendTotal = - // Format.satoshisToAmount(txHexOrError["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txHexOrError["fees"].toString()); - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - // - // return txid; - // } else { - // //TODO provide more info - // throw Exception("Transaction failed."); - // } - // } - // } catch (e, s) { - // Logging.instance.log("Exception rethrown in firo send(): $e\n$s", - // level: LogLevel.Error); - // rethrow; - // } - // } - Future> _getMnemonicList() async { final _mnemonicString = await mnemonicString; if (_mnemonicString == null) { @@ -1442,16 +1369,32 @@ class FiroWallet extends CoinServiceAPI feeRatePerKB: selectedTxFeeRate, ); - if (feeForOneOutput < vSizeForOneOutput + 1) { - feeForOneOutput = vSizeForOneOutput + 1; - } - - final int amount = satoshiAmountToSend - feeForOneOutput; + int amount = satoshiAmountToSend - feeForOneOutput; dynamic txn = await buildTransaction( utxoSigningData: utxoSigningData, recipients: recipientsArray, satoshiAmounts: [amount], ); + + int count = 0; + int fee = feeForOneOutput; + int vsize = txn["vSize"] as int; + + while (fee < vsize && count < 10) { + // 10 being some reasonable max + count++; + fee += count; + amount = satoshiAmountToSend - fee; + + txn = await buildTransaction( + utxoSigningData: utxoSigningData, + recipients: recipientsArray, + satoshiAmounts: [amount], + ); + + vsize = txn["vSize"] as int; + } + Map transactionObject = { "hex": txn["hex"], "recipient": recipientsArray[0], @@ -2192,7 +2135,6 @@ class FiroWallet extends CoinServiceAPI value: "", ); - await firoUpdateJIndex([]); // Generate and add addresses to relevant arrays final initialReceivingAddress = await _generateAddressForChain(0, 0); final initialChangeAddress = await _generateAddressForChain(1, 0); @@ -2248,9 +2190,9 @@ class FiroWallet extends CoinServiceAPI _feeObject = Future(() => feeObj); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.60, walletId)); - final lelantusCoins = getLelantusCoinMap(); - Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", - level: LogLevel.Warning, printFullLength: true); + // final lelantusCoins = getLelantusCoinMap(); + // Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", + // level: LogLevel.Warning, printFullLength: true); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId)); await _refreshLelantusData(); @@ -2327,7 +2269,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Error); } - final List lelantusCoins = await _getUnspentCoins(); + final List lelantusCoins = + await _getUnspentCoins(); final root = await Bip32Utils.getBip32Root( _mnemonic!, @@ -2339,7 +2282,7 @@ class FiroWallet extends CoinServiceAPI final derivePath = constructDerivePath( networkWIF: _network.wif, chain: MINT_INDEX, - index: coin.index, + index: coin.mintIndex, ); final keyPair = await Bip32Utils.getBip32NodeFromRoot(root, derivePath); @@ -2349,76 +2292,34 @@ class FiroWallet extends CoinServiceAPI } final String privateKey = Format.uint8listToString(keyPair.privateKey!); return DartLelantusEntry(coin.isUsed ? 1 : 0, 0, coin.anonymitySetId, - coin.value, coin.index, privateKey); + int.parse(coin.value), coin.mintIndex, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); if (lelantusEntries.isNotEmpty) { + // should be redundant as _getUnspentCoins() should + // already remove all where value=0 lelantusEntries.removeWhere((element) => element.amount == 0); } return lelantusEntries; } - List> getLelantusCoinMap() { - final _l = firoGetLelantusCoins(); - final List> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } - return lelantusCoins; - } + Future> _getUnspentCoins() async { + final lelantusCoinsList = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .isUsedEqualTo(false) + .not() + .group((q) => q + .valueEqualTo("0") + .or() + .anonymitySetIdEqualTo(ANONYMITY_SET_EMPTY_ID)) + .findAll(); - Future> _getUnspentCoins() async { - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } - final jindexes = firoGetJIndex(); - - List coins = []; - - List lelantusCoinsList = - lelantusCoins.fold([], (previousValue, element) { - previousValue.add(element.values.first); - return previousValue; - }); - - final currentChainHeight = await chainHeight; - - for (int i = 0; i < lelantusCoinsList.length; i++) { - // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - final txid = lelantusCoinsList[i].txId; - final txn = await cachedElectrumXClient.getTransaction( - txHash: txid, - verbose: true, - coin: coin, - ); - final confirmations = txn["confirmations"]; - bool isUnconfirmed = confirmations is int && confirmations < 1; - - final tx = await db.getTransaction(walletId, txid); - - if (!jindexes!.contains(lelantusCoinsList[i].index) && - tx != null && - tx.isLelantus == true && - !(tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { - isUnconfirmed = true; - } - - if (tx != null && - !tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - continue; - } - if (!lelantusCoinsList[i].isUsed && - lelantusCoinsList[i].anonymitySetId != ANONYMITY_SET_EMPTY_ID && - !isUnconfirmed) { - coins.add(lelantusCoinsList[i]); - } - } - return coins; + return lelantusCoinsList; } // index 0 and 1 for the funds available to spend. @@ -2427,62 +2328,48 @@ class FiroWallet extends CoinServiceAPI Future _refreshBalance() async { try { final utxosUpdateFuture = _refreshUTXOs(); - final List> lelantusCoins = - getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .isUsedEqualTo(false) + .not() + .valueEqualTo(0.toString()) + .findAll(); final currentChainHeight = await chainHeight; - final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; - for (final element in lelantusCoins) { - element.forEach((key, lelantusCoin) { - isar_models.Transaction? txn = db.isar.transactions - .where() - .txidWalletIdEqualTo( - lelantusCoin.txId, - walletId, - ) - .findFirstSync(); + for (final lelantusCoin in lelantusCoins) { + isar_models.Transaction? txn = db.isar.transactions + .where() + .txidWalletIdEqualTo( + lelantusCoin.txid, + walletId, + ) + .findFirstSync(); - if (txn == null) { - // TODO: ?????????????????????????????????????? - } else { - bool isLelantus = txn.isLelantus == true; - if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { - if (!lelantusCoin.isUsed && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - // mint tx, add value to balance - intLelantusBalance += lelantusCoin.value; - } /* else { - // This coin is not confirmed and may be replaced - }*/ - } else if (jindexes.contains(lelantusCoin.index) && - isLelantus && - !lelantusCoin.isUsed && - !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - unconfirmedLelantusBalance += lelantusCoin.value; - } else if (jindexes.contains(lelantusCoin.index) && - !lelantusCoin.isUsed) { - intLelantusBalance += lelantusCoin.value; - } else if (!lelantusCoin.isUsed && - (txn.isLelantus == true - ? true - : txn.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += lelantusCoin.value; - } else if (!isLelantus && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { - unconfirmedLelantusBalance += lelantusCoin.value; - } + if (txn == null) { + Logging.instance.log( + "Transaction not found in DB for lelantus coin: $lelantusCoin", + level: LogLevel.Fatal, + ); + } else { + if (txn.isLelantus != true) { + Logging.instance.log( + "Bad database state found in $walletName $walletId for _refreshBalance lelantus", + level: LogLevel.Fatal, + ); } - }); + + if (txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + // mint tx, add value to balance + intLelantusBalance += int.parse(lelantusCoin.value); + } else { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); + } + } } _balancePrivate = Balance( @@ -2551,17 +2438,19 @@ class FiroWallet extends CoinServiceAPI } } - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); + final data = await _txnData; for (final value in data) { if (value.inputs.isNotEmpty) { for (var element in value.inputs) { - if (lelantusCoins - .any((element) => element.keys.contains(value.txid)) && + if (lelantusCoins.any((e) => e.txid == value.txid) && spendableOutputs.firstWhere( (output) => output?.txid == element.txid, orElse: () => null) != @@ -2626,24 +2515,93 @@ class FiroWallet extends CoinServiceAPI } Future>> createMintsFromAmount(int total) async { - var tmpTotal = total; - var index = 1; - var mints = >[]; - final nextFreeMintIndex = firoGetMintIndex(); + if (total > MINT_LIMIT) { + throw Exception( + "Lelantus mints of more than 5001 are currently disabled"); + } + + int tmpTotal = total; + int counter = 0; + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; + + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, + ); + + final mints = >[]; while (tmpTotal > 0) { - final mintValue = min(tmpTotal, MINT_LIMIT); - final mint = await _getMintHex( - mintValue, - nextFreeMintIndex + index, + final index = nextFreeMintIndex + counter; + + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: index, + ), ); - mints.add({ - "value": mintValue, - "script": mint, - "index": nextFreeMintIndex + index, - "publicCoin": "", - }); - tmpTotal = tmpTotal - MINT_LIMIT; - index++; + + final String mintTag = CreateTag( + Format.uint8listToString(mintKeyPair.privateKey!), + index, + Format.uint8listToString(mintKeyPair.identifier), + isTestnet: coin == Coin.firoTestNet, + ); + final List> anonymitySets; + try { + anonymitySets = await fetchAnonymitySets(); + } catch (e, s) { + Logging.instance.log( + "Firo needs better internet to create mints: $e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } + + bool isUsedMintTag = false; + + // stupid dynamic maps + for (final set in anonymitySets) { + final setCoins = set["coins"] as List; + for (final coin in setCoins) { + if (coin[1] == mintTag) { + isUsedMintTag = true; + break; + } + } + if (isUsedMintTag) { + break; + } + } + + if (isUsedMintTag) { + Logging.instance.log( + "Found used index when minting", + level: LogLevel.Warning, + ); + } + + if (!isUsedMintTag) { + final mintValue = min(tmpTotal, + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT)); + final mint = await _getMintHex( + mintValue, + index, + ); + + mints.add({ + "value": mintValue, + "script": mint, + "index": index, + }); + tmpTotal = tmpTotal - + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); + } + + counter++; } return mints; } @@ -2669,8 +2627,6 @@ class FiroWallet extends CoinServiceAPI int satoshisPerRecipient, List> mintsMap, ) async { - //todo: check if print needed - // debugPrint(utxosToUse.toString()); List addressStringsToGet = []; // Populating the addresses to derive @@ -2794,9 +2750,6 @@ class FiroWallet extends CoinServiceAPI amount += utxosToUse[i].value; } - final index = firoGetMintIndex(); - Logging.instance.log("index of mint $index", level: LogLevel.Info); - for (var mintsElement in mintsMap) { Logging.instance.log("using $mintsElement", level: LogLevel.Info); Uint8List mintu8 = @@ -2829,7 +2782,6 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -2843,146 +2795,75 @@ class FiroWallet extends CoinServiceAPI }; } + // TODO: verify this function does what we think it does Future _refreshLelantusData() async { - final List> lelantusCoins = getLelantusCoinMap(); - final jindexes = firoGetJIndex(); - - // Get all joinsplit transaction ids - - final listLelantusTxData = await db - .getTransactions(walletId) + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) .filter() - .isLelantusEqualTo(true) + .isUsedEqualTo(false) + .not() + .valueEqualTo(0.toString()) .findAll(); - List joinsplits = []; - for (final tx in listLelantusTxData) { - if (tx.subType == isar_models.TransactionSubType.join) { - joinsplits.add(tx.txid); - } - } - for (final coin - in lelantusCoins.fold([], (previousValue, element) { - (previousValue as List).add(element.values.first); - return previousValue; - })) { - if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txId)) { - joinsplits.add(coin.txId); - } - } - } - Map> data = - {}; - for (final entry in listLelantusTxData) { - data[entry.txid] = Tuple2(entry.address.value, entry); - } + final List updatedCoins = []; - // Grab the most recent information on all the joinsplits + final usedSerialNumbersSet = (await getUsedCoinSerials()).toSet(); - final updatedJSplit = await getJMintTransactions( - cachedElectrumXClient, - joinsplits, - coin, + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, ); - final currentChainHeight = await chainHeight; + for (final coin in lelantusCoins) { + final _derivePath = constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: coin.mintIndex, + ); + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + _derivePath, + ); - // update all of joinsplits that are now confirmed. - for (final tx in updatedJSplit.entries) { - isar_models.Transaction? currentTx; + final String serialNumber = GetSerialNumber( + int.parse(coin.value), + Format.uint8listToString(mintKeyPair.privateKey!), + coin.mintIndex, + isTestnet: this.coin == Coin.firoTestNet, + ); + final bool isUsed = usedSerialNumbersSet.contains(serialNumber); + if (isUsed) { + updatedCoins.add(coin.copyWith(isUsed: isUsed)); + } + + final tx = await db.getTransaction(walletId, coin.txid); + if (tx == null) { + print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + } + + if (updatedCoins.isNotEmpty) { try { - currentTx = - listLelantusTxData.firstWhere((e) => e.txid == tx.value.txid); - } catch (_) { - currentTx = null; - } - - if (currentTx == null) { - // this send was accidentally not included in the list - tx.value.isLelantus = true; - data[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); - - continue; - } - if (currentTx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) != - tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - tx.value.isLelantus = true; - data[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } } - - // Logging.instance.log(txData.txChunks); - final listTxData = await _txnData; - for (final value in listTxData) { - // ignore change addresses - // bool hasAtLeastOneReceive = false; - // int howManyReceiveInputs = 0; - // for (var element in value.inputs) { - // if (listLelantusTxData.containsKey(element.txid) && - // listLelantusTxData[element.txid]!.txType == "Received" - // // && - // // listLelantusTxData[element.txid].subType != "mint" - // ) { - // // hasAtLeastOneReceive = true; - // // howManyReceiveInputs++; - // } - // } - - if (value.type == isar_models.TransactionType.incoming && - value.subType != isar_models.TransactionSubType.mint) { - // Every receive other than a mint should be shown. Mints will be collected and shown from the send side - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } else if (value.type == isar_models.TransactionType.outgoing) { - // all sends should be shown, mints will be displayed correctly in the ui - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } - } - - // TODO: optimize this whole lelantus process - - final List> txnsData = - []; - - for (final value in data.values) { - // allow possible null address on mints as we don't display address - // this should normally never be null anyways but old (dbVersion up to 4) - // migrated transactions may not have had an address (full rescan should - // fix this) - isar_models.Address? transactionAddress; - try { - transactionAddress = - value.item2.subType == isar_models.TransactionSubType.mint - ? value.item1 - : value.item1!; - } catch (_) { - Logging.instance - .log("_refreshLelantusData value: $value", level: LogLevel.Fatal); - } - final outs = - value.item2.outputs.where((_) => true).toList(growable: false); - final ins = value.item2.inputs.where((_) => true).toList(growable: false); - - txnsData.add(Tuple2( - value.item2.copyWith(inputs: ins, outputs: outs).item1, - transactionAddress)); - } - - await db.addNewTransactionData(txnsData, walletId); - - // // update the _lelantusTransactionData - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(listLelantusTxData); - // // Logging.instance.log(newTxData.txChunks); - // _lelantusTransactionData = Future(() => newTxData); - // await DB.instance.put( - // boxName: walletId, key: 'latest_lelantus_tx_model', value: newTxData); - // return newTxData; } Future _getMintHex(int amount, int index) async { @@ -3030,51 +2911,63 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log( "_submitLelantusToNetwork txid: ${transactionInfo['txid']}", level: LogLevel.Info); + if (txid == transactionInfo['txid']) { - final index = firoGetMintIndex(); - final List> lelantusCoins = - getLelantusCoinMap(); - List> coins; - if (lelantusCoins.isEmpty) { - coins = []; - } else { - coins = [...lelantusCoins]; - } + final lastUsedIndex = + await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { // This is a joinsplit + final spentCoinIndexes = + transactionInfo['spendCoinIndexes'] as List; + final List updatedCoins = []; + // Update all of the coins that have been spent. - for (final lCoinMap in coins) { - final lCoin = lCoinMap.values.first; - if ((transactionInfo['spendCoinIndexes'] as List) - .contains(lCoin.index)) { - lCoinMap[lCoinMap.keys.first] = LelantusCoin( - lCoin.index, - lCoin.value, - lCoin.publicCoin, - lCoin.txId, - lCoin.anonymitySetId, - true); + + for (final index in spentCoinIndexes) { + final possibleCoin = await db.isar.lelantusCoins + .where() + .mintIndexWalletIdEqualTo(index, walletId) + .findFirst(); + + if (possibleCoin != null) { + updatedCoins.add(possibleCoin.copyWith(isUsed: true)); } } // if a jmint was made add it to the unspent coin index - LelantusCoin jmint = LelantusCoin( - index, - transactionInfo['jmintValue'] as int? ?? 0, - transactionInfo['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false); - if (jmint.value > 0) { - coins.add({jmint.txId: jmint}); - final jindexes = firoGetJIndex()!; - jindexes.add(index); - await firoUpdateJIndex(jindexes); - await firoUpdateMintIndex(index + 1); + final jmint = isar_models.LelantusCoin( + walletId: walletId, + mintIndex: nextFreeMintIndex, + value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + isJMint: true, + otherData: null, + ); + + try { + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + + await db.isar.lelantusCoins.put(jmint); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } - await firoUpdateLelantusCoins(coins); final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3087,14 +2980,8 @@ class FiroWallet extends CoinServiceAPI txid: transactionInfo['txid'] as String, timestamp: transactionInfo['timestamp'] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), - type: transactionInfo['txType'] == "Received" - ? isar_models.TransactionType.incoming - : isar_models.TransactionType.outgoing, - subType: transactionInfo["subType"] == "mint" - ? isar_models.TransactionSubType.mint - : transactionInfo["subType"] == "join" - ? isar_models.TransactionSubType.join - : isar_models.TransactionSubType.none, + type: isar_models.TransactionType.outgoing, + subType: isar_models.TransactionSubType.join, amount: amount.raw.toInt(), amountString: amount.toJsonString(), fee: Amount.fromDecimal( @@ -3133,40 +3020,40 @@ class FiroWallet extends CoinServiceAPI txnsData.add(Tuple2(transaction, transactionAddress)); await db.addNewTransactionData(txnsData, walletId); - - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(transactions); - // await DB.instance.put( - // boxName: walletId, - // key: 'latest_lelantus_tx_model', - // value: newTxData); - // final ldata = DB.instance.get( - // boxName: walletId, - // key: 'latest_lelantus_tx_model') as models.TransactionData; - // _lelantusTransactionData = Future(() => ldata); } else { // This is a mint Logging.instance.log("this is a mint", level: LogLevel.Info); - // TODO: transactionInfo['mintsMap'] + final List updatedCoins = []; + for (final mintMap in transactionInfo['mintsMap'] as List>) { - final index = mintMap['index'] as int?; - LelantusCoin mint = LelantusCoin( - index!, - mintMap['value'] as int, - mintMap['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false, + final index = mintMap['index'] as int; + final mint = isar_models.LelantusCoin( + walletId: walletId, + mintIndex: index, + value: (mintMap['value'] as int).toString(), + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + isJMint: false, + otherData: null, ); - if (mint.value > 0) { - coins.add({mint.txId: mint}); - await firoUpdateMintIndex(index + 1); - } + + updatedCoins.add(mint); } // Logging.instance.log(coins); - await firoUpdateLelantusCoins(coins); + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } } return true; } else { @@ -3204,20 +3091,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info); return feeObject; - - // final result = await electrumXClient.getFeeRate(); - // - // final locale = await Devicelocale.currentLocale; - // final String fee = - // Format.satoshiAmountToPrettyString(result["rate"] as int, locale!); - // - // final fees = { - // "fast": fee, - // "average": fee, - // "slow": fee, - // }; - // final FeeObject feeObject = FeeObject.fromJson(fees); - // return feeObject; } catch (e) { Logging.instance .log("Exception rethrown from _getFees(): $e", level: LogLevel.Error); @@ -3666,11 +3539,21 @@ class FiroWallet extends CoinServiceAPI ), ); } + final txid = txObject["txid"] as String; const subType = isar_models.TransactionSubType.join; + final type = nonWalletAddressFoundInOutputs ? isar_models.TransactionType.outgoing - : isar_models.TransactionType.incoming; + : (await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(txid) + .findFirst()) == + null + ? isar_models.TransactionType.incoming + : isar_models.TransactionType.sentToSelf; final amount = nonWalletAddressFoundInOutputs ? totalOutputValue @@ -3697,7 +3580,7 @@ class FiroWallet extends CoinServiceAPI final tx = isar_models.Transaction( walletId: walletId, - txid: txObject["txid"] as String, + txid: txid, timestamp: txObject["blocktime"] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), type: type, @@ -3947,7 +3830,6 @@ class FiroWallet extends CoinServiceAPI coin: coin, ); - // todo check here if we should mark as blocked final utxo = isar_models.UTXO( walletId: walletId, txid: txn["txid"] as String, @@ -3985,7 +3867,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance .log('Outputs fetched: $outputArray', level: LogLevel.Info); - // TODO move this out of here and into IDB await db.isar.writeTxn(() async { await db.isar.utxos.where().walletIdEqualTo(walletId).deleteAll(); await db.isar.utxos.putAll(outputArray); @@ -4072,45 +3953,6 @@ class FiroWallet extends CoinServiceAPI ); } - // /// Takes in a list of isar_models.UTXOs and adds a name (dependent on object index within list) - // /// and checks for the txid associated with the utxo being blocked and marks it accordingly. - // /// Now also checks for output labeling. - // Future _sortOutputs(List utxos) async { - // final blockedHashArray = - // DB.instance.get(boxName: walletId, key: 'blocked_tx_hashes') - // as List?; - // final List lst = []; - // if (blockedHashArray != null) { - // for (var hash in blockedHashArray) { - // lst.add(hash as String); - // } - // } - // final labels = - // DB.instance.get(boxName: walletId, key: 'labels') as Map? ?? - // {}; - // - // _outputsList = []; - // - // for (var i = 0; i < utxos.length; i++) { - // if (labels[utxos[i].txid] != null) { - // utxos[i].txName = labels[utxos[i].txid] as String? ?? ""; - // } else { - // utxos[i].txName = 'Output #$i'; - // } - // - // if (utxos[i].status.confirmed == false) { - // _outputsList.add(utxos[i]); - // } else { - // if (lst.contains(utxos[i].txid)) { - // utxos[i].blocked = true; - // _outputsList.add(utxos[i]); - // } else if (!lst.contains(utxos[i].txid)) { - // _outputsList.add(utxos[i]); - // } - // } - // } - // } - @override Future fullRescan( int maxUnusedAddressGap, @@ -4620,6 +4462,7 @@ class FiroWallet extends CoinServiceAPI "setDataMap": setDataMap, "usedSerialNumbers": usedSerialNumbers, "network": _network, + "walletId": walletId, }); await Future.wait([dataFuture]); @@ -4638,11 +4481,20 @@ class FiroWallet extends CoinServiceAPI await chainHeight, ); - await Future.wait([ - firoUpdateMintIndex(message['mintIndex'] as int), - firoUpdateLelantusCoins(message['_lelantus_coins'] as List), - firoUpdateJIndex(message['jindex'] as List), - ]); + final coins = message['_lelantus_coins'] as List; + + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(coins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + // don't just rethrow since isar likes to strip stack traces for some reason + throw Exception("e=$e & s=$s"); + } final transactionMap = message["newTxMap"] as Map; @@ -4726,7 +4578,8 @@ class FiroWallet extends CoinServiceAPI int spendAmount, String address, bool subtractFeeFromAmount) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; - final index = firoGetMintIndex(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); final locktime = await getBlockHead(electrumXClient); @@ -4740,7 +4593,7 @@ class FiroWallet extends CoinServiceAPI "subtractFeeFromAmount": subtractFeeFromAmount, "mnemonic": _mnemonic, "mnemonicPassphrase": _mnemonicPassphrase, - "index": index, + "index": nextFreeMintIndex, // "price": price, "lelantusEntries": lelantusEntry, "locktime": locktime, @@ -4828,75 +4681,6 @@ class FiroWallet extends CoinServiceAPI this.isActive = isActive; }; - Future getCoinsToJoinSplit( - int required, - ) async { - List coins = await _getLelantusEntry(); - if (required > LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION) { - return false; - } - - int availableBalance = coins.fold( - 0, (previousValue, element) => previousValue + element.amount); - - if (required > availableBalance) { - return false; - } - - // sort by biggest amount. if it is same amount we will prefer the older block - coins.sort((a, b) => - (a.amount != b.amount ? a.amount > b.amount : a.height < b.height) - ? -1 - : 1); - int spendVal = 0; - - List coinsToSpend = []; - - while (spendVal < required) { - if (coins.isEmpty) { - break; - } - - DartLelantusEntry? chosen; - int need = required - spendVal; - - var itr = coins.first; - if (need >= itr.amount) { - chosen = itr; - coins.remove(itr); - } else { - for (int index = coins.length - 1; index != 0; index--) { - var coinIt = coins[index]; - var nextItr = coins[index - 1]; - - if (coinIt.amount >= need && - (index - 1 == 0 || nextItr.amount != coinIt.amount)) { - chosen = coinIt; - coins.remove(chosen); - break; - } - } - } - - // TODO: investigate the bug here where chosen is null, conditions, given one mint - spendVal += chosen!.amount; - coinsToSpend.insert(coinsToSpend.length, chosen); - } - - // sort by group id ay ascending order. it is mandatory for creating proper joinsplit - coinsToSpend.sort((a, b) => a.anonymitySetId < b.anonymitySetId ? 1 : -1); - - int changeToMint = spendVal - required; - List indices = []; - for (var l in coinsToSpend) { - indices.add(l.index); - } - List coinsToBeSpentOut = []; - coinsToBeSpentOut.addAll(coinsToSpend); - - return {"changeToMint": changeToMint, "coinsToSpend": coinsToBeSpentOut}; - } - Future estimateJoinSplitFee( int spendAmount, ) async { @@ -4927,36 +4711,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log('Closing estimateJoinSplit!', level: LogLevel.Info); return (message as LelantusFeeData).fee; } - // int fee; - // int size; - // - // for (fee = 0;;) { - // int currentRequired = spendAmount; - // - // TODO: investigate the bug here - // var map = await getCoinsToJoinSplit(currentRequired); - // if (map is bool && !map) { - // return 0; - // } - // - // List coinsToBeSpent = - // map['coinsToSpend'] as List; - // - // // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data - // // 179 other parts of tx, assuming 1 utxo and 1 jmint - // size = 1054 + 2560 * coinsToBeSpent.length + 180; - // // uint64_t feeNeeded = GetMinimumFee(size, DEFAULT_TX_CONFIRM_TARGET); - // int feeNeeded = - // size; //TODO(Levon) temporary, use real estimation methods here - // - // if (fee >= feeNeeded) { - // break; - // } - // - // fee = feeNeeded; - // } - // - // return fee; @override Future estimateFeeFor(Amount amount, int feeRate) async { @@ -5019,7 +4773,6 @@ class FiroWallet extends CoinServiceAPI } } - // TODO: correct formula for firo? Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index b84c6c6a0..703ec5cf6 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -36,6 +36,7 @@ import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_ import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/electrum_x_parsing.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; @@ -109,7 +110,12 @@ String constructDerivePath({ } class LitecoinWallet extends CoinServiceAPI - with WalletCache, WalletDB, ElectrumXParsing, CoinControlInterface + with + WalletCache, + WalletDB, + ElectrumXParsing, + CoinControlInterface, + OrdinalsInterface implements XPubAble { LitecoinWallet({ required String walletId, @@ -130,6 +136,7 @@ class LitecoinWallet extends CoinServiceAPI _secureStore = secureStore; initCache(walletId, coin); initWalletDB(mockableOverride: mockableOverride); + initOrdinalsInterface(walletId: walletId, coin: coin, db: db); initCoinControlInterface( walletId: walletId, walletName: walletName, @@ -1864,14 +1871,6 @@ class LitecoinWallet extends CoinServiceAPI String? blockReason; String? label; - final utxoAmount = jsonUTXO["value"] as int; - - if (utxoAmount <= 10000) { - shouldBlock = true; - blockReason = "May contain ordinal"; - label = "Possible ordinal"; - } - final vout = jsonUTXO["tx_pos"] as int; final outputs = txn["vout"] as List; @@ -1886,6 +1885,25 @@ class LitecoinWallet extends CoinServiceAPI } } + final utxoAmount = jsonUTXO["value"] as int; + + // TODO check the specific output, not just the address in general + // TODO optimize by freezing output in OrdinalsInterface, so one ordinal API calls is made (or at least many less) + if (utxoOwnerAddress != null) { + if (await inscriptionInAddress(utxoOwnerAddress!)) { + shouldBlock = true; + blockReason = "Ordinal"; + label = "Ordinal detected at address"; + } + } else { + // TODO implement inscriptionInOutput + if (utxoAmount <= 10000) { + shouldBlock = true; + blockReason = "May contain ordinal"; + label = "Possible ordinal"; + } + } + final utxo = isar_models.UTXO( walletId: walletId, txid: txn["txid"] as String, @@ -1910,7 +1928,12 @@ class LitecoinWallet extends CoinServiceAPI level: LogLevel.Info, ); - await db.updateUTXOs(walletId, outputArray); + bool inscriptionsRefreshNeeded = + await db.updateUTXOs(walletId, outputArray); + + if (inscriptionsRefreshNeeded) { + await refreshInscriptions(); + } // finally update balance await _updateBalance(); diff --git a/lib/services/coins/manager.dart b/lib/services/coins/manager.dart index 4e3dd460e..1f5ae55f3 100644 --- a/lib/services/coins/manager.dart +++ b/lib/services/coins/manager.dart @@ -21,6 +21,7 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/mixins/coin_control_interface.dart'; +import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -244,6 +245,8 @@ class Manager with ChangeNotifier { bool get hasCoinControlSupport => _currentWallet is CoinControlInterface; + bool get hasOrdinalsSupport => _currentWallet is OrdinalsInterface; + bool get hasTokenSupport => _currentWallet.coin == Coin.ethereum; bool get hasWhirlpoolSupport => false; diff --git a/lib/services/coins/nano/nano_wallet.dart b/lib/services/coins/nano/nano_wallet.dart index 1e783bf5f..391303675 100644 --- a/lib/services/coins/nano/nano_wallet.dart +++ b/lib/services/coins/nano/nano_wallet.dart @@ -24,9 +24,9 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/nano_api.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -42,8 +42,7 @@ const int MINIMUM_CONFIRMATIONS = 1; const String DEFAULT_REPRESENTATIVE = "nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579"; -class NanoWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { +class NanoWallet extends CoinServiceAPI with WalletCache, WalletDB { NanoWallet({ required String walletId, required String walletName, @@ -937,4 +936,51 @@ class NanoWallet extends CoinServiceAPI ); await updateCachedChainHeight(height ?? 0); } + + Future getCurrentRepresentative() async { + final serverURI = Uri.parse(getCurrentNode().host); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + return response.accountInfo?.representative ?? DEFAULT_REPRESENTATIVE; + } + + Future changeRepresentative(String newRepresentative) async { + try { + final serverURI = Uri.parse(getCurrentNode().host); + final balance = this.balance.spendable.raw.toString(); + final String privateKey = await getPrivateKeyFromMnemonic(); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + if (response.accountInfo == null) { + throw response.exception ?? Exception("Failed to get account info"); + } + + final work = await requestWork(response.accountInfo!.frontier); + + return await NanoAPI.changeRepresentative( + server: serverURI, + accountType: NanoAccountType.NANO, + account: address, + newRepresentative: newRepresentative, + previousBlock: response.accountInfo!.frontier, + balance: balance, + privateKey: privateKey, + work: work!, + ); + } catch (_) { + rethrow; + } + } } diff --git a/lib/services/ethereum/ethereum_api.dart b/lib/services/ethereum/ethereum_api.dart index d30b9ea27..5e95e3aa1 100644 --- a/lib/services/ethereum/ethereum_api.dart +++ b/lib/services/ethereum/ethereum_api.dart @@ -10,7 +10,6 @@ import 'dart:convert'; -import 'package:decimal/decimal.dart'; import 'package:http/http.dart'; import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart'; import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart'; @@ -50,6 +49,7 @@ abstract class EthereumAPI { static Future>> getEthTransactions({ required String address, int firstBlock = 0, + bool includeTokens = false, }) async { try { final response = await get( @@ -67,7 +67,7 @@ abstract class EthereumAPI { for (final map in list!) { final txn = EthTxDTO.fromMap(Map.from(map as Map)); - if (txn.hasToken == 0) { + if (txn.hasToken == 0 || includeTokens) { txns.add(txn); } } @@ -76,9 +76,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getEthTransactions($address) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -196,9 +198,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getEthTransactionNonces($txns) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -252,13 +256,13 @@ abstract class EthereumAPI { ); } else { throw EthApiException( - "getEthTransaction($txids) response is empty but status code is " + "getEthTokenTransactionsByTxids($txids) response is empty but status code is " "${response.statusCode}", ); } } else { throw EthApiException( - "getEthTransaction($txids) failed with status code: " + "getEthTokenTransactionsByTxids($txids) failed with status code: " "${response.statusCode}", ); } @@ -269,7 +273,7 @@ abstract class EthereumAPI { ); } catch (e, s) { Logging.instance.log( - "getEthTransaction($txids): $e\n$s", + "getEthTokenTransactionsByTxids($txids): $e\n$s", level: LogLevel.Error, ); return EthereumResponse( @@ -307,9 +311,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getTokenTransactions($address, $tokenContractAddress) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -424,10 +430,10 @@ abstract class EthereumAPI { final map = json["data"].first as Map; final balance = - Decimal.tryParse(map["balance"].toString()) ?? Decimal.zero; + BigInt.tryParse(map["units"].toString()) ?? BigInt.zero; return EthereumResponse( - Amount.fromDecimal(balance, fractionDigits: map["decimals"] as int), + Amount(rawValue: balance, fractionDigits: map["decimals"] as int), null, ); } else { diff --git a/lib/services/exchange/exchange_data_loading_service.dart b/lib/services/exchange/exchange_data_loading_service.dart index 6fefba289..ee0ed50a0 100644 --- a/lib/services/exchange/exchange_data_loading_service.dart +++ b/lib/services/exchange/exchange_data_loading_service.dart @@ -135,6 +135,9 @@ class ExchangeDataLoadingService { Future loadAll() async { if (!_locked) { _locked = true; + if (_isar == null) { + await initDB(); + } Logging.instance.log( "ExchangeDataLoadingService.loadAll starting...", level: LogLevel.Info, diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart new file mode 100644 index 000000000..d5cd3d733 --- /dev/null +++ b/lib/services/litescribe_api.dart @@ -0,0 +1,68 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/dto/ordinals/litescribe_response.dart'; + +class LitescribeAPI { + static final LitescribeAPI _instance = LitescribeAPI._internal(); + + factory LitescribeAPI({required String baseUrl}) { + _instance.baseUrl = baseUrl; + return _instance; + } + + LitescribeAPI._internal(); + + late String baseUrl; + + Future _getResponse(String endpoint) async { + final response = await http.get(Uri.parse('$baseUrl$endpoint')); + if (response.statusCode == 200) { + return LitescribeResponse(data: _validateJson(response.body)); + } else { + throw Exception('LitescribeAPI _getResponse exception: Failed to load data'); + } + } + + Map _validateJson(String responseBody) { + final parsed = jsonDecode(responseBody); + if (parsed is Map) { + return parsed; + } else { + throw const FormatException('LitescribeAPI _validateJson exception: Invalid JSON format'); + } + } + + Future> getInscriptionsByAddress(String address, {int cursor = 0, int size = 1000}) async { + // size param determines how many inscriptions are returned per response + // default of 1000 is used to cover most addresses (I assume) + // if the total number of inscriptions at the address exceeds the length of the list of inscriptions returned, another call with a higher size is made + final int defaultLimit = 1000; + final response = await _getResponse('/address/inscriptions?address=$address&cursor=$cursor&size=$size'); + + // Check if the number of returned inscriptions equals the limit + final list = response.data['result']['list']; + final int total = response.data['result']['total'] as int; + final int currentSize = list.length as int; + + if (currentSize == size && currentSize < total) { + // If the number of returned inscriptions equals the limit and there are more inscriptions available, + // increment the cursor and make the next API call to fetch the remaining inscriptions. + final int newCursor = cursor + size; + return getInscriptionsByAddress(address, cursor: newCursor, size: size); + + } else { + try { + // Iterate through the list and create InscriptionData objects from each element + final List inscriptions = (list as List) + .map((json) => InscriptionData.fromJson(json as Map)) + .toList(); + + return inscriptions; + } catch (e) { + throw const FormatException('LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); + } + } + } +} \ No newline at end of file diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart deleted file mode 100644 index be9845453..000000000 --- a/lib/services/mixins/firo_hive.dart +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of Stack Wallet. - * - * Copyright (c) 2023 Cypher Stack - * All Rights Reserved. - * The code is distributed under GPLv3 license, see LICENSE file for details. - * Generated by Cypher Stack on 2023-05-26 - * - */ - -import 'package:stackwallet/db/hive/db.dart'; - -mixin FiroHive { - late final String _walletId; - - void initFiroHive(String walletId) { - _walletId = walletId; - } - - // jindex - List? firoGetJIndex() { - return DB.instance.get(boxName: _walletId, key: "jindex") as List?; - } - - Future firoUpdateJIndex(List jIndex) async { - await DB.instance.put( - boxName: _walletId, - key: "jindex", - value: jIndex, - ); - } - - // _lelantus_coins - List? firoGetLelantusCoins() { - return DB.instance.get(boxName: _walletId, key: "_lelantus_coins") - as List?; - } - - Future firoUpdateLelantusCoins(List lelantusCoins) async { - await DB.instance.put( - boxName: _walletId, - key: "_lelantus_coins", - value: lelantusCoins, - ); - } - - // mintIndex - int firoGetMintIndex() { - return DB.instance.get(boxName: _walletId, key: "mintIndex") - as int? ?? - 0; - } - - Future firoUpdateMintIndex(int mintIndex) async { - await DB.instance.put( - boxName: _walletId, - key: "mintIndex", - value: mintIndex, - ); - } -} diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart new file mode 100644 index 000000000..5b17f3b48 --- /dev/null +++ b/lib/services/mixins/ordinals_interface.dart @@ -0,0 +1,94 @@ +import 'dart:async'; + +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; +import 'package:stackwallet/services/litescribe_api.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; + +mixin OrdinalsInterface { + late final String _walletId; + late final Coin _coin; + late final MainDB _db; + + void initOrdinalsInterface({ + required String walletId, + required Coin coin, + required MainDB db, + }) { + _walletId = walletId; + _coin = coin; + _db = db; + } + + final LitescribeAPI litescribeAPI = + LitescribeAPI(baseUrl: 'https://litescribe.io/api'); + + Future refreshInscriptions() async { + final uniqueAddresses = await _db + .getUTXOs(_walletId) + .filter() + .addressIsNotNull() + .distinctByAddress() + .addressProperty() + .findAll(); + final inscriptions = + await _getInscriptionDataFromAddresses(uniqueAddresses.cast()); + + final ords = inscriptions + .map((e) => Ordinal.fromInscriptionData(e, _walletId)) + .toList(); + + await _db.isar.writeTxn(() async { + await _db.isar.ordinals + .where() + .filter() + .walletIdEqualTo(_walletId) + .deleteAll(); + await _db.isar.ordinals.putAll(ords); + }); + } + + Future> _getInscriptionDataFromAddresses( + List addresses) async { + List allInscriptions = []; + for (String address in addresses) { + try { + var inscriptions = + await litescribeAPI.getInscriptionsByAddress(address); + allInscriptions.addAll(inscriptions); + } catch (e) { + throw Exception("Error fetching inscriptions for address $address: $e"); + } + } + return allInscriptions; + } + + // check if an inscription is in a given output + Future inscriptionInOutput(UTXO output) async { + if (output.address != null) { + var inscriptions = + await litescribeAPI.getInscriptionsByAddress("${output.address}"); + if (inscriptions.isNotEmpty) { + return true; + } else { + return false; + } + } else { + throw UnimplementedError( + 'TODO look up utxo without address. utxo->txid:output->address'); + } + } + + // check if an inscription is in a given output + Future inscriptionInAddress(String address) async { + var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + if (inscriptions.isNotEmpty) { + return true; + } else { + return false; + } + } +} diff --git a/lib/services/nano_api.dart b/lib/services/nano_api.dart new file mode 100644 index 000000000..3173bfbe6 --- /dev/null +++ b/lib/services/nano_api.dart @@ -0,0 +1,130 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:nanodart/nanodart.dart'; + +class NanoAPI { + static Future< + ({ + NAccountInfo? accountInfo, + Exception? exception, + })> getAccountInfo({ + required Uri server, + required bool representative, + required String account, + }) async { + NAccountInfo? accountInfo; + Exception? exception; + + try { + final response = await http.post( + server, + headers: { + "Content-Type": "application/json", + }, + body: jsonEncode({ + "action": "account_info", + "representative": "true", + "account": account, + }), + ); + + final map = jsonDecode(response.body); + + if (map is Map && map["error"] != null) { + throw Exception(map["error"].toString()); + } + + accountInfo = NAccountInfo( + frontier: map["frontier"] as String, + representative: map["representative"] as String, + ); + } on Exception catch (e) { + exception = e; + } catch (e) { + exception = Exception(e.toString()); + } + + return (accountInfo: accountInfo, exception: exception); + } + + static Future changeRepresentative({ + required Uri server, + required int accountType, + required String account, + required String newRepresentative, + required String previousBlock, + required String balance, + required String privateKey, + required String work, + }) async { + Map block = { + "type": "state", + "account": account, + "previous": previousBlock, + "representative": newRepresentative, + "balance": balance, + "link": + "0000000000000000000000000000000000000000000000000000000000000000", + "work": work, + }; + + final String hash; + + try { + hash = NanoBlocks.computeStateHash( + accountType, + account, + previousBlock, + newRepresentative, + BigInt.parse(balance), + block["link"] as String, + ); + } catch (e) { + if (e is RangeError) { + throw Exception("Invalid representative format"); + } + rethrow; + } + + final signature = NanoSignatures.signBlock(hash, privateKey); + + block["signature"] = signature; + + final map = await postBlock(server: server, block: block); + + if (map is Map && map["error"] != null) { + throw Exception(map["error"].toString()); + } + + return map["error"] == null; + } + + // TODO: GET RID OF DYNAMIC AND USED TYPED DATA + static Future postBlock({ + required Uri server, + required Map block, + }) async { + final response = await http.post( + server, + headers: { + "Content-Type": "application/json", + }, + body: jsonEncode({ + "action": "process", + "json_block": "true", + "subtype": "change", + "block": block, + }), + ); + + return jsonDecode(response.body); + } +} + +class NAccountInfo { + final String frontier; + final String representative; + + NAccountInfo({required this.frontier, required this.representative}); +} diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index e1c42fabe..3ef97462a 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -26,6 +26,7 @@ class NotificationApi { priority: Priority.high, ticker: 'ticker'), iOS: IOSNotificationDetails(), + macOS: MacOSNotificationDetails(), ); } @@ -34,8 +35,13 @@ class NotificationApi { const iOS = IOSInitializationSettings(); const linux = LinuxInitializationSettings( defaultActionName: "temporary_stack_wallet"); - const settings = - InitializationSettings(android: android, iOS: iOS, linux: linux); + const macOS = MacOSInitializationSettings(); + const settings = InitializationSettings( + android: android, + iOS: iOS, + linux: linux, + macOS: macOS, + ); await _notifications.initialize( settings, onSelectNotification: (payload) async { @@ -71,8 +77,10 @@ class NotificationApi { final id = prefs.currentNotificationId; String confirms = ""; - if (txid != null) { - confirms = " (${confirmations!}/${requiredConfirmations!})"; + if (txid != null && + confirmations != null && + requiredConfirmations != null) { + confirms = " ($confirmations/$requiredConfirmations)"; } final NotificationModel model = NotificationModel( diff --git a/lib/themes/coin_card_provider.dart b/lib/themes/coin_card_provider.dart index fc84faebd..b34e9e6f1 100644 --- a/lib/themes/coin_card_provider.dart +++ b/lib/themes/coin_card_provider.dart @@ -22,3 +22,14 @@ final coinCardProvider = Provider.family((ref, coin) { return null; } }); + +final coinCardFavoritesProvider = Provider.family((ref, coin) { + final assets = ref.watch(themeAssetsProvider); + + if (assets is ThemeAssetsV3) { + return assets.coinCardFavoritesImages?[coin.mainNetVersion] ?? + assets.coinCardImages?[coin.mainNetVersion]; + } else { + return null; + } +}); diff --git a/lib/utilities/amount/amount_formatter.dart b/lib/utilities/amount/amount_formatter.dart index 6743bcd53..1ae577507 100644 --- a/lib/utilities/amount/amount_formatter.dart +++ b/lib/utilities/amount/amount_formatter.dart @@ -70,6 +70,11 @@ class AmountFormatter { String string, { EthContract? ethContract, }) { - return unit.tryParse(string, locale: locale, coin: coin); + return unit.tryParse( + string, + locale: locale, + coin: coin, + tokenContract: ethContract, + ); } } diff --git a/lib/utilities/amount/amount_unit.dart b/lib/utilities/amount/amount_unit.dart index fd2366dd4..4f57a35dc 100644 --- a/lib/utilities/amount/amount_unit.dart +++ b/lib/utilities/amount/amount_unit.dart @@ -170,6 +170,7 @@ extension AmountUnitExt on AmountUnit { required String locale, required Coin coin, EthContract? tokenContract, + bool overrideWithDecimalPlacesFromString = false, }) { final precisionLost = value.startsWith("~"); @@ -203,7 +204,9 @@ extension AmountUnitExt on AmountUnit { return null; } - final decimalPlaces = tokenContract?.decimals ?? coin.decimals; + final decimalPlaces = overrideWithDecimalPlacesFromString + ? decimal.scale + : tokenContract?.decimals ?? coin.decimals; final realShift = math.min(shift, decimalPlaces); return decimal.shift(0 - realShift).toAmount(fractionDigits: decimalPlaces); diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index b8ec501e8..d1d7a55d8 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -92,6 +92,7 @@ class _SVG { final coinControl = const _COIN_CONTROL(); + String get monkey => "assets/svg/monkey.svg"; String get circleSliders => "assets/svg/configuration.svg"; String get circlePlus => "assets/svg/plus-circle.svg"; String get circlePlusFilled => "assets/svg/circle-plus-filled.svg"; @@ -205,6 +206,8 @@ class _SVG { String get messageQuestion => "assets/svg/message-question-1.svg"; String get list => "assets/svg/list-ul.svg"; String get unclaimedPaynym => "assets/svg/unclaimed.svg"; + String get send => "assets/svg/send.svg"; + String get ordinal => "assets/svg/ordinal.svg"; String get trocadorRatingA => "assets/svg/trocador_rating_a.svg"; String get trocadorRatingB => "assets/svg/trocador_rating_b.svg"; diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 5a5a2c33f..f79e22ff6 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -59,7 +59,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentDataVersion = 10; + static const int currentDataVersion = 11; static const int rescanV1 = 1; diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 293b5d48c..f54c2b85a 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -180,7 +180,6 @@ class DbVersionMigrator with WalletDB { // clear possible broken firo cache await DB.instance.clearSharedTransactionCache(coin: Coin.firo); - // update version await DB.instance.put( boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 4); @@ -343,12 +342,85 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(10, secureStore: secureStore); + case 10: + // migrate + await _v10(secureStore); + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 11); + + // try to continue migrating + return await migrate(11, secureStore: secureStore); + default: // finally return return; } } + Future _v10(SecureStorageInterface secureStore) async { + await Hive.openBox(DB.boxNameAllWalletsData); + await Hive.openBox(DB.boxNamePrefs); + final walletsService = WalletsService(secureStorageInterface: secureStore); + final prefs = Prefs.instance; + final walletInfoList = await walletsService.walletNames; + await prefs.init(); + await MainDB.instance.initMainDB(); + + for (final walletId in walletInfoList.keys) { + final info = walletInfoList[walletId]!; + assert(info.walletId == walletId); + + if (info.coin == Coin.firo && + MainDB.instance.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .countSync() == + 0) { + final walletBox = await Hive.openBox(walletId); + + final hiveLCoins = DB.instance.get( + boxName: walletId, + key: "_lelantus_coins", + ) as List? ?? + []; + + final jindexes = (DB.instance + .get(boxName: walletId, key: "jindex") as List? ?? + []) + .cast(); + + final List coins = []; + for (final e in hiveLCoins) { + final map = e as Map; + final lcoin = map.values.first as LelantusCoin; + + final isJMint = jindexes.contains(lcoin.index); + + final coin = isar_models.LelantusCoin( + walletId: walletId, + txid: lcoin.txId, + value: lcoin.value.toString(), + mintIndex: lcoin.index, + anonymitySetId: lcoin.anonymitySetId, + isUsed: lcoin.isUsed, + isJMint: isJMint, + otherData: null, + ); + + coins.add(coin); + } + + if (coins.isNotEmpty) { + await MainDB.instance.isar.writeTxn(() async { + await MainDB.instance.isar.lelantusCoins.putAll(coins); + }); + } + } + } + } + Future _v4(SecureStorageInterface secureStore) async { await Hive.openBox(DB.boxNameAllWalletsData); await Hive.openBox(DB.boxNamePrefs); diff --git a/lib/utilities/show_loading.dart b/lib/utilities/show_loading.dart index db4437d6f..759eaa05c 100644 --- a/lib/utilities/show_loading.dart +++ b/lib/utilities/show_loading.dart @@ -12,15 +12,17 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; -Future showLoading({ +Future showLoading({ required Future whileFuture, required BuildContext context, required String message, String? subMessage, bool isDesktop = false, bool opaqueBG = false, + void Function(Exception)? onException, }) async { unawaited( showDialog( @@ -43,10 +45,24 @@ Future showLoading({ ), ); - final result = await whileFuture; + Exception? ex; + T? result; + + try { + result = await whileFuture; + } catch (e, s) { + Logging.instance.log( + "showLoading caught: $e\n$s", + level: LogLevel.Warning, + ); + ex = e is Exception ? e : Exception(e.toString()); + } if (context.mounted) { Navigator.of(context, rootNavigator: isDesktop).pop(); + if (ex != null) { + onException?.call(ex); + } } return result; diff --git a/lib/utilities/stack_file_system.dart b/lib/utilities/stack_file_system.dart index 843bb1b71..f36fadf01 100644 --- a/lib/utilities/stack_file_system.dart +++ b/lib/utilities/stack_file_system.dart @@ -27,8 +27,8 @@ abstract class StackFileSystem { } else if (Platform.isWindows) { appDirectory = await getApplicationSupportDirectory(); } else if (Platform.isMacOS) { - // currently run in ipad mode?? - throw Exception("Unsupported platform"); + appDirectory = await getLibraryDirectory(); + appDirectory = Directory("${appDirectory.path}/stackwallet"); } else if (Platform.isIOS) { // todo: check if we need different behaviour here if (Util.isDesktop) { diff --git a/lib/utilities/text_styles.dart b/lib/utilities/text_styles.dart index 4a1ced812..fab828bef 100644 --- a/lib/utilities/text_styles.dart +++ b/lib/utilities/text_styles.dart @@ -306,6 +306,17 @@ class STextStyles { } } + static TextStyle w600_16(BuildContext context) { + switch (_theme(context).themeId) { + default: + return GoogleFonts.inter( + color: _theme(context).textDark, + fontWeight: FontWeight.w600, + fontSize: 16, + ); + } + } + static TextStyle w500_14(BuildContext context) { switch (_theme(context).themeId) { default: @@ -339,6 +350,17 @@ class STextStyles { } } + static TextStyle w500_8(BuildContext context) { + switch (_theme(context).themeId) { + default: + return GoogleFonts.inter( + color: _theme(context).textSubtitle1, + fontWeight: FontWeight.w500, + fontSize: 8, + ); + } + } + static TextStyle w600_20(BuildContext context) { switch (_theme(context).themeId) { default: diff --git a/lib/widgets/coin_card.dart b/lib/widgets/coin_card.dart index 0bebc5fd8..ebc67bb4e 100644 --- a/lib/widgets/coin_card.dart +++ b/lib/widgets/coin_card.dart @@ -25,11 +25,13 @@ class CoinCard extends ConsumerWidget { required this.walletId, required this.width, required this.height, + required this.isFavorite, }); final String walletId; final double width; final double height; + final bool isFavorite; @override Widget build(BuildContext context, WidgetRef ref) { @@ -38,7 +40,7 @@ class CoinCard extends ConsumerWidget { .select((value) => value.getManager(walletId).coin), ); - final bool hasCardImageBg = ref.watch(coinCardProvider(coin)) != null; + final bool hasCardImageBg = (isFavorite) ? ref.watch(coinCardFavoritesProvider(coin)) != null : ref.watch(coinCardProvider(coin)) != null; return Stack( children: [ @@ -54,7 +56,9 @@ class CoinCard extends ConsumerWidget { fit: BoxFit.cover, image: FileImage( File( - ref.watch(coinCardProvider(coin))!, + (isFavorite) + ? ref.watch(coinCardFavoritesProvider(coin))! + : ref.watch(coinCardProvider(coin))!, ), ), ), diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index f94c74ed9..b570ae4f9 100644 --- a/lib/widgets/crypto_notifications.dart +++ b/lib/widgets/crypto_notifications.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -84,6 +85,8 @@ class _CryptoNotificationsState extends ConsumerState { @override void initState() { + NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); + NotificationApi.notificationsService = ref.read(notificationsProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on() .listen( diff --git a/lib/widgets/transaction_card.dart b/lib/widgets/transaction_card.dart index 7791ff3a0..41b81e2da 100644 --- a/lib/widgets/transaction_card.dart +++ b/lib/widgets/transaction_card.dart @@ -230,7 +230,9 @@ class _TransactionCardState extends ConsumerState { fit: BoxFit.scaleDown, child: Text( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction.type, coin, diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index 63b3a4a0e..09dcb0e2e 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -140,7 +140,7 @@ class SimpleWalletCard extends ConsumerWidget { isDesktop: Util.isDesktop, ); - if (!success) { + if (!success!) { // TODO: show error dialog here? Logging.instance.log( "Failed to load token wallet for $contract", diff --git a/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart new file mode 100644 index 000000000..4c23bf20a --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart @@ -0,0 +1,28 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; + +class OrdinalsNavIcon extends StatelessWidget { + const OrdinalsNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.ordinal, + height: 20, + width: 20, + color: Theme.of(context).extension()!.bottomNavIconIcon, + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b741c3c0a..b044b4b00 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,9 @@ import FlutterMacOS import Foundation import connectivity_plus +import cw_monero +import cw_shared_external +import cw_wownero import desktop_drop import device_info_plus import devicelocale @@ -13,10 +16,10 @@ import flutter_libepiccash import flutter_local_notifications import flutter_secure_storage_macos import isar_flutter_libs +import lelantus import package_info_plus import path_provider_foundation import share_plus -import shared_preferences_foundation import stack_wallet_backup import url_launcher_macos import wakelock_macos @@ -24,6 +27,9 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) + CwSharedExternalPlugin.register(with: registry.registrar(forPlugin: "CwSharedExternalPlugin")) + CwWowneroPlugin.register(with: registry.registrar(forPlugin: "CwWowneroPlugin")) DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) @@ -31,10 +37,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + LelantusPlugin.register(with: registry.registrar(forPlugin: "LelantusPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) diff --git a/macos/Podfile b/macos/Podfile index dade8dfad..c795730db 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.11' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -31,6 +31,9 @@ target 'Runner' do use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 82579cd8e..69c91af48 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,24 +1,76 @@ PODS: - - connectivity_plus_macos (0.0.1): + - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift + - cw_monero (0.0.1): + - cw_monero/Boost (= 0.0.1) + - cw_monero/Monero (= 0.0.1) + - cw_monero/OpenSSL (= 0.0.1) + - cw_monero/Sodium (= 0.0.1) + - cw_monero/Unbound (= 0.0.1) + - FlutterMacOS + - cw_monero/Boost (0.0.1): + - FlutterMacOS + - cw_monero/Monero (0.0.1): + - FlutterMacOS + - cw_monero/OpenSSL (0.0.1): + - FlutterMacOS + - cw_monero/Sodium (0.0.1): + - FlutterMacOS + - cw_monero/Unbound (0.0.1): + - FlutterMacOS + - cw_shared_external (0.0.1): + - cw_shared_external/Boost (= 0.0.1) + - cw_shared_external/OpenSSL (= 0.0.1) + - cw_shared_external/Sodium (= 0.0.1) + - FlutterMacOS + - cw_shared_external/Boost (0.0.1): + - FlutterMacOS + - cw_shared_external/OpenSSL (0.0.1): + - FlutterMacOS + - cw_shared_external/Sodium (0.0.1): + - FlutterMacOS + - cw_wownero (0.0.1): + - cw_wownero/Boost (= 0.0.1) + - cw_wownero/OpenSSL (= 0.0.1) + - cw_wownero/Sodium (= 0.0.1) + - cw_wownero/Unbound (= 0.0.1) + - cw_wownero/Wownero (= 0.0.1) + - FlutterMacOS + - cw_wownero/Boost (0.0.1): + - FlutterMacOS + - cw_wownero/OpenSSL (0.0.1): + - FlutterMacOS + - cw_wownero/Sodium (0.0.1): + - FlutterMacOS + - cw_wownero/Unbound (0.0.1): + - FlutterMacOS + - cw_wownero/Wownero (0.0.1): + - FlutterMacOS + - desktop_drop (0.0.1): + - FlutterMacOS + - device_info_plus (0.0.1): + - FlutterMacOS + - devicelocale (0.0.1): + - FlutterMacOS - flutter_libepiccash (0.0.1): - FlutterMacOS - flutter_local_notifications (0.0.1): - FlutterMacOS - - flutter_secure_storage_macos (3.3.1): + - flutter_secure_storage_macos (6.1.1): - FlutterMacOS - FlutterMacOS (1.0.0) - isar_flutter_libs (1.0.0): - FlutterMacOS - - package_info_plus_macos (0.0.1): + - lelantus (0.0.1): - FlutterMacOS - - path_provider_macos (0.0.1): + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter - FlutterMacOS - ReachabilitySwift (5.0.0) - - share_plus_macos (0.0.1): - - FlutterMacOS - - shared_preferences_macos (0.0.1): + - share_plus (0.0.1): - FlutterMacOS - stack_wallet_backup (0.0.1): - FlutterMacOS @@ -30,16 +82,22 @@ PODS: - FlutterMacOS DEPENDENCIES: - - connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`) + - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) + - cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`) + - cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`) + - cw_wownero (from `Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos`) + - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) - flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`) - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`) - - package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`) - - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) + - lelantus (from `Flutter/ephemeral/.symlinks/plugins/lelantus/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) @@ -50,8 +108,20 @@ SPEC REPOS: - ReachabilitySwift EXTERNAL SOURCES: - connectivity_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos + connectivity_plus: + :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos + cw_monero: + :path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos + cw_shared_external: + :path: Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos + cw_wownero: + :path: Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos + desktop_drop: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + devicelocale: + :path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos flutter_libepiccash: :path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos flutter_local_notifications: @@ -62,14 +132,14 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral isar_flutter_libs: :path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos - package_info_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos - path_provider_macos: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos - share_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos - shared_preferences_macos: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos + lelantus: + :path: Flutter/ephemeral/.symlinks/plugins/lelantus/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos stack_wallet_backup: :path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos url_launcher_macos: @@ -80,22 +150,28 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos SPEC CHECKSUMS: - connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 - flutter_libepiccash: b33f7396504712b513b8ff019a3f6f3bdae54cfb + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + cw_monero: a3442556ad3c06365c912735e4a23942a28692b1 + cw_shared_external: 1f631d1132521baac5f4caed43176fa10d4e0d8b + cw_wownero: b4adb1e701fc363de27fa222fcaf4eff6f5fa63a + desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 + flutter_libepiccash: 9113ac75dd325f8bcf00bc3ab583c7fc2780cf3c flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 - flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 - isar_flutter_libs: 1948109973b6c2e46d6196b1537688a36a6edeac - package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c - path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 + flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a + lelantus: 3dfbf92b1e66b3573494dfe3d6a21c4988b5361b + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 - shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 + share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c - url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 + url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 COCOAPODS: 1.11.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6506a5075..c2c2e62ad 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -21,15 +21,27 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */; }; + B98151812A674022009D013C /* mobileliblelantus.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; }; + B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151832A674143009D013C /* libsqlite3.0.tbd */; }; + BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */; }; + F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 33CC10E52044A3C60003C045 /* Project object */; @@ -46,6 +58,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */, ); name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; @@ -53,9 +66,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0FA914E59929120BA65E8403 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* Stack Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Stack Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* Stack Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Stack Wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -67,26 +85,47 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 41EE721BF40B8DE895352A2C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + ACB8E553D75AA4AC9A7656CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + B98151802A674022009D013C /* mobileliblelantus.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mobileliblelantus.framework; path = ../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus/mobileliblelantus.framework; sourceTree = ""; }; + B98151832A674143009D013C /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; + BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EA2044A3C60003C045 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */, + B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */, + B98151812A674022009D013C /* mobileliblelantus.framework in Frameworks */, + F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; 33BA886A226E78AF003329D5 /* Configs */ = { isa = PBXGroup; children = ( @@ -103,9 +142,10 @@ children = ( 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 9000119722579F22067B9BC0 /* Pods */, + F0D4A0626F78BE1EF2A1E0D6 /* Pods */, ); sourceTree = ""; }; @@ -113,6 +153,7 @@ isa = PBXGroup; children = ( 33CC10ED2044A3C60003C045 /* Stack Wallet.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -152,39 +193,63 @@ path = Runner; sourceTree = ""; }; - 9000119722579F22067B9BC0 /* Pods */ = { - isa = PBXGroup; - children = ( - EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */, - 937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */, - BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */, + B98151832A674143009D013C /* libsqlite3.0.tbd */, + B98151802A674022009D013C /* mobileliblelantus.framework */, + E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */, + 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; }; + F0D4A0626F78BE1EF2A1E0D6 /* Pods */ = { + isa = PBXGroup; + children = ( + 41EE721BF40B8DE895352A2C /* Pods-Runner.debug.xcconfig */, + 0FA914E59929120BA65E8403 /* Pods-Runner.release.xcconfig */, + ACB8E553D75AA4AC9A7656CE /* Pods-Runner.profile.xcconfig */, + BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */, + 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */, + 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + FF8CC09C83E12FC1C6EE6A8F /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 33CC10EC2044A3C60003C045 /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */, + AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */, + 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -206,6 +271,10 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; @@ -236,12 +305,20 @@ projectRoot = ""; targets = ( 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, 33CC111A2044C6BA0003C045 /* Flutter Assemble */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EB2044A3C60003C045 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -256,6 +333,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -291,7 +369,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */ = { + 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -308,7 +386,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */ = { + AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -330,9 +408,39 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + FF8CC09C83E12FC1C6EE6A8F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10E92044A3C60003C045 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -346,6 +454,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; @@ -366,6 +479,51 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet"; + }; + name = Profile; + }; 338D0CE9231458BD00FA5F75 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; @@ -404,9 +562,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -421,6 +581,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"", + "\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -483,10 +667,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -530,9 +715,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -547,6 +734,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"", + "\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -567,6 +778,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"", + "\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -596,6 +831,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c9216c9be..a9d38bc3b 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -37,6 +37,17 @@ + + + + com.apple.security.network.server + com.apple.security.network.client + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 2722837ec..3cc05eb23 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -3,7 +3,7 @@ import FlutterMacOS class MainFlutterWindow: NSWindow { override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() + let flutterViewController = FlutterViewController() let windowFrame = self.frame self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a47..48271acc9 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.network.client + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..5418c9f53 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock index 743a302f7..e260cb994 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,14 +25,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.12.30" - ansicolor: - dependency: transitive - description: - name: ansicolor - sha256: "607f8fa9786f392043f169898923e6c59b4518242b68b8862eb8a8b7d9c30b4a" - url: "https://pub.dev" - source: hosted - version: "2.0.1" archive: dependency: "direct main" description: @@ -45,18 +37,18 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.1" asn1lib: dependency: transitive description: name: asn1lib - sha256: b74e3842a52c61f8819a1ec8444b4de5419b41a7465e69d4aa681445377398b0 + sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" async: dependency: "direct main" description: @@ -154,10 +146,10 @@ packages: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: "43865b79fbb78532e4bff7c33087aa43b1d488c4fdef014eaef568af6d8016dc" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" build_config: dependency: transitive description: @@ -178,26 +170,26 @@ packages: dependency: transitive description: name: build_resolvers - sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.0" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "220ae4553e50d7c21a17c051afc7b183d28a24a420502e842f303f8e4e6edced" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.4" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + sha256: "88a57f2ac99849362e73878334caa9f06ee25f31d2adced882b8337838c84e1e" url: "https://pub.dev" source: hosted - version: "7.2.10" + version: "7.2.9" built_collection: dependency: transitive description: @@ -210,10 +202,10 @@ packages: dependency: transitive description: name: built_value - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + sha256: "7dd62d9faf105c434f3d829bbe9c4be02ec67f5ed94832222116122df67c5452" url: "https://pub.dev" source: hosted - version: "8.6.1" + version: "8.6.0" characters: dependency: transitive description: @@ -234,10 +226,10 @@ packages: dependency: transitive description: name: cli_util - sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "0.4.0" clock: dependency: transitive description: @@ -250,10 +242,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.4.0" collection: dependency: transitive description: @@ -322,10 +314,10 @@ packages: dependency: transitive description: name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.17.3" cw_core: dependency: "direct main" description: @@ -390,18 +382,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.1" dartx: dependency: transitive description: name: dartx - sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" + sha256: "45d7176701f16c5a5e00a4798791c1964bc231491b879369c818dd9a9c764871" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.0" dbus: dependency: transitive description: @@ -414,10 +406,10 @@ packages: dependency: "direct main" description: name: decimal - sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21" + sha256: eece91944f523657c75a3a008a90ec7f7eb3986191153a78570c7d0ac8ef3d01 url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.2" dependency_validator: dependency: "direct dev" description: @@ -458,22 +450,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" - dio: - dependency: transitive - description: - name: dio - sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" - url: "https://pub.dev" - source: hosted - version: "4.0.6" dropdown_button2: dependency: "direct main" description: name: dropdown_button2 - sha256: "83c54a5022f898d63e3abe21240b64b937e676103207287e6705d3f9bb04d654" + sha256: "374f2390161bf782b4896f0b1b24cbb2b5daaa1cfb11047c3307461dcdf44e07" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.1.3" eip1559: dependency: transitive description: @@ -558,10 +542,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: b1729fc96627dd44012d0a901558177418818d6bd428df59dcfeb594e5f66432 + sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.3.1" fixnum: dependency: transitive description: @@ -600,10 +584,10 @@ packages: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" url: "https://pub.dev" source: hosted - version: "0.11.0" + version: "0.13.1" flutter_libepiccash: dependency: "direct main" description: @@ -622,10 +606,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.1" flutter_local_notifications: dependency: "direct main" description: @@ -662,10 +646,10 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: "6777a3abb974021a39b5fdd2d46a03ca390e03903b6351f21d10e7ecc969f12d" + sha256: ba45d8cfbd778478a74696b012f33ffb6b1760c9bc531b21e2964444a4870dae url: "https://pub.dev" source: hosted - version: "2.2.16" + version: "2.3.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -686,10 +670,10 @@ packages: dependency: "direct main" description: name: flutter_rounded_date_picker - sha256: e6aa2dc5d3b44e8bbe85ef901be69eac59ba4136427f11f4c8b2a303e1e774e7 + sha256: e7143cc5cbf3aec1536286653e38b0809abc99fb76c91bd910dbd98ae003d890 url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.3" flutter_secure_storage: dependency: "direct main" description: @@ -837,10 +821,10 @@ packages: dependency: transitive description: name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8" url: "https://pub.dev" source: hosted - version: "0.15.4" + version: "0.15.3" http: dependency: "direct main" description: @@ -869,10 +853,10 @@ packages: dependency: transitive description: name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "4.0.17" import_sorter: dependency: "direct dev" description: @@ -950,14 +934,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - json_serializable: - dependency: transitive - description: - name: json_serializable - sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 - url: "https://pub.dev" - source: hosted - version: "6.7.1" keyboard_dismisser: dependency: "direct main" description: @@ -972,15 +948,15 @@ packages: path: "crypto_plugins/flutter_liblelantus" relative: true source: path - version: "0.0.1" + version: "0.0.2" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.0" local_auth: dependency: "direct main" description: @@ -1001,10 +977,10 @@ packages: dependency: "direct main" description: name: lottie - sha256: "0793a5866062e5cc8a8b24892fa94c3095953ea914a7fdf790f550dd7537fe60" + sha256: "23522951540d20a57a60202ed7022e6376bed206a4eee1c347a91f58bd57eb9f" url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.3.2" matcher: dependency: transitive description: @@ -1021,14 +997,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" - memoize: - dependency: transitive - description: - name: memoize - sha256: "51481d328c86cbdc59711369179bac88551ca0556569249be5317e66fc796cac" - url: "https://pub.dev" - source: hosted - version: "3.0.0" meta: dependency: transitive description: @@ -1065,10 +1033,10 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" + sha256: "8b46d7eb40abdda92d62edd01546051f0c27365e65608c284de336dccfef88cc" url: "https://pub.dev" source: hosted - version: "5.4.2" + version: "5.4.1" mocktail: dependency: transitive description: @@ -1177,10 +1145,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" + sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.3" path_provider_linux: dependency: transitive description: @@ -1201,50 +1169,50 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.6" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" + sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" url: "https://pub.dev" source: hosted - version: "10.4.3" + version: "10.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: c0c9754479a4c4b1c1f3862ddc11930c9b3f03bef2816bb4ea6eed1e13551d6f + sha256: d8cc6a62ded6d0f49c6eac337e080b066ee3bce4d405bd9439a61e1f1927bfe8 url: "https://pub.dev" source: hosted - version: "10.3.2" + version: "10.2.1" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "9.0.8" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" + sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" url: "https://pub.dev" source: hosted - version: "3.11.3" + version: "3.9.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.1.2" petitparser: dependency: transitive description: @@ -1253,14 +1221,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" - pinenacl: - dependency: transitive - description: - name: pinenacl - sha256: e5fb0bce1717b7f136f35ee98b5c02b3e6383211f8a77ca882fa7812232a07b9 - url: "https://pub.dev" - source: hosted - version: "0.3.4" platform: dependency: transitive description: @@ -1293,14 +1253,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - pretty_dio_logger: - dependency: transitive - description: - name: pretty_dio_logger - sha256: "948f7eeb36e7aa0760b51c1a8e3331d4b21e36fabd39efca81f585ed93893544" - url: "https://pub.dev" - source: hosted - version: "1.2.0-beta-1" process: dependency: transitive description: @@ -1349,14 +1301,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 - url: "https://pub.dev" - source: hosted - version: "3.2.1" rational: dependency: transitive description: @@ -1365,14 +1309,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.2" - retry: - dependency: transitive - description: - name: retry - sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc" - url: "https://pub.dev" - source: hosted - version: "3.1.2" riverpod: dependency: transitive description: @@ -1413,62 +1349,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4 - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d - url: "https://pub.dev" - source: hosted - version: "2.3.0" shelf: dependency: transitive description: @@ -1510,18 +1390,18 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" source_helper: dependency: transitive description: name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" url: "https://pub.dev" source: hosted - version: "1.3.4" + version: "1.3.3" source_map_stack_trace: dependency: transitive description: @@ -1643,14 +1523,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" - tezart: - dependency: "direct main" - description: - name: tezart - sha256: "35d526f2e6ca250c64461ebfb4fa9f64b6599fab8c4242c8e89ae27d4ac2e15a" - url: "https://pub.dev" - source: hosted - version: "2.0.5" time: dependency: transitive description: @@ -1687,10 +1559,10 @@ packages: dependency: "direct main" description: name: tuple - sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + sha256: "0ea99cd2f9352b2586583ab2ce6489d1f95a5f6de6fb9492faaf97ae2060f0aa" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.1" typed_data: dependency: transitive description: @@ -1711,18 +1583,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" + sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 url: "https://pub.dev" source: hosted - version: "6.1.12" + version: "6.1.11" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03" + sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 url: "https://pub.dev" source: hosted - version: "6.0.36" + version: "6.0.35" url_launcher_ios: dependency: transitive description: @@ -1743,34 +1615,34 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" + sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.5" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea + sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 + sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" url: "https://pub.dev" source: hosted - version: "2.0.18" + version: "2.0.17" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" + sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.0.6" uuid: dependency: "direct main" description: @@ -1896,18 +1768,18 @@ packages: dependency: transitive description: name: win32 - sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" url: "https://pub.dev" source: hosted - version: "5.0.5" + version: "4.1.4" win32_registry: dependency: transitive description: name: win32_registry - sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 + sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" window_size: dependency: "direct main" description: @@ -1959,4 +1831,4 @@ packages: version: "1.0.0" sdks: dart: ">=3.0.2 <4.0.0" - flutter: ">=3.10.0" + flutter: ">=3.10.3" diff --git a/pubspec.yaml b/pubspec.yaml index 052f8f3eb..4648f199c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Stack Wallet # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.7.15+181 +version: 1.7.16+182 environment: sdk: ">=3.0.2 <4.0.0" @@ -146,7 +146,7 @@ dev_dependencies: integration_test: sdk: flutter build_runner: ^2.1.7 - flutter_launcher_icons: ^0.11.0 + flutter_launcher_icons: ^0.13.1 hive_generator: ^2.0.0 dependency_validator: ^3.1.2 hive_test: ^1.0.1 @@ -158,7 +158,7 @@ dev_dependencies: flutter_lints: ^2.0.1 isar_generator: 3.0.5 -flutter_icons: +flutter_launcher_icons: android: true ios: true image_path: assets/icon/icon.png @@ -171,7 +171,7 @@ flutter_icons: icon_size: 48 # min:48, max:256, default: 48 macos: generate: true - image_path: assets/icon/icon.png + image_path: assets/icon/macos-icon.png flutter_native_splash: image: assets/images/splash.png @@ -337,6 +337,9 @@ flutter: - assets/svg/trocador_rating_b.svg - assets/svg/trocador_rating_c.svg - assets/svg/trocador_rating_d.svg + - assets/svg/send.svg + - assets/svg/ordinal.svg + - assets/svg/monkey.svg # coin control icons - assets/svg/coin_control/ diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh new file mode 100755 index 000000000..646274593 --- /dev/null +++ b/scripts/macos/build_all.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +(cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) & + +wait +echo "Done building" \ No newline at end of file diff --git a/test/pages/send_view/send_view_test.mocks.dart b/test/pages/send_view/send_view_test.mocks.dart index 71401e8cd..6ad56e711 100644 --- a/test/pages/send_view/send_view_test.mocks.dart +++ b/test/pages/send_view/send_view_test.mocks.dart @@ -2958,6 +2958,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart index a5f53d82b..b389043c0 100644 --- a/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/add_address_book_view_screen_test.mocks.dart @@ -388,6 +388,11 @@ class MockManager extends _i1.Mock implements _i12.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart index 630726884..87f97997e 100644 --- a/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/address_book_entry_details_view_screen_test.mocks.dart @@ -349,6 +349,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart index c64135241..72ac2f1a7 100644 --- a/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart +++ b/test/screen_tests/address_book_view/subviews/edit_address_book_entry_view_screen_test.mocks.dart @@ -347,6 +347,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/lockscreen_view_screen_test.mocks.dart b/test/screen_tests/lockscreen_view_screen_test.mocks.dart index 3f7609c1f..b028c6819 100644 --- a/test/screen_tests/lockscreen_view_screen_test.mocks.dart +++ b/test/screen_tests/lockscreen_view_screen_test.mocks.dart @@ -667,6 +667,11 @@ class MockManager extends _i1.Mock implements _i13.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart index 909d04a86..d0f5f89a4 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testA_test.mocks.dart @@ -454,6 +454,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart index 76ca1a64a..fa528b669 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testB_test.mocks.dart @@ -454,6 +454,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart index 416090add..be30eacbc 100644 --- a/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart +++ b/test/screen_tests/main_view_tests/main_view_screen_testC_test.mocks.dart @@ -454,6 +454,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart index 7022de852..34f177901 100644 --- a/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_view_screen_test.mocks.dart @@ -221,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart index 4fad26d9f..9e5723ef5 100644 --- a/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/backup_key_warning_view_screen_test.mocks.dart @@ -452,6 +452,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart index c04081c52..bb9e1c91b 100644 --- a/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/create_pin_view_screen_test.mocks.dart @@ -667,6 +667,11 @@ class MockManager extends _i1.Mock implements _i13.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart index 9a351b683..bd475e066 100644 --- a/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/restore_wallet_view_screen_test.mocks.dart @@ -508,6 +508,11 @@ class MockManager extends _i1.Mock implements _i13.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart b/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart index 496739e8e..a02c62b4d 100644 --- a/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart +++ b/test/screen_tests/onboarding/verify_backup_key_view_screen_test.mocks.dart @@ -221,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart index 5b81467df..fbf9c2ed5 100644 --- a/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/currency_view_screen_test.mocks.dart @@ -221,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart index 1c5de9829..5922e7f9b 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/add_custom_node_view_screen_test.mocks.dart @@ -436,6 +436,11 @@ class MockManager extends _i1.Mock implements _i12.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart index 760b143bd..133ca38a5 100644 --- a/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/network_settings_subviews/node_details_view_screen_test.mocks.dart @@ -436,6 +436,11 @@ class MockManager extends _i1.Mock implements _i12.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart index 191e1eca7..99b8dc33f 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_backup_view_screen_test.mocks.dart @@ -221,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart index df6bac383..c216d11b3 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/rescan_warning_view_screen_test.mocks.dart @@ -221,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart index 2ae50c123..f12df9272 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_subviews/wallet_delete_mnemonic_view_screen_test.mocks.dart @@ -452,6 +452,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart index 3548ce5a4..6f60f60bc 100644 --- a/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_subviews/wallet_settings_view_screen_test.mocks.dart @@ -688,6 +688,11 @@ class MockManager extends _i1.Mock implements _i15.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart index daee1f95e..6b20221c3 100644 --- a/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart +++ b/test/screen_tests/settings_view/settings_view_screen_test.mocks.dart @@ -452,6 +452,11 @@ class MockManager extends _i1.Mock implements _i10.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart index 7914d71f4..cffb87869 100644 --- a/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart +++ b/test/screen_tests/transaction_subviews/transaction_search_results_view_screen_test.mocks.dart @@ -223,6 +223,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart index f493222d9..01c78d796 100644 --- a/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/confirm_send_view_screen_test.mocks.dart @@ -222,6 +222,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart index 6e0382474..b5999d56c 100644 --- a/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/receive_view_screen_test.mocks.dart @@ -221,6 +221,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart index 97f744995..964343a65 100644 --- a/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/send_view_screen_test.mocks.dart @@ -263,6 +263,11 @@ class MockManager extends _i1.Mock implements _i9.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart index 4a0a14f47..e4b7791c6 100644 --- a/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart +++ b/test/screen_tests/wallet_view/wallet_view_screen_test.mocks.dart @@ -223,6 +223,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index 68db15437..05b4aeeff 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -11,9 +11,7 @@ import 'package:mockito/mockito.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; -import 'package:stackwallet/models/lelantus_coin.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -72,6 +70,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ); const currentHeight = 100000000000; @@ -113,10 +112,7 @@ void main() { final result = await staticProcessRestore(txData, message, currentHeight); expect(result, isA>()); - expect(result["mintIndex"], 8); - expect(result["jindex"], [2, 4, 6]); - expect( - result["_lelantus_coins"], isA>>()); + expect(result["_lelantus_coins"], isA>()); expect(result["newTxMap"], isA>()); }); @@ -133,6 +129,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ), throwsA(isA())); }); @@ -530,18 +527,10 @@ void main() { group("FiroWallet service class functions that depend on shared storage", () { const testWalletId = "testWalletID"; const testWalletName = "Test Wallet"; - bool hiveAdaptersRegistered = false; setUp(() async { await setUpTestHive(); - if (!hiveAdaptersRegistered) { - hiveAdaptersRegistered = true; - - // Registering Lelantus Model Adapters - Hive.registerAdapter(LelantusCoinAdapter()); - } - final wallets = await Hive.openBox('wallets'); await wallets.put('currentWalletName', testWalletName); }); @@ -1202,13 +1191,33 @@ void main() { txHash: BuildMintTxTestParams.utxoInfo["txid"] as String, coin: Coin.firo, )).thenAnswer((_) async => BuildMintTxTestParams.cachedClientResponse); + when(cachedClient.getAnonymitySet( + groupId: "1", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); + when(cachedClient.getAnonymitySet( + groupId: "2", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); when(client.getBlockHeadTip()).thenAnswer( (_) async => {"height": 455873, "hex": "this value not used here"}); + when(client.getLatestCoinId()).thenAnswer((_) async => 2); when(mainDB.getAddress("${testWalletId}buildMintTransaction", any)) .thenAnswer((realInvocation) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "${testWalletId}submitHexToNetwork")) + .thenAnswer((_) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "testWalletIDbuildMintTransaction")) + .thenAnswer((_) async => null); + final firo = FiroWallet( walletName: testWalletName, walletId: "${testWalletId}buildMintTransaction", diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 95d1750e9..9548981a4 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -856,7 +856,7 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); @override - _i5.Future updateUTXOs( + _i5.Future updateUTXOs( String? walletId, List<_i12.UTXO>? utxos, ) => @@ -868,9 +868,9 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { utxos, ], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Stream<_i12.UTXO?> watchUTXO({ required int? id, @@ -1151,4 +1151,14 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override + _i5.Future getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); } diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 8b48a38a9..5656feb88 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -14,7 +14,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i5; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4; import 'package:stackwallet/models/balance.dart' as _i6; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i13; -import 'package:stackwallet/models/lelantus_coin.dart' as _i15; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i3; import 'package:stackwallet/models/signing_data.dart' as _i14; import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i10; @@ -589,15 +588,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - List> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: >[], - ) as List>); - @override _i11.Future anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -755,15 +745,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i11.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i11.Future.value(), - ) as _i11.Future); - @override _i11.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( @@ -1061,51 +1042,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i11.Future firoUpdateJIndex(List? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - @override - _i11.Future firoUpdateLelantusCoins(List? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i11.Future firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); } /// A class which mocks [ElectrumX]. diff --git a/test/widget_tests/managed_favorite_test.mocks.dart b/test/widget_tests/managed_favorite_test.mocks.dart index ee7b22e84..723a76d30 100644 --- a/test/widget_tests/managed_favorite_test.mocks.dart +++ b/test/widget_tests/managed_favorite_test.mocks.dart @@ -2952,6 +2952,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/widget_tests/table_view/table_view_row_test.mocks.dart b/test/widget_tests/table_view/table_view_row_test.mocks.dart index cce7fd163..cfae6ecb9 100644 --- a/test/widget_tests/table_view/table_view_row_test.mocks.dart +++ b/test/widget_tests/table_view/table_view_row_test.mocks.dart @@ -2198,6 +2198,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 4fc6f8832..ad152959a 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -585,6 +585,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, @@ -1602,15 +1607,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - List> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: >[], - ) as List>); - @override _i19.Future anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -1768,15 +1764,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - _i19.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override _i19.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( @@ -2075,51 +2062,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future firoUpdateJIndex(List? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future firoUpdateLelantusCoins(List? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i19.Future firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); } /// A class which mocks [LocaleService]. @@ -3261,7 +3203,7 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - _i19.Future updateUTXOs( + _i19.Future updateUTXOs( String? walletId, List<_i22.UTXO>? utxos, ) => @@ -3275,7 +3217,7 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { ), returnValue: _i19.Future.value(), returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); + ) as _i19.Future); @override _i19.Stream<_i22.UTXO?> watchUTXO({ required int? id, @@ -3559,4 +3501,14 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { returnValue: _i19.Future.value(), returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); + @override + _i19.Future getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i19.Future.value(), + ) as _i19.Future); } diff --git a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart index 78483fb45..56c5d2485 100644 --- a/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart @@ -2307,6 +2307,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false, diff --git a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart index f0a2750fd..a234b0e9c 100644 --- a/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart +++ b/test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart @@ -2410,6 +2410,11 @@ class MockManager extends _i1.Mock implements _i6.Manager { returnValue: false, ) as bool); @override + bool get hasOrdinalsSupport => (super.noSuchMethod( + Invocation.getter(#hasOrdinalsSupport), + returnValue: false, + ) as bool); + @override bool get hasTokenSupport => (super.noSuchMethod( Invocation.getter(#hasTokenSupport), returnValue: false,