From 58c97de86c03f60c887a287f086db78ede971cf8 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 18 Jul 2023 10:15:05 -0600 Subject: [PATCH 001/128] WIP ordinal ui --- dockerfile.linux | 17 -- lib/models/ordinal.dart | 13 ++ lib/pages/ordinals/ordinal_details_view.dart | 116 +++++++++++ lib/pages/ordinals/ordinals_list.dart | 36 ++++ lib/pages/ordinals/ordinals_view.dart | 197 ++++++++++++++++++ lib/pages/ordinals/widgets/ordinal_card.dart | 55 +++++ lib/pages/wallet_view/wallet_view.dart | 17 ++ lib/route_generator.dart | 32 +++ .../coins/litecoin/litecoin_wallet.dart | 8 +- lib/services/coins/manager.dart | 3 + lib/services/mixins/ordinals_interface.dart | 3 + lib/services/ordinals_api.dart | 9 + lib/utilities/text_styles.dart | 22 ++ 13 files changed, 510 insertions(+), 18 deletions(-) delete mode 100644 dockerfile.linux create mode 100644 lib/models/ordinal.dart create mode 100644 lib/pages/ordinals/ordinal_details_view.dart create mode 100644 lib/pages/ordinals/ordinals_list.dart create mode 100644 lib/pages/ordinals/ordinals_view.dart create mode 100644 lib/pages/ordinals/widgets/ordinal_card.dart create mode 100644 lib/services/mixins/ordinals_interface.dart create mode 100644 lib/services/ordinals_api.dart 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/models/ordinal.dart b/lib/models/ordinal.dart new file mode 100644 index 000000000..9c4e15392 --- /dev/null +++ b/lib/models/ordinal.dart @@ -0,0 +1,13 @@ +class Ordinal { + final String name; + final String inscription; + final String rank; + + // TODO: make a proper class instead of this placeholder + + Ordinal({ + required this.name, + required this.inscription, + required this.rank, + }); +} diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart new file mode 100644 index 000000000..c39679903 --- /dev/null +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/models/ordinal.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'; + +class OrdinalDetailsView extends StatefulWidget { + const OrdinalDetailsView({ + super.key, + required this.walletId, + required this.ordinal, + }); + + final String walletId; + final Ordinal ordinal; + + static const routeName = "/ordinalDetailsView"; + + @override + State createState() => _OrdinalDetailsViewState(); +} + +class _OrdinalDetailsViewState extends State { + @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: Column(), + ), + ), + ); + } +} + +class _OrdinalImageGroup extends StatelessWidget { + const _OrdinalImageGroup({ + super.key, + required this.ordinal, + }); + + final Ordinal ordinal; + + static const _spacing = 12.0; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + ordinal.name, + style: STextStyles.w600_16(context), + ), + const SizedBox( + height: _spacing, + ), + AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.red, + ), + ), + const SizedBox( + height: _spacing, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Download", + icon: SvgPicture.asset(Assets.svg.arrowDown), + buttonHeight: ButtonHeight.s, + onPressed: () { + // TODO: save and download image to device + }, + ), + ), + const SizedBox( + width: _spacing, + ), + Expanded( + child: PrimaryButton( + label: "Send", + icon: SvgPicture.asset( + Assets.svg.star, + ), + buttonHeight: ButtonHeight.s, + onPressed: () { + // TODO: try send + }, + ), + ), + ], + ), + ], + ); + } +} diff --git a/lib/pages/ordinals/ordinals_list.dart b/lib/pages/ordinals/ordinals_list.dart new file mode 100644 index 000000000..d3b3cd83e --- /dev/null +++ b/lib/pages/ordinals/ordinals_list.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; + +class OrdinalsList extends StatefulWidget { + const OrdinalsList({ + super.key, + required this.ordinals, + }); + + final List ordinals; + + @override + State createState() => _OrdinalsListState(); +} + +class _OrdinalsListState extends State { + static const spacing = 10.0; + + @override + Widget build(BuildContext context) { + return GridView.builder( + shrinkWrap: true, + itemCount: widget.ordinals.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: spacing, + mainAxisSpacing: spacing, + crossAxisCount: 2, + childAspectRatio: 3 / 4, + ), + itemBuilder: (_, i) => OrdinalCard( + ordinal: widget.ordinals[i], + ), + ); + } +} diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart new file mode 100644 index 000000000..2bfa2e9f3 --- /dev/null +++ b/lib/pages/ordinals/ordinals_view.dart @@ -0,0 +1,197 @@ +/* + * 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/models/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_list.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.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/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_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: () { + // todo refresh + }, + ), + ), + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + size: 36, + icon: SvgPicture.asset( + Assets.svg.filter, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: () { + // todo filter view + }, + ), + ), + ], + ), + 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( + ordinals: [ + for (int i = 0; i < 13; i++) + Ordinal( + name: "dummy name $i", + inscription: "insc$i", + rank: "r$i", + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart new file mode 100644 index 000000000..2af610241 --- /dev/null +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class OrdinalCard extends StatelessWidget { + const OrdinalCard({ + super.key, + required this.walletId, + required this.ordinal, + }); + + final String walletId; + final Ordinal ordinal; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + radiusMultiplier: 2, + onPressed: () { + Navigator.of(context).pushNamed( + OrdinalDetailsView.routeName, + arguments: widget.walletId, + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.red, + child: const Center( + child: Text( + "replace red container with image", + ), + ), + ), + ), + const Spacer(), + Text( + ordinal.name, + style: STextStyles.w500_12(context), + ), + const Spacer(), + Text( + "INSC. ${ordinal.inscription} RANK ${ordinal.rank}", + style: STextStyles.w500_8(context), + ), + ], + ), + ); + } +} diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 3e7fb37fa..278d43d35 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -23,6 +23,7 @@ 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/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'; @@ -1007,6 +1008,22 @@ class _WalletViewState extends ConsumerState { } }, ), + if (ref.watch( + walletsChangeNotifierProvider.select( + (value) => + value.getManager(widget.walletId).hasOrdinalsSupport, + ), + )) + WalletNavigationBarItemData( + label: "Ordinals", + icon: const CoinControlNavIcon(), + onTap: () { + Navigator.of(context).pushNamed( + OrdinalsView.routeName, + arguments: widget.walletId, + ); + }, + ), ], ), ], diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7ccc378c9..cf82dce08 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -18,6 +18,7 @@ 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/isar_models.dart'; +import 'package:stackwallet/models/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'; @@ -57,6 +58,8 @@ 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/notification_views/notifications_view.dart'; +import 'package:stackwallet/pages/ordinals/ordinal_details_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'; @@ -404,6 +407,35 @@ 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 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 UtxoDetailsView.routeName: if (args is Tuple2) { return getRoute( diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index b84c6c6a0..2edc32fc6 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, 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/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart new file mode 100644 index 000000000..06f2377e9 --- /dev/null +++ b/lib/services/mixins/ordinals_interface.dart @@ -0,0 +1,3 @@ +mixin OrdinalsInterface { + // TODO wallet ordinals functionality +} diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart new file mode 100644 index 000000000..97cc45834 --- /dev/null +++ b/lib/services/ordinals_api.dart @@ -0,0 +1,9 @@ +import 'package:stackwallet/models/ordinal.dart'; + +class OrdinalsAPI { + // dummy class with sample functions to be changed / filled out + + static Future> fetch() async { + return []; + } +} 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: From 79670c5d478472610762c664b2403e259c247b3e Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 18 Jul 2023 11:03:57 -0600 Subject: [PATCH 002/128] WIP ordinal details view --- assets/svg/send.svg | 5 + lib/pages/ordinals/ordinal_details_view.dart | 148 ++++++++++++++++++- lib/pages/ordinals/ordinals_filter_view.dart | 0 lib/pages/ordinals/ordinals_list.dart | 3 + lib/pages/ordinals/ordinals_view.dart | 1 + lib/pages/ordinals/widgets/ordinal_card.dart | 2 +- lib/route_generator.dart | 3 + lib/utilities/assets.dart | 1 + pubspec.yaml | 1 + 9 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 assets/svg/send.svg create mode 100644 lib/pages/ordinals/ordinals_filter_view.dart 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/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index c39679903..a79847af7 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -1,6 +1,10 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -8,6 +12,7 @@ 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({ @@ -26,6 +31,8 @@ class OrdinalDetailsView extends StatefulWidget { } class _OrdinalDetailsViewState extends State { + static const _spacing = 12.0; + @override Widget build(BuildContext context) { return Background( @@ -42,19 +49,136 @@ class _OrdinalDetailsViewState extends State { style: STextStyles.navBarTitle(context), ), ), - body: Column(), + 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.inscription, + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Rank", + data: widget.ordinal.rank, + ), + const SizedBox( + height: _spacing, + ), + // todo: add utxo status + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Amount", + data: "FIXME", + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Owner address", + data: "FIXME", + ), + const SizedBox( + height: _spacing, + ), + _DetailsItemWCopy( + title: "Transaction ID", + data: "FIXME", + ), + const SizedBox( + height: _spacing, + ), + ], + ), + ), + ), ), ), ); } } +class _DetailsItemWCopy extends StatelessWidget { + const _DetailsItemWCopy({ + super.key, + required this.title, + required this.data, + }); + + 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({ super.key, + required this.walletId, required this.ordinal, }); + final String walletId; final Ordinal ordinal; static const _spacing = 12.0; @@ -86,8 +210,16 @@ class _OrdinalImageGroup extends StatelessWidget { Expanded( child: SecondaryButton( label: "Download", - icon: SvgPicture.asset(Assets.svg.arrowDown), - buttonHeight: ButtonHeight.s, + 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 }, @@ -100,9 +232,15 @@ class _OrdinalImageGroup extends StatelessWidget { child: PrimaryButton( label: "Send", icon: SvgPicture.asset( - Assets.svg.star, + Assets.svg.send, + width: 10, + height: 10, + color: Theme.of(context) + .extension()! + .buttonTextPrimary, ), - buttonHeight: ButtonHeight.s, + buttonHeight: ButtonHeight.l, + iconSpacing: 4, onPressed: () { // TODO: try send }, diff --git a/lib/pages/ordinals/ordinals_filter_view.dart b/lib/pages/ordinals/ordinals_filter_view.dart new file mode 100644 index 000000000..e69de29bb diff --git a/lib/pages/ordinals/ordinals_list.dart b/lib/pages/ordinals/ordinals_list.dart index d3b3cd83e..ca7c71446 100644 --- a/lib/pages/ordinals/ordinals_list.dart +++ b/lib/pages/ordinals/ordinals_list.dart @@ -5,9 +5,11 @@ import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; class OrdinalsList extends StatefulWidget { const OrdinalsList({ super.key, + required this.walletId, required this.ordinals, }); + final String walletId; final List ordinals; @override @@ -29,6 +31,7 @@ class _OrdinalsListState extends State { childAspectRatio: 3 / 4, ), itemBuilder: (_, i) => OrdinalCard( + walletId: widget.walletId, ordinal: widget.ordinals[i], ), ); diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 2bfa2e9f3..3eca289ef 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -177,6 +177,7 @@ class _OrdinalsViewState extends ConsumerState { ), Expanded( child: OrdinalsList( + walletId: widget.walletId, ordinals: [ for (int i = 0; i < 13; i++) Ordinal( diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index 2af610241..a3419ae87 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -21,7 +21,7 @@ class OrdinalCard extends StatelessWidget { onPressed: () { Navigator.of(context).pushNamed( OrdinalDetailsView.routeName, - arguments: widget.walletId, + arguments: (walletId: walletId, ordinal: ordinal), ); }, child: Column( diff --git a/lib/route_generator.dart b/lib/route_generator.dart index cf82dce08..a4239dfdd 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -422,6 +422,9 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case OrdinalDetailsView.routeName: + + print(args.runtimeType); + if (args is ({Ordinal ordinal, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index b8ec501e8..bb3054325 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -205,6 +205,7 @@ 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 trocadorRatingA => "assets/svg/trocador_rating_a.svg"; String get trocadorRatingB => "assets/svg/trocador_rating_b.svg"; diff --git a/pubspec.yaml b/pubspec.yaml index fd5eac1dc..ca8289bc8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -335,6 +335,7 @@ flutter: - assets/svg/trocador_rating_b.svg - assets/svg/trocador_rating_c.svg - assets/svg/trocador_rating_d.svg + - assets/svg/send.svg # coin control icons - assets/svg/coin_control/ From 8b71fa70c9fcf0bac38e44a3378527c88a90236e Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 18 Jul 2023 11:26:43 -0600 Subject: [PATCH 003/128] added a couple dialogs --- lib/pages/ordinals/ordinal_details_view.dart | 11 +++- lib/pages/ordinals/ordinals_view.dart | 2 +- lib/pages/ordinals/widgets/dialogs.dart | 62 +++++++++++++++++++ .../ordinals/{ => widgets}/ordinals_list.dart | 0 4 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 lib/pages/ordinals/widgets/dialogs.dart rename lib/pages/ordinals/{ => widgets}/ordinals_list.dart (100%) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index a79847af7..2057e8792 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/models/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'; @@ -241,8 +242,14 @@ class _OrdinalImageGroup extends StatelessWidget { ), buttonHeight: ButtonHeight.l, iconSpacing: 4, - onPressed: () { - // TODO: try send + 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_view.dart b/lib/pages/ordinals/ordinals_view.dart index 3eca289ef..c45974281 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -12,7 +12,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/ordinal.dart'; -import 'package:stackwallet/pages/ordinals/ordinals_list.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; 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/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart similarity index 100% rename from lib/pages/ordinals/ordinals_list.dart rename to lib/pages/ordinals/widgets/ordinals_list.dart From 2af1a8db88a49e7bae0d63381f60f34129ef00d2 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 18 Jul 2023 11:48:15 -0600 Subject: [PATCH 004/128] ordinals filter view --- lib/pages/ordinals/ordinals_filter_view.dart | 890 +++++++++++++++++++ lib/pages/ordinals/ordinals_view.dart | 5 +- lib/route_generator.dart | 10 +- 3 files changed, 901 insertions(+), 4 deletions(-) diff --git a/lib/pages/ordinals/ordinals_filter_view.dart b/lib/pages/ordinals/ordinals_filter_view.dart index e69de29bb..e06294098 100644 --- a/lib/pages/ordinals/ordinals_filter_view.dart +++ b/lib/pages/ordinals/ordinals_filter_view.dart @@ -0,0 +1,890 @@ +/* + * 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/rounded_white_container.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 index c45974281..fe60b5e28 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -12,6 +12,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -104,7 +105,9 @@ class _OrdinalsViewState extends ConsumerState { .topNavIconPrimary, ), onPressed: () { - // todo filter view + Navigator.of(context).pushNamed( + OrdinalsFilterView.routeName, + ); }, ), ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index a4239dfdd..6217cf201 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -59,6 +59,7 @@ import 'package:stackwallet/pages/intro_view.dart'; import 'package:stackwallet/pages/manage_favorites_view/manage_favorites_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'; @@ -422,9 +423,6 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case OrdinalDetailsView.routeName: - - print(args.runtimeType); - if (args is ({Ordinal ordinal, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, @@ -439,6 +437,12 @@ class RouteGenerator { } 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( From 5b4a80321536b4f4f6a86c96dd666e0e15226d0a Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 18 Jul 2023 11:51:57 -0600 Subject: [PATCH 005/128] add ordinals icon --- assets/svg/ordinal.svg | 12 ++++++++ lib/pages/wallet_view/wallet_view.dart | 3 +- lib/utilities/assets.dart | 1 + .../components/icons/ordinals_nav_icon.dart | 28 +++++++++++++++++++ pubspec.yaml | 1 + 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 assets/svg/ordinal.svg create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/ordinals_nav_icon.dart 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/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 278d43d35..3e51d25a9 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -73,6 +73,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'; @@ -1016,7 +1017,7 @@ class _WalletViewState extends ConsumerState { )) WalletNavigationBarItemData( label: "Ordinals", - icon: const CoinControlNavIcon(), + icon: const OrdinalsNavIcon(), onTap: () { Navigator.of(context).pushNamed( OrdinalsView.routeName, diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index bb3054325..2686c1463 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -206,6 +206,7 @@ class _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/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/pubspec.yaml b/pubspec.yaml index ca8289bc8..e68cae35f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -336,6 +336,7 @@ flutter: - assets/svg/trocador_rating_c.svg - assets/svg/trocador_rating_d.svg - assets/svg/send.svg + - assets/svg/ordinal.svg # coin control icons - assets/svg/coin_control/ From b60e3fbf1bc58554207f0b27c1b81f6feb47f194 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 18 Jul 2023 16:36:26 -0500 Subject: [PATCH 006/128] add dtos for ord-litecoin documented endpoint responses --- lib/dto/ordinals/address_response.dart | 48 +++++++++++ lib/dto/ordinals/block_response.dart | 58 +++++++++++++ lib/dto/ordinals/content_response.dart | 19 +++++ lib/dto/ordinals/feed_response.dart | 14 ++++ lib/dto/ordinals/inscription_response.dart | 95 ++++++++++++++++++++++ lib/dto/ordinals/output_response | 42 ++++++++++ lib/dto/ordinals/preview_response.dart | 19 +++++ lib/dto/ordinals/sat_response.dart | 82 +++++++++++++++++++ lib/dto/ordinals/transaction_response.dart | 78 ++++++++++++++++++ 9 files changed, 455 insertions(+) create mode 100644 lib/dto/ordinals/address_response.dart create mode 100644 lib/dto/ordinals/block_response.dart create mode 100644 lib/dto/ordinals/content_response.dart create mode 100644 lib/dto/ordinals/feed_response.dart create mode 100644 lib/dto/ordinals/inscription_response.dart create mode 100644 lib/dto/ordinals/output_response create mode 100644 lib/dto/ordinals/preview_response.dart create mode 100644 lib/dto/ordinals/sat_response.dart create mode 100644 lib/dto/ordinals/transaction_response.dart diff --git a/lib/dto/ordinals/address_response.dart b/lib/dto/ordinals/address_response.dart new file mode 100644 index 000000000..024543b92 --- /dev/null +++ b/lib/dto/ordinals/address_response.dart @@ -0,0 +1,48 @@ +class AddressResponse { + final AddressLinks links; + final String address; + final List inscriptions; + + AddressResponse({ + required this.links, + required this.address, + required this.inscriptions, + }); + + factory AddressResponse.fromJson(Map json) { + final inscriptionsJson = json['inscriptions'] as List; + final inscriptions = inscriptionsJson + .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson)) + .toList(); + + return AddressResponse( + links: AddressLinks.fromJson(json['_links']), + address: json['address'], + inscriptions: inscriptions, + ); + } +} + +class AddressLinks { + final AddressLink? self; + + AddressLinks({ + this.self, + }); + + factory AddressLinks.fromJson(Map json) { + return AddressLinks( + self: AddressLink.fromJson(json['self']), + ); + } +} + +class AddressLink { + final String href; + + AddressLink({required this.href}); + + factory AddressLink.fromJson(Map json) { + return AddressLink(href: json['href']); + } +} diff --git a/lib/dto/ordinals/block_response.dart b/lib/dto/ordinals/block_response.dart new file mode 100644 index 000000000..365098594 --- /dev/null +++ b/lib/dto/ordinals/block_response.dart @@ -0,0 +1,58 @@ +class BlockResponse { + final BlockLinks links; + final String hash; + final String previousBlockhash; + final int size; + final String target; + final String timestamp; + final int weight; + + BlockResponse({ + required this.links, + required this.hash, + required this.previousBlockhash, + required this.size, + required this.target, + required this.timestamp, + required this.weight, + }); + + factory BlockResponse.fromJson(Map json) { + return BlockResponse( + links: BlockLinks.fromJson(json['_links']), + hash: json['hash'], + previousBlockhash: json['previous_blockhash'], + size: json['size'], + target: json['target'], + timestamp: json['timestamp'], + weight: json['weight'], + ); + } +} + +class BlockLinks { + final BlockLink? prev; + final BlockLink? self; + + BlockLinks({ + this.prev, + this.self, + }); + + factory BlockLinks.fromJson(Map json) { + return BlockLinks( + prev: BlockLink.fromJson(json['prev']), + self: BlockLink.fromJson(json['self']), + ); + } +} + +class BlockLink { + final String href; + + BlockLink({required this.href}); + + factory BlockLink.fromJson(Map json) { + return BlockLink(href: json['href']); + } +} diff --git a/lib/dto/ordinals/content_response.dart b/lib/dto/ordinals/content_response.dart new file mode 100644 index 000000000..35a2c8ef8 --- /dev/null +++ b/lib/dto/ordinals/content_response.dart @@ -0,0 +1,19 @@ +class ContentResponse { + final FileLink fileLink; + + ContentResponse({required this.fileLink}); + + factory ContentResponse.fromJson(Map json) { + return ContentResponse(fileLink: FileLink.fromJson(json['_links']['file'])); + } +} + +class FileLink { + final String href; + + FileLink({required this.href}); + + factory FileLink.fromJson(Map json) { + return FileLink(href: json['href']); + } +} diff --git a/lib/dto/ordinals/feed_response.dart b/lib/dto/ordinals/feed_response.dart new file mode 100644 index 000000000..b6af1c1ca --- /dev/null +++ b/lib/dto/ordinals/feed_response.dart @@ -0,0 +1,14 @@ +class FeedResponse { + final List inscriptions; + + FeedResponse(this.inscriptions); + + factory FeedResponse.fromJson(Map json) { + final inscriptionsJson = json['_links']['inscriptions'] as List; + final inscriptions = inscriptionsJson + .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson)) + .toList(); + + return FeedResponse(inscriptions); + } +} diff --git a/lib/dto/ordinals/inscription_response.dart b/lib/dto/ordinals/inscription_response.dart new file mode 100644 index 000000000..e06ddca6d --- /dev/null +++ b/lib/dto/ordinals/inscription_response.dart @@ -0,0 +1,95 @@ +class InscriptionResponse { + final InscriptionLinks links; + final String address; + final int contentLength; + final String contentType; + final int genesisFee; + final int genesisHeight; + final String genesisTransaction; + final String location; + final int number; + final int offset; + final String output; + final dynamic sat; // Change to appropriate type if available + final String timestamp; + + InscriptionResponse({ + required this.links, + required this.address, + required this.contentLength, + required this.contentType, + required this.genesisFee, + required this.genesisHeight, + required this.genesisTransaction, + required this.location, + required this.number, + required this.offset, + required this.output, + required this.sat, + required this.timestamp, + }); + + factory InscriptionResponse.fromJson(Map json) { + return InscriptionResponse( + links: InscriptionLinks.fromJson(json['_links']), + address: json['address'], + contentLength: json['content_length'], + contentType: json['content_type'], + genesisFee: json['genesis_fee'], + genesisHeight: json['genesis_height'], + genesisTransaction: json['genesis_transaction'], + location: json['location'], + number: json['number'], + offset: json['offset'], + output: json['output'], + sat: json['sat'], + timestamp: json['timestamp'], + ); + } +} + +class InscriptionLinks { + final InscriptionLink? content; + final InscriptionLink? genesisTransaction; + final InscriptionLink? next; + final InscriptionLink? output; + final InscriptionLink? prev; + final InscriptionLink? preview; + final InscriptionLink? sat; + final InscriptionLink? self; + + InscriptionLinks({ + this.content, + this.genesisTransaction, + this.next, + this.output, + this.prev, + this.preview, + this.sat, + this.self, + }); + + factory InscriptionLinks.fromJson(Map json) { + return InscriptionLinks( + content: InscriptionLink.fromJson(json['content']), + genesisTransaction: InscriptionLink.fromJson(json['genesis_transaction']), + next: InscriptionLink.fromJson(json['next']), + output: InscriptionLink.fromJson(json['output']), + prev: InscriptionLink.fromJson(json['prev']), + preview: InscriptionLink.fromJson(json['preview']), + sat: InscriptionLink.fromJson(json['sat']), + self: InscriptionLink.fromJson(json['self']), + ); + } +} + +class InscriptionLink { + final String href; + final String title; + + InscriptionLink({required this.href, required this.title}); + + factory InscriptionLink.fromJson(Map json) { + return InscriptionLink(href: json['href'], title: json['title']); + } +} diff --git a/lib/dto/ordinals/output_response b/lib/dto/ordinals/output_response new file mode 100644 index 000000000..cd25fe961 --- /dev/null +++ b/lib/dto/ordinals/output_response @@ -0,0 +1,42 @@ +class OutputResponse { + final OutputLinks links; + final String address; + final String scriptPubkey; + final String transaction; + final int value; + + OutputResponse({ + required this.links, + required this.address, + required this.scriptPubkey, + required this.transaction, + required this.value, + }); + + factory OutputResponse.fromJson(Map json) { + return OutputResponse( + links: OutputLinks.fromJson(json['_links']), + address: json['address'], + scriptPubkey: json['script_pubkey'], + transaction: json['transaction'], + value: json['value'], + ); + } +} + +class OutputLinks { + final OutputLink? self; + final TransactionLink? transaction; + + OutputLinks({ + this.self, + this.transaction, + }); + + factory OutputLinks.fromJson(Map json) { + return OutputLinks( + self: OutputLink.fromJson(json['self']), + transaction: TransactionLink.fromJson(json['transaction']), + ); + } +} \ No newline at end of file diff --git a/lib/dto/ordinals/preview_response.dart b/lib/dto/ordinals/preview_response.dart new file mode 100644 index 000000000..d7171702d --- /dev/null +++ b/lib/dto/ordinals/preview_response.dart @@ -0,0 +1,19 @@ +class PreviewResponse { + final ImageLink imageLink; + + PreviewResponse({required this.imageLink}); + + factory PreviewResponse.fromJson(Map json) { + return PreviewResponse(imageLink: ImageLink.fromJson(json['_links']['image'])); + } +} + +class ImageLink { + final String href; + + ImageLink({required this.href}); + + factory ImageLink.fromJson(Map json) { + return ImageLink(href: json['href']); + } +} \ No newline at end of file diff --git a/lib/dto/ordinals/sat_response.dart b/lib/dto/ordinals/sat_response.dart new file mode 100644 index 000000000..0f88918f3 --- /dev/null +++ b/lib/dto/ordinals/sat_response.dart @@ -0,0 +1,82 @@ +class SatResponse { + final SatLinks links; + final int block; + final int cycle; + final String decimal; + final String degree; + final int epoch; + final String name; + final int offset; + final String percentile; + final int period; + final String rarity; + final String timestamp; + + SatResponse({ + required this.links, + required this.block, + required this.cycle, + required this.decimal, + required this.degree, + required this.epoch, + required this.name, + required this.offset, + required this.percentile, + required this.period, + required this.rarity, + required this.timestamp, + }); + + factory SatResponse.fromJson(Map json) { + return SatResponse( + links: SatLinks.fromJson(json['_links']), + block: json['block'], + cycle: json['cycle'], + decimal: json['decimal'], + degree: json['degree'], + epoch: json['epoch'], + name: json['name'], + offset: json['offset'], + percentile: json['percentile'], + period: json['period'], + rarity: json['rarity'], + timestamp: json['timestamp'], + ); + } +} + +class SatLinks { + final SatLink? block; + final SatLink? inscription; + final SatLink? next; + final SatLink? prev; + final SatLink? self; + + SatLinks({ + this.block, + this.inscription, + this.next, + this.prev, + this.self, + }); + + factory SatLinks.fromJson(Map json) { + return SatLinks( + block: SatLink.fromJson(json['block']), + inscription: SatLink.fromJson(json['inscription']), + next: SatLink.fromJson(json['next']), + prev: SatLink.fromJson(json['prev']), + self: SatLink.fromJson(json['self']), + ); + } +} + +class SatLink { + final String href; + + SatLink({required this.href}); + + factory SatLink.fromJson(Map json) { + return SatLink(href: json['href']); + } +} diff --git a/lib/dto/ordinals/transaction_response.dart b/lib/dto/ordinals/transaction_response.dart new file mode 100644 index 000000000..b030a1daa --- /dev/null +++ b/lib/dto/ordinals/transaction_response.dart @@ -0,0 +1,78 @@ +class TransactionResponse { + final TransactionLinks links; + final List inputs; + final InscriptionLink inscription; + final List outputs; + final TransactionLink self; + final String transaction; + + TransactionResponse({ + required this.links, + required this.inputs, + required this.inscription, + required this.outputs, + required this.self, + required this.transaction, + }); + + factory TransactionResponse.fromJson(Map json) { + final inputsJson = json['_links']['inputs'] as List; + final inputs = inputsJson + .map((inputJson) => OutputLink.fromJson(inputJson)) + .toList(); + + final outputsJson = json['_links']['outputs'] as List; + final outputs = outputsJson + .map((outputJson) => OutputLink.fromJson(outputJson)) + .toList(); + + return TransactionResponse( + links: TransactionLinks.fromJson(json['_links']), + inputs: inputs, + inscription: InscriptionLink.fromJson(json['_links']['inscription']), + outputs: outputs, + self: TransactionLink.fromJson(json['_links']['self']), + transaction: json['transaction'], + ); + } +} + +class TransactionLinks { + final TransactionLink? block; + final InscriptionLink? inscription; + final TransactionLink? self; + + TransactionLinks({ + this.block, + this.inscription, + this.self, + }); + + factory TransactionLinks.fromJson(Map json) { + return TransactionLinks( + block: TransactionLink.fromJson(json['block']), + inscription: InscriptionLink.fromJson(json['inscription']), + self: TransactionLink.fromJson(json['self']), + ); + } +} + +class TransactionLink { + final String href; + + TransactionLink({required this.href}); + + factory TransactionLink.fromJson(Map json) { + return TransactionLink(href: json['href']); + } +} + +class OutputLink { + final String href; + + OutputLink({required this.href}); + + factory OutputLink.fromJson(Map json) { + return OutputLink(href: json['href']); + } +} From 51155372d30ca33ccccd1b66c9370da9bb6e012c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:37:42 -0500 Subject: [PATCH 007/128] add inscription endpoint --- lib/dto/ordinals/feed_response.dart | 24 +++- lib/dto/ordinals/inscription_response.dart | 123 ++++++++++----------- lib/services/ordinals_api.dart | 38 ++++++- 3 files changed, 112 insertions(+), 73 deletions(-) diff --git a/lib/dto/ordinals/feed_response.dart b/lib/dto/ordinals/feed_response.dart index b6af1c1ca..d958fbe4c 100644 --- a/lib/dto/ordinals/feed_response.dart +++ b/lib/dto/ordinals/feed_response.dart @@ -1,14 +1,28 @@ class FeedResponse { final List inscriptions; - FeedResponse(this.inscriptions); + FeedResponse({required this.inscriptions}); factory FeedResponse.fromJson(Map json) { - final inscriptionsJson = json['_links']['inscriptions'] as List; - final inscriptions = inscriptionsJson - .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson)) + final List inscriptionsJson = json['_links']['inscriptions'] as List; + final List inscriptions = inscriptionsJson + .map((json) => InscriptionLink.fromJson(json as Map)) .toList(); - return FeedResponse(inscriptions); + return FeedResponse(inscriptions: inscriptions); + } +} + +class InscriptionLink { + final String href; + final String title; + + InscriptionLink({required this.href, required this.title}); + + factory InscriptionLink.fromJson(Map json) { + return InscriptionLink( + href: json['href'] as String ?? '', + title: json['title'] as String ?? '', + ); } } diff --git a/lib/dto/ordinals/inscription_response.dart b/lib/dto/ordinals/inscription_response.dart index e06ddca6d..ab093b750 100644 --- a/lib/dto/ordinals/inscription_response.dart +++ b/lib/dto/ordinals/inscription_response.dart @@ -1,17 +1,17 @@ class InscriptionResponse { - final InscriptionLinks links; - final String address; - final int contentLength; - final String contentType; - final int genesisFee; - final int genesisHeight; - final String genesisTransaction; - final String location; - final int number; - final int offset; - final String output; - final dynamic sat; // Change to appropriate type if available - final String timestamp; + late final Links links; + late final String address; + late final int contentLength; + late final String contentType; + late final int genesisFee; + late final int genesisHeight; + late final String genesisTransaction; + late final String location; + late final int number; + late final int offset; + late final String output; + late final String? sat; // Make sure to update the type to allow null + late final String timestamp; InscriptionResponse({ required this.links, @@ -29,67 +29,62 @@ class InscriptionResponse { required this.timestamp, }); - factory InscriptionResponse.fromJson(Map json) { - return InscriptionResponse( - links: InscriptionLinks.fromJson(json['_links']), - address: json['address'], - contentLength: json['content_length'], - contentType: json['content_type'], - genesisFee: json['genesis_fee'], - genesisHeight: json['genesis_height'], - genesisTransaction: json['genesis_transaction'], - location: json['location'], - number: json['number'], - offset: json['offset'], - output: json['output'], - sat: json['sat'], - timestamp: json['timestamp'], - ); + InscriptionResponse.fromJson(Map json) { + links = Links.fromJson(json['_links'] as Map); + address = json['address'] as String; + contentLength = json['content_length'] as int; + contentType = json['content_type'] as String; + genesisFee = json['genesis_fee'] as int; + genesisHeight = json['genesis_height'] as int; + genesisTransaction = json['genesis_transaction'] as String; + location = json['location'] as String; + number = json['number'] as int; + offset = json['offset'] as int; + output = json['output'] as String; + sat = json['sat'] as String?; + timestamp = json['timestamp'] as String; } } -class InscriptionLinks { - final InscriptionLink? content; - final InscriptionLink? genesisTransaction; - final InscriptionLink? next; - final InscriptionLink? output; - final InscriptionLink? prev; - final InscriptionLink? preview; - final InscriptionLink? sat; - final InscriptionLink? self; +class Links { + late final Link content; + late final Link genesisTransaction; + late final Link next; + late final Link output; + late final Link prev; + late final Link preview; + late final Link? sat; // Make sure to update the type to allow null + late final Link self; - InscriptionLinks({ - this.content, - this.genesisTransaction, - this.next, - this.output, - this.prev, - this.preview, + Links({ + required this.content, + required this.genesisTransaction, + required this.next, + required this.output, + required this.prev, + required this.preview, this.sat, - this.self, + required this.self, }); - factory InscriptionLinks.fromJson(Map json) { - return InscriptionLinks( - content: InscriptionLink.fromJson(json['content']), - genesisTransaction: InscriptionLink.fromJson(json['genesis_transaction']), - next: InscriptionLink.fromJson(json['next']), - output: InscriptionLink.fromJson(json['output']), - prev: InscriptionLink.fromJson(json['prev']), - preview: InscriptionLink.fromJson(json['preview']), - sat: InscriptionLink.fromJson(json['sat']), - self: InscriptionLink.fromJson(json['self']), - ); + Links.fromJson(Map json) { + content = Link.fromJson(json['content'] as Map); + genesisTransaction = Link.fromJson(json['genesis_transaction'] as Map); + next = Link.fromJson(json['next'] as Map); + output = Link.fromJson(json['output'] as Map); + prev = Link.fromJson(json['prev'] as Map); + preview = Link.fromJson(json['preview'] as Map); + sat = json['sat'] != null ? Link.fromJson(json['sat'] as Map) : null; + self = Link.fromJson(json['self'] as Map); } } -class InscriptionLink { - final String href; - final String title; +class Link { + late final String href; - InscriptionLink({required this.href, required this.title}); + Link({required this.href}); - factory InscriptionLink.fromJson(Map json) { - return InscriptionLink(href: json['href'], title: json['title']); + Link.fromJson(Map json) { + href = json['href'] as String; } -} +} \ No newline at end of file diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index 97cc45834..a966988d7 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -1,9 +1,39 @@ -import 'package:stackwallet/models/ordinal.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:stackwallet/dto/ordinals/feed_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_response.dart'; class OrdinalsAPI { - // dummy class with sample functions to be changed / filled out + final String baseUrl; - static Future> fetch() async { - return []; + OrdinalsAPI({required this.baseUrl}); + + Future> _getResponse(String endpoint) async { + final response = await http.get(Uri.parse('$baseUrl$endpoint')); + if (response.statusCode == 200) { + return _validateJson(response.body); + } else { + throw Exception('Failed to load data'); + } + } + + Map _validateJson(String responseBody) { + final parsed = jsonDecode(responseBody); + if (parsed is Map) { + return parsed; + } else { + throw const FormatException('Invalid JSON format'); + } + } + + Future getLatestInscriptions() async { + final response = await _getResponse('/feed'); + return FeedResponse.fromJson(response); + } + + Future getInscriptionDetails(String inscriptionId) async { + final response = await _getResponse('/inscription/$inscriptionId'); + return InscriptionResponse.fromJson(response); } } From 828782f00cd120125a9996cb9666aa8ed0c075f2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:41:23 -0500 Subject: [PATCH 008/128] add sat endpoint --- lib/dto/ordinals/sat_response.dart | 36 +++++++++++++++--------------- lib/services/ordinals_api.dart | 6 +++++ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/dto/ordinals/sat_response.dart b/lib/dto/ordinals/sat_response.dart index 0f88918f3..b57ccb3cf 100644 --- a/lib/dto/ordinals/sat_response.dart +++ b/lib/dto/ordinals/sat_response.dart @@ -29,18 +29,18 @@ class SatResponse { factory SatResponse.fromJson(Map json) { return SatResponse( - links: SatLinks.fromJson(json['_links']), - block: json['block'], - cycle: json['cycle'], - decimal: json['decimal'], - degree: json['degree'], - epoch: json['epoch'], - name: json['name'], - offset: json['offset'], - percentile: json['percentile'], - period: json['period'], - rarity: json['rarity'], - timestamp: json['timestamp'], + links: SatLinks.fromJson(json['_links'] as Map), + block: json['block'] as int, + cycle: json['cycle'] as int, + decimal: json['decimal'] as String, + degree: json['degree'] as String, + epoch: json['epoch'] as int, + name: json['name'] as String, + offset: json['offset'] as int, + percentile: json['percentile'] as String, + period: json['period'] as int, + rarity: json['rarity'] as String, + timestamp: json['timestamp'] as String, ); } } @@ -62,11 +62,11 @@ class SatLinks { factory SatLinks.fromJson(Map json) { return SatLinks( - block: SatLink.fromJson(json['block']), - inscription: SatLink.fromJson(json['inscription']), - next: SatLink.fromJson(json['next']), - prev: SatLink.fromJson(json['prev']), - self: SatLink.fromJson(json['self']), + block: SatLink.fromJson(json['block'] as Map), + inscription: SatLink.fromJson(json['inscription'] as Map), + next: SatLink.fromJson(json['next'] as Map), + prev: SatLink.fromJson(json['prev'] as Map), + self: SatLink.fromJson(json['self'] as Map), ); } } @@ -77,6 +77,6 @@ class SatLink { SatLink({required this.href}); factory SatLink.fromJson(Map json) { - return SatLink(href: json['href']); + return SatLink(href: json['href'] as String); } } diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index a966988d7..264223f8a 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -3,6 +3,7 @@ import 'package:http/http.dart' as http; import 'package:stackwallet/dto/ordinals/feed_response.dart'; import 'package:stackwallet/dto/ordinals/inscription_response.dart'; +import 'package:stackwallet/dto/ordinals/sat_response.dart'; class OrdinalsAPI { final String baseUrl; @@ -36,4 +37,9 @@ class OrdinalsAPI { final response = await _getResponse('/inscription/$inscriptionId'); return InscriptionResponse.fromJson(response); } + + Future getSatDetails(int satNumber) async { + final response = await _getResponse('/sat/$satNumber'); + return SatResponse.fromJson(response); + } } From 48e4f8c57790bd790fd194069f55f9e94d59e169 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:45:18 -0500 Subject: [PATCH 009/128] add output endpoint --- .../{output_response => output_response.dart} | 16 +++++++++------- lib/services/ordinals_api.dart | 12 ++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) rename lib/dto/ordinals/{output_response => output_response.dart} (57%) diff --git a/lib/dto/ordinals/output_response b/lib/dto/ordinals/output_response.dart similarity index 57% rename from lib/dto/ordinals/output_response rename to lib/dto/ordinals/output_response.dart index cd25fe961..6faf40845 100644 --- a/lib/dto/ordinals/output_response +++ b/lib/dto/ordinals/output_response.dart @@ -1,3 +1,5 @@ +import 'package:stackwallet/dto/ordinals/transaction_response.dart'; + class OutputResponse { final OutputLinks links; final String address; @@ -15,11 +17,11 @@ class OutputResponse { factory OutputResponse.fromJson(Map json) { return OutputResponse( - links: OutputLinks.fromJson(json['_links']), - address: json['address'], - scriptPubkey: json['script_pubkey'], - transaction: json['transaction'], - value: json['value'], + links: OutputLinks.fromJson(json['_links'] as Map), + address: json['address'] as String, + scriptPubkey: json['script_pubkey'] as String, + transaction: json['transaction'] as String, + value: json['value'] as int, ); } } @@ -35,8 +37,8 @@ class OutputLinks { factory OutputLinks.fromJson(Map json) { return OutputLinks( - self: OutputLink.fromJson(json['self']), - transaction: TransactionLink.fromJson(json['transaction']), + self: OutputLink.fromJson(json['self'] as Map), + transaction: TransactionLink.fromJson(json['transaction'] as Map), ); } } \ No newline at end of file diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index 264223f8a..e83e9eb9e 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -4,6 +4,8 @@ import 'package:http/http.dart' as http; import 'package:stackwallet/dto/ordinals/feed_response.dart'; import 'package:stackwallet/dto/ordinals/inscription_response.dart'; import 'package:stackwallet/dto/ordinals/sat_response.dart'; +import 'package:stackwallet/dto/ordinals/transaction_response.dart'; +import 'package:stackwallet/dto/ordinals/output_response.dart'; class OrdinalsAPI { final String baseUrl; @@ -42,4 +44,14 @@ class OrdinalsAPI { final response = await _getResponse('/sat/$satNumber'); return SatResponse.fromJson(response); } + + Future getTransaction(String transactionId) async { + final response = await _getResponse('/tx/$transactionId'); + return TransactionResponse.fromJson(response); + } + + Future getTransactionOutputs(String transactionId) async { + final response = await _getResponse('/output/$transactionId'); + return OutputResponse.fromJson(response); + } } From f972c34dc4eb0a7f19627da3b4ecd0e375bce8a9 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:48:54 -0500 Subject: [PATCH 010/128] add address endpoint --- lib/dto/ordinals/address_response.dart | 23 ++++++++++++++++++----- lib/dto/ordinals/output_response.dart | 2 +- lib/services/ordinals_api.dart | 6 ++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/dto/ordinals/address_response.dart b/lib/dto/ordinals/address_response.dart index 024543b92..7932d8ec7 100644 --- a/lib/dto/ordinals/address_response.dart +++ b/lib/dto/ordinals/address_response.dart @@ -1,3 +1,5 @@ +import 'package:stackwallet/dto/ordinals/inscription_response.dart'; + class AddressResponse { final AddressLinks links; final String address; @@ -12,12 +14,12 @@ class AddressResponse { factory AddressResponse.fromJson(Map json) { final inscriptionsJson = json['inscriptions'] as List; final inscriptions = inscriptionsJson - .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson)) + .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson as Map)) .toList(); return AddressResponse( - links: AddressLinks.fromJson(json['_links']), - address: json['address'], + links: AddressLinks.fromJson(json['_links'] as Map), + address: json['address'] as String, inscriptions: inscriptions, ); } @@ -32,7 +34,7 @@ class AddressLinks { factory AddressLinks.fromJson(Map json) { return AddressLinks( - self: AddressLink.fromJson(json['self']), + self: AddressLink.fromJson(json['self'] as Map), ); } } @@ -43,6 +45,17 @@ class AddressLink { AddressLink({required this.href}); factory AddressLink.fromJson(Map json) { - return AddressLink(href: json['href']); + return AddressLink(href: json['href'] as String); + } +} + +class InscriptionLink { + final String href; + final String title; + + InscriptionLink(this.href, this.title); + + factory InscriptionLink.fromJson(Map json) { + return InscriptionLink(json['href'] as String, json['title'] as String); } } diff --git a/lib/dto/ordinals/output_response.dart b/lib/dto/ordinals/output_response.dart index 6faf40845..cd4bde563 100644 --- a/lib/dto/ordinals/output_response.dart +++ b/lib/dto/ordinals/output_response.dart @@ -41,4 +41,4 @@ class OutputLinks { transaction: TransactionLink.fromJson(json['transaction'] as Map), ); } -} \ No newline at end of file +} diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index e83e9eb9e..a0586d1ec 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -6,6 +6,7 @@ import 'package:stackwallet/dto/ordinals/inscription_response.dart'; import 'package:stackwallet/dto/ordinals/sat_response.dart'; import 'package:stackwallet/dto/ordinals/transaction_response.dart'; import 'package:stackwallet/dto/ordinals/output_response.dart'; +import 'package:stackwallet/dto/ordinals/address_response.dart'; class OrdinalsAPI { final String baseUrl; @@ -54,4 +55,9 @@ class OrdinalsAPI { final response = await _getResponse('/output/$transactionId'); return OutputResponse.fromJson(response); } + + Future getInscriptionsByAddress(String address) async { + final response = await _getResponse('/address/$address'); + return AddressResponse.fromJson(response); + } } From 6d772b0acdabdd19f0ed66663528a9a2912eb165 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:50:56 -0500 Subject: [PATCH 011/128] add block endpoint --- lib/dto/ordinals/block_response.dart | 20 ++++++++++---------- lib/services/ordinals_api.dart | 6 ++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/dto/ordinals/block_response.dart b/lib/dto/ordinals/block_response.dart index 365098594..a3de1ee1c 100644 --- a/lib/dto/ordinals/block_response.dart +++ b/lib/dto/ordinals/block_response.dart @@ -19,13 +19,13 @@ class BlockResponse { factory BlockResponse.fromJson(Map json) { return BlockResponse( - links: BlockLinks.fromJson(json['_links']), - hash: json['hash'], - previousBlockhash: json['previous_blockhash'], - size: json['size'], - target: json['target'], - timestamp: json['timestamp'], - weight: json['weight'], + links: BlockLinks.fromJson(json['_links'] as Map), + hash: json['hash'] as String, + previousBlockhash: json['previous_blockhash'] as String, + size: json['size'] as int, + target: json['target'] as String, + timestamp: json['timestamp'] as String, + weight: json['weight'] as int, ); } } @@ -41,8 +41,8 @@ class BlockLinks { factory BlockLinks.fromJson(Map json) { return BlockLinks( - prev: BlockLink.fromJson(json['prev']), - self: BlockLink.fromJson(json['self']), + prev: BlockLink.fromJson(json['prev'] as Map), + self: BlockLink.fromJson(json['self'] as Map), ); } } @@ -53,6 +53,6 @@ class BlockLink { BlockLink({required this.href}); factory BlockLink.fromJson(Map json) { - return BlockLink(href: json['href']); + return BlockLink(href: json['href'] as String); } } diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index a0586d1ec..de9740554 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -7,6 +7,7 @@ import 'package:stackwallet/dto/ordinals/sat_response.dart'; import 'package:stackwallet/dto/ordinals/transaction_response.dart'; import 'package:stackwallet/dto/ordinals/output_response.dart'; import 'package:stackwallet/dto/ordinals/address_response.dart'; +import 'package:stackwallet/dto/ordinals/block_response.dart'; class OrdinalsAPI { final String baseUrl; @@ -60,4 +61,9 @@ class OrdinalsAPI { final response = await _getResponse('/address/$address'); return AddressResponse.fromJson(response); } + + Future getBlock(int blockNumber) async { + final response = await _getResponse('/block/$blockNumber'); + return BlockResponse.fromJson(response); + } } From d623480a758ee0a1acdfdc707ea503be03f314d2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:51:46 -0500 Subject: [PATCH 012/128] add content endpoint --- lib/dto/ordinals/content_response.dart | 4 ++-- lib/services/ordinals_api.dart | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/dto/ordinals/content_response.dart b/lib/dto/ordinals/content_response.dart index 35a2c8ef8..d1131950b 100644 --- a/lib/dto/ordinals/content_response.dart +++ b/lib/dto/ordinals/content_response.dart @@ -4,7 +4,7 @@ class ContentResponse { ContentResponse({required this.fileLink}); factory ContentResponse.fromJson(Map json) { - return ContentResponse(fileLink: FileLink.fromJson(json['_links']['file'])); + return ContentResponse(fileLink: FileLink.fromJson(json['_links']['file'] as Map)); } } @@ -14,6 +14,6 @@ class FileLink { FileLink({required this.href}); factory FileLink.fromJson(Map json) { - return FileLink(href: json['href']); + return FileLink(href: json['href'] as String); } } diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index de9740554..4228ddd72 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -8,6 +8,7 @@ import 'package:stackwallet/dto/ordinals/transaction_response.dart'; import 'package:stackwallet/dto/ordinals/output_response.dart'; import 'package:stackwallet/dto/ordinals/address_response.dart'; import 'package:stackwallet/dto/ordinals/block_response.dart'; +import 'package:stackwallet/dto/ordinals/content_response.dart'; class OrdinalsAPI { final String baseUrl; @@ -66,4 +67,9 @@ class OrdinalsAPI { final response = await _getResponse('/block/$blockNumber'); return BlockResponse.fromJson(response); } + + Future getInscriptionContent(String inscriptionId) async { + final response = await _getResponse('/content/$inscriptionId'); + return ContentResponse.fromJson(response); + } } From 697f40fce1835e1e6cd494fb6a99a02bf319b11d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 10:55:46 -0500 Subject: [PATCH 013/128] add preview endpoint --- lib/dto/ordinals/preview_response.dart | 4 ++-- lib/services/ordinals_api.dart | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/dto/ordinals/preview_response.dart b/lib/dto/ordinals/preview_response.dart index d7171702d..9eabd590a 100644 --- a/lib/dto/ordinals/preview_response.dart +++ b/lib/dto/ordinals/preview_response.dart @@ -4,7 +4,7 @@ class PreviewResponse { PreviewResponse({required this.imageLink}); factory PreviewResponse.fromJson(Map json) { - return PreviewResponse(imageLink: ImageLink.fromJson(json['_links']['image'])); + return PreviewResponse(imageLink: ImageLink.fromJson(json['_links']['image'] as Map)); } } @@ -14,6 +14,6 @@ class ImageLink { ImageLink({required this.href}); factory ImageLink.fromJson(Map json) { - return ImageLink(href: json['href']); + return ImageLink(href: json['href'] as String); } } \ No newline at end of file diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index 4228ddd72..4d8360e06 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -9,6 +9,7 @@ import 'package:stackwallet/dto/ordinals/output_response.dart'; import 'package:stackwallet/dto/ordinals/address_response.dart'; import 'package:stackwallet/dto/ordinals/block_response.dart'; import 'package:stackwallet/dto/ordinals/content_response.dart'; +import 'package:stackwallet/dto/ordinals/preview_response.dart'; class OrdinalsAPI { final String baseUrl; @@ -72,4 +73,9 @@ class OrdinalsAPI { final response = await _getResponse('/content/$inscriptionId'); return ContentResponse.fromJson(response); } + + Future getInscriptionPreview(String inscriptionId) async { + final response = await _getResponse('/preview/$inscriptionId'); + return PreviewResponse.fromJson(response); + } } From afbf818ab5815b07481bab41e1c1c27ffa704751 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 11:04:19 -0500 Subject: [PATCH 014/128] refactor InscriptionLink factory, casting TODO replace casting throughout ordinals DTOs with refactored validation --- lib/dto/ordinals/address_response.dart | 13 +----------- lib/dto/ordinals/feed_response.dart | 16 ++------------- lib/dto/ordinals/inscription_link.dart | 13 ++++++++++++ lib/dto/ordinals/transaction_response.dart | 24 ++++++++++++---------- 4 files changed, 29 insertions(+), 37 deletions(-) create mode 100644 lib/dto/ordinals/inscription_link.dart diff --git a/lib/dto/ordinals/address_response.dart b/lib/dto/ordinals/address_response.dart index 7932d8ec7..5c1f66323 100644 --- a/lib/dto/ordinals/address_response.dart +++ b/lib/dto/ordinals/address_response.dart @@ -1,4 +1,4 @@ -import 'package:stackwallet/dto/ordinals/inscription_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_link.dart'; class AddressResponse { final AddressLinks links; @@ -48,14 +48,3 @@ class AddressLink { return AddressLink(href: json['href'] as String); } } - -class InscriptionLink { - final String href; - final String title; - - InscriptionLink(this.href, this.title); - - factory InscriptionLink.fromJson(Map json) { - return InscriptionLink(json['href'] as String, json['title'] as String); - } -} diff --git a/lib/dto/ordinals/feed_response.dart b/lib/dto/ordinals/feed_response.dart index d958fbe4c..e29d2d334 100644 --- a/lib/dto/ordinals/feed_response.dart +++ b/lib/dto/ordinals/feed_response.dart @@ -1,3 +1,5 @@ +import 'package:stackwallet/dto/ordinals/inscription_link.dart'; + class FeedResponse { final List inscriptions; @@ -12,17 +14,3 @@ class FeedResponse { return FeedResponse(inscriptions: inscriptions); } } - -class InscriptionLink { - final String href; - final String title; - - InscriptionLink({required this.href, required this.title}); - - factory InscriptionLink.fromJson(Map json) { - return InscriptionLink( - href: json['href'] as String ?? '', - title: json['title'] as String ?? '', - ); - } -} diff --git a/lib/dto/ordinals/inscription_link.dart b/lib/dto/ordinals/inscription_link.dart new file mode 100644 index 000000000..f23b63248 --- /dev/null +++ b/lib/dto/ordinals/inscription_link.dart @@ -0,0 +1,13 @@ +class InscriptionLink { + final String href; + final String title; + + InscriptionLink({required this.href, required this.title}); + + factory InscriptionLink.fromJson(Map json) { + return InscriptionLink( + href: json['href'] as String ?? '', + title: json['title'] as String ?? '', + ); + } +} diff --git a/lib/dto/ordinals/transaction_response.dart b/lib/dto/ordinals/transaction_response.dart index b030a1daa..11912a3ad 100644 --- a/lib/dto/ordinals/transaction_response.dart +++ b/lib/dto/ordinals/transaction_response.dart @@ -1,3 +1,5 @@ +import 'package:stackwallet/dto/ordinals/inscription_link.dart'; + class TransactionResponse { final TransactionLinks links; final List inputs; @@ -18,21 +20,21 @@ class TransactionResponse { factory TransactionResponse.fromJson(Map json) { final inputsJson = json['_links']['inputs'] as List; final inputs = inputsJson - .map((inputJson) => OutputLink.fromJson(inputJson)) + .map((inputJson) => OutputLink.fromJson(inputJson as Map)) .toList(); final outputsJson = json['_links']['outputs'] as List; final outputs = outputsJson - .map((outputJson) => OutputLink.fromJson(outputJson)) + .map((outputJson) => OutputLink.fromJson(outputJson as Map)) .toList(); return TransactionResponse( - links: TransactionLinks.fromJson(json['_links']), + links: TransactionLinks.fromJson(json['_links'] as Map), inputs: inputs, - inscription: InscriptionLink.fromJson(json['_links']['inscription']), + inscription: InscriptionLink.fromJson(json['_links']['inscription'] as Map), outputs: outputs, - self: TransactionLink.fromJson(json['_links']['self']), - transaction: json['transaction'], + self: TransactionLink.fromJson(json['_links']['self'] as Map), + transaction: json['transaction'] as String, ); } } @@ -50,9 +52,9 @@ class TransactionLinks { factory TransactionLinks.fromJson(Map json) { return TransactionLinks( - block: TransactionLink.fromJson(json['block']), - inscription: InscriptionLink.fromJson(json['inscription']), - self: TransactionLink.fromJson(json['self']), + block: TransactionLink.fromJson(json['block'] as Map), + inscription: InscriptionLink.fromJson(json['inscription'] as Map), + self: TransactionLink.fromJson(json['self'] as Map), ); } } @@ -63,7 +65,7 @@ class TransactionLink { TransactionLink({required this.href}); factory TransactionLink.fromJson(Map json) { - return TransactionLink(href: json['href']); + return TransactionLink(href: json['href'] as String); } } @@ -73,6 +75,6 @@ class OutputLink { OutputLink({required this.href}); factory OutputLink.fromJson(Map json) { - return OutputLink(href: json['href']); + return OutputLink(href: json['href'] as String); } } From 8e17556e4128f00f915460f19eb7464c5ae54e16 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 13:03:17 -0500 Subject: [PATCH 015/128] use an OrdinalsResponse response type which can return an error --- lib/dto/ordinals/address_response.dart | 14 +++-- lib/dto/ordinals/block_response.dart | 25 +++++---- lib/dto/ordinals/content_response.dart | 9 ++- lib/dto/ordinals/feed_response.dart | 11 ++-- lib/dto/ordinals/inscription_response.dart | 61 ++++++++++++--------- lib/dto/ordinals/ordinals_response.dart | 6 ++ lib/dto/ordinals/output_response.dart | 21 ++++--- lib/dto/ordinals/preview_response.dart | 11 ++-- lib/dto/ordinals/sat_response.dart | 41 +++++++------- lib/dto/ordinals/transaction_response.dart | 24 ++++---- lib/services/mixins/ordinals_interface.dart | 18 +++++- lib/services/ordinals_api.dart | 5 +- 12 files changed, 148 insertions(+), 98 deletions(-) create mode 100644 lib/dto/ordinals/ordinals_response.dart diff --git a/lib/dto/ordinals/address_response.dart b/lib/dto/ordinals/address_response.dart index 5c1f66323..9136aa523 100644 --- a/lib/dto/ordinals/address_response.dart +++ b/lib/dto/ordinals/address_response.dart @@ -1,6 +1,7 @@ import 'package:stackwallet/dto/ordinals/inscription_link.dart'; +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; -class AddressResponse { +class AddressResponse extends OrdinalsResponse { final AddressLinks links; final String address; final List inscriptions; @@ -11,15 +12,16 @@ class AddressResponse { required this.inscriptions, }); - factory AddressResponse.fromJson(Map json) { - final inscriptionsJson = json['inscriptions'] as List; + factory AddressResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; + final inscriptionsJson = data['inscriptions'] as List; final inscriptions = inscriptionsJson .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson as Map)) .toList(); return AddressResponse( - links: AddressLinks.fromJson(json['_links'] as Map), - address: json['address'] as String, + links: AddressLinks.fromJson(data['_links'] as Map), + address: data['address'] as String, inscriptions: inscriptions, ); } @@ -34,7 +36,7 @@ class AddressLinks { factory AddressLinks.fromJson(Map json) { return AddressLinks( - self: AddressLink.fromJson(json['self'] as Map), + self: json['self'] != null ? AddressLink.fromJson(json['self'] as Map) : null, ); } } diff --git a/lib/dto/ordinals/block_response.dart b/lib/dto/ordinals/block_response.dart index a3de1ee1c..0eef8d569 100644 --- a/lib/dto/ordinals/block_response.dart +++ b/lib/dto/ordinals/block_response.dart @@ -1,4 +1,6 @@ -class BlockResponse { +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; + +class BlockResponse extends OrdinalsResponse { final BlockLinks links; final String hash; final String previousBlockhash; @@ -17,15 +19,16 @@ class BlockResponse { required this.weight, }); - factory BlockResponse.fromJson(Map json) { + factory BlockResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; return BlockResponse( - links: BlockLinks.fromJson(json['_links'] as Map), - hash: json['hash'] as String, - previousBlockhash: json['previous_blockhash'] as String, - size: json['size'] as int, - target: json['target'] as String, - timestamp: json['timestamp'] as String, - weight: json['weight'] as int, + links: BlockLinks.fromJson(data['_links'] as Map), + hash: data['hash'] as String, + previousBlockhash: data['previous_blockhash'] as String, + size: data['size'] as int, + target: data['target'] as String, + timestamp: data['timestamp'] as String, + weight: data['weight'] as int, ); } } @@ -41,8 +44,8 @@ class BlockLinks { factory BlockLinks.fromJson(Map json) { return BlockLinks( - prev: BlockLink.fromJson(json['prev'] as Map), - self: BlockLink.fromJson(json['self'] as Map), + prev: json['prev'] != null ? BlockLink.fromJson(json['prev'] as Map) : null, + self: json['self'] != null ? BlockLink.fromJson(json['self'] as Map) : null, ); } } diff --git a/lib/dto/ordinals/content_response.dart b/lib/dto/ordinals/content_response.dart index d1131950b..7cfbaf9fd 100644 --- a/lib/dto/ordinals/content_response.dart +++ b/lib/dto/ordinals/content_response.dart @@ -1,10 +1,13 @@ -class ContentResponse { +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; + +class ContentResponse extends OrdinalsResponse { final FileLink fileLink; ContentResponse({required this.fileLink}); - factory ContentResponse.fromJson(Map json) { - return ContentResponse(fileLink: FileLink.fromJson(json['_links']['file'] as Map)); + factory ContentResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; + return ContentResponse(fileLink: FileLink.fromJson(data['_links']['file'] as Map)); // TODO don't cast as Map } } diff --git a/lib/dto/ordinals/feed_response.dart b/lib/dto/ordinals/feed_response.dart index e29d2d334..525a7f727 100644 --- a/lib/dto/ordinals/feed_response.dart +++ b/lib/dto/ordinals/feed_response.dart @@ -1,16 +1,17 @@ import 'package:stackwallet/dto/ordinals/inscription_link.dart'; +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; -class FeedResponse { +class FeedResponse extends OrdinalsResponse { final List inscriptions; FeedResponse({required this.inscriptions}); - - factory FeedResponse.fromJson(Map json) { - final List inscriptionsJson = json['_links']['inscriptions'] as List; + + factory FeedResponse.fromJson(OrdinalsResponse json) { + final List inscriptionsJson = json.data['_links']['inscriptions'] as List; final List inscriptions = inscriptionsJson .map((json) => InscriptionLink.fromJson(json as Map)) .toList(); return FeedResponse(inscriptions: inscriptions); } -} +} \ No newline at end of file diff --git a/lib/dto/ordinals/inscription_response.dart b/lib/dto/ordinals/inscription_response.dart index ab093b750..d45300aee 100644 --- a/lib/dto/ordinals/inscription_response.dart +++ b/lib/dto/ordinals/inscription_response.dart @@ -1,4 +1,6 @@ -class InscriptionResponse { +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; + +class InscriptionResponse extends OrdinalsResponse { late final Links links; late final String address; late final int contentLength; @@ -29,20 +31,23 @@ class InscriptionResponse { required this.timestamp, }); - InscriptionResponse.fromJson(Map json) { - links = Links.fromJson(json['_links'] as Map); - address = json['address'] as String; - contentLength = json['content_length'] as int; - contentType = json['content_type'] as String; - genesisFee = json['genesis_fee'] as int; - genesisHeight = json['genesis_height'] as int; - genesisTransaction = json['genesis_transaction'] as String; - location = json['location'] as String; - number = json['number'] as int; - offset = json['offset'] as int; - output = json['output'] as String; - sat = json['sat'] as String?; - timestamp = json['timestamp'] as String; + factory InscriptionResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; + return InscriptionResponse( + links: Links.fromJson(data['_links'] as Map), + address: data['address'] as String, + contentLength: data['content_length'] as int, + contentType: data['content_type'] as String, + genesisFee: data['genesis_fee'] as int, + genesisHeight: data['genesis_height'] as int, + genesisTransaction: data['genesis_transaction'] as String, + location: data['location'] as String, + number: data['number'] as int, + offset: data['offset'] as int, + output: data['output'] as String, + sat: data['sat'] as String?, + timestamp: data['timestamp'] as String, + ); } } @@ -67,15 +72,17 @@ class Links { required this.self, }); - Links.fromJson(Map json) { - content = Link.fromJson(json['content'] as Map); - genesisTransaction = Link.fromJson(json['genesis_transaction'] as Map); - next = Link.fromJson(json['next'] as Map); - output = Link.fromJson(json['output'] as Map); - prev = Link.fromJson(json['prev'] as Map); - preview = Link.fromJson(json['preview'] as Map); - sat = json['sat'] != null ? Link.fromJson(json['sat'] as Map) : null; - self = Link.fromJson(json['self'] as Map); + factory Links.fromJson(Map json) { + return Links( + content: Link.fromJson(json['content'] as Map), + genesisTransaction: Link.fromJson(json['genesis_transaction'] as Map), + next: Link.fromJson(json['next'] as Map), + output: Link.fromJson(json['output'] as Map), + prev: Link.fromJson(json['prev'] as Map), + preview: Link.fromJson(json['preview'] as Map), + sat: json['sat'] != null ? Link.fromJson(json['sat'] as Map) : null, + self: Link.fromJson(json['self'] as Map), + ); } } @@ -84,7 +91,7 @@ class Link { Link({required this.href}); - Link.fromJson(Map json) { - href = json['href'] as String; + factory Link.fromJson(Map json) { + return Link(href: json['href'] as String); } -} \ No newline at end of file +} diff --git a/lib/dto/ordinals/ordinals_response.dart b/lib/dto/ordinals/ordinals_response.dart new file mode 100644 index 000000000..bf57db46b --- /dev/null +++ b/lib/dto/ordinals/ordinals_response.dart @@ -0,0 +1,6 @@ +class OrdinalsResponse { + final T? data; + final String? error; + + OrdinalsResponse({this.data, this.error}); +} diff --git a/lib/dto/ordinals/output_response.dart b/lib/dto/ordinals/output_response.dart index cd4bde563..cc7b2107f 100644 --- a/lib/dto/ordinals/output_response.dart +++ b/lib/dto/ordinals/output_response.dart @@ -1,6 +1,7 @@ import 'package:stackwallet/dto/ordinals/transaction_response.dart'; +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; -class OutputResponse { +class OutputResponse extends OrdinalsResponse { final OutputLinks links; final String address; final String scriptPubkey; @@ -15,13 +16,15 @@ class OutputResponse { required this.value, }); - factory OutputResponse.fromJson(Map json) { + factory OutputResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; + return OutputResponse( - links: OutputLinks.fromJson(json['_links'] as Map), - address: json['address'] as String, - scriptPubkey: json['script_pubkey'] as String, - transaction: json['transaction'] as String, - value: json['value'] as int, + links: OutputLinks.fromJson(data['_links'] as Map), + address: data['address'] as String, + scriptPubkey: data['script_pubkey'] as String, + transaction: data['transaction'] as String, + value: data['value'] as int, ); } } @@ -37,8 +40,8 @@ class OutputLinks { factory OutputLinks.fromJson(Map json) { return OutputLinks( - self: OutputLink.fromJson(json['self'] as Map), - transaction: TransactionLink.fromJson(json['transaction'] as Map), + self: json['self'] != null ? OutputLink.fromJson(json['self'] as Map) : null, + transaction: json['transaction'] != null ? TransactionLink.fromJson(json['transaction'] as Map) : null, ); } } diff --git a/lib/dto/ordinals/preview_response.dart b/lib/dto/ordinals/preview_response.dart index 9eabd590a..b3e184acd 100644 --- a/lib/dto/ordinals/preview_response.dart +++ b/lib/dto/ordinals/preview_response.dart @@ -1,10 +1,13 @@ -class PreviewResponse { +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; + +class PreviewResponse extends OrdinalsResponse { final ImageLink imageLink; PreviewResponse({required this.imageLink}); - factory PreviewResponse.fromJson(Map json) { - return PreviewResponse(imageLink: ImageLink.fromJson(json['_links']['image'] as Map)); + factory PreviewResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; + return PreviewResponse(imageLink: ImageLink.fromJson(data['_links']['image'] as Map)); } } @@ -16,4 +19,4 @@ class ImageLink { factory ImageLink.fromJson(Map json) { return ImageLink(href: json['href'] as String); } -} \ No newline at end of file +} diff --git a/lib/dto/ordinals/sat_response.dart b/lib/dto/ordinals/sat_response.dart index b57ccb3cf..40efb1440 100644 --- a/lib/dto/ordinals/sat_response.dart +++ b/lib/dto/ordinals/sat_response.dart @@ -1,4 +1,6 @@ -class SatResponse { +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; + +class SatResponse extends OrdinalsResponse { final SatLinks links; final int block; final int cycle; @@ -27,20 +29,21 @@ class SatResponse { required this.timestamp, }); - factory SatResponse.fromJson(Map json) { + factory SatResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; return SatResponse( - links: SatLinks.fromJson(json['_links'] as Map), - block: json['block'] as int, - cycle: json['cycle'] as int, - decimal: json['decimal'] as String, - degree: json['degree'] as String, - epoch: json['epoch'] as int, - name: json['name'] as String, - offset: json['offset'] as int, - percentile: json['percentile'] as String, - period: json['period'] as int, - rarity: json['rarity'] as String, - timestamp: json['timestamp'] as String, + links: SatLinks.fromJson(data['_links'] as Map), + block: data['block'] as int, + cycle: data['cycle'] as int, + decimal: data['decimal'] as String, + degree: data['degree'] as String, + epoch: data['epoch'] as int, + name: data['name'] as String, + offset: data['offset'] as int, + percentile: data['percentile'] as String, + period: data['period'] as int, + rarity: data['rarity'] as String, + timestamp: data['timestamp'] as String, ); } } @@ -62,11 +65,11 @@ class SatLinks { factory SatLinks.fromJson(Map json) { return SatLinks( - block: SatLink.fromJson(json['block'] as Map), - inscription: SatLink.fromJson(json['inscription'] as Map), - next: SatLink.fromJson(json['next'] as Map), - prev: SatLink.fromJson(json['prev'] as Map), - self: SatLink.fromJson(json['self'] as Map), + block: json['block'] != null ? SatLink.fromJson(json['block'] as Map) : null, + inscription: json['inscription'] != null ? SatLink.fromJson(json['inscription'] as Map) : null, + next: json['next'] != null ? SatLink.fromJson(json['next'] as Map) : null, + prev: json['prev'] != null ? SatLink.fromJson(json['prev'] as Map) : null, + self: json['self'] != null ? SatLink.fromJson(json['self'] as Map) : null, ); } } diff --git a/lib/dto/ordinals/transaction_response.dart b/lib/dto/ordinals/transaction_response.dart index 11912a3ad..c77e07914 100644 --- a/lib/dto/ordinals/transaction_response.dart +++ b/lib/dto/ordinals/transaction_response.dart @@ -1,6 +1,7 @@ import 'package:stackwallet/dto/ordinals/inscription_link.dart'; +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; -class TransactionResponse { +class TransactionResponse extends OrdinalsResponse { final TransactionLinks links; final List inputs; final InscriptionLink inscription; @@ -17,24 +18,25 @@ class TransactionResponse { required this.transaction, }); - factory TransactionResponse.fromJson(Map json) { - final inputsJson = json['_links']['inputs'] as List; + factory TransactionResponse.fromJson(OrdinalsResponse json) { + final data = json.data as Map; + final inputsJson = data['_links']['inputs'] as List; final inputs = inputsJson .map((inputJson) => OutputLink.fromJson(inputJson as Map)) .toList(); - final outputsJson = json['_links']['outputs'] as List; + final outputsJson = data['_links']['outputs'] as List; final outputs = outputsJson .map((outputJson) => OutputLink.fromJson(outputJson as Map)) .toList(); return TransactionResponse( - links: TransactionLinks.fromJson(json['_links'] as Map), + links: TransactionLinks.fromJson(data['_links'] as Map), inputs: inputs, - inscription: InscriptionLink.fromJson(json['_links']['inscription'] as Map), + inscription: InscriptionLink.fromJson(data['_links']['inscription'] as Map), outputs: outputs, - self: TransactionLink.fromJson(json['_links']['self'] as Map), - transaction: json['transaction'] as String, + self: TransactionLink.fromJson(data['_links']['self'] as Map), + transaction: data['transaction'] as String, ); } } @@ -52,9 +54,9 @@ class TransactionLinks { factory TransactionLinks.fromJson(Map json) { return TransactionLinks( - block: TransactionLink.fromJson(json['block'] as Map), - inscription: InscriptionLink.fromJson(json['inscription'] as Map), - self: TransactionLink.fromJson(json['self'] as Map), + block: json['block'] != null ? TransactionLink.fromJson(json['block'] as Map) : null, + inscription: json['inscription'] != null ? InscriptionLink.fromJson(json['inscription'] as Map) : null, + self: json['self'] != null ? TransactionLink.fromJson(json['self'] as Map) : null, ); } } diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 06f2377e9..57a76fbd6 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -1,3 +1,19 @@ +import 'package:stackwallet/services/ordinals_api.dart'; +import 'package:stackwallet/dto/ordinals/feed_response.dart'; + mixin OrdinalsInterface { - // TODO wallet ordinals functionality + Future fetchLatestInscriptions(OrdinalsAPI ordinalsAPI) async { + try { + final feedResponse = await ordinalsAPI.getLatestInscriptions(); + // Process the feedResponse data as needed + print('Latest Inscriptions:'); + for (var inscription in feedResponse.inscriptions) { + print('Title: ${inscription.title}, Href: ${inscription.href}'); + } + return feedResponse; + } catch (e) { + // Handle errors + throw Exception('Error in OrdinalsInterface: $e'); + } + } } diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index 4d8360e06..e9d734203 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; import 'package:stackwallet/dto/ordinals/feed_response.dart'; import 'package:stackwallet/dto/ordinals/inscription_response.dart'; import 'package:stackwallet/dto/ordinals/sat_response.dart'; @@ -16,10 +17,10 @@ class OrdinalsAPI { OrdinalsAPI({required this.baseUrl}); - Future> _getResponse(String endpoint) async { + Future _getResponse(String endpoint) async { final response = await http.get(Uri.parse('$baseUrl$endpoint')); if (response.statusCode == 200) { - return _validateJson(response.body); + return OrdinalsResponse(data: _validateJson(response.body)); } else { throw Exception('Failed to load data'); } From bc8f5ce8f93ba2d7163a7d47878c92522314ca69 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 15:58:26 -0500 Subject: [PATCH 016/128] update OrdinalsInterface to not need baseUrl declared by withee classes --- lib/services/mixins/ordinals_interface.dart | 10 ++++++---- lib/services/ordinals_api.dart | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 57a76fbd6..1ab157b2c 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -1,8 +1,10 @@ -import 'package:stackwallet/services/ordinals_api.dart'; -import 'package:stackwallet/dto/ordinals/feed_response.dart'; +import 'package:stackwallet/dto/ordinals/feed_response.dart'; // Assuming this import is necessary +import 'package:stackwallet/services/ordinals_api.dart'; // Assuming this import is necessary mixin OrdinalsInterface { - Future fetchLatestInscriptions(OrdinalsAPI ordinalsAPI) async { + final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'http://ord-litecoin.stackwallet.com'); + + Future fetchLatestInscriptions() async { try { final feedResponse = await ordinalsAPI.getLatestInscriptions(); // Process the feedResponse data as needed @@ -16,4 +18,4 @@ mixin OrdinalsInterface { throw Exception('Error in OrdinalsInterface: $e'); } } -} +} \ No newline at end of file diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index e9d734203..c1d3aad94 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -12,10 +12,19 @@ import 'package:stackwallet/dto/ordinals/block_response.dart'; import 'package:stackwallet/dto/ordinals/content_response.dart'; import 'package:stackwallet/dto/ordinals/preview_response.dart'; -class OrdinalsAPI { - final String baseUrl; +import 'package:stackwallet/dto/ordinals/feed_response.dart'; // Assuming this import is necessary - OrdinalsAPI({required this.baseUrl}); +class OrdinalsAPI { + static final OrdinalsAPI _instance = OrdinalsAPI._internal(); + + factory OrdinalsAPI({required String baseUrl}) { + _instance.baseUrl = baseUrl; + return _instance; + } + + OrdinalsAPI._internal(); + + late String baseUrl; Future _getResponse(String endpoint) async { final response = await http.get(Uri.parse('$baseUrl$endpoint')); From 2de8ffe5934d9534d79705a16e7d991c2c2369fe Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 15:58:34 -0500 Subject: [PATCH 017/128] add test button --- lib/pages/ordinals/ordinals_view.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index fe60b5e28..2d051b07a 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -14,6 +14,8 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/services/ordinals_api.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'; @@ -39,7 +41,7 @@ class OrdinalsView extends ConsumerStatefulWidget { ConsumerState createState() => _OrdinalsViewState(); } -class _OrdinalsViewState extends ConsumerState { +class _OrdinalsViewState extends ConsumerState with OrdinalsInterface { late final TextEditingController searchController; late final FocusNode searchFocus; @@ -49,6 +51,7 @@ class _OrdinalsViewState extends ConsumerState { void initState() { searchController = TextEditingController(); searchFocus = FocusNode(); + super.initState(); } @@ -178,6 +181,13 @@ class _OrdinalsViewState extends ConsumerState { const SizedBox( height: 16, ), + TextButton(onPressed: () async { + await fetchLatestInscriptions(); + }, child: Text( + "Test", + style: STextStyles.navBarTitle(context), + ) + ), Expanded( child: OrdinalsList( walletId: widget.walletId, From c435c378e0f6eda2f0c690f6150a23a82a57a678 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 19 Jul 2023 15:37:11 -0600 Subject: [PATCH 018/128] rough outline for isar schema class --- lib/models/ordinal.dart | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/models/ordinal.dart b/lib/models/ordinal.dart index 9c4e15392..1feb98b9d 100644 --- a/lib/models/ordinal.dart +++ b/lib/models/ordinal.dart @@ -1,13 +1,26 @@ +enum OrdCollection { + punks, + moonbirds, +} + class Ordinal { final String name; final String inscription; final String rank; + final OrdCollection collection; - // TODO: make a proper class instead of this placeholder + // following two are used to look up the UTXO object in isar combined w/ walletId + final String utxoTXID; + final int utxoVOUT; + + // TODO: make a proper Isar class instead of this placeholder Ordinal({ required this.name, required this.inscription, required this.rank, + required this.collection, + required this.utxoTXID, + required this.utxoVOUT, }); } From 0284bb2951e362cc7c76309c0bd7683731e345bd Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 19 Jul 2023 17:13:54 -0500 Subject: [PATCH 019/128] use https api, implement other methods --- lib/pages/ordinals/ordinals_view.dart | 2 +- lib/services/mixins/ordinals_interface.dart | 87 +++++++++++++++++++-- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 2d051b07a..3dd8b7ae7 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -182,7 +182,7 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf height: 16, ), TextButton(onPressed: () async { - await fetchLatestInscriptions(); + await getTransaction('ed5a5c4e555e204768ec54c049ae0b01c86fdcc8b126a9d100c4dff745e7d3ca'); }, child: Text( "Test", style: STextStyles.navBarTitle(context), diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 1ab157b2c..92974c67d 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -1,21 +1,94 @@ -import 'package:stackwallet/dto/ordinals/feed_response.dart'; // Assuming this import is necessary +import 'package:stackwallet/dto/ordinals/feed_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_response.dart'; +import 'package:stackwallet/dto/ordinals/sat_response.dart'; +import 'package:stackwallet/dto/ordinals/transaction_response.dart'; +import 'package:stackwallet/dto/ordinals/output_response.dart'; +import 'package:stackwallet/dto/ordinals/address_response.dart'; +import 'package:stackwallet/dto/ordinals/block_response.dart'; +import 'package:stackwallet/dto/ordinals/content_response.dart'; +import 'package:stackwallet/dto/ordinals/preview_response.dart'; import 'package:stackwallet/services/ordinals_api.dart'; // Assuming this import is necessary mixin OrdinalsInterface { - final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'http://ord-litecoin.stackwallet.com'); + final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com'); Future fetchLatestInscriptions() async { try { final feedResponse = await ordinalsAPI.getLatestInscriptions(); // Process the feedResponse data as needed - print('Latest Inscriptions:'); - for (var inscription in feedResponse.inscriptions) { - print('Title: ${inscription.title}, Href: ${inscription.href}'); - } + // print('Latest Inscriptions:'); + // for (var inscription in feedResponse.inscriptions) { + // print('Title: ${inscription.title}, Href: ${inscription.href}'); + // } return feedResponse; } catch (e) { // Handle errors - throw Exception('Error in OrdinalsInterface: $e'); + throw Exception('Error in OrdinalsInterface fetchLatestInscriptions: $e'); + } + } + + Future getInscriptionDetails(String inscriptionId) async { + try { + return await ordinalsAPI.getInscriptionDetails(inscriptionId); + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptionDetails: $e'); + } + } + + Future getSatDetails(int satNumber) async { + try { + return await ordinalsAPI.getSatDetails(satNumber); + } catch (e) { + throw Exception('Error in OrdinalsInterface getSatDetails: $e'); + } + } + + Future getTransaction(String transactionId) async { + try { + print(1); + return await ordinalsAPI.getTransaction(transactionId); + } catch (e) { + throw Exception('Error in OrdinalsInterface getTransaction: $e'); + } + } + + Future getTransactionOutputs(String transactionId) async { + try { + return await ordinalsAPI.getTransactionOutputs(transactionId); + } catch (e) { + throw Exception('Error in OrdinalsInterface getTransactionOutputs: $e'); + } + } + + Future getInscriptionsByAddress(String address) async { + try { + return await ordinalsAPI.getInscriptionsByAddress(address); + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); + } + } + + Future getBlock(int blockNumber) async { + try { + return await ordinalsAPI.getBlock(blockNumber); + } catch (e) { + throw Exception('Error in OrdinalsInterface getBlock: $e'); + } + } + + Future getInscriptionContent(String inscriptionId) async { + try { + return await ordinalsAPI.getInscriptionContent(inscriptionId); + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptionContent: $e'); + } + } + + Future getInscriptionPreview(String inscriptionId) async { + try { + return await ordinalsAPI.getInscriptionPreview(inscriptionId); + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptionPreview: $e'); } } } \ No newline at end of file From d02b7f7ad4bbc4c5335fac7c0cce3f588760c580 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 14:05:51 -0500 Subject: [PATCH 020/128] formatting and model updates --- lib/pages/ordinals/ordinals_view.dart | 3 +++ lib/services/ordinals_api.dart | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 3dd8b7ae7..a60bf4226 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -197,6 +197,9 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf name: "dummy name $i", inscription: "insc$i", rank: "r$i", + collection: OrdCollection.moonbirds, + utxoTXID: 'txid', + utxoVOUT: 1 ), ], ), diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index c1d3aad94..290b1c869 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -12,8 +12,6 @@ import 'package:stackwallet/dto/ordinals/block_response.dart'; import 'package:stackwallet/dto/ordinals/content_response.dart'; import 'package:stackwallet/dto/ordinals/preview_response.dart'; -import 'package:stackwallet/dto/ordinals/feed_response.dart'; // Assuming this import is necessary - class OrdinalsAPI { static final OrdinalsAPI _instance = OrdinalsAPI._internal(); From f046912c89a2e64d28687f5d1631f0279659506d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 14:38:56 -0500 Subject: [PATCH 021/128] litescribe api and demo --- .../address_inscription_response.dart | 91 +++++++++++++++++++ lib/dto/ordinals/litescribe_response.dart | 6 ++ lib/pages/ordinals/ordinals_view.dart | 5 +- lib/services/litescribe_api.dart | 45 +++++++++ lib/services/mixins/ordinals_interface.dart | 38 ++++++-- lib/services/ordinals_api.dart | 4 +- 6 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 lib/dto/ordinals/address_inscription_response.dart create mode 100644 lib/dto/ordinals/litescribe_response.dart create mode 100644 lib/services/litescribe_api.dart diff --git a/lib/dto/ordinals/address_inscription_response.dart b/lib/dto/ordinals/address_inscription_response.dart new file mode 100644 index 000000000..c424d73e0 --- /dev/null +++ b/lib/dto/ordinals/address_inscription_response.dart @@ -0,0 +1,91 @@ +import 'package:stackwallet/dto/ordinals/litescribe_response.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) => AddressInscription.fromJson(item as Map)).toList(), + total: json['total'] as int, + ); + } +} + +class AddressInscription { + 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; + + AddressInscription({ + 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 AddressInscription.fromJson(Map json) { + return AddressInscription( + 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, + ); + } +} 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/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index a60bf4226..4bd7385d3 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -182,7 +182,10 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf height: 16, ), TextButton(onPressed: () async { - await getTransaction('ed5a5c4e555e204768ec54c049ae0b01c86fdcc8b126a9d100c4dff745e7d3ca'); + // await fetchLatestInscriptions(); + // await getTransaction('ed5a5c4e555e204768ec54c049ae0b01c86fdcc8b126a9d100c4dff745e7d3ca'); + // await getBlock('31278055ba414fe6dbed75e4a77e841da4481972ac09bd2a214c445da1a44aad'); + await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); }, child: Text( "Test", style: STextStyles.navBarTitle(context), diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart new file mode 100644 index 000000000..507492ee6 --- /dev/null +++ b/lib/services/litescribe_api.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:stackwallet/dto/ordinals/address_inscription_response.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 = 1000 = hardcoded limit as default limit to inscriptions returned from API call, TODO increase limit if returned inscriptions = limit + final response = await _getResponse('/address/inscriptions?address=$address&cursor=$cursor&size=$size'); + try { + return AddressInscriptionResponse.fromJson(response.data as Map); + } catch(e) { + throw const FormatException('LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); + } + } +} \ No newline at end of file diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 92974c67d..a9fbcbca6 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -1,15 +1,32 @@ -import 'package:stackwallet/dto/ordinals/feed_response.dart'; -import 'package:stackwallet/dto/ordinals/inscription_response.dart'; -import 'package:stackwallet/dto/ordinals/sat_response.dart'; -import 'package:stackwallet/dto/ordinals/transaction_response.dart'; -import 'package:stackwallet/dto/ordinals/output_response.dart'; -import 'package:stackwallet/dto/ordinals/address_response.dart'; -import 'package:stackwallet/dto/ordinals/block_response.dart'; -import 'package:stackwallet/dto/ordinals/content_response.dart'; -import 'package:stackwallet/dto/ordinals/preview_response.dart'; -import 'package:stackwallet/services/ordinals_api.dart'; // Assuming this import is necessary +// ord-litecoin-specific imports +// import 'package:stackwallet/dto/ordinals/feed_response.dart'; +// import 'package:stackwallet/dto/ordinals/inscription_response.dart'; +// import 'package:stackwallet/dto/ordinals/sat_response.dart'; +// import 'package:stackwallet/dto/ordinals/transaction_response.dart'; +// import 'package:stackwallet/dto/ordinals/output_response.dart'; +// import 'package:stackwallet/dto/ordinals/address_response.dart'; +// import 'package:stackwallet/dto/ordinals/block_response.dart'; +// import 'package:stackwallet/dto/ordinals/content_response.dart'; +// import 'package:stackwallet/dto/ordinals/preview_response.dart'; +// import 'package:stackwallet/services/ordinals_api.dart'; + +import 'package:stackwallet/dto/ordinals/address_inscription_response.dart'; // verbose due to Litescribe being the 2nd API +import 'package:stackwallet/services/litescribe_api.dart'; mixin OrdinalsInterface { + final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api'); + + Future> getInscriptionsByAddress(String address) async { + try { + var response = await litescribeAPI.getInscriptionsByAddress(address); + print("Found ${response.result.total} inscriptions at address $address"); // TODO disable (POC) + return response.result.list; + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); + } + } + + /* // ord-litecoin interface final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com'); Future fetchLatestInscriptions() async { @@ -91,4 +108,5 @@ mixin OrdinalsInterface { throw Exception('Error in OrdinalsInterface getInscriptionPreview: $e'); } } + */ // /ord-litecoin interface } \ No newline at end of file diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart index 290b1c869..e6df3c05e 100644 --- a/lib/services/ordinals_api.dart +++ b/lib/services/ordinals_api.dart @@ -72,8 +72,8 @@ class OrdinalsAPI { return AddressResponse.fromJson(response); } - Future getBlock(int blockNumber) async { - final response = await _getResponse('/block/$blockNumber'); + Future getBlock(String blockHash) async { + final response = await _getResponse('/block/$blockHash'); return BlockResponse.fromJson(response); } From a322c1395432a18f1fc1f53eff53037ec7e3499b Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 14:46:15 -0500 Subject: [PATCH 022/128] return inscriptions in batches of 1000 --- lib/services/litescribe_api.dart | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index 507492ee6..edb46f1d2 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -34,12 +34,29 @@ class LitescribeAPI { } } - Future getInscriptionsByAddress(String address, {int cursor = 0, int size = 1000}) async { // size = 1000 = hardcoded limit as default limit to inscriptions returned from API call, TODO increase limit if returned inscriptions = limit + 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'); - try { - return AddressInscriptionResponse.fromJson(response.data as Map); - } catch(e) { - throw const FormatException('LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); + + // Check if the number of returned inscriptions equals the limit + final list = response.data['result']['list'] as List; + final int total = response.data['result']['total'] as int; + final int currentSize = list.length; + + if (currentSize == size && currentSize < total) { + // If the number of returned inscriptions equals the limit and there are more inscriptions available, + // increase the size to fetch all inscriptions. + return getInscriptionsByAddress(address, cursor: cursor, size: total+1); // potential off-by-one error, but should be safe + // TODO don't re-request the same inscriptions previously returned; increment cursor (probably) by size and only request the rest. ex: cursor=0 size=1000 probably returns inscriptions 0-999, so set cursor=size (or size-1?) to get 1000-1999 + } else { + try { + return AddressInscriptionResponse.fromJson(response.data as Map); + } catch (e) { + throw const FormatException('LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); + } } } } \ No newline at end of file From 22e222ffd6c0ea7bd1851a36ce2e64e9fc8f32cb Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 14:48:12 -0500 Subject: [PATCH 023/128] paginate inscription responses --- lib/services/litescribe_api.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index edb46f1d2..abc28123e 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -48,9 +48,10 @@ class LitescribeAPI { if (currentSize == size && currentSize < total) { // If the number of returned inscriptions equals the limit and there are more inscriptions available, - // increase the size to fetch all inscriptions. - return getInscriptionsByAddress(address, cursor: cursor, size: total+1); // potential off-by-one error, but should be safe - // TODO don't re-request the same inscriptions previously returned; increment cursor (probably) by size and only request the rest. ex: cursor=0 size=1000 probably returns inscriptions 0-999, so set cursor=size (or size-1?) to get 1000-1999 + // 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); + // TODO test logic with smaller size "pagination" } else { try { return AddressInscriptionResponse.fromJson(response.data as Map); From 4be7919e46c288668d28b18b1d2d19e5cd6a8eaf Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 14:52:05 -0500 Subject: [PATCH 024/128] expand upon demo TODO remove WIP, hook directly into StreamBuilder --- lib/pages/ordinals/ordinals_view.dart | 9 ++++++++- lib/services/mixins/ordinals_interface.dart | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 4bd7385d3..d2b6a34d6 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -185,7 +185,14 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf // await fetchLatestInscriptions(); // await getTransaction('ed5a5c4e555e204768ec54c049ae0b01c86fdcc8b126a9d100c4dff745e7d3ca'); // await getBlock('31278055ba414fe6dbed75e4a77e841da4481972ac09bd2a214c445da1a44aad'); - await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); + var inscriptions = await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); + for (var inscription in inscriptions) { + print(inscription); + print(inscription.address); + print(inscription.content); + print(inscription.inscriptionId); + print(inscription.inscriptionNumber); + } }, child: Text( "Test", style: STextStyles.navBarTitle(context), diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index a9fbcbca6..f39b2259f 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -19,7 +19,7 @@ mixin OrdinalsInterface { Future> getInscriptionsByAddress(String address) async { try { var response = await litescribeAPI.getInscriptionsByAddress(address); - print("Found ${response.result.total} inscriptions at address $address"); // TODO disable (POC) + print("Found ${response.result.total} inscription${response.result.total > 1 ? 's' : ''} at address $address"); // TODO disable (POC) return response.result.list; } catch (e) { throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); From ca8e930904cd9497f38fd13cc15d2f78b07d6b3a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 15:32:39 -0500 Subject: [PATCH 025/128] move demo into refresh button, add refreshInscriptions() stub w prev poc --- lib/pages/ordinals/ordinals_view.dart | 22 +++------------------ lib/services/mixins/ordinals_interface.dart | 12 +++++++++++ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index d2b6a34d6..7a0572c78 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -14,6 +14,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/services/litescribe_api.dart'; import 'package:stackwallet/services/ordinals_api.dart'; import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -90,8 +91,8 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf .extension()! .topNavIconPrimary, ), - onPressed: () { - // todo refresh + onPressed: () async { + refreshInscriptions(); }, ), ), @@ -181,23 +182,6 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf const SizedBox( height: 16, ), - TextButton(onPressed: () async { - // await fetchLatestInscriptions(); - // await getTransaction('ed5a5c4e555e204768ec54c049ae0b01c86fdcc8b126a9d100c4dff745e7d3ca'); - // await getBlock('31278055ba414fe6dbed75e4a77e841da4481972ac09bd2a214c445da1a44aad'); - var inscriptions = await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); - for (var inscription in inscriptions) { - print(inscription); - print(inscription.address); - print(inscription.content); - print(inscription.inscriptionId); - print(inscription.inscriptionNumber); - } - }, child: Text( - "Test", - style: STextStyles.navBarTitle(context), - ) - ), Expanded( child: OrdinalsList( walletId: widget.walletId, diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index f39b2259f..d60786b4d 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -26,6 +26,18 @@ mixin OrdinalsInterface { } } + void refreshInscriptions() async { + // TODO get all inscriptions at all addresses in wallet + var inscriptions = await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); + for (var inscription in inscriptions) { + print(inscription); + print(inscription.address); + print(inscription.content); + print(inscription.inscriptionId); + print(inscription.inscriptionNumber); + } + } + /* // ord-litecoin interface final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com'); From 06c433ff8565479860cbb3ddd2f7f18e389a83c6 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 15:56:11 -0500 Subject: [PATCH 026/128] add init method, build on poc --- lib/services/mixins/ordinals_interface.dart | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index d60786b4d..caac7a854 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -1,3 +1,8 @@ +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; + // ord-litecoin-specific imports // import 'package:stackwallet/dto/ordinals/feed_response.dart'; // import 'package:stackwallet/dto/ordinals/inscription_response.dart'; @@ -13,7 +18,21 @@ import 'package:stackwallet/dto/ordinals/address_inscription_response.dart'; // verbose due to Litescribe being the 2nd API import 'package:stackwallet/services/litescribe_api.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> getInscriptionsByAddress(String address) async { @@ -27,6 +46,13 @@ mixin OrdinalsInterface { } void refreshInscriptions() async { + List _inscriptions; + final utxos = await _db.getUTXOs(_walletId).findAll(); + final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); + for (String address in uniqueAddresses) { + // TODO fetch all inscriptions from all addresses + // TODO save those inscriptions to isar, which a StreamBuilder will be "subscribed"-to + } // TODO get all inscriptions at all addresses in wallet var inscriptions = await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); for (var inscription in inscriptions) { @@ -38,6 +64,16 @@ mixin OrdinalsInterface { } } + List getUniqueAddressesFromUTXOs(List utxos) { + final Set uniqueAddresses = {}; + for (var utxo in utxos) { + if (utxo.address != null) { + uniqueAddresses.add(utxo.address!); + } + } + return uniqueAddresses.toList(); + } + /* // ord-litecoin interface final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com'); From 4db0328c73a859f4550fedafd3486e508a599921 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 16:30:39 -0500 Subject: [PATCH 027/128] WIP _db not initialized, refresh on ordinals view for poc --- .../address_inscription_response.dart | 58 +------------------ lib/dto/ordinals/inscription_data.dart | 53 +++++++++++++++++ lib/pages/ordinals/ordinals_view.dart | 6 +- .../coins/litecoin/litecoin_wallet.dart | 1 + lib/services/litescribe_api.dart | 8 +-- lib/services/mixins/ordinals_interface.dart | 52 +++++++++-------- 6 files changed, 93 insertions(+), 85 deletions(-) create mode 100644 lib/dto/ordinals/inscription_data.dart diff --git a/lib/dto/ordinals/address_inscription_response.dart b/lib/dto/ordinals/address_inscription_response.dart index c424d73e0..240374284 100644 --- a/lib/dto/ordinals/address_inscription_response.dart +++ b/lib/dto/ordinals/address_inscription_response.dart @@ -1,4 +1,5 @@ import 'package:stackwallet/dto/ordinals/litescribe_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; class AddressInscriptionResponse extends LitescribeResponse { final int status; @@ -21,7 +22,7 @@ class AddressInscriptionResponse extends LitescribeResponse list; + final List list; final int total; AddressInscriptionResult({ @@ -31,61 +32,8 @@ class AddressInscriptionResult { factory AddressInscriptionResult.fromJson(Map json) { return AddressInscriptionResult( - list: (json['list'] as List).map((item) => AddressInscription.fromJson(item as Map)).toList(), + list: (json['list'] as List).map((item) => InscriptionData.fromJson(item as Map)).toList(), total: json['total'] as int, ); } } - -class AddressInscription { - 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; - - AddressInscription({ - 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 AddressInscription.fromJson(Map json) { - return AddressInscription( - 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, - ); - } -} diff --git a/lib/dto/ordinals/inscription_data.dart b/lib/dto/ordinals/inscription_data.dart new file mode 100644 index 000000000..b7bba8697 --- /dev/null +++ b/lib/dto/ordinals/inscription_data.dart @@ -0,0 +1,53 @@ +// 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, + ); + } +} diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 7a0572c78..f5f3ca23a 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -14,8 +14,9 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; -import 'package:stackwallet/services/litescribe_api.dart'; -import 'package:stackwallet/services/ordinals_api.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +// import 'package:stackwallet/services/litescribe_api.dart'; +// import 'package:stackwallet/services/ordinals_api.dart'; import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -28,6 +29,7 @@ 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 OrdinalsView extends ConsumerStatefulWidget { const OrdinalsView({ super.key, diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index 2edc32fc6..944182abd 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -136,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, diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index abc28123e..f7634b387 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:stackwallet/dto/ordinals/address_inscription_response.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/dto/ordinals/litescribe_response.dart'; class LitescribeAPI { @@ -34,7 +34,7 @@ class LitescribeAPI { } } - Future getInscriptionsByAddress(String address, {int cursor = 0, int size = 1000}) async { + 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 @@ -42,7 +42,7 @@ class LitescribeAPI { 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'] as List; + final list = response.data['result']['list'] as List; final int total = response.data['result']['total'] as int; final int currentSize = list.length; @@ -54,7 +54,7 @@ class LitescribeAPI { // TODO test logic with smaller size "pagination" } else { try { - return AddressInscriptionResponse.fromJson(response.data as Map); + return list; } catch (e) { throw const FormatException('LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure'); } diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index caac7a854..fbd41d80b 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -15,8 +15,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; // import 'package:stackwallet/dto/ordinals/preview_response.dart'; // import 'package:stackwallet/services/ordinals_api.dart'; -import 'package:stackwallet/dto/ordinals/address_inscription_response.dart'; // verbose due to Litescribe being the 2nd API import 'package:stackwallet/services/litescribe_api.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; mixin OrdinalsInterface { @@ -29,39 +29,29 @@ mixin OrdinalsInterface { required Coin coin, required MainDB db, }) { + print('init'); _walletId = walletId; _coin = coin; _db = db; } final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api'); - Future> getInscriptionsByAddress(String address) async { - try { - var response = await litescribeAPI.getInscriptionsByAddress(address); - print("Found ${response.result.total} inscription${response.result.total > 1 ? 's' : ''} at address $address"); // TODO disable (POC) - return response.result.list; - } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); - } - } + // Future> getInscriptionsByAddress(String address) async { + // try { + // var response = await litescribeAPI.getInscriptionsByAddress(address); + // // print("Found ${response.result.total} inscription${response.result.total > 1 ? 's' : ''} at address $address"); // TODO disable (POC) + // return response.result.list; + // } catch (e) { + // throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); + // } + // } void refreshInscriptions() async { List _inscriptions; final utxos = await _db.getUTXOs(_walletId).findAll(); final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - for (String address in uniqueAddresses) { - // TODO fetch all inscriptions from all addresses - // TODO save those inscriptions to isar, which a StreamBuilder will be "subscribed"-to - } - // TODO get all inscriptions at all addresses in wallet - var inscriptions = await getInscriptionsByAddress('ltc1qk4e8hdq5w6rvk5xvkxajjak78v45pkul8a2cg9'); - for (var inscription in inscriptions) { - print(inscription); - print(inscription.address); - print(inscription.content); - print(inscription.inscriptionId); - print(inscription.inscriptionNumber); - } + _inscriptions = await getAllInscriptionsFromAddresses(uniqueAddresses); + // TODO save inscriptions to isar which gets watched by a StreamBuilder } List getUniqueAddressesFromUTXOs(List utxos) { @@ -74,7 +64,21 @@ mixin OrdinalsInterface { return uniqueAddresses.toList(); } - /* // ord-litecoin interface + Future> getAllInscriptionsFromAddresses(List addresses) async { + List allInscriptions = []; + for (String address in addresses) { + try { + var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + print("Found ${inscriptions.length} inscription${inscriptions.length > 1 ? 's' : ''} at address $address"); + allInscriptions.addAll(inscriptions); + } catch (e) { + print("Error fetching inscriptions for address $address: $e"); + } + } + return allInscriptions; + } + +/* // ord-litecoin interface final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com'); Future fetchLatestInscriptions() async { From b773811eac6007dd878b4337711e7b2df963cdaa Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 16:49:26 -0500 Subject: [PATCH 028/128] working proof of concept --- lib/pages/ordinals/ordinals_view.dart | 10 +++++----- lib/services/litescribe_api.dart | 11 ++++++++--- lib/services/mixins/ordinals_interface.dart | 7 ++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index f5f3ca23a..8993ed4e7 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -15,8 +15,6 @@ import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; -// import 'package:stackwallet/services/litescribe_api.dart'; -// import 'package:stackwallet/services/ordinals_api.dart'; import 'package:stackwallet/services/mixins/ordinals_interface.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -29,7 +27,6 @@ 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 OrdinalsView extends ConsumerStatefulWidget { const OrdinalsView({ super.key, @@ -44,7 +41,7 @@ class OrdinalsView extends ConsumerStatefulWidget { ConsumerState createState() => _OrdinalsViewState(); } -class _OrdinalsViewState extends ConsumerState with OrdinalsInterface { +class _OrdinalsViewState extends ConsumerState { late final TextEditingController searchController; late final FocusNode searchFocus; @@ -94,7 +91,10 @@ class _OrdinalsViewState extends ConsumerState with OrdinalsInterf .topNavIconPrimary, ), onPressed: () async { - refreshInscriptions(); + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + + (manager.wallet as OrdinalsInterface).refreshInscriptions(); }, ), ), diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index f7634b387..7fc92910d 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -42,7 +42,7 @@ class LitescribeAPI { 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'] as List; + final list = response.data['result']['list']; final int total = response.data['result']['total'] as int; final int currentSize = list.length; @@ -51,10 +51,15 @@ class LitescribeAPI { // 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); - // TODO test logic with smaller size "pagination" + } else { try { - return list; + // 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'); } diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index fbd41d80b..a04b6522c 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:isar/isar.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; @@ -18,7 +20,6 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/services/litescribe_api.dart'; import 'package:stackwallet/dto/ordinals/inscription_data.dart'; - mixin OrdinalsInterface { late final String _walletId; late final Coin _coin; @@ -50,7 +51,7 @@ mixin OrdinalsInterface { List _inscriptions; final utxos = await _db.getUTXOs(_walletId).findAll(); final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - _inscriptions = await getAllInscriptionsFromAddresses(uniqueAddresses); + _inscriptions = await getInscriptionsFromAddresses(uniqueAddresses); // TODO save inscriptions to isar which gets watched by a StreamBuilder } @@ -64,7 +65,7 @@ mixin OrdinalsInterface { return uniqueAddresses.toList(); } - Future> getAllInscriptionsFromAddresses(List addresses) async { + Future> getInscriptionsFromAddresses(List addresses) async { List allInscriptions = []; for (String address in addresses) { try { From 20fdcf48174c8cb102e950fd3f8ed146be96998a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 20 Jul 2023 17:15:25 -0500 Subject: [PATCH 029/128] refactor manager var out to _manager, comment update --- lib/pages/ordinals/ordinals_view.dart | 9 +++++---- lib/services/mixins/ordinals_interface.dart | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 8993ed4e7..bf0545d4c 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -46,12 +46,16 @@ class _OrdinalsViewState extends ConsumerState { late final FocusNode searchFocus; String _searchTerm = ""; + dynamic _manager; @override void initState() { searchController = TextEditingController(); searchFocus = FocusNode(); + _manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + super.initState(); } @@ -91,10 +95,7 @@ class _OrdinalsViewState extends ConsumerState { .topNavIconPrimary, ), onPressed: () async { - final manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId))); - - (manager.wallet as OrdinalsInterface).refreshInscriptions(); + (_manager.wallet as OrdinalsInterface).refreshInscriptions(); }, ), ), diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index a04b6522c..2d887bfcd 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -52,7 +52,7 @@ mixin OrdinalsInterface { final utxos = await _db.getUTXOs(_walletId).findAll(); final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); _inscriptions = await getInscriptionsFromAddresses(uniqueAddresses); - // TODO save inscriptions to isar which gets watched by a StreamBuilder + // TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder } List getUniqueAddressesFromUTXOs(List utxos) { From af30826e9e9fa33502a5ac583eba14d0d4ff8dcd Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 10:03:05 -0500 Subject: [PATCH 030/128] remove ord-litecoin imports --- lib/dto/ordinals/address_response.dart | 52 ----------- lib/dto/ordinals/block_response.dart | 61 ------------- lib/dto/ordinals/content_response.dart | 22 ----- lib/dto/ordinals/feed_response.dart | 17 ---- lib/dto/ordinals/inscription_link.dart | 13 --- lib/dto/ordinals/inscription_response.dart | 97 --------------------- lib/dto/ordinals/ordinals_response.dart | 6 -- lib/dto/ordinals/output_response.dart | 47 ---------- lib/dto/ordinals/preview_response.dart | 22 ----- lib/dto/ordinals/sat_response.dart | 85 ------------------ lib/dto/ordinals/transaction_response.dart | 82 ----------------- lib/services/mixins/ordinals_interface.dart | 12 --- 12 files changed, 516 deletions(-) delete mode 100644 lib/dto/ordinals/address_response.dart delete mode 100644 lib/dto/ordinals/block_response.dart delete mode 100644 lib/dto/ordinals/content_response.dart delete mode 100644 lib/dto/ordinals/feed_response.dart delete mode 100644 lib/dto/ordinals/inscription_link.dart delete mode 100644 lib/dto/ordinals/inscription_response.dart delete mode 100644 lib/dto/ordinals/ordinals_response.dart delete mode 100644 lib/dto/ordinals/output_response.dart delete mode 100644 lib/dto/ordinals/preview_response.dart delete mode 100644 lib/dto/ordinals/sat_response.dart delete mode 100644 lib/dto/ordinals/transaction_response.dart diff --git a/lib/dto/ordinals/address_response.dart b/lib/dto/ordinals/address_response.dart deleted file mode 100644 index 9136aa523..000000000 --- a/lib/dto/ordinals/address_response.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:stackwallet/dto/ordinals/inscription_link.dart'; -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class AddressResponse extends OrdinalsResponse { - final AddressLinks links; - final String address; - final List inscriptions; - - AddressResponse({ - required this.links, - required this.address, - required this.inscriptions, - }); - - factory AddressResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - final inscriptionsJson = data['inscriptions'] as List; - final inscriptions = inscriptionsJson - .map((inscriptionJson) => InscriptionLink.fromJson(inscriptionJson as Map)) - .toList(); - - return AddressResponse( - links: AddressLinks.fromJson(data['_links'] as Map), - address: data['address'] as String, - inscriptions: inscriptions, - ); - } -} - -class AddressLinks { - final AddressLink? self; - - AddressLinks({ - this.self, - }); - - factory AddressLinks.fromJson(Map json) { - return AddressLinks( - self: json['self'] != null ? AddressLink.fromJson(json['self'] as Map) : null, - ); - } -} - -class AddressLink { - final String href; - - AddressLink({required this.href}); - - factory AddressLink.fromJson(Map json) { - return AddressLink(href: json['href'] as String); - } -} diff --git a/lib/dto/ordinals/block_response.dart b/lib/dto/ordinals/block_response.dart deleted file mode 100644 index 0eef8d569..000000000 --- a/lib/dto/ordinals/block_response.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class BlockResponse extends OrdinalsResponse { - final BlockLinks links; - final String hash; - final String previousBlockhash; - final int size; - final String target; - final String timestamp; - final int weight; - - BlockResponse({ - required this.links, - required this.hash, - required this.previousBlockhash, - required this.size, - required this.target, - required this.timestamp, - required this.weight, - }); - - factory BlockResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - return BlockResponse( - links: BlockLinks.fromJson(data['_links'] as Map), - hash: data['hash'] as String, - previousBlockhash: data['previous_blockhash'] as String, - size: data['size'] as int, - target: data['target'] as String, - timestamp: data['timestamp'] as String, - weight: data['weight'] as int, - ); - } -} - -class BlockLinks { - final BlockLink? prev; - final BlockLink? self; - - BlockLinks({ - this.prev, - this.self, - }); - - factory BlockLinks.fromJson(Map json) { - return BlockLinks( - prev: json['prev'] != null ? BlockLink.fromJson(json['prev'] as Map) : null, - self: json['self'] != null ? BlockLink.fromJson(json['self'] as Map) : null, - ); - } -} - -class BlockLink { - final String href; - - BlockLink({required this.href}); - - factory BlockLink.fromJson(Map json) { - return BlockLink(href: json['href'] as String); - } -} diff --git a/lib/dto/ordinals/content_response.dart b/lib/dto/ordinals/content_response.dart deleted file mode 100644 index 7cfbaf9fd..000000000 --- a/lib/dto/ordinals/content_response.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class ContentResponse extends OrdinalsResponse { - final FileLink fileLink; - - ContentResponse({required this.fileLink}); - - factory ContentResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - return ContentResponse(fileLink: FileLink.fromJson(data['_links']['file'] as Map)); // TODO don't cast as Map - } -} - -class FileLink { - final String href; - - FileLink({required this.href}); - - factory FileLink.fromJson(Map json) { - return FileLink(href: json['href'] as String); - } -} diff --git a/lib/dto/ordinals/feed_response.dart b/lib/dto/ordinals/feed_response.dart deleted file mode 100644 index 525a7f727..000000000 --- a/lib/dto/ordinals/feed_response.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:stackwallet/dto/ordinals/inscription_link.dart'; -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class FeedResponse extends OrdinalsResponse { - final List inscriptions; - - FeedResponse({required this.inscriptions}); - - factory FeedResponse.fromJson(OrdinalsResponse json) { - final List inscriptionsJson = json.data['_links']['inscriptions'] as List; - final List inscriptions = inscriptionsJson - .map((json) => InscriptionLink.fromJson(json as Map)) - .toList(); - - return FeedResponse(inscriptions: inscriptions); - } -} \ No newline at end of file diff --git a/lib/dto/ordinals/inscription_link.dart b/lib/dto/ordinals/inscription_link.dart deleted file mode 100644 index f23b63248..000000000 --- a/lib/dto/ordinals/inscription_link.dart +++ /dev/null @@ -1,13 +0,0 @@ -class InscriptionLink { - final String href; - final String title; - - InscriptionLink({required this.href, required this.title}); - - factory InscriptionLink.fromJson(Map json) { - return InscriptionLink( - href: json['href'] as String ?? '', - title: json['title'] as String ?? '', - ); - } -} diff --git a/lib/dto/ordinals/inscription_response.dart b/lib/dto/ordinals/inscription_response.dart deleted file mode 100644 index d45300aee..000000000 --- a/lib/dto/ordinals/inscription_response.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class InscriptionResponse extends OrdinalsResponse { - late final Links links; - late final String address; - late final int contentLength; - late final String contentType; - late final int genesisFee; - late final int genesisHeight; - late final String genesisTransaction; - late final String location; - late final int number; - late final int offset; - late final String output; - late final String? sat; // Make sure to update the type to allow null - late final String timestamp; - - InscriptionResponse({ - required this.links, - required this.address, - required this.contentLength, - required this.contentType, - required this.genesisFee, - required this.genesisHeight, - required this.genesisTransaction, - required this.location, - required this.number, - required this.offset, - required this.output, - required this.sat, - required this.timestamp, - }); - - factory InscriptionResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - return InscriptionResponse( - links: Links.fromJson(data['_links'] as Map), - address: data['address'] as String, - contentLength: data['content_length'] as int, - contentType: data['content_type'] as String, - genesisFee: data['genesis_fee'] as int, - genesisHeight: data['genesis_height'] as int, - genesisTransaction: data['genesis_transaction'] as String, - location: data['location'] as String, - number: data['number'] as int, - offset: data['offset'] as int, - output: data['output'] as String, - sat: data['sat'] as String?, - timestamp: data['timestamp'] as String, - ); - } -} - -class Links { - late final Link content; - late final Link genesisTransaction; - late final Link next; - late final Link output; - late final Link prev; - late final Link preview; - late final Link? sat; // Make sure to update the type to allow null - late final Link self; - - Links({ - required this.content, - required this.genesisTransaction, - required this.next, - required this.output, - required this.prev, - required this.preview, - this.sat, - required this.self, - }); - - factory Links.fromJson(Map json) { - return Links( - content: Link.fromJson(json['content'] as Map), - genesisTransaction: Link.fromJson(json['genesis_transaction'] as Map), - next: Link.fromJson(json['next'] as Map), - output: Link.fromJson(json['output'] as Map), - prev: Link.fromJson(json['prev'] as Map), - preview: Link.fromJson(json['preview'] as Map), - sat: json['sat'] != null ? Link.fromJson(json['sat'] as Map) : null, - self: Link.fromJson(json['self'] as Map), - ); - } -} - -class Link { - late final String href; - - Link({required this.href}); - - factory Link.fromJson(Map json) { - return Link(href: json['href'] as String); - } -} diff --git a/lib/dto/ordinals/ordinals_response.dart b/lib/dto/ordinals/ordinals_response.dart deleted file mode 100644 index bf57db46b..000000000 --- a/lib/dto/ordinals/ordinals_response.dart +++ /dev/null @@ -1,6 +0,0 @@ -class OrdinalsResponse { - final T? data; - final String? error; - - OrdinalsResponse({this.data, this.error}); -} diff --git a/lib/dto/ordinals/output_response.dart b/lib/dto/ordinals/output_response.dart deleted file mode 100644 index cc7b2107f..000000000 --- a/lib/dto/ordinals/output_response.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:stackwallet/dto/ordinals/transaction_response.dart'; -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class OutputResponse extends OrdinalsResponse { - final OutputLinks links; - final String address; - final String scriptPubkey; - final String transaction; - final int value; - - OutputResponse({ - required this.links, - required this.address, - required this.scriptPubkey, - required this.transaction, - required this.value, - }); - - factory OutputResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - - return OutputResponse( - links: OutputLinks.fromJson(data['_links'] as Map), - address: data['address'] as String, - scriptPubkey: data['script_pubkey'] as String, - transaction: data['transaction'] as String, - value: data['value'] as int, - ); - } -} - -class OutputLinks { - final OutputLink? self; - final TransactionLink? transaction; - - OutputLinks({ - this.self, - this.transaction, - }); - - factory OutputLinks.fromJson(Map json) { - return OutputLinks( - self: json['self'] != null ? OutputLink.fromJson(json['self'] as Map) : null, - transaction: json['transaction'] != null ? TransactionLink.fromJson(json['transaction'] as Map) : null, - ); - } -} diff --git a/lib/dto/ordinals/preview_response.dart b/lib/dto/ordinals/preview_response.dart deleted file mode 100644 index b3e184acd..000000000 --- a/lib/dto/ordinals/preview_response.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class PreviewResponse extends OrdinalsResponse { - final ImageLink imageLink; - - PreviewResponse({required this.imageLink}); - - factory PreviewResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - return PreviewResponse(imageLink: ImageLink.fromJson(data['_links']['image'] as Map)); - } -} - -class ImageLink { - final String href; - - ImageLink({required this.href}); - - factory ImageLink.fromJson(Map json) { - return ImageLink(href: json['href'] as String); - } -} diff --git a/lib/dto/ordinals/sat_response.dart b/lib/dto/ordinals/sat_response.dart deleted file mode 100644 index 40efb1440..000000000 --- a/lib/dto/ordinals/sat_response.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class SatResponse extends OrdinalsResponse { - final SatLinks links; - final int block; - final int cycle; - final String decimal; - final String degree; - final int epoch; - final String name; - final int offset; - final String percentile; - final int period; - final String rarity; - final String timestamp; - - SatResponse({ - required this.links, - required this.block, - required this.cycle, - required this.decimal, - required this.degree, - required this.epoch, - required this.name, - required this.offset, - required this.percentile, - required this.period, - required this.rarity, - required this.timestamp, - }); - - factory SatResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - return SatResponse( - links: SatLinks.fromJson(data['_links'] as Map), - block: data['block'] as int, - cycle: data['cycle'] as int, - decimal: data['decimal'] as String, - degree: data['degree'] as String, - epoch: data['epoch'] as int, - name: data['name'] as String, - offset: data['offset'] as int, - percentile: data['percentile'] as String, - period: data['period'] as int, - rarity: data['rarity'] as String, - timestamp: data['timestamp'] as String, - ); - } -} - -class SatLinks { - final SatLink? block; - final SatLink? inscription; - final SatLink? next; - final SatLink? prev; - final SatLink? self; - - SatLinks({ - this.block, - this.inscription, - this.next, - this.prev, - this.self, - }); - - factory SatLinks.fromJson(Map json) { - return SatLinks( - block: json['block'] != null ? SatLink.fromJson(json['block'] as Map) : null, - inscription: json['inscription'] != null ? SatLink.fromJson(json['inscription'] as Map) : null, - next: json['next'] != null ? SatLink.fromJson(json['next'] as Map) : null, - prev: json['prev'] != null ? SatLink.fromJson(json['prev'] as Map) : null, - self: json['self'] != null ? SatLink.fromJson(json['self'] as Map) : null, - ); - } -} - -class SatLink { - final String href; - - SatLink({required this.href}); - - factory SatLink.fromJson(Map json) { - return SatLink(href: json['href'] as String); - } -} diff --git a/lib/dto/ordinals/transaction_response.dart b/lib/dto/ordinals/transaction_response.dart deleted file mode 100644 index c77e07914..000000000 --- a/lib/dto/ordinals/transaction_response.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:stackwallet/dto/ordinals/inscription_link.dart'; -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; - -class TransactionResponse extends OrdinalsResponse { - final TransactionLinks links; - final List inputs; - final InscriptionLink inscription; - final List outputs; - final TransactionLink self; - final String transaction; - - TransactionResponse({ - required this.links, - required this.inputs, - required this.inscription, - required this.outputs, - required this.self, - required this.transaction, - }); - - factory TransactionResponse.fromJson(OrdinalsResponse json) { - final data = json.data as Map; - final inputsJson = data['_links']['inputs'] as List; - final inputs = inputsJson - .map((inputJson) => OutputLink.fromJson(inputJson as Map)) - .toList(); - - final outputsJson = data['_links']['outputs'] as List; - final outputs = outputsJson - .map((outputJson) => OutputLink.fromJson(outputJson as Map)) - .toList(); - - return TransactionResponse( - links: TransactionLinks.fromJson(data['_links'] as Map), - inputs: inputs, - inscription: InscriptionLink.fromJson(data['_links']['inscription'] as Map), - outputs: outputs, - self: TransactionLink.fromJson(data['_links']['self'] as Map), - transaction: data['transaction'] as String, - ); - } -} - -class TransactionLinks { - final TransactionLink? block; - final InscriptionLink? inscription; - final TransactionLink? self; - - TransactionLinks({ - this.block, - this.inscription, - this.self, - }); - - factory TransactionLinks.fromJson(Map json) { - return TransactionLinks( - block: json['block'] != null ? TransactionLink.fromJson(json['block'] as Map) : null, - inscription: json['inscription'] != null ? InscriptionLink.fromJson(json['inscription'] as Map) : null, - self: json['self'] != null ? TransactionLink.fromJson(json['self'] as Map) : null, - ); - } -} - -class TransactionLink { - final String href; - - TransactionLink({required this.href}); - - factory TransactionLink.fromJson(Map json) { - return TransactionLink(href: json['href'] as String); - } -} - -class OutputLink { - final String href; - - OutputLink({required this.href}); - - factory OutputLink.fromJson(Map json) { - return OutputLink(href: json['href'] as String); - } -} diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 2d887bfcd..c7bc5ff28 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -5,18 +5,6 @@ import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; -// ord-litecoin-specific imports -// import 'package:stackwallet/dto/ordinals/feed_response.dart'; -// import 'package:stackwallet/dto/ordinals/inscription_response.dart'; -// import 'package:stackwallet/dto/ordinals/sat_response.dart'; -// import 'package:stackwallet/dto/ordinals/transaction_response.dart'; -// import 'package:stackwallet/dto/ordinals/output_response.dart'; -// import 'package:stackwallet/dto/ordinals/address_response.dart'; -// import 'package:stackwallet/dto/ordinals/block_response.dart'; -// import 'package:stackwallet/dto/ordinals/content_response.dart'; -// import 'package:stackwallet/dto/ordinals/preview_response.dart'; -// import 'package:stackwallet/services/ordinals_api.dart'; - import 'package:stackwallet/services/litescribe_api.dart'; import 'package:stackwallet/dto/ordinals/inscription_data.dart'; From 39eaa937fc83fb3aec9882ad1b6744ff1367abef Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 10:06:34 -0500 Subject: [PATCH 031/128] add convenience method, remove ord-litecoin API file, cast dynamic->int --- lib/services/litescribe_api.dart | 2 +- lib/services/mixins/ordinals_interface.dart | 6 ++ lib/services/ordinals_api.dart | 89 --------------------- 3 files changed, 7 insertions(+), 90 deletions(-) delete mode 100644 lib/services/ordinals_api.dart diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index 7fc92910d..d5cd3d733 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -44,7 +44,7 @@ class LitescribeAPI { // 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; + 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, diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index c7bc5ff28..51323fa0c 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -43,6 +43,12 @@ mixin OrdinalsInterface { // TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder } + Future> getInscriptions() async { + final utxos = await _db.getUTXOs(_walletId).findAll(); + final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); + return await getInscriptionsFromAddresses(uniqueAddresses); + } + List getUniqueAddressesFromUTXOs(List utxos) { final Set uniqueAddresses = {}; for (var utxo in utxos) { diff --git a/lib/services/ordinals_api.dart b/lib/services/ordinals_api.dart deleted file mode 100644 index e6df3c05e..000000000 --- a/lib/services/ordinals_api.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'dart:convert'; -import 'package:http/http.dart' as http; - -import 'package:stackwallet/dto/ordinals/ordinals_response.dart'; -import 'package:stackwallet/dto/ordinals/feed_response.dart'; -import 'package:stackwallet/dto/ordinals/inscription_response.dart'; -import 'package:stackwallet/dto/ordinals/sat_response.dart'; -import 'package:stackwallet/dto/ordinals/transaction_response.dart'; -import 'package:stackwallet/dto/ordinals/output_response.dart'; -import 'package:stackwallet/dto/ordinals/address_response.dart'; -import 'package:stackwallet/dto/ordinals/block_response.dart'; -import 'package:stackwallet/dto/ordinals/content_response.dart'; -import 'package:stackwallet/dto/ordinals/preview_response.dart'; - -class OrdinalsAPI { - static final OrdinalsAPI _instance = OrdinalsAPI._internal(); - - factory OrdinalsAPI({required String baseUrl}) { - _instance.baseUrl = baseUrl; - return _instance; - } - - OrdinalsAPI._internal(); - - late String baseUrl; - - Future _getResponse(String endpoint) async { - final response = await http.get(Uri.parse('$baseUrl$endpoint')); - if (response.statusCode == 200) { - return OrdinalsResponse(data: _validateJson(response.body)); - } else { - throw Exception('Failed to load data'); - } - } - - Map _validateJson(String responseBody) { - final parsed = jsonDecode(responseBody); - if (parsed is Map) { - return parsed; - } else { - throw const FormatException('Invalid JSON format'); - } - } - - Future getLatestInscriptions() async { - final response = await _getResponse('/feed'); - return FeedResponse.fromJson(response); - } - - Future getInscriptionDetails(String inscriptionId) async { - final response = await _getResponse('/inscription/$inscriptionId'); - return InscriptionResponse.fromJson(response); - } - - Future getSatDetails(int satNumber) async { - final response = await _getResponse('/sat/$satNumber'); - return SatResponse.fromJson(response); - } - - Future getTransaction(String transactionId) async { - final response = await _getResponse('/tx/$transactionId'); - return TransactionResponse.fromJson(response); - } - - Future getTransactionOutputs(String transactionId) async { - final response = await _getResponse('/output/$transactionId'); - return OutputResponse.fromJson(response); - } - - Future getInscriptionsByAddress(String address) async { - final response = await _getResponse('/address/$address'); - return AddressResponse.fromJson(response); - } - - Future getBlock(String blockHash) async { - final response = await _getResponse('/block/$blockHash'); - return BlockResponse.fromJson(response); - } - - Future getInscriptionContent(String inscriptionId) async { - final response = await _getResponse('/content/$inscriptionId'); - return ContentResponse.fromJson(response); - } - - Future getInscriptionPreview(String inscriptionId) async { - final response = await _getResponse('/preview/$inscriptionId'); - return PreviewResponse.fromJson(response); - } -} From c295ca9a6f1c7146960b455c3bb428a82fe7de72 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 10:12:31 -0500 Subject: [PATCH 032/128] fix ordinal view crash --- lib/pages/ordinals/ordinals_view.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index bf0545d4c..c340fa6c6 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -53,9 +53,6 @@ class _OrdinalsViewState extends ConsumerState { searchController = TextEditingController(); searchFocus = FocusNode(); - _manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId))); - super.initState(); } @@ -95,6 +92,8 @@ class _OrdinalsViewState extends ConsumerState { .topNavIconPrimary, ), onPressed: () async { + _manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); (_manager.wallet as OrdinalsInterface).refreshInscriptions(); }, ), From f750bbfe102239ec7d0f7ac195896ca23218693b Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 10:30:47 -0500 Subject: [PATCH 033/128] hook OrdinalsList up to OrdinalsInterface --- lib/pages/ordinals/ordinals_view.dart | 22 +++---- lib/pages/ordinals/widgets/ordinal_card.dart | 24 +++---- lib/pages/ordinals/widgets/ordinals_list.dart | 62 +++++++++++-------- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index c340fa6c6..b12fb78c4 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -56,6 +56,14 @@ class _OrdinalsViewState extends ConsumerState { super.initState(); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // Set _manager here when the widget's dependencies change + _manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + } + @override void dispose() { searchController.dispose(); @@ -92,8 +100,6 @@ class _OrdinalsViewState extends ConsumerState { .topNavIconPrimary, ), onPressed: () async { - _manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId))); (_manager.wallet as OrdinalsInterface).refreshInscriptions(); }, ), @@ -187,17 +193,7 @@ class _OrdinalsViewState extends ConsumerState { Expanded( child: OrdinalsList( walletId: widget.walletId, - ordinals: [ - for (int i = 0; i < 13; i++) - Ordinal( - name: "dummy name $i", - inscription: "insc$i", - rank: "r$i", - collection: OrdCollection.moonbirds, - utxoTXID: 'txid', - utxoVOUT: 1 - ), - ], + ordinalsFuture: (_manager.wallet as OrdinalsInterface).getInscriptions(), ), ), ], diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index a3419ae87..a4d58711e 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -1,18 +1,19 @@ import 'package:flutter/material.dart'; -import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/ordinal.dart'; // TODO generalize InscriptionData models -> Ordinal import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; class OrdinalCard extends StatelessWidget { const OrdinalCard({ - super.key, + Key? key, required this.walletId, - required this.ordinal, - }); + required this.inscriptionData, + }) : super(key: key); final String walletId; - final Ordinal ordinal; + final InscriptionData inscriptionData; @override Widget build(BuildContext context) { @@ -21,7 +22,7 @@ class OrdinalCard extends StatelessWidget { onPressed: () { Navigator.of(context).pushNamed( OrdinalDetailsView.routeName, - arguments: (walletId: walletId, ordinal: ordinal), + arguments: (walletId: walletId, inscriptionData: inscriptionData), ); }, child: Column( @@ -31,21 +32,20 @@ class OrdinalCard extends StatelessWidget { aspectRatio: 1, child: Container( color: Colors.red, - child: const Center( - child: Text( - "replace red container with image", - ), + child: Image.network( + inscriptionData.preview, // Use the preview URL as the image source + fit: BoxFit.cover, ), ), ), const Spacer(), Text( - ordinal.name, + inscriptionData.address, style: STextStyles.w500_12(context), ), const Spacer(), Text( - "INSC. ${ordinal.inscription} RANK ${ordinal.rank}", + "INSC. ${inscriptionData.inscriptionNumber} ID ${inscriptionData.inscriptionId}", style: STextStyles.w500_8(context), ), ], diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index ca7c71446..d161cafeb 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -1,39 +1,49 @@ import 'package:flutter/material.dart'; -import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; + import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; -class OrdinalsList extends StatefulWidget { +class OrdinalsList extends StatelessWidget { const OrdinalsList({ - super.key, + Key? key, required this.walletId, - required this.ordinals, - }); + required this.ordinalsFuture, + }) : super(key: key); final String walletId; - final List ordinals; + final Future> ordinalsFuture; - @override - State createState() => _OrdinalsListState(); -} - -class _OrdinalsListState extends State { - static const spacing = 10.0; + get spacing => 2.0; @override Widget build(BuildContext context) { - return GridView.builder( - shrinkWrap: true, - itemCount: widget.ordinals.length, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisSpacing: spacing, - mainAxisSpacing: spacing, - crossAxisCount: 2, - childAspectRatio: 3 / 4, - ), - itemBuilder: (_, i) => OrdinalCard( - walletId: widget.walletId, - ordinal: widget.ordinals[i], - ), + return FutureBuilder>( + future: ordinalsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const CircularProgressIndicator(); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + final List inscriptions = snapshot.data!; + return GridView.builder( + shrinkWrap: true, + itemCount: inscriptions.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: spacing as double, + mainAxisSpacing: spacing as double, + crossAxisCount: 2, + childAspectRatio: 3 / 4, + ), + itemBuilder: (_, i) => OrdinalCard( + walletId: walletId, + inscriptionData: inscriptions[i], + ), + ); + } else { + return Text('No data found.'); + } + }, ); } -} +} \ No newline at end of file From 65e8c34e34fd33dabff5909d3ee0666a06e1f50b Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 10:43:02 -0500 Subject: [PATCH 034/128] hook up details view to OrdinalsInterface --- lib/pages/ordinals/ordinal_details_view.dart | 47 ++++++++++---------- lib/route_generator.dart | 10 ++--- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 2057e8792..c1ca67500 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -15,20 +15,21 @@ 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({ - super.key, + Key? key, required this.walletId, - required this.ordinal, - }); + required this.inscriptionData, + }) : super(key: key); final String walletId; - final Ordinal ordinal; + final InscriptionData inscriptionData; static const routeName = "/ordinalDetailsView"; @override - State createState() => _OrdinalDetailsViewState(); + _OrdinalDetailsViewState createState() => _OrdinalDetailsViewState(); } class _OrdinalDetailsViewState extends State { @@ -40,10 +41,10 @@ class _OrdinalDetailsViewState extends State { child: SafeArea( child: Scaffold( backgroundColor: - Theme.of(context).extension()!.background, + Theme.of(context).extension()!.background, appBar: AppBar( backgroundColor: - Theme.of(context).extension()!.background, + Theme.of(context).extension()!.background, leading: const AppBarBackButton(), title: Text( "Ordinal details", @@ -61,20 +62,20 @@ class _OrdinalDetailsViewState extends State { horizontal: 39, ), child: _OrdinalImageGroup( - ordinal: widget.ordinal, + inscriptionData: widget.inscriptionData, walletId: widget.walletId, ), ), _DetailsItemWCopy( title: "Inscription number", - data: widget.ordinal.inscription, + data: widget.inscriptionData.inscriptionNumber.toString(), ), const SizedBox( height: _spacing, ), _DetailsItemWCopy( - title: "Rank", - data: widget.ordinal.rank, + title: "ID", + data: widget.inscriptionData.inscriptionId, ), const SizedBox( height: _spacing, @@ -85,21 +86,21 @@ class _OrdinalDetailsViewState extends State { ), _DetailsItemWCopy( title: "Amount", - data: "FIXME", + data: "${widget.inscriptionData.outputValue}", ), const SizedBox( height: _spacing, ), _DetailsItemWCopy( title: "Owner address", - data: "FIXME", + data: widget.inscriptionData.address, ), const SizedBox( height: _spacing, ), _DetailsItemWCopy( title: "Transaction ID", - data: "FIXME", + data: widget.inscriptionData.genesisTransaction, ), const SizedBox( height: _spacing, @@ -116,10 +117,10 @@ class _OrdinalDetailsViewState extends State { class _DetailsItemWCopy extends StatelessWidget { const _DetailsItemWCopy({ - super.key, + Key? key, required this.title, required this.data, - }); + }) : super(key: key); final String title; final String data; @@ -153,7 +154,7 @@ class _DetailsItemWCopy extends StatelessWidget { child: SvgPicture.asset( Assets.svg.copy, color: - Theme.of(context).extension()!.infoItemIcons, + Theme.of(context).extension()!.infoItemIcons, width: 12, ), ), @@ -174,13 +175,13 @@ class _DetailsItemWCopy extends StatelessWidget { class _OrdinalImageGroup extends StatelessWidget { const _OrdinalImageGroup({ - super.key, + Key? key, required this.walletId, - required this.ordinal, - }); + required this.inscriptionData, + }) : super(key: key); final String walletId; - final Ordinal ordinal; + final InscriptionData inscriptionData; static const _spacing = 12.0; @@ -191,7 +192,7 @@ class _OrdinalImageGroup extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( - ordinal.name, + "${inscriptionData.inscriptionId}", // Use any other property you want style: STextStyles.w600_16(context), ), const SizedBox( diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 6217cf201..e67254135 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -12,13 +12,15 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart'; 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/ordinal.dart'; +import 'package:stackwallet/models/ordinal.dart'; // TODO generalize InscriptionData -> Ordinal 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'; @@ -168,8 +170,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. @@ -423,12 +423,12 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case OrdinalDetailsView.routeName: - if (args is ({Ordinal ordinal, String walletId})) { + if (args is ({InscriptionData inscriptionData, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => OrdinalDetailsView( walletId: args.walletId, - ordinal: args.ordinal, + inscriptionData: args.inscriptionData, ), settings: RouteSettings( name: settings.name, From ddba1c54f76fcb82ede425cc6676813640b12295 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 10:49:30 -0500 Subject: [PATCH 035/128] show ordinal image --- lib/pages/ordinals/ordinal_details_view.dart | 12 ++++++++++-- lib/pages/ordinals/widgets/ordinal_card.dart | 9 +++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index c1ca67500..bd5b9a17e 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -200,8 +200,16 @@ class _OrdinalImageGroup extends StatelessWidget { ), AspectRatio( aspectRatio: 1, - child: Container( - color: Colors.red, + child: AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.red, + child: Image.network( + inscriptionData.content, // Use the preview URL as the image source + fit: BoxFit.cover, + filterQuality: FilterQuality.none, // Set the filter mode to nearest + ), + ), ), ), const SizedBox( diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index a4d58711e..0c05f686a 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -28,13 +28,14 @@ class OrdinalCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - AspectRatio( - aspectRatio: 1, - child: Container( + AspectRatio( + aspectRatio: 1, + child: Container( color: Colors.red, child: Image.network( - inscriptionData.preview, // Use the preview URL as the image source + inscriptionData.content, // Use the preview URL as the image source fit: BoxFit.cover, + filterQuality: FilterQuality.none, // Set the filter mode to nearest ), ), ), From f46a37d4d4775cb9f8bc3feb825350e577d202f2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 11:48:31 -0500 Subject: [PATCH 036/128] convert/map/cast InscriptionData->Ordinal --- lib/models/ordinal.dart | 33 ++-- lib/pages/ordinals/ordinal_details_view.dart | 30 ++-- lib/pages/ordinals/ordinals_view.dart | 2 +- lib/pages/ordinals/widgets/ordinal_card.dart | 16 +- lib/pages/ordinals/widgets/ordinals_list.dart | 12 +- lib/route_generator.dart | 4 +- lib/services/mixins/ordinals_interface.dart | 146 ++++++------------ 7 files changed, 99 insertions(+), 144 deletions(-) diff --git a/lib/models/ordinal.dart b/lib/models/ordinal.dart index 1feb98b9d..66a69de94 100644 --- a/lib/models/ordinal.dart +++ b/lib/models/ordinal.dart @@ -1,26 +1,29 @@ -enum OrdCollection { - punks, - moonbirds, -} +import 'package:stackwallet/dto/ordinals/inscription_data.dart'; class Ordinal { - final String name; - final String inscription; - final String rank; - final OrdCollection collection; + 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; - // TODO: make a proper Isar class instead of this placeholder - Ordinal({ - required this.name, - required this.inscription, - required this.rank, - required this.collection, + required this.inscriptionId, + required this.inscriptionNumber, + required this.content, required this.utxoTXID, required this.utxoVOUT, }); -} + + factory Ordinal.fromInscriptionData(InscriptionData data) { + return Ordinal( + inscriptionId: data.inscriptionId, + inscriptionNumber: data.inscriptionNumber, + content: data.content, + utxoTXID: data.output.split(':')[0], // "output": "062f32e21aa04246b8873b5d9a929576addd0339881e1ea478b406795d6b6c47:0" + utxoVOUT: int.parse(data.output.split(':')[1]), + ); + } +} \ No newline at end of file diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index bd5b9a17e..6aab95586 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/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'; @@ -15,16 +16,15 @@ 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.inscriptionData, + required this.ordinal, }) : super(key: key); final String walletId; - final InscriptionData inscriptionData; + final Ordinal ordinal; static const routeName = "/ordinalDetailsView"; @@ -62,20 +62,20 @@ class _OrdinalDetailsViewState extends State { horizontal: 39, ), child: _OrdinalImageGroup( - inscriptionData: widget.inscriptionData, + ordinal: widget.ordinal, walletId: widget.walletId, ), ), _DetailsItemWCopy( title: "Inscription number", - data: widget.inscriptionData.inscriptionNumber.toString(), + data: widget.ordinal.inscriptionNumber.toString(), ), const SizedBox( height: _spacing, ), _DetailsItemWCopy( title: "ID", - data: widget.inscriptionData.inscriptionId, + data: widget.ordinal.inscriptionId, ), const SizedBox( height: _spacing, @@ -84,23 +84,23 @@ class _OrdinalDetailsViewState extends State { const SizedBox( height: _spacing, ), - _DetailsItemWCopy( + const _DetailsItemWCopy( title: "Amount", - data: "${widget.inscriptionData.outputValue}", + data: "TODO", // TODO infer from utxo utxoTXID:utxoVOUT ), const SizedBox( height: _spacing, ), - _DetailsItemWCopy( + const _DetailsItemWCopy( title: "Owner address", - data: widget.inscriptionData.address, + data: "TODO", // infer from address associated w utxoTXID ), const SizedBox( height: _spacing, ), _DetailsItemWCopy( title: "Transaction ID", - data: widget.inscriptionData.genesisTransaction, + data: widget.ordinal.utxoTXID, ), const SizedBox( height: _spacing, @@ -177,11 +177,11 @@ class _OrdinalImageGroup extends StatelessWidget { const _OrdinalImageGroup({ Key? key, required this.walletId, - required this.inscriptionData, + required this.ordinal, }) : super(key: key); final String walletId; - final InscriptionData inscriptionData; + final Ordinal ordinal; static const _spacing = 12.0; @@ -192,7 +192,7 @@ class _OrdinalImageGroup extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( - "${inscriptionData.inscriptionId}", // Use any other property you want + "${ordinal.inscriptionId}", // Use any other property you want style: STextStyles.w600_16(context), ), const SizedBox( @@ -205,7 +205,7 @@ class _OrdinalImageGroup extends StatelessWidget { child: Container( color: Colors.red, child: Image.network( - inscriptionData.content, // Use the preview URL as the image source + ordinal.content, // Use the preview URL as the image source fit: BoxFit.cover, filterQuality: FilterQuality.none, // Set the filter mode to nearest ), diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index b12fb78c4..a4adfa019 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -193,7 +193,7 @@ class _OrdinalsViewState extends ConsumerState { Expanded( child: OrdinalsList( walletId: widget.walletId, - ordinalsFuture: (_manager.wallet as OrdinalsInterface).getInscriptions(), + ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(), ), ), ], diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index 0c05f686a..c9060b32e 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:stackwallet/dto/ordinals/inscription_data.dart'; -import 'package:stackwallet/models/ordinal.dart'; // TODO generalize InscriptionData models -> Ordinal + +import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -9,11 +9,11 @@ class OrdinalCard extends StatelessWidget { const OrdinalCard({ Key? key, required this.walletId, - required this.inscriptionData, + required this.ordinal, }) : super(key: key); final String walletId; - final InscriptionData inscriptionData; + final Ordinal ordinal; @override Widget build(BuildContext context) { @@ -22,7 +22,7 @@ class OrdinalCard extends StatelessWidget { onPressed: () { Navigator.of(context).pushNamed( OrdinalDetailsView.routeName, - arguments: (walletId: walletId, inscriptionData: inscriptionData), + arguments: (walletId: walletId, ordinal: ordinal), ); }, child: Column( @@ -33,7 +33,7 @@ class OrdinalCard extends StatelessWidget { child: Container( color: Colors.red, child: Image.network( - inscriptionData.content, // Use the preview URL as the image source + ordinal.content, // Use the preview URL as the image source fit: BoxFit.cover, filterQuality: FilterQuality.none, // Set the filter mode to nearest ), @@ -41,12 +41,12 @@ class OrdinalCard extends StatelessWidget { ), const Spacer(), Text( - inscriptionData.address, + 'TODO', // infer from address associated with utxoTXID style: STextStyles.w500_12(context), ), const Spacer(), Text( - "INSC. ${inscriptionData.inscriptionNumber} ID ${inscriptionData.inscriptionId}", + "INSC. ${ordinal.inscriptionNumber} 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 index d161cafeb..fe7618cd0 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; @@ -11,13 +11,13 @@ class OrdinalsList extends StatelessWidget { }) : super(key: key); final String walletId; - final Future> ordinalsFuture; + final Future> ordinalsFuture; get spacing => 2.0; @override Widget build(BuildContext context) { - return FutureBuilder>( + return FutureBuilder>( future: ordinalsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { @@ -25,10 +25,10 @@ class OrdinalsList extends StatelessWidget { } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasData) { - final List inscriptions = snapshot.data!; + final List ordinals = snapshot.data!; return GridView.builder( shrinkWrap: true, - itemCount: inscriptions.length, + itemCount: ordinals.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisSpacing: spacing as double, mainAxisSpacing: spacing as double, @@ -37,7 +37,7 @@ class OrdinalsList extends StatelessWidget { ), itemBuilder: (_, i) => OrdinalCard( walletId: walletId, - inscriptionData: inscriptions[i], + ordinal: ordinals[i], ), ); } else { diff --git a/lib/route_generator.dart b/lib/route_generator.dart index e67254135..7c0807caf 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -423,12 +423,12 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case OrdinalDetailsView.routeName: - if (args is ({InscriptionData inscriptionData, String walletId})) { + if (args is ({Ordinal ordinal, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => OrdinalDetailsView( walletId: args.walletId, - inscriptionData: args.inscriptionData, + ordinal: args.ordinal, ), settings: RouteSettings( name: settings.name, diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 51323fa0c..a779f5b2e 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:isar/isar.dart'; import 'package:stackwallet/db/isar/main_db.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; - -import 'package:stackwallet/services/litescribe_api.dart'; import 'package:stackwallet/dto/ordinals/inscription_data.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; +import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/services/litescribe_api.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; mixin OrdinalsInterface { late final String _walletId; @@ -25,28 +25,32 @@ mixin OrdinalsInterface { } final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api'); - // Future> getInscriptionsByAddress(String address) async { - // try { - // var response = await litescribeAPI.getInscriptionsByAddress(address); - // // print("Found ${response.result.total} inscription${response.result.total > 1 ? 's' : ''} at address $address"); // TODO disable (POC) - // return response.result.list; - // } catch (e) { - // throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); - // } - // } - void refreshInscriptions() async { List _inscriptions; final utxos = await _db.getUTXOs(_walletId).findAll(); final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - _inscriptions = await getInscriptionsFromAddresses(uniqueAddresses); + _inscriptions = await getInscriptionDataFromAddresses(uniqueAddresses); // TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder } - Future> getInscriptions() async { - final utxos = await _db.getUTXOs(_walletId).findAll(); - final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - return await getInscriptionsFromAddresses(uniqueAddresses); + Future> getInscriptionData() async { + try { + final utxos = await _db.getUTXOs(_walletId).findAll(); + final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); + return await getInscriptionDataFromAddresses(uniqueAddresses); + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptions: $e'); + } + } + + Future> getOrdinals() async { + try { + final utxos = await _db.getUTXOs(_walletId).findAll(); + final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); + return await getOrdinalsFromAddresses(uniqueAddresses); + } catch (e) { + throw Exception('Error in OrdinalsInterface getOrdinals: $e'); + } } List getUniqueAddressesFromUTXOs(List utxos) { @@ -59,12 +63,22 @@ mixin OrdinalsInterface { return uniqueAddresses.toList(); } - Future> getInscriptionsFromAddresses(List addresses) async { + Future> getInscriptionDataFromAddress(String address) async { + List allInscriptions = []; + try { + var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + allInscriptions.addAll(inscriptions); + } catch (e) { + throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); + } + return allInscriptions; + } + + Future> getInscriptionDataFromAddresses(List addresses) async { List allInscriptions = []; for (String address in addresses) { try { var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - print("Found ${inscriptions.length} inscription${inscriptions.length > 1 ? 's' : ''} at address $address"); allInscriptions.addAll(inscriptions); } catch (e) { print("Error fetching inscriptions for address $address: $e"); @@ -73,87 +87,25 @@ mixin OrdinalsInterface { return allInscriptions; } -/* // ord-litecoin interface - final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com'); - - Future fetchLatestInscriptions() async { + Future> getOrdinalsFromAddress(String address) async { try { - final feedResponse = await ordinalsAPI.getLatestInscriptions(); - // Process the feedResponse data as needed - // print('Latest Inscriptions:'); - // for (var inscription in feedResponse.inscriptions) { - // print('Title: ${inscription.title}, Href: ${inscription.href}'); - // } - return feedResponse; + var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + return inscriptions.map((data) => Ordinal.fromInscriptionData(data)).toList(); } catch (e) { - // Handle errors - throw Exception('Error in OrdinalsInterface fetchLatestInscriptions: $e'); + throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e'); } } - Future getInscriptionDetails(String inscriptionId) async { - try { - return await ordinalsAPI.getInscriptionDetails(inscriptionId); - } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptionDetails: $e'); + Future> getOrdinalsFromAddresses(List addresses) async { + List allOrdinals = []; + for (String address in addresses) { + try { + var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + allOrdinals.addAll(inscriptions.map((data) => Ordinal.fromInscriptionData(data))); + } catch (e) { + print("Error fetching inscriptions for address $address: $e"); + } } + return allOrdinals; } - - Future getSatDetails(int satNumber) async { - try { - return await ordinalsAPI.getSatDetails(satNumber); - } catch (e) { - throw Exception('Error in OrdinalsInterface getSatDetails: $e'); - } - } - - Future getTransaction(String transactionId) async { - try { - print(1); - return await ordinalsAPI.getTransaction(transactionId); - } catch (e) { - throw Exception('Error in OrdinalsInterface getTransaction: $e'); - } - } - - Future getTransactionOutputs(String transactionId) async { - try { - return await ordinalsAPI.getTransactionOutputs(transactionId); - } catch (e) { - throw Exception('Error in OrdinalsInterface getTransactionOutputs: $e'); - } - } - - Future getInscriptionsByAddress(String address) async { - try { - return await ordinalsAPI.getInscriptionsByAddress(address); - } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); - } - } - - Future getBlock(int blockNumber) async { - try { - return await ordinalsAPI.getBlock(blockNumber); - } catch (e) { - throw Exception('Error in OrdinalsInterface getBlock: $e'); - } - } - - Future getInscriptionContent(String inscriptionId) async { - try { - return await ordinalsAPI.getInscriptionContent(inscriptionId); - } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptionContent: $e'); - } - } - - Future getInscriptionPreview(String inscriptionId) async { - try { - return await ordinalsAPI.getInscriptionPreview(inscriptionId); - } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptionPreview: $e'); - } - } - */ // /ord-litecoin interface } \ No newline at end of file From 5a845f866936bdc6e2f5b66e540cfb29878ed05c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 16:35:00 -0500 Subject: [PATCH 037/128] hide certain fields on ordinals views, change ordinal card aspect ratio --- lib/pages/ordinals/ordinal_details_view.dart | 14 +++++++------- lib/pages/ordinals/widgets/ordinal_card.dart | 12 ++++++------ lib/pages/ordinals/widgets/ordinals_list.dart | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 6aab95586..95cff734e 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -191,13 +191,13 @@ class _OrdinalImageGroup extends StatelessWidget { 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, - ), + // Text( + // "${ordinal.inscriptionId}", // Use any other property you want + // style: STextStyles.w600_16(context), + // ), + // const SizedBox( + // height: _spacing, + // ), AspectRatio( aspectRatio: 1, child: AspectRatio( diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index c9060b32e..4ac4fd877 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -41,14 +41,14 @@ class OrdinalCard extends StatelessWidget { ), const Spacer(), Text( - 'TODO', // infer from address associated with utxoTXID + 'INSC. ${ordinal.inscriptionNumber}', // infer from address associated with utxoTXID style: STextStyles.w500_12(context), ), - const Spacer(), - Text( - "INSC. ${ordinal.inscriptionNumber} ID ${ordinal.inscriptionId}", - style: STextStyles.w500_8(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 index fe7618cd0..62d620130 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -33,7 +33,7 @@ class OrdinalsList extends StatelessWidget { crossAxisSpacing: spacing as double, mainAxisSpacing: spacing as double, crossAxisCount: 2, - childAspectRatio: 3 / 4, + childAspectRatio: 6 / 7, // was 3/4, less data displayed now ), itemBuilder: (_, i) => OrdinalCard( walletId: walletId, @@ -41,7 +41,7 @@ class OrdinalsList extends StatelessWidget { ), ); } else { - return Text('No data found.'); + return const Text('No data found.'); } }, ); From af5f6849b333b8dd0aae745717830f71a7e38702 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 17:54:51 -0500 Subject: [PATCH 038/128] add desktop ordinals page stub --- .../sub_widgets/desktop_wallet_features.dart | 11 ++ .../more_features/more_features_dialog.dart | 11 +- .../ordinals/desktop_ordinals_view.dart | 115 ++++++++++++++++++ lib/route_generator.dart | 15 +++ 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart 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..f116e6656 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 @@ -19,6 +19,7 @@ import 'package:stackwallet/notifications/show_flush_bar.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/ordinals/desktop_ordinals_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'; @@ -80,6 +81,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { onCoinControlPressed: _onCoinControlPressed, onAnonymizeAllPressed: _onAnonymizeAllPressed, onWhirlpoolPressed: _onWhirlpoolPressed, + onOrdinalsPressed: _onOrdinalsPressed, ), ); } @@ -313,6 +315,15 @@ class _DesktopWalletFeaturesState extends ConsumerState { } } + 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( 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..fc9c03de4 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,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { required this.onCoinControlPressed, required this.onAnonymizeAllPressed, required this.onWhirlpoolPressed, + required this.onOrdinalsPressed, }) : super(key: key); final String walletId; @@ -36,6 +37,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { final VoidCallback? onCoinControlPressed; final VoidCallback? onAnonymizeAllPressed; final VoidCallback? onWhirlpoolPressed; + final VoidCallback? onOrdinalsPressed; @override ConsumerState createState() => _MoreFeaturesDialogState(); @@ -100,9 +102,16 @@ class _MoreFeaturesDialogState extends ConsumerState { _MoreFeaturesItem( label: "PayNym", detail: "Increased address privacy using BIP47", - iconAsset: Assets.svg.robotHead, + iconAsset: Assets.svg.ordinal, 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(), + ), const SizedBox( height: 28, ), 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..a2f6adf68 --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -0,0 +1,115 @@ +/* + * 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/models/isar/models/contact_entry.dart'; +import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart'; +import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; +import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart'; +import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart'; +import 'package:stackwallet/providers/global/address_book_service_provider.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/providers/ui/address_book_providers/address_book_filter_provider.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'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/address_book_card.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.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/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/rounded_container.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 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 = ""; + dynamic _manager; + + @override + void initState() { + _searchController = TextEditingController(); + _searchFocusNode = FocusNode(); + + super.initState(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // Set _manager here when the widget's dependencies change + _manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + } + + @override + void dispose() { + _searchController.dispose(); + _searchFocusNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + debugPrint("BUILD: $runtimeType"); + + return DesktopScaffold( + appBar: DesktopAppBar( + isCompactHeight: true, + leading: Row( + children: [ + const SizedBox( + width: 24, + ), + Text( + "Ordinals", + style: STextStyles.desktopH3(context), + ) + ], + ), + ), + body: const Padding( + padding: EdgeInsets.only( + left: 24, + right: 24, + bottom: 24, + ), + child: Text( + "TODO") + ), + ); + } +} diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7c0807caf..06395906e 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -146,6 +146,7 @@ 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_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'; @@ -422,6 +423,20 @@ class RouteGenerator { } 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( From 8118f98e5eac1cb774977294e357c29222dcf6af Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 18:13:40 -0500 Subject: [PATCH 039/128] mobile ordinals in desktop TODO make desktop OrdinalsList and OrdinalsDetailsView --- .../ordinals/desktop_ordinals_view.dart | 97 ++++++++++++++++--- 1 file changed, 86 insertions(+), 11 deletions(-) diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index a2f6adf68..46d732e3a 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -14,11 +14,13 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart'; import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/ui/address_book_providers/address_book_filter_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/constants.dart'; @@ -37,6 +39,7 @@ 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 DesktopOrdinalsView extends ConsumerStatefulWidget { const DesktopOrdinalsView({ super.key, @@ -52,16 +55,16 @@ class DesktopOrdinalsView extends ConsumerStatefulWidget { } class _DesktopOrdinals extends ConsumerState { - late final TextEditingController _searchController; - late final FocusNode _searchFocusNode; + late final TextEditingController searchController; + late final FocusNode searchFocusNode; String _searchTerm = ""; dynamic _manager; @override - void initState() { - _searchController = TextEditingController(); - _searchFocusNode = FocusNode(); + void initState() { + searchController = TextEditingController(); + searchFocusNode = FocusNode(); super.initState(); } @@ -76,8 +79,8 @@ class _DesktopOrdinals extends ConsumerState { @override void dispose() { - _searchController.dispose(); - _searchFocusNode.dispose(); + searchController.dispose(); + searchFocusNode.dispose(); super.dispose(); } @@ -101,14 +104,86 @@ class _DesktopOrdinals extends ConsumerState { ], ), ), - body: const Padding( - padding: EdgeInsets.only( + body: Padding( + padding: const EdgeInsets.only( left: 24, right: 24, bottom: 24, ), - child: Text( - "TODO") + child: 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: 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: 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, + ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(), + ), + ), + ], + ), + ), ), ); } From 9d5e33da01dbb17799c4e1f8dc88a2c707c2518f Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 18:21:15 -0500 Subject: [PATCH 040/128] DesktopOrdinalsList widget --- .../ordinals/desktop_ordinals_view.dart | 4 +- .../subwidgets/desktop_ordinals_list.dart | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index 46d732e3a..a50cc85db 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -14,7 +14,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/isar/models/contact_entry.dart'; import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; -import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart'; import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart'; import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart'; import 'package:stackwallet/providers/global/address_book_service_provider.dart'; @@ -176,7 +176,7 @@ class _DesktopOrdinals extends ConsumerState { height: 16, ), Expanded( - child: OrdinalsList( + child: DesktopOrdinalsList( walletId: widget.walletId, ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(), ), diff --git a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart new file mode 100644 index 000000000..f12fe6834 --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/models/ordinal.dart'; + +import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; + +class DesktopOrdinalsList extends StatelessWidget { + const DesktopOrdinalsList({ + Key? key, + required this.walletId, + required this.ordinalsFuture, + }) : super(key: key); + + final String walletId; + final Future> ordinalsFuture; + + get spacing => 2.0; + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: ordinalsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const CircularProgressIndicator(); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + final List ordinals = snapshot.data!; + return GridView.builder( + shrinkWrap: true, + itemCount: ordinals.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: spacing as double, + mainAxisSpacing: spacing as double, + crossAxisCount: 4, + childAspectRatio: 6 / 7, // was 3/4, less data displayed now + ), + itemBuilder: (_, i) => OrdinalCard( + walletId: walletId, + ordinal: ordinals[i], + ), + ); + } else { + return const Text('No data found.'); + } + }, + ); + } +} \ No newline at end of file From fe41bce563cd4f3ca1a3a24be9f5635fc7bf2dad Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 17:24:20 -0600 Subject: [PATCH 041/128] disable collection type in ord filter --- lib/pages/ordinals/ordinals_filter_view.dart | 309 +++++++++---------- 1 file changed, 154 insertions(+), 155 deletions(-) diff --git a/lib/pages/ordinals/ordinals_filter_view.dart b/lib/pages/ordinals/ordinals_filter_view.dart index e06294098..631a9833a 100644 --- a/lib/pages/ordinals/ordinals_filter_view.dart +++ b/lib/pages/ordinals/ordinals_filter_view.dart @@ -26,21 +26,20 @@ 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/rounded_white_container.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 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.isMoonbird, + // required this.isPunk, required this.from, required this.to, required this.inscription, @@ -48,16 +47,16 @@ class OrdinalFilter { }); OrdinalFilter copyWith({ - bool? isMoonbird, - bool? isPunk, + // bool? isMoonbird, + // bool? isPunk, DateTime? from, DateTime? to, String? inscription, String? keyword, }) { return OrdinalFilter( - isMoonbird: isMoonbird ?? this.isMoonbird, - isPunk: isPunk ?? this.isPunk, + // isMoonbird: isMoonbird ?? this.isMoonbird, + // isPunk: isPunk ?? this.isPunk, from: from ?? this.from, to: to ?? this.to, inscription: inscription ?? this.inscription, @@ -83,8 +82,8 @@ class _OrdinalsFilterViewState extends ConsumerState { final _inscriptionTextEditingController = TextEditingController(); final _keywordTextEditingController = TextEditingController(); - bool _isPunk = false; - bool _isMoonbird = false; + // bool _isPunk = false; + // bool _isMoonbird = false; String _fromDateString = ""; String _toDateString = ""; @@ -99,8 +98,8 @@ class _OrdinalsFilterViewState extends ConsumerState { baseColor = ref.read(themeProvider.state).state.textSubtitle2; final filterState = ref.read(ordinalFilterProvider.state).state; if (filterState != null) { - _isMoonbird = filterState.isMoonbird; - _isPunk = filterState.isPunk; + // _isMoonbird = filterState.isMoonbird; + // _isPunk = filterState.isPunk; _selectedToDate = filterState.to; _selectedFromDate = filterState.from; _keywordTextEditingController.text = filterState.keyword; @@ -521,146 +520,146 @@ class _OrdinalsFilterViewState extends ConsumerState { 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, - ), + // 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( @@ -875,8 +874,8 @@ class _OrdinalsFilterViewState extends ConsumerState { Future _onApplyPressed() async { final filter = OrdinalFilter( - isPunk: _isPunk, - isMoonbird: _isMoonbird, + // isPunk: _isPunk, + // isMoonbird: _isMoonbird, from: _selectedFromDate, to: _selectedToDate, inscription: _inscriptionTextEditingController.text, From d9d7f256927bf8289230f1292a483de0759d1b24 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 18:30:41 -0500 Subject: [PATCH 042/128] add desktop ordinals list, card, and details view --- .../desktop_ordinal_details_view.dart | 269 ++++++++++++++++++ .../subwidgets/desktop_ordinal_card.dart | 56 ++++ .../subwidgets/desktop_ordinals_list.dart | 4 +- lib/route_generator.dart | 16 ++ 4 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart create mode 100644 lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart 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..36c8e33cb --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -0,0 +1,269 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/models/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 DesktopOrdinalDetailsView extends StatefulWidget { + const DesktopOrdinalDetailsView({ + Key? key, + required this.walletId, + required this.ordinal, + }) : super(key: key); + + final String walletId; + final Ordinal ordinal; + + static const routeName = "/desktopOrdinalDetailsView"; + + @override + _DesktopOrdinalDetailsViewState createState() => _DesktopOrdinalDetailsViewState(); +} + +class _DesktopOrdinalDetailsViewState 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: AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.red, + 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_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart new file mode 100644 index 000000000..9d94a4b0a --- /dev/null +++ b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class DesktopOrdinalCard extends StatelessWidget { + const DesktopOrdinalCard({ + 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( + DesktopOrdinalDetailsView.routeName, + arguments: (walletId: walletId, ordinal: ordinal), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.red, + 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_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart index f12fe6834..b30e94dd4 100644 --- a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart +++ b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:stackwallet/models/ordinal.dart'; -import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; +import 'package:stackwallet/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart'; class DesktopOrdinalsList extends StatelessWidget { const DesktopOrdinalsList({ @@ -35,7 +35,7 @@ class DesktopOrdinalsList extends StatelessWidget { crossAxisCount: 4, childAspectRatio: 6 / 7, // was 3/4, less data displayed now ), - itemBuilder: (_, i) => OrdinalCard( + itemBuilder: (_, i) => DesktopOrdinalCard( walletId: walletId, ordinal: ordinals[i], ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 5a5c1df17..03c7f13bc 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -147,6 +147,7 @@ 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'; @@ -453,6 +454,21 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case DesktopOrdinalDetailsView.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 OrdinalsFilterView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, From 8edaa9d353cfbe34b5547d2ea57b4000345224d2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 18:50:18 -0500 Subject: [PATCH 043/128] desktop ordinal details view fix --- .../desktop_ordinal_details_view.dart | 172 +++++++++++------- lib/route_generator.dart | 5 +- 2 files changed, 105 insertions(+), 72 deletions(-) diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index 36c8e33cb..03cae351e 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -9,8 +9,9 @@ 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/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/rounded_white_container.dart'; @@ -36,77 +37,110 @@ class _DesktopOrdinalDetailsViewState extends State { @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), + 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, + ), + SvgPicture.asset( + Assets.svg.ordinal, + width: 32, + height: 32, + color: + Theme.of(context).extension()!.textSubtitle1, + ), + const SizedBox( + width: 12, + ), + Text( + "Ordinals", + style: STextStyles.desktopH3(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, - ), - ], + useSpacers: false, + isCompactHeight: true, + ), + 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, + ), + ], ), ), ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 03c7f13bc..b3a104dd0 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -12,7 +12,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; -import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart'; import 'package:stackwallet/models/buy/response_objects/quote.dart'; @@ -20,7 +19,7 @@ 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/ordinal.dart'; // TODO generalize InscriptionData -> Ordinal +import 'package:stackwallet/models/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'; @@ -458,7 +457,7 @@ class RouteGenerator { if (args is ({Ordinal ordinal, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => OrdinalDetailsView( + builder: (_) => DesktopOrdinalDetailsView( walletId: args.walletId, ordinal: args.ordinal, ), From fb39e96308acfd452119737cd7a3b6a42f895b51 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Fri, 21 Jul 2023 17:56:01 -0600 Subject: [PATCH 044/128] WIP: monkey view + fetching monkey dialog --- lib/pages/monkey/monkey_view.dart | 127 ++++++++++++++++ .../sub_widgets/fetch_monkey_dialog.dart | 135 ++++++++++++++++++ lib/pages/wallet_view/wallet_view.dart | 25 ++++ lib/route_generator.dart | 16 +++ 4 files changed, 303 insertions(+) create mode 100644 lib/pages/monkey/monkey_view.dart create mode 100644 lib/pages/monkey/sub_widgets/fetch_monkey_dialog.dart diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart new file mode 100644 index 000000000..e74d5bbdf --- /dev/null +++ b/lib/pages/monkey/monkey_view.dart @@ -0,0 +1,127 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/pages/monkey/sub_widgets/fetch_monkey_dialog.dart'; +import 'package:stackwallet/services/coins/manager.dart'; +import 'package:stackwallet/themes/coin_icon_provider.dart'; +import 'package:stackwallet/themes/stack_colors.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/primary_button.dart'; + +class MonkeyView extends ConsumerStatefulWidget { + const MonkeyView({ + Key? key, + required this.walletId, + required this.managerProvider, + }) : super(key: key); + + static const String routeName = "/monkey"; + static const double navBarHeight = 65.0; + + final String walletId; + final ChangeNotifierProvider managerProvider; + + @override + ConsumerState createState() => _MonkeyViewState(); +} + +class _MonkeyViewState extends ConsumerState { + late final String walletId; + late final ChangeNotifierProvider managerProvider; + + @override + void initState() { + walletId = widget.walletId; + managerProvider = widget.managerProvider; + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); + + return Background( + child: Stack( + children: [ + Scaffold( + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + ), + body: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Spacer(), + Center( + child: Column( + children: [ + SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 164, + height: 164, + ), + const SizedBox( + height: 40, + ), + 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, + ), + ], + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return FetchMonkeyDialog( + onCancel: () async { + Navigator.of(context).pop(); + }, + ); + }, + ); + }, + ), + ), + ], + ), + ), + ], + ), + ); + } +} 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/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 3e7fb37fa..bd1c10b3c 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -22,6 +22,7 @@ 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/paynym/paynym_claim_view.dart'; import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; @@ -924,6 +925,30 @@ class _WalletViewState extends ConsumerState { ); }, ), + if (ref.watch( + walletsChangeNotifierProvider.select( + (value) => value + .getManager(widget.walletId) + .hasCoinControlSupport, + ), + ) && + ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.enableCoinControl, + ), + )) + WalletNavigationBarItemData( + icon: SvgPicture.asset(Assets.svg.circlePlus), + label: "MonKey", + onTap: () { + Navigator.of(context).pushNamed( + MonkeyView.routeName, + arguments: Tuple2( + widget.walletId, + widget.managerProvider, + ), + ); + }), if (ref.watch( walletsChangeNotifierProvider.select( (value) => value diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7ccc378c9..4513fd1d6 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -56,6 +56,7 @@ 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/paynym/add_new_paynym_follow_view.dart'; import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; @@ -375,6 +376,21 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case MonkeyView.routeName: + if (args is Tuple2>) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => MonkeyView( + 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( From ee5a97c2fbccd9d7a116b0e84bad2611c9ed988e Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Fri, 21 Jul 2023 17:59:01 -0600 Subject: [PATCH 045/128] add monkey icon --- assets/svg/monkey.svg | 3 +++ lib/pages/wallet_view/wallet_view.dart | 2 +- lib/utilities/assets.dart | 1 + pubspec.yaml | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 assets/svg/monkey.svg 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/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index bd1c10b3c..6af2eeb65 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -938,7 +938,7 @@ class _WalletViewState extends ConsumerState { ), )) WalletNavigationBarItemData( - icon: SvgPicture.asset(Assets.svg.circlePlus), + icon: SvgPicture.asset(Assets.svg.monkey), label: "MonKey", onTap: () { Navigator.of(context).pushNamed( diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index b8ec501e8..f22ab6341 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"; diff --git a/pubspec.yaml b/pubspec.yaml index f5b4cf27b..9999e8e65 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -335,6 +335,7 @@ flutter: - assets/svg/trocador_rating_b.svg - assets/svg/trocador_rating_c.svg - assets/svg/trocador_rating_d.svg + - assets/svg/monkey.svg # coin control icons - assets/svg/coin_control/ From 8ac085fe240b001b3db939352ff8d8549319570d Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Fri, 21 Jul 2023 18:01:38 -0600 Subject: [PATCH 046/128] fix monkey icon --- lib/pages/wallet_view/wallet_view.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 6af2eeb65..0dcfe18dc 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -938,7 +938,10 @@ class _WalletViewState extends ConsumerState { ), )) WalletNavigationBarItemData( - icon: SvgPicture.asset(Assets.svg.monkey), + icon: SvgPicture.asset(Assets.svg.monkey, + height: 20, + width: 20, + color: Theme.of(context).extension()!.bottomNavIconIcon,), label: "MonKey", onTap: () { Navigator.of(context).pushNamed( From c7d114e2683eaa1a0fd8315ca199ffd4c3a63481 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 21 Jul 2023 19:11:33 -0500 Subject: [PATCH 047/128] remove nested AspectRatio --- lib/pages/ordinals/ordinal_details_view.dart | 23 ++-- .../desktop_ordinal_details_view.dart | 109 +++++++++--------- 2 files changed, 64 insertions(+), 68 deletions(-) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 95cff734e..6f6d0b8a7 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart'; @@ -41,10 +40,10 @@ class _OrdinalDetailsViewState extends State { child: SafeArea( child: Scaffold( backgroundColor: - Theme.of(context).extension()!.background, + Theme.of(context).extension()!.background, appBar: AppBar( backgroundColor: - Theme.of(context).extension()!.background, + Theme.of(context).extension()!.background, leading: const AppBarBackButton(), title: Text( "Ordinal details", @@ -154,7 +153,7 @@ class _DetailsItemWCopy extends StatelessWidget { child: SvgPicture.asset( Assets.svg.copy, color: - Theme.of(context).extension()!.infoItemIcons, + Theme.of(context).extension()!.infoItemIcons, width: 12, ), ), @@ -200,15 +199,13 @@ class _OrdinalImageGroup extends StatelessWidget { // ), AspectRatio( aspectRatio: 1, - child: AspectRatio( - aspectRatio: 1, - child: Container( - color: Colors.red, - 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 - ), + child: Container( + color: Colors.red, + 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 ), ), ), diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index 03cae351e..1477a0d2f 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -29,7 +29,8 @@ class DesktopOrdinalDetailsView extends StatefulWidget { static const routeName = "/desktopOrdinalDetailsView"; @override - _DesktopOrdinalDetailsViewState createState() => _DesktopOrdinalDetailsViewState(); + _DesktopOrdinalDetailsViewState createState() => + _DesktopOrdinalDetailsViewState(); } class _DesktopOrdinalDetailsViewState extends State { @@ -38,55 +39,55 @@ class _DesktopOrdinalDetailsViewState extends State { @override Widget build(BuildContext context) { 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, + 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()! - .textFieldDefaultBG, - shadows: const [], - icon: SvgPicture.asset( - Assets.svg.arrowLeft, - width: 18, - height: 18, - color: Theme.of(context) - .extension()! - .topNavIconPrimary, - ), - onPressed: Navigator.of(context).pop, + .topNavIconPrimary, ), - const SizedBox( - width: 18, - ), - SvgPicture.asset( - Assets.svg.ordinal, - width: 32, - height: 32, - color: - Theme.of(context).extension()!.textSubtitle1, - ), - const SizedBox( - width: 12, - ), - Text( - "Ordinals", - style: STextStyles.desktopH3(context), - ), - ], - ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox( + width: 18, + ), + SvgPicture.asset( + Assets.svg.ordinal, + width: 32, + height: 32, + color: + Theme.of(context).extension()!.textSubtitle1, + ), + const SizedBox( + width: 12, + ), + Text( + "Ordinals", + style: STextStyles.desktopH3(context), + ), + ], ), - useSpacers: false, - isCompactHeight: true, ), - body: SingleChildScrollView( + useSpacers: false, + isCompactHeight: true, + ), + body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( @@ -187,7 +188,7 @@ class _DetailsItemWCopy extends StatelessWidget { child: SvgPicture.asset( Assets.svg.copy, color: - Theme.of(context).extension()!.infoItemIcons, + Theme.of(context).extension()!.infoItemIcons, width: 12, ), ), @@ -233,15 +234,13 @@ class _OrdinalImageGroup extends StatelessWidget { // ), AspectRatio( aspectRatio: 1, - child: AspectRatio( - aspectRatio: 1, - child: Container( - color: Colors.red, - 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 - ), + child: Container( + color: Colors.red, + 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 ), ), ), From b3e4d63a89b1d98fdbff751bdfe6c0df9bcb7b74 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 17:30:27 -0600 Subject: [PATCH 048/128] strongly typed spacing --- lib/pages/ordinals/widgets/ordinals_list.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index 62d620130..c654f1522 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:stackwallet/models/ordinal.dart'; - import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; class OrdinalsList extends StatelessWidget { @@ -13,7 +12,7 @@ class OrdinalsList extends StatelessWidget { final String walletId; final Future> ordinalsFuture; - get spacing => 2.0; + double get spacing => 2.0; @override Widget build(BuildContext context) { @@ -30,8 +29,8 @@ class OrdinalsList extends StatelessWidget { shrinkWrap: true, itemCount: ordinals.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisSpacing: spacing as double, - mainAxisSpacing: spacing as double, + crossAxisSpacing: spacing, + mainAxisSpacing: spacing, crossAxisCount: 2, childAspectRatio: 6 / 7, // was 3/4, less data displayed now ), @@ -46,4 +45,4 @@ class OrdinalsList extends StatelessWidget { }, ); } -} \ No newline at end of file +} From 974924de8df5ca2a46192dd2cca67f4684ad0d1f Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 17:50:21 -0600 Subject: [PATCH 049/128] updated mocks --- test/pages/send_view/send_view_test.mocks.dart | 5 +++++ .../subviews/add_address_book_view_screen_test.mocks.dart | 5 +++++ .../address_book_entry_details_view_screen_test.mocks.dart | 5 +++++ .../edit_address_book_entry_view_screen_test.mocks.dart | 5 +++++ test/screen_tests/lockscreen_view_screen_test.mocks.dart | 5 +++++ .../main_view_tests/main_view_screen_testA_test.mocks.dart | 5 +++++ .../main_view_tests/main_view_screen_testB_test.mocks.dart | 5 +++++ .../main_view_tests/main_view_screen_testC_test.mocks.dart | 5 +++++ .../onboarding/backup_key_view_screen_test.mocks.dart | 5 +++++ .../backup_key_warning_view_screen_test.mocks.dart | 5 +++++ .../onboarding/create_pin_view_screen_test.mocks.dart | 5 +++++ .../onboarding/restore_wallet_view_screen_test.mocks.dart | 5 +++++ .../onboarding/verify_backup_key_view_screen_test.mocks.dart | 5 +++++ .../settings_subviews/currency_view_screen_test.mocks.dart | 5 +++++ .../add_custom_node_view_screen_test.mocks.dart | 5 +++++ .../node_details_view_screen_test.mocks.dart | 5 +++++ .../wallet_backup_view_screen_test.mocks.dart | 5 +++++ .../rescan_warning_view_screen_test.mocks.dart | 5 +++++ .../wallet_delete_mnemonic_view_screen_test.mocks.dart | 5 +++++ .../wallet_settings_view_screen_test.mocks.dart | 5 +++++ .../settings_view/settings_view_screen_test.mocks.dart | 5 +++++ .../transaction_search_results_view_screen_test.mocks.dart | 5 +++++ .../wallet_view/confirm_send_view_screen_test.mocks.dart | 5 +++++ .../wallet_view/receive_view_screen_test.mocks.dart | 5 +++++ .../wallet_view/send_view_screen_test.mocks.dart | 5 +++++ .../wallet_view/wallet_view_screen_test.mocks.dart | 5 +++++ test/widget_tests/managed_favorite_test.mocks.dart | 5 +++++ test/widget_tests/table_view/table_view_row_test.mocks.dart | 5 +++++ test/widget_tests/transaction_card_test.mocks.dart | 5 +++++ .../wallet_info_row_balance_future_test.mocks.dart | 5 +++++ .../wallet_info_row/wallet_info_row_test.mocks.dart | 5 +++++ 31 files changed, 155 insertions(+) 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/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..83c8e8c82 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, 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, From 48109f3c49cab97568332ef1bb25c009e4453199 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 17:51:04 -0600 Subject: [PATCH 050/128] use isar ordinal model --- lib/models/isar/ordinal.dart | 76 + lib/models/isar/ordinal.g.dart | 1489 +++++++++++++++++ lib/models/ordinal.dart | 29 - lib/pages/ordinals/widgets/ordinals_list.dart | 3 +- lib/route_generator.dart | 2 +- lib/services/mixins/ordinals_interface.dart | 31 +- 6 files changed, 1588 insertions(+), 42 deletions(-) create mode 100644 lib/models/isar/ordinal.dart create mode 100644 lib/models/isar/ordinal.g.dart delete mode 100644 lib/models/ordinal.dart diff --git a/lib/models/isar/ordinal.dart b/lib/models/isar/ordinal.dart new file mode 100644 index 000000000..0af1d1120 --- /dev/null +++ b/lib/models/isar/ordinal.dart @@ -0,0 +1,76 @@ +import 'package:isar/isar.dart'; +import 'package:stackwallet/dto/ordinals/inscription_data.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, + ); + } + + @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/ordinal.dart b/lib/models/ordinal.dart deleted file mode 100644 index 66a69de94..000000000 --- a/lib/models/ordinal.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:stackwallet/dto/ordinals/inscription_data.dart'; - -class Ordinal { - 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.inscriptionId, - required this.inscriptionNumber, - required this.content, - required this.utxoTXID, - required this.utxoVOUT, - }); - - factory Ordinal.fromInscriptionData(InscriptionData data) { - return Ordinal( - inscriptionId: data.inscriptionId, - inscriptionNumber: data.inscriptionNumber, - content: data.content, - utxoTXID: data.output.split(':')[0], // "output": "062f32e21aa04246b8873b5d9a929576addd0339881e1ea478b406795d6b6c47:0" - utxoVOUT: int.parse(data.output.split(':')[1]), - ); - } -} \ No newline at end of file diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index c654f1522..6f5a5fbf1 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; class OrdinalsList extends StatelessWidget { const OrdinalsList({ @@ -20,7 +21,7 @@ class OrdinalsList extends StatelessWidget { future: ordinalsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const CircularProgressIndicator(); + return const LoadingIndicator(); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasData) { diff --git a/lib/route_generator.dart b/lib/route_generator.dart index b3a104dd0..4c77021ea 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -19,7 +19,7 @@ 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/ordinal.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'; diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index a779f5b2e..0b7d5497d 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -4,7 +4,7 @@ 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/ordinal.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/services/litescribe_api.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -18,12 +18,13 @@ mixin OrdinalsInterface { required Coin coin, required MainDB db, }) { - print('init'); _walletId = walletId; _coin = coin; _db = db; } - final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api'); + + final LitescribeAPI litescribeAPI = + LitescribeAPI(baseUrl: 'https://litescribe.io/api'); void refreshInscriptions() async { List _inscriptions; @@ -63,22 +64,26 @@ mixin OrdinalsInterface { return uniqueAddresses.toList(); } - Future> getInscriptionDataFromAddress(String address) async { + Future> getInscriptionDataFromAddress( + String address) async { List allInscriptions = []; try { var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); allInscriptions.addAll(inscriptions); } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e'); + throw Exception( + 'Error in OrdinalsInterface getInscriptionsByAddress: $e'); } return allInscriptions; } - Future> getInscriptionDataFromAddresses(List addresses) async { + Future> getInscriptionDataFromAddresses( + List addresses) async { List allInscriptions = []; for (String address in addresses) { try { - var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + var inscriptions = + await litescribeAPI.getInscriptionsByAddress(address); allInscriptions.addAll(inscriptions); } catch (e) { print("Error fetching inscriptions for address $address: $e"); @@ -90,7 +95,9 @@ mixin OrdinalsInterface { Future> getOrdinalsFromAddress(String address) async { try { var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - return inscriptions.map((data) => Ordinal.fromInscriptionData(data)).toList(); + return inscriptions + .map((data) => Ordinal.fromInscriptionData(data, _walletId)) + .toList(); } catch (e) { throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e'); } @@ -100,12 +107,14 @@ mixin OrdinalsInterface { List allOrdinals = []; for (String address in addresses) { try { - var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - allOrdinals.addAll(inscriptions.map((data) => Ordinal.fromInscriptionData(data))); + var inscriptions = + await litescribeAPI.getInscriptionsByAddress(address); + allOrdinals.addAll(inscriptions + .map((data) => Ordinal.fromInscriptionData(data, _walletId))); } catch (e) { print("Error fetching inscriptions for address $address: $e"); } } return allOrdinals; } -} \ No newline at end of file +} From dffa33abd33e668f9fc72862f8432199927deda0 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 18:43:59 -0600 Subject: [PATCH 051/128] load ordinals into isar as a cache and backend view model --- lib/db/isar/main_db.dart | 2 + lib/pages/ordinals/ordinal_details_view.dart | 4 +- lib/pages/ordinals/ordinals_view.dart | 25 +-- lib/pages/ordinals/widgets/ordinal_card.dart | 17 +- lib/pages/ordinals/widgets/ordinals_list.dart | 112 ++++++++---- .../desktop_ordinal_details_view.dart | 4 +- .../ordinals/desktop_ordinals_view.dart | 56 ++---- .../subwidgets/desktop_ordinal_card.dart | 56 ------ .../subwidgets/desktop_ordinals_list.dart | 49 ------ lib/services/mixins/ordinals_interface.dart | 163 ++++++++++-------- 10 files changed, 218 insertions(+), 270 deletions(-) delete mode 100644 lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart delete mode 100644 lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index bb6ea5e8d..9d445aebb 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,7 @@ class MainDB { TransactionBlockExplorerSchema, StackThemeSchema, ContactEntrySchema, + OrdinalSchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 6f6d0b8a7..0bbcbe37a 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/models/ordinal.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'; @@ -28,7 +28,7 @@ class OrdinalDetailsView extends StatefulWidget { static const routeName = "/ordinalDetailsView"; @override - _OrdinalDetailsViewState createState() => _OrdinalDetailsViewState(); + State createState() => _OrdinalDetailsViewState(); } class _OrdinalDetailsViewState extends State { diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index a4adfa019..4e1e985dd 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -11,7 +11,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/models/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; @@ -19,6 +18,7 @@ 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/background.dart'; @@ -46,7 +46,6 @@ class _OrdinalsViewState extends ConsumerState { late final FocusNode searchFocus; String _searchTerm = ""; - dynamic _manager; @override void initState() { @@ -56,14 +55,6 @@ class _OrdinalsViewState extends ConsumerState { super.initState(); } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - // Set _manager here when the widget's dependencies change - _manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId))); - } - @override void dispose() { searchController.dispose(); @@ -73,6 +64,9 @@ class _OrdinalsViewState extends ConsumerState { @override Widget build(BuildContext context) { + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + return Background( child: SafeArea( child: Scaffold( @@ -100,7 +94,15 @@ class _OrdinalsViewState extends ConsumerState { .topNavIconPrimary, ), onPressed: () async { - (_manager.wallet as OrdinalsInterface).refreshInscriptions(); + // show loading for a minimum of 2 seconds on refreshing + await showLoading( + whileFuture: Future.wait([ + Future.delayed(const Duration(seconds: 2)), + (manager.wallet as OrdinalsInterface) + .refreshInscriptions() + ]), + context: context, + message: "Refreshing..."); }, ), ), @@ -193,7 +195,6 @@ class _OrdinalsViewState extends ConsumerState { Expanded( child: OrdinalsList( walletId: widget.walletId, - ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(), ), ), ], diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index 4ac4fd877..e537611f8 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; - -import 'package:stackwallet/models/ordinal.dart'; +import 'package:stackwallet/models/isar/ordinal.dart'; import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; +import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -28,14 +28,17 @@ class OrdinalCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - AspectRatio( - aspectRatio: 1, - child: Container( - color: Colors.red, + 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 + filterQuality: + FilterQuality.none, // Set the filter mode to nearest ), ), ), diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index 6f5a5fbf1..ad333d2ed 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -1,48 +1,96 @@ -import 'package:flutter/material.dart'; -import 'package:stackwallet/models/ordinal.dart'; -import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; -import 'package:stackwallet/widgets/loading_indicator.dart'; +import 'dart:async'; -class OrdinalsList extends StatelessWidget { +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/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, - required this.ordinalsFuture, }) : super(key: key); final String walletId; - final Future> ordinalsFuture; - double get spacing => 2.0; + @override + ConsumerState createState() => _OrdinalsListState(); +} + +class _OrdinalsListState extends ConsumerState { + static const double _spacing = 10.0; + + 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 FutureBuilder>( - future: ordinalsFuture, + return StreamBuilder?>( + stream: _stream, builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const LoadingIndicator(); - } else if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } else if (snapshot.hasData) { - final List ordinals = snapshot.data!; - return GridView.builder( - shrinkWrap: true, - itemCount: ordinals.length, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisSpacing: spacing, - mainAxisSpacing: spacing, - crossAxisCount: 2, - childAspectRatio: 6 / 7, // was 3/4, less data displayed now - ), - itemBuilder: (_, i) => OrdinalCard( - walletId: walletId, - ordinal: ordinals[i], - ), - ); - } else { - return const Text('No data found.'); + 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: STextStyles.label(context), + ), + ), + ), + ], + ); + } + + 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_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index 1477a0d2f..a477f6228 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/models/ordinal.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'; @@ -29,7 +29,7 @@ class DesktopOrdinalDetailsView extends StatefulWidget { static const routeName = "/desktopOrdinalDetailsView"; @override - _DesktopOrdinalDetailsViewState createState() => + State createState() => _DesktopOrdinalDetailsViewState(); } diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index a50cc85db..0068e4033 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -11,35 +11,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/models/isar/models/contact_entry.dart'; -import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart'; -import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart'; -import 'package:stackwallet/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart'; -import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_address_book_scaffold.dart'; -import 'package:stackwallet/pages_desktop_specific/address_book_view/subwidgets/desktop_contact_details.dart'; -import 'package:stackwallet/providers/global/address_book_service_provider.dart'; +import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart'; import 'package:stackwallet/providers/providers.dart'; -import 'package:stackwallet/providers/ui/address_book_providers/address_book_filter_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/constants.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/util.dart'; -import 'package:stackwallet/widgets/address_book_card.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; -import 'package:stackwallet/widgets/desktop/desktop_dialog.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/icon_widgets/x_icon.dart'; -import 'package:stackwallet/widgets/rounded_container.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 DesktopOrdinalsView extends ConsumerStatefulWidget { const DesktopOrdinalsView({ super.key, @@ -62,7 +45,7 @@ class _DesktopOrdinals extends ConsumerState { dynamic _manager; @override - void initState() { + void initState() { searchController = TextEditingController(); searchFocusNode = FocusNode(); @@ -151,23 +134,23 @@ class _DesktopOrdinals extends ConsumerState { ), 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 = ""; - }); - }, + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + searchController.text = ""; + _searchTerm = ""; + }); + }, + ), + ], + ), ), - ], - ), - ), - ) + ) : null, ), ), @@ -176,9 +159,8 @@ class _DesktopOrdinals extends ConsumerState { height: 16, ), Expanded( - child: DesktopOrdinalsList( + child: OrdinalsList( walletId: widget.walletId, - ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(), ), ), ], diff --git a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart deleted file mode 100644 index 9d94a4b0a..000000000 --- a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:stackwallet/models/ordinal.dart'; -import 'package:stackwallet/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart'; -import 'package:stackwallet/utilities/text_styles.dart'; -import 'package:stackwallet/widgets/rounded_white_container.dart'; - -class DesktopOrdinalCard extends StatelessWidget { - const DesktopOrdinalCard({ - 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( - DesktopOrdinalDetailsView.routeName, - arguments: (walletId: walletId, ordinal: ordinal), - ); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - AspectRatio( - aspectRatio: 1, - child: Container( - color: Colors.red, - 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_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart b/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart deleted file mode 100644 index b30e94dd4..000000000 --- a/lib/pages_desktop_specific/ordinals/subwidgets/desktop_ordinals_list.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stackwallet/models/ordinal.dart'; - -import 'package:stackwallet/pages_desktop_specific/ordinals/subwidgets/desktop_ordinal_card.dart'; - -class DesktopOrdinalsList extends StatelessWidget { - const DesktopOrdinalsList({ - Key? key, - required this.walletId, - required this.ordinalsFuture, - }) : super(key: key); - - final String walletId; - final Future> ordinalsFuture; - - get spacing => 2.0; - - @override - Widget build(BuildContext context) { - return FutureBuilder>( - future: ordinalsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const CircularProgressIndicator(); - } else if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } else if (snapshot.hasData) { - final List ordinals = snapshot.data!; - return GridView.builder( - shrinkWrap: true, - itemCount: ordinals.length, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisSpacing: spacing as double, - mainAxisSpacing: spacing as double, - crossAxisCount: 4, - childAspectRatio: 6 / 7, // was 3/4, less data displayed now - ), - itemBuilder: (_, i) => DesktopOrdinalCard( - walletId: walletId, - ordinal: ordinals[i], - ), - ); - } else { - return const Text('No data found.'); - } - }, - ); - } -} \ No newline at end of file diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 0b7d5497d..51a82307a 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -26,58 +26,75 @@ mixin OrdinalsInterface { final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api'); - void refreshInscriptions() async { - List _inscriptions; - final utxos = await _db.getUTXOs(_walletId).findAll(); - final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - _inscriptions = await getInscriptionDataFromAddresses(uniqueAddresses); - // TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder - } + Future refreshInscriptions() async { + final uniqueAddresses = await _db + .getUTXOs(_walletId) + .filter() + .addressIsNotNull() + .distinctByAddress() + .addressProperty() + .findAll(); + final inscriptions = + await _getInscriptionDataFromAddresses(uniqueAddresses.cast()); - Future> getInscriptionData() async { - try { - final utxos = await _db.getUTXOs(_walletId).findAll(); - final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - return await getInscriptionDataFromAddresses(uniqueAddresses); - } catch (e) { - throw Exception('Error in OrdinalsInterface getInscriptions: $e'); - } - } + final ords = inscriptions + .map((e) => Ordinal.fromInscriptionData(e, _walletId)) + .toList(); - Future> getOrdinals() async { - try { - final utxos = await _db.getUTXOs(_walletId).findAll(); - final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - return await getOrdinalsFromAddresses(uniqueAddresses); - } catch (e) { - throw Exception('Error in OrdinalsInterface getOrdinals: $e'); - } + await _db.isar.writeTxn(() async { + await _db.isar.ordinals + .where() + .filter() + .walletIdEqualTo(_walletId) + .deleteAll(); + await _db.isar.ordinals.putAll(ords); + }); } + // + // Future> getInscriptionData() async { + // try { + // final utxos = await _db.getUTXOs(_walletId).findAll(); + // final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); + // return await _getInscriptionDataFromAddresses(uniqueAddresses); + // } catch (e) { + // throw Exception('Error in OrdinalsInterface getInscriptions: $e'); + // } + // } + // + // Future> getOrdinals() async { + // try { + // final utxos = await _db.getUTXOs(_walletId).findAll(); + // final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); + // return await getOrdinalsFromAddresses(uniqueAddresses); + // } catch (e) { + // throw Exception('Error in OrdinalsInterface getOrdinals: $e'); + // } + // } + // + // List getUniqueAddressesFromUTXOs(List utxos) { + // final Set uniqueAddresses = {}; + // for (var utxo in utxos) { + // if (utxo.address != null) { + // uniqueAddresses.add(utxo.address!); + // } + // } + // return uniqueAddresses.toList(); + // } + // + // Future> getInscriptionDataFromAddress( + // String address) async { + // List allInscriptions = []; + // try { + // var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + // allInscriptions.addAll(inscriptions); + // } catch (e) { + // throw Exception( + // 'Error in OrdinalsInterface getInscriptionsByAddress: $e'); + // } + // return allInscriptions; + // } - List getUniqueAddressesFromUTXOs(List utxos) { - final Set uniqueAddresses = {}; - for (var utxo in utxos) { - if (utxo.address != null) { - uniqueAddresses.add(utxo.address!); - } - } - return uniqueAddresses.toList(); - } - - Future> getInscriptionDataFromAddress( - String address) async { - List allInscriptions = []; - try { - var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - allInscriptions.addAll(inscriptions); - } catch (e) { - throw Exception( - 'Error in OrdinalsInterface getInscriptionsByAddress: $e'); - } - return allInscriptions; - } - - Future> getInscriptionDataFromAddresses( + Future> _getInscriptionDataFromAddresses( List addresses) async { List allInscriptions = []; for (String address in addresses) { @@ -92,29 +109,29 @@ mixin OrdinalsInterface { return allInscriptions; } - Future> getOrdinalsFromAddress(String address) async { - try { - var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - return inscriptions - .map((data) => Ordinal.fromInscriptionData(data, _walletId)) - .toList(); - } catch (e) { - throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e'); - } - } - - Future> getOrdinalsFromAddresses(List addresses) async { - List allOrdinals = []; - for (String address in addresses) { - try { - var inscriptions = - await litescribeAPI.getInscriptionsByAddress(address); - allOrdinals.addAll(inscriptions - .map((data) => Ordinal.fromInscriptionData(data, _walletId))); - } catch (e) { - print("Error fetching inscriptions for address $address: $e"); - } - } - return allOrdinals; - } + // Future> getOrdinalsFromAddress(String address) async { + // try { + // var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); + // return inscriptions + // .map((data) => Ordinal.fromInscriptionData(data, _walletId)) + // .toList(); + // } catch (e) { + // throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e'); + // } + // } + // + // Future> getOrdinalsFromAddresses(List addresses) async { + // List allOrdinals = []; + // for (String address in addresses) { + // try { + // var inscriptions = + // await litescribeAPI.getInscriptionsByAddress(address); + // allOrdinals.addAll(inscriptions + // .map((data) => Ordinal.fromInscriptionData(data, _walletId))); + // } catch (e) { + // print("Error fetching inscriptions for address $address: $e"); + // } + // } + // return allOrdinals; + // } } From c2df7ea1132f187b9f3cdaef1857efba224c9ae7 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 19:05:19 -0600 Subject: [PATCH 052/128] wip desktop ordinals list --- lib/pages/ordinals/ordinals_view.dart | 8 +- lib/pages/ordinals/widgets/ordinals_list.dart | 10 +- .../ordinals/desktop_ordinals_view.dart | 250 +++++++++++------- 3 files changed, 172 insertions(+), 96 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 4e1e985dd..059c81c41 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -64,9 +64,6 @@ class _OrdinalsViewState extends ConsumerState { @override Widget build(BuildContext context) { - final manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId))); - return Background( child: SafeArea( child: Scaffold( @@ -98,7 +95,10 @@ class _OrdinalsViewState extends ConsumerState { await showLoading( whileFuture: Future.wait([ Future.delayed(const Duration(seconds: 2)), - (manager.wallet as OrdinalsInterface) + (ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as OrdinalsInterface) .refreshInscriptions() ]), context: context, diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index ad333d2ed..8244416b9 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -6,6 +6,7 @@ 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'; @@ -23,7 +24,7 @@ class OrdinalsList extends ConsumerStatefulWidget { } class _OrdinalsListState extends ConsumerState { - static const double _spacing = 10.0; + final double _spacing = Util.isDesktop ? 16 : 10; late List _data; @@ -69,7 +70,12 @@ class _OrdinalsListState extends ConsumerState { child: Center( child: Text( "Your ordinals will appear here", - style: STextStyles.label(context), + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1) + : STextStyles.label(context), ), ), ), diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index 0068e4033..9d4742db7 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -13,12 +13,17 @@ 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'; @@ -42,7 +47,6 @@ class _DesktopOrdinals extends ConsumerState { late final FocusNode searchFocusNode; String _searchTerm = ""; - dynamic _manager; @override void initState() { @@ -52,14 +56,6 @@ class _DesktopOrdinals extends ConsumerState { super.initState(); } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - // Set _manager here when the widget's dependencies change - _manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(widget.walletId))); - } - @override void dispose() { searchController.dispose(); @@ -74,97 +70,171 @@ class _DesktopOrdinals extends ConsumerState { return DesktopScaffold( appBar: DesktopAppBar( + background: Theme.of(context).extension()!.popupBG, isCompactHeight: true, - leading: Row( - children: [ - const SizedBox( - width: 24, - ), - Text( - "Ordinals", - style: STextStyles.desktopH3(context), - ) - ], + 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.only( - left: 24, - right: 24, - bottom: 24, - ), - child: 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: 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: 16, - ), - child: SvgPicture.asset( - Assets.svg.search, - width: 16, - height: 16, + padding: const EdgeInsets.all(24), + child: Column( + children: [ + Row( + children: [ + 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, ), ), - 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, + // 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: OrdinalsList( + walletId: widget.walletId, ), - ], - ), + ), + ], ), ), ); From 6c6c9c832516ea55bc5fab1ce7b486df6838d1a8 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 19:15:26 -0600 Subject: [PATCH 053/128] desktop ordinals in wrap --- lib/pages/ordinals/widgets/ordinals_list.dart | 44 +++++++++++++------ .../ordinals/desktop_ordinals_view.dart | 7 ++- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/lib/pages/ordinals/widgets/ordinals_list.dart b/lib/pages/ordinals/widgets/ordinals_list.dart index 8244416b9..481b0ef0a 100644 --- a/lib/pages/ordinals/widgets/ordinals_list.dart +++ b/lib/pages/ordinals/widgets/ordinals_list.dart @@ -83,20 +83,36 @@ class _OrdinalsListState extends ConsumerState { ); } - 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], - ), - ); + 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_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index 9d4742db7..414ece75d 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -117,6 +117,7 @@ class _DesktopOrdinals extends ConsumerState { body: Padding( padding: const EdgeInsets.all(24), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ @@ -230,8 +231,10 @@ class _DesktopOrdinals extends ConsumerState { height: 16, ), Expanded( - child: OrdinalsList( - walletId: widget.walletId, + child: SingleChildScrollView( + child: OrdinalsList( + walletId: widget.walletId, + ), ), ), ], From e559ee1cc5cde4d244665754ebc0eadff79c8d43 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 21 Jul 2023 19:52:43 -0600 Subject: [PATCH 054/128] WIP desktop ordinal details view --- lib/dto/ordinals/inscription_data.dart | 20 ++ lib/models/isar/ordinal.dart | 13 + lib/pages/ordinals/widgets/ordinal_card.dart | 6 +- .../desktop_ordinal_details_view.dart | 335 +++++++++--------- 4 files changed, 213 insertions(+), 161 deletions(-) diff --git a/lib/dto/ordinals/inscription_data.dart b/lib/dto/ordinals/inscription_data.dart index b7bba8697..2f12bd670 100644 --- a/lib/dto/ordinals/inscription_data.dart +++ b/lib/dto/ordinals/inscription_data.dart @@ -50,4 +50,24 @@ class InscriptionData { 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/models/isar/ordinal.dart b/lib/models/isar/ordinal.dart index 0af1d1120..06ba52ef5 100644 --- a/lib/models/isar/ordinal.dart +++ b/lib/models/isar/ordinal.dart @@ -1,5 +1,7 @@ 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'; @@ -62,6 +64,17 @@ class Ordinal { ); } + UTXO? getUTXO(MainDB db) { + return db.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(utxoTXID) + .and() + .voutEqualTo(utxoVOUT) + .findFirstSync(); + } + @override String toString() { return 'Ordinal {' diff --git a/lib/pages/ordinals/widgets/ordinal_card.dart b/lib/pages/ordinals/widgets/ordinal_card.dart index e537611f8..c74366d74 100644 --- a/lib/pages/ordinals/widgets/ordinal_card.dart +++ b/lib/pages/ordinals/widgets/ordinal_card.dart @@ -1,8 +1,10 @@ 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 { @@ -21,7 +23,9 @@ class OrdinalCard extends StatelessWidget { radiusMultiplier: 2, onPressed: () { Navigator.of(context).pushNamed( - OrdinalDetailsView.routeName, + Util.isDesktop + ? DesktopOrdinalDetailsView.routeName + : OrdinalDetailsView.routeName, arguments: (walletId: walletId, ordinal: ordinal), ); }, diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index a477f6228..27c651213 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -2,12 +2,20 @@ 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/pages/ordinals/widgets/dialogs.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'; @@ -16,7 +24,7 @@ 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 DesktopOrdinalDetailsView extends StatefulWidget { +class DesktopOrdinalDetailsView extends ConsumerStatefulWidget { const DesktopOrdinalDetailsView({ Key? key, required this.walletId, @@ -29,15 +37,27 @@ class DesktopOrdinalDetailsView extends StatefulWidget { static const routeName = "/desktopOrdinalDetailsView"; @override - State createState() => + ConsumerState createState() => _DesktopOrdinalDetailsViewState(); } -class _DesktopOrdinalDetailsViewState extends State { +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, @@ -67,18 +87,8 @@ class _DesktopOrdinalDetailsViewState extends State { const SizedBox( width: 18, ), - SvgPicture.asset( - Assets.svg.ordinal, - width: 32, - height: 32, - color: - Theme.of(context).extension()!.textSubtitle1, - ), - const SizedBox( - width: 12, - ), Text( - "Ordinals", + "Ordinal details", style: STextStyles.desktopH3(context), ), ], @@ -87,62 +97,161 @@ class _DesktopOrdinalDetailsViewState extends State { useSpacers: false, isCompactHeight: true, ), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 39, + 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: _OrdinalImageGroup( - ordinal: widget.ordinal, - walletId: widget.walletId, + 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 ), ), - _DetailsItemWCopy( - title: "Inscription number", - data: widget.ordinal.inscriptionNumber.toString(), + ), + 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, + ), + ], + ), + ), ), - 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, - ), - ], - ), + ), + ], ), ), ); @@ -206,97 +315,3 @@ class _DetailsItemWCopy extends StatelessWidget { ); } } - -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.red, - 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 - } - }, - ), - ), - ], - ), - ], - ); - } -} From 55e55ef8c887b493e717409d525e8ff435634083 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sat, 22 Jul 2023 21:09:42 -0500 Subject: [PATCH 055/128] print->throw Exception --- lib/services/mixins/ordinals_interface.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 51a82307a..88a416a1c 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -103,7 +103,7 @@ mixin OrdinalsInterface { await litescribeAPI.getInscriptionsByAddress(address); allInscriptions.addAll(inscriptions); } catch (e) { - print("Error fetching inscriptions for address $address: $e"); + throw Exception("Error fetching inscriptions for address $address: $e"); } } return allInscriptions; From 475490d4f0ea0f72f1c3ad8a0aea1924a43ce8f3 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sat, 22 Jul 2023 21:10:22 -0500 Subject: [PATCH 056/128] remove commented functions --- lib/services/mixins/ordinals_interface.dart | 69 --------------------- 1 file changed, 69 deletions(-) diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 88a416a1c..0dfe2a782 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -50,49 +50,6 @@ mixin OrdinalsInterface { await _db.isar.ordinals.putAll(ords); }); } - // - // Future> getInscriptionData() async { - // try { - // final utxos = await _db.getUTXOs(_walletId).findAll(); - // final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - // return await _getInscriptionDataFromAddresses(uniqueAddresses); - // } catch (e) { - // throw Exception('Error in OrdinalsInterface getInscriptions: $e'); - // } - // } - // - // Future> getOrdinals() async { - // try { - // final utxos = await _db.getUTXOs(_walletId).findAll(); - // final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); - // return await getOrdinalsFromAddresses(uniqueAddresses); - // } catch (e) { - // throw Exception('Error in OrdinalsInterface getOrdinals: $e'); - // } - // } - // - // List getUniqueAddressesFromUTXOs(List utxos) { - // final Set uniqueAddresses = {}; - // for (var utxo in utxos) { - // if (utxo.address != null) { - // uniqueAddresses.add(utxo.address!); - // } - // } - // return uniqueAddresses.toList(); - // } - // - // Future> getInscriptionDataFromAddress( - // String address) async { - // List allInscriptions = []; - // try { - // var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - // allInscriptions.addAll(inscriptions); - // } catch (e) { - // throw Exception( - // 'Error in OrdinalsInterface getInscriptionsByAddress: $e'); - // } - // return allInscriptions; - // } Future> _getInscriptionDataFromAddresses( List addresses) async { @@ -108,30 +65,4 @@ mixin OrdinalsInterface { } return allInscriptions; } - - // Future> getOrdinalsFromAddress(String address) async { - // try { - // var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); - // return inscriptions - // .map((data) => Ordinal.fromInscriptionData(data, _walletId)) - // .toList(); - // } catch (e) { - // throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e'); - // } - // } - // - // Future> getOrdinalsFromAddresses(List addresses) async { - // List allOrdinals = []; - // for (String address in addresses) { - // try { - // var inscriptions = - // await litescribeAPI.getInscriptionsByAddress(address); - // allOrdinals.addAll(inscriptions - // .map((data) => Ordinal.fromInscriptionData(data, _walletId))); - // } catch (e) { - // print("Error fetching inscriptions for address $address: $e"); - // } - // } - // return allOrdinals; - // } } From 58c3bdbf2be041296809f4b7f17c742d91cdded2 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sat, 22 Jul 2023 21:50:29 -0500 Subject: [PATCH 057/128] don't flash red on load --- lib/pages/ordinals/ordinal_details_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 0bbcbe37a..d54f1e24c 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -200,7 +200,7 @@ class _OrdinalImageGroup extends StatelessWidget { AspectRatio( aspectRatio: 1, child: Container( - color: Colors.red, + color: Colors.transparent, child: Image.network( ordinal.content, // Use the preview URL as the image source fit: BoxFit.cover, From 8e5f2d190d81b68d01ec2f477e1e1e42857cbb4c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sat, 22 Jul 2023 21:50:49 -0500 Subject: [PATCH 058/128] temporarily disable send button --- lib/pages/ordinals/ordinal_details_view.dart | 54 +++++++++--------- .../desktop_ordinal_details_view.dart | 56 +++++++++---------- 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index d54f1e24c..ced80c42c 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -232,33 +232,33 @@ class _OrdinalImageGroup extends StatelessWidget { }, ), ), - 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 - } - }, - ), - ), + // 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_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index 27c651213..cb76e036d 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -7,7 +7,6 @@ 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/pages/ordinals/widgets/dialogs.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'; @@ -20,7 +19,6 @@ 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/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -150,33 +148,33 @@ class _DesktopOrdinalDetailsViewState 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, - ), + // 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", From 7da49c7ea0d0c4c6b437c2b9e55f6a243ab1506c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sat, 22 Jul 2023 22:14:18 -0500 Subject: [PATCH 059/128] improve ordinal heuristic and formatting --- .../coins/litecoin/litecoin_wallet.dart | 29 +++++++++++++------ lib/services/mixins/ordinals_interface.dart | 26 +++++++++++++++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index 944182abd..5ea1e67d9 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -136,7 +136,7 @@ class LitecoinWallet extends CoinServiceAPI _secureStore = secureStore; initCache(walletId, coin); initWalletDB(mockableOverride: mockableOverride); - initOrdinalsInterface(walletId:walletId, coin: coin, db: db); + initOrdinalsInterface(walletId: walletId, coin: coin, db: db); initCoinControlInterface( walletId: walletId, walletName: walletName, @@ -1871,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; @@ -1893,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 querying the litescribe API for all addresses at once, instead of one API call per output + 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, diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 0dfe2a782..5b17f3b48 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -65,4 +65,30 @@ mixin OrdinalsInterface { } 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; + } + } } From f32359ac1e8d96e97e4e0a186f50551373b6a4d8 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 23 Jul 2023 18:01:51 -0500 Subject: [PATCH 060/128] refresh inscriptions after general refresh if any are detected --- lib/services/coins/litecoin/litecoin_wallet.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index 5ea1e67d9..4698883f8 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -1856,6 +1856,7 @@ class LitecoinWallet extends CoinServiceAPI } final List outputArray = []; + bool inscriptionsRefreshNeeded = false; for (int i = 0; i < fetchedUtxoList.length; i++) { for (int j = 0; j < fetchedUtxoList[i].length; j++) { @@ -1890,10 +1891,12 @@ class LitecoinWallet extends CoinServiceAPI // TODO check the specific output, not just the address in general // TODO optimize by querying the litescribe API for all addresses at once, instead of one API call per output if (utxoOwnerAddress != null) { + // TODO add inscription to database if (await inscriptionInAddress(utxoOwnerAddress!)) { shouldBlock = true; blockReason = "Ordinal"; label = "Ordinal detected at address"; + inscriptionsRefreshNeeded = true; } } else { // TODO implement inscriptionInOutput @@ -1901,6 +1904,7 @@ class LitecoinWallet extends CoinServiceAPI shouldBlock = true; blockReason = "May contain ordinal"; label = "Possible ordinal"; + inscriptionsRefreshNeeded = true; } } @@ -1923,6 +1927,10 @@ class LitecoinWallet extends CoinServiceAPI } } + if (inscriptionsRefreshNeeded) { + await refreshInscriptions(); + } + Logging.instance.log( 'Outputs fetched: $outputArray', level: LogLevel.Info, From abd4bf9d7a7f5f592a0148835e65fbe5f64afe2d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 23 Jul 2023 18:19:38 -0500 Subject: [PATCH 061/128] refresh inscriptions if needed and add utility func --- lib/services/coins/litecoin/litecoin_wallet.dart | 10 +++++----- lib/services/mixins/ordinals_interface.dart | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index 4698883f8..ff36bc5c6 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -1891,7 +1891,6 @@ class LitecoinWallet extends CoinServiceAPI // TODO check the specific output, not just the address in general // TODO optimize by querying the litescribe API for all addresses at once, instead of one API call per output if (utxoOwnerAddress != null) { - // TODO add inscription to database if (await inscriptionInAddress(utxoOwnerAddress!)) { shouldBlock = true; blockReason = "Ordinal"; @@ -1927,15 +1926,16 @@ class LitecoinWallet extends CoinServiceAPI } } - if (inscriptionsRefreshNeeded) { - await refreshInscriptions(); - } - Logging.instance.log( 'Outputs fetched: $outputArray', level: LogLevel.Info, ); + // TODO replace with refreshInscriptions if outputs are changed + if (inscriptionsRefreshNeeded) { + await refreshInscriptions(); + } + await db.updateUTXOs(walletId, outputArray); // finally update balance diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index 5b17f3b48..f25508517 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -91,4 +91,14 @@ mixin OrdinalsInterface { return false; } } + + // check if an inscription is in a given output + Future inscriptionInAddresses(List addresses) async { + var inscriptions = await _getInscriptionDataFromAddresses(addresses); + if (inscriptions.isNotEmpty) { + return true; + } else { + return false; + } + } } From 18a0bf279ef451b9036d4a538a310cbf7f4cd5f3 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 23 Jul 2023 18:29:42 -0500 Subject: [PATCH 062/128] refresh inscriptions if new output detected --- lib/db/isar/main_db.dart | 7 ++++++- lib/services/coins/litecoin/litecoin_wallet.dart | 11 ++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 9d445aebb..80ecde478 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -248,7 +248,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) { @@ -270,12 +271,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({ diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index ff36bc5c6..703ec5cf6 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -1856,7 +1856,6 @@ class LitecoinWallet extends CoinServiceAPI } final List outputArray = []; - bool inscriptionsRefreshNeeded = false; for (int i = 0; i < fetchedUtxoList.length; i++) { for (int j = 0; j < fetchedUtxoList[i].length; j++) { @@ -1889,13 +1888,12 @@ class LitecoinWallet extends CoinServiceAPI final utxoAmount = jsonUTXO["value"] as int; // TODO check the specific output, not just the address in general - // TODO optimize by querying the litescribe API for all addresses at once, instead of one API call per output + // 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"; - inscriptionsRefreshNeeded = true; } } else { // TODO implement inscriptionInOutput @@ -1903,7 +1901,6 @@ class LitecoinWallet extends CoinServiceAPI shouldBlock = true; blockReason = "May contain ordinal"; label = "Possible ordinal"; - inscriptionsRefreshNeeded = true; } } @@ -1931,13 +1928,13 @@ class LitecoinWallet extends CoinServiceAPI level: LogLevel.Info, ); - // TODO replace with refreshInscriptions if outputs are changed + bool inscriptionsRefreshNeeded = + await db.updateUTXOs(walletId, outputArray); + if (inscriptionsRefreshNeeded) { await refreshInscriptions(); } - await db.updateUTXOs(walletId, outputArray); - // finally update balance await _updateBalance(); } catch (e, s) { From 38c2415a61c7a4883d5e2e24f971b18676eb448a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 23 Jul 2023 23:16:43 -0500 Subject: [PATCH 063/128] change function signature of test to match inscription-refresh hackfix --- test/widget_tests/transaction_card_test.mocks.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 83c8e8c82..667ef211a 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -3266,7 +3266,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, ) => @@ -3280,7 +3280,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, From ce1bd8212fc4326ad580420475a9b27e81d4e14c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sun, 23 Jul 2023 23:18:23 -0500 Subject: [PATCH 064/128] remove Future inscriptionInAddresses(List addresses) inscriptionInAddress is next, after its usage is removed from _updateUTXOs, updateUTXOs (or another hook for detecting if a refresh has detected a new output) will trigger the more general refreshInscriptions --- lib/services/mixins/ordinals_interface.dart | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/services/mixins/ordinals_interface.dart b/lib/services/mixins/ordinals_interface.dart index f25508517..5b17f3b48 100644 --- a/lib/services/mixins/ordinals_interface.dart +++ b/lib/services/mixins/ordinals_interface.dart @@ -91,14 +91,4 @@ mixin OrdinalsInterface { return false; } } - - // check if an inscription is in a given output - Future inscriptionInAddresses(List addresses) async { - var inscriptions = await _getInscriptionDataFromAddresses(addresses); - if (inscriptions.isNotEmpty) { - return true; - } else { - return false; - } - } } From 6ff4c5d49aa50359f41998b24269ec40e9c70dcb Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 09:06:10 -0600 Subject: [PATCH 065/128] clean up --- lib/services/coins/firo/firo_wallet.dart | 64 ------------------------ 1 file changed, 64 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index e62b7d069..3527c9802 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3133,17 +3133,6 @@ 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); @@ -3204,20 +3193,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); @@ -4072,45 +4047,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, From baa34ca9f2cc4c9165073680930be8551a0fd905 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 09:06:48 -0600 Subject: [PATCH 066/128] isar lelantus coin schema --- lib/db/isar/main_db.dart | 2 + .../models/firo_specific/lelantus_coin.dart | 75 + .../models/firo_specific/lelantus_coin.g.dart | 1570 +++++++++++++++++ 3 files changed, 1647 insertions(+) create mode 100644 lib/models/isar/models/firo_specific/lelantus_coin.dart create mode 100644 lib/models/isar/models/firo_specific/lelantus_coin.g.dart diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index bb6ea5e8d..209f13c5d 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -14,6 +14,7 @@ import 'package:isar/isar.dart'; 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/firo_specific/lelantus_coin.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -54,6 +55,7 @@ class MainDB { TransactionBlockExplorerSchema, StackThemeSchema, ContactEntrySchema, + LelantusCoinSchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, 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..3dd48b2dc --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -0,0 +1,75 @@ +import 'package:isar/isar.dart'; + +part 'lelantus_coin.g.dart'; + +@collection +class LelantusCoin { + Id id = Isar.autoIncrement; + + @Index() + final String walletId; + + @Index( + unique: true, + composite: [ + CompositeIndex("walletId"), + CompositeIndex("txid"), + ], + replace: false, + ) + final String publicCoin; + + final String txid; + + final String value; // can't use BigInt in isar :shrug: + + final int index; + + final int anonymitySetId; + + final bool isUsed; + + LelantusCoin({ + required this.walletId, + required this.publicCoin, + required this.txid, + required this.value, + required this.index, + required this.anonymitySetId, + required this.isUsed, + }); + + LelantusCoin copyWith({ + String? walletId, + String? publicCoin, + String? txid, + String? value, + int? index, + int? anonymitySetId, + bool? isUsed, + }) { + return LelantusCoin( + walletId: walletId ?? this.walletId, + publicCoin: publicCoin ?? this.publicCoin, + txid: txid ?? this.txid, + value: value ?? this.value, + index: index ?? this.index, + anonymitySetId: anonymitySetId ?? this.anonymitySetId, + isUsed: isUsed ?? this.isUsed, + ); + } + + @override + String toString() { + return 'LelantusCoin{' + 'id: $id, ' + 'walletId: $walletId, ' + 'publicCoin: $publicCoin, ' + 'txid: $txid, ' + 'value: $value, ' + 'index: $index, ' + 'anonymitySetId: $anonymitySetId, ' + '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..8fafde571 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -0,0 +1,1570 @@ +// 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'index': PropertySchema( + id: 1, + name: r'index', + type: IsarType.long, + ), + r'isUsed': PropertySchema( + id: 2, + name: r'isUsed', + type: IsarType.bool, + ), + r'publicCoin': PropertySchema( + id: 3, + name: r'publicCoin', + type: IsarType.string, + ), + r'txid': PropertySchema( + id: 4, + name: r'txid', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 5, + name: r'value', + type: IsarType.string, + ), + r'walletId': PropertySchema( + id: 6, + 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'publicCoin_walletId_txid': IndexSchema( + id: 5610740154835640070, + name: r'publicCoin_walletId_txid', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'publicCoin', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'txid', + 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; + bytesCount += 3 + object.publicCoin.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.writeLong(offsets[1], object.index); + writer.writeBool(offsets[2], object.isUsed); + writer.writeString(offsets[3], object.publicCoin); + writer.writeString(offsets[4], object.txid); + writer.writeString(offsets[5], object.value); + writer.writeString(offsets[6], object.walletId); +} + +LelantusCoin _lelantusCoinDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LelantusCoin( + anonymitySetId: reader.readLong(offsets[0]), + index: reader.readLong(offsets[1]), + isUsed: reader.readBool(offsets[2]), + publicCoin: reader.readString(offsets[3]), + txid: reader.readString(offsets[4]), + value: reader.readString(offsets[5]), + walletId: reader.readString(offsets[6]), + ); + 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.readLong(offset)) as P; + case 2: + return (reader.readBool(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + case 4: + return (reader.readString(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + case 6: + 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 getByPublicCoinWalletIdTxid( + String publicCoin, String walletId, String txid) { + return getByIndex( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + LelantusCoin? getByPublicCoinWalletIdTxidSync( + String publicCoin, String walletId, String txid) { + return getByIndexSync( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + Future deleteByPublicCoinWalletIdTxid( + String publicCoin, String walletId, String txid) { + return deleteByIndex( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + bool deleteByPublicCoinWalletIdTxidSync( + String publicCoin, String walletId, String txid) { + return deleteByIndexSync( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + Future> getAllByPublicCoinWalletIdTxid( + List publicCoinValues, + List walletIdValues, + List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return getAllByIndex(r'publicCoin_walletId_txid', values); + } + + List getAllByPublicCoinWalletIdTxidSync( + List publicCoinValues, + List walletIdValues, + List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return getAllByIndexSync(r'publicCoin_walletId_txid', values); + } + + Future deleteAllByPublicCoinWalletIdTxid(List publicCoinValues, + List walletIdValues, List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return deleteAllByIndex(r'publicCoin_walletId_txid', values); + } + + int deleteAllByPublicCoinWalletIdTxidSync(List publicCoinValues, + List walletIdValues, List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return deleteAllByIndexSync(r'publicCoin_walletId_txid', values); + } + + Future putByPublicCoinWalletIdTxid(LelantusCoin object) { + return putByIndex(r'publicCoin_walletId_txid', object); + } + + Id putByPublicCoinWalletIdTxidSync(LelantusCoin object, + {bool saveLinks = true}) { + return putByIndexSync(r'publicCoin_walletId_txid', object, + saveLinks: saveLinks); + } + + Future> putAllByPublicCoinWalletIdTxid(List objects) { + return putAllByIndex(r'publicCoin_walletId_txid', objects); + } + + List putAllByPublicCoinWalletIdTxidSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'publicCoin_walletId_txid', 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 + publicCoinEqualToAnyWalletIdTxid(String publicCoin) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'publicCoin_walletId_txid', + value: [publicCoin], + )); + }); + } + + QueryBuilder + publicCoinNotEqualToAnyWalletIdTxid(String publicCoin) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [], + upper: [publicCoin], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [], + upper: [publicCoin], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + publicCoinWalletIdEqualToAnyTxid(String publicCoin, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'publicCoin_walletId_txid', + value: [publicCoin, walletId], + )); + }); + } + + QueryBuilder + publicCoinEqualToWalletIdNotEqualToAnyTxid( + String publicCoin, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + upper: [publicCoin, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + includeLower: false, + upper: [publicCoin], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + includeLower: false, + upper: [publicCoin], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + upper: [publicCoin, walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + publicCoinWalletIdTxidEqualTo( + String publicCoin, String walletId, String txid) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'publicCoin_walletId_txid', + value: [publicCoin, walletId, txid], + )); + }); + } + + QueryBuilder + publicCoinWalletIdEqualToTxidNotEqualTo( + String publicCoin, String walletId, String txid) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + upper: [publicCoin, walletId, txid], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId, txid], + includeLower: false, + upper: [publicCoin, walletId], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId, txid], + includeLower: false, + upper: [publicCoin, walletId], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + upper: [publicCoin, walletId, txid], + 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 indexEqualTo( + int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'index', + value: value, + )); + }); + } + + QueryBuilder + indexGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'index', + value: value, + )); + }); + } + + QueryBuilder indexLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'index', + value: value, + )); + }); + } + + QueryBuilder indexBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'index', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder isUsedEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isUsed', + value: value, + )); + }); + } + + QueryBuilder + publicCoinEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinBetween( + 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'publicCoin', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'publicCoin', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'publicCoin', + value: '', + )); + }); + } + + QueryBuilder + publicCoinIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'publicCoin', + 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 sortByIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', Sort.asc); + }); + } + + QueryBuilder sortByIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', 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 sortByPublicCoin() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', Sort.asc); + }); + } + + QueryBuilder + sortByPublicCoinDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', 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 thenByIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', Sort.asc); + }); + } + + QueryBuilder thenByIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', 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 thenByPublicCoin() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', Sort.asc); + }); + } + + QueryBuilder + thenByPublicCoinDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', 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 distinctByIndex() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'index'); + }); + } + + QueryBuilder distinctByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isUsed'); + }); + } + + QueryBuilder distinctByPublicCoin( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'publicCoin', 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 indexProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'index'); + }); + } + + QueryBuilder isUsedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isUsed'); + }); + } + + QueryBuilder publicCoinProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'publicCoin'); + }); + } + + 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'); + }); + } +} From a5ba67aa1d3a44de01d1ef931931f951a9db4429 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 09:42:29 -0600 Subject: [PATCH 067/128] WIP migrate lcoins hive => isar --- lib/models/isar/models/isar_models.dart | 1 + lib/models/lelantus_coin.dart | 2 + lib/utilities/constants.dart | 2 +- lib/utilities/db_version_migration.dart | 65 ++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) 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/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/utilities/constants.dart b/lib/utilities/constants.dart index 2dd7be287..ad9d68997 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -58,7 +58,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..3f69161f4 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,76 @@ 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 List coins = []; + for (final e in hiveLCoins) { + final lcoin = e as LelantusCoin; + + final coin = isar_models.LelantusCoin( + walletId: walletId, + publicCoin: lcoin.publicCoin, + txid: lcoin.txId, + value: lcoin.value.toString(), + index: lcoin.index, + anonymitySetId: lcoin.anonymitySetId, + isUsed: lcoin.isUsed, + ); + + 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); From df0b004b82478600ddcc073c222701b820022591 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 12:30:01 -0600 Subject: [PATCH 068/128] finish up basic migrate lcoins hive => isar --- lib/services/coins/firo/firo_wallet.dart | 565 ++++++++++++----------- lib/services/mixins/firo_hive.dart | 26 +- lib/utilities/db_version_migration.dart | 3 +- 3 files changed, 301 insertions(+), 293 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 3527c9802..004e5399c 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'; @@ -161,6 +160,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 +170,7 @@ Future executeNative(Map arguments) async { setDataMap, usedSerialNumbers, network, + walletId, ); sendPort.send(restoreData); return; @@ -206,13 +207,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 +241,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 +266,23 @@ 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.index == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + index: currentIndex, + value: amount.toString(), + publicCoin: publicCoin, + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + ), ); - 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 +315,22 @@ 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.index == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + index: currentIndex, + value: amount.toString(), + publicCoin: publicCoin, + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + ), ); - 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); @@ -396,73 +379,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 +456,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))); @@ -2248,9 +2233,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 +2312,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Error); } - final List lelantusCoins = await _getUnspentCoins(); + final List lelantusCoins = + await _getUnspentCoins(); final root = await Bip32Utils.getBip32Root( _mnemonic!, @@ -2349,7 +2335,7 @@ 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.index, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); @@ -2361,61 +2347,55 @@ class FiroWallet extends CoinServiceAPI 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 jindexes = firoGetJIndex() ?? []; - Future> _getUnspentCoins() async { - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } - final jindexes = firoGetJIndex(); + final lelantusCoinsList = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo("0") + .findAll(); - List coins = []; - - List lelantusCoinsList = - lelantusCoins.fold([], (previousValue, element) { - previousValue.add(element.values.first); - return previousValue; - }); + List coins = []; 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 coin = lelantusCoinsList[i]; - final tx = await db.getTransaction(walletId, txid); + final tx = await db.getTransaction(walletId, coin.txid); - if (!jindexes!.contains(lelantusCoinsList[i].index) && - tx != null && - tx.isLelantus == true && - !(tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { - isUnconfirmed = true; + // TODO check if sane default + bool isUnconfirmed = false; + + if (tx != null) { + bool isConfirmed = tx.isConfirmed( + currentChainHeight, + MINIMUM_CONFIRMATIONS, + ); + if (!jindexes.contains(coin.index) && + tx.isLelantus == true && + !isConfirmed) { + isUnconfirmed = true; + } else if (!isConfirmed) { + continue; + } + } else { + final txn = await cachedElectrumXClient.getTransaction( + txHash: coin.txid, + verbose: true, + coin: this.coin, + ); + final confirmations = txn["confirmations"]; + isUnconfirmed = confirmations is int && confirmations < 1; } - - if (tx != null && - !tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - continue; - } - if (!lelantusCoinsList[i].isUsed && - lelantusCoinsList[i].anonymitySetId != ANONYMITY_SET_EMPTY_ID && + if (!coin.isUsed && + coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && !isUnconfirmed) { - coins.add(lelantusCoinsList[i]); + coins.add(coin); } } return coins; @@ -2427,62 +2407,65 @@ 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() + .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 { + if (txn == null) { + // TODO: ?????????????????????????????????????? + Logging.instance.log( + "Transaction not found in DB for lelantus coin: $lelantusCoin", + level: LogLevel.Fatal, + ); + } 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 += int.parse(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; - } + } else if (jindexes.contains(lelantusCoin.index) && + isLelantus && + !lelantusCoin.isUsed && + !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); + } else if (jindexes.contains(lelantusCoin.index) && + !lelantusCoin.isUsed) { + intLelantusBalance += int.parse(lelantusCoin.value); + } else if (!lelantusCoin.isUsed && + (txn.isLelantus == true + ? true + : txn.isConfirmed( + currentChainHeight, MINIMUM_CONFIRMATIONS) != + false)) { + intLelantusBalance += int.parse(lelantusCoin.value); + } else if (!isLelantus && + txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == + false) { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); } - }); + } } _balancePrivate = Balance( @@ -2551,17 +2534,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) != @@ -2844,7 +2829,14 @@ class FiroWallet extends CoinServiceAPI } Future _refreshLelantusData() async { - final List> lelantusCoins = getLelantusCoinMap(); + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); + final jindexes = firoGetJIndex(); // Get all joinsplit transaction ids @@ -2855,19 +2847,16 @@ class FiroWallet extends CoinServiceAPI .isLelantusEqualTo(true) .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; - })) { + for (final coin in lelantusCoins) { if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txId)) { - joinsplits.add(coin.txId); + if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txid)) { + joinsplits.add(coin.txid); } } } @@ -3030,51 +3019,66 @@ 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]; - } 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 possibleCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .indexEqualTo(index) + .findAll(); + + if (possibleCoins.isNotEmpty) { + if (possibleCoins.length > 1) { + print( + "======================= possibleCoins.length > 1 !!! ================================="); + } else { + final spentCoin = possibleCoins.first; + updatedCoins.add(spentCoin.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 jmint = isar_models.LelantusCoin( + walletId: walletId, + index: index, + value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), + publicCoin: transactionInfo['publicCoin'] as String, + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + ); + if (int.parse(jmint.value) > 0) { + updatedCoins.add(jmint); final jindexes = firoGetJIndex()!; jindexes.add(index); await firoUpdateJIndex(jindexes); await firoUpdateMintIndex(index + 1); } - await firoUpdateLelantusCoins(coins); + + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( + c.publicCoin, + c.walletId, + c.txid, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3087,14 +3091,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( @@ -3137,25 +3135,30 @@ class FiroWallet extends CoinServiceAPI // This is a mint Logging.instance.log("this is a mint", level: LogLevel.Info); + final List updatedCoins = []; + // TODO: transactionInfo['mintsMap'] 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, + index: index, + value: (mintMap['value'] as int).toString(), + publicCoin: mintMap['publicCoin'] as String, + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, ); - if (mint.value > 0) { - coins.add({mint.txId: mint}); + if (int.parse(mint.value) > 0) { + updatedCoins.add(mint); await firoUpdateMintIndex(index + 1); } } // Logging.instance.log(coins); - await firoUpdateLelantusCoins(coins); + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(updatedCoins); + }); } return true; } else { @@ -4556,6 +4559,7 @@ class FiroWallet extends CoinServiceAPI "setDataMap": setDataMap, "usedSerialNumbers": usedSerialNumbers, "network": _network, + "walletId": walletId, }); await Future.wait([dataFuture]); @@ -4576,7 +4580,10 @@ class FiroWallet extends CoinServiceAPI await Future.wait([ firoUpdateMintIndex(message['mintIndex'] as int), - firoUpdateLelantusCoins(message['_lelantus_coins'] as List), + db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll( + message['_lelantus_coins'] as List); + }), firoUpdateJIndex(message['jindex'] as List), ]); diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart index be9845453..631aebe6d 100644 --- a/lib/services/mixins/firo_hive.dart +++ b/lib/services/mixins/firo_hive.dart @@ -30,19 +30,19 @@ mixin FiroHive { ); } - // _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, - ); - } + // // _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() { diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 3f69161f4..dac1dbe82 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -388,7 +388,8 @@ class DbVersionMigrator with WalletDB { final List coins = []; for (final e in hiveLCoins) { - final lcoin = e as LelantusCoin; + final map = e as Map; + final lcoin = map.values.first as LelantusCoin; final coin = isar_models.LelantusCoin( walletId: walletId, From a286ba7b8f3dbc088ed3a811b27d7102778d2658 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 24 Jul 2023 12:43:33 -0600 Subject: [PATCH 069/128] monkey view page --- lib/pages/monkey/monkey_view.dart | 164 ++++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 45 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index e74d5bbdf..10b9c91bf 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -7,11 +7,13 @@ import 'package:stackwallet/pages/monkey/sub_widgets/fetch_monkey_dialog.dart'; import 'package:stackwallet/services/coins/manager.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/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'; class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ @@ -50,6 +52,7 @@ class _MonkeyViewState extends ConsumerState { @override Widget build(BuildContext context) { final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); + bool isMonkey = false; return Background( child: Stack( @@ -65,60 +68,131 @@ class _MonkeyViewState extends ConsumerState { "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( - // mainAxisAlignment: MainAxisAlignment.center, - // crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(), - Center( - child: Column( + body: isMonkey + ? Column( children: [ - SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), + Spacer(), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + SecondaryButton( + label: "Download as SVG", + onPressed: () {}, + ), + const SizedBox(height: 12), + SecondaryButton( + label: "Download as PNG", + onPressed: () {}, + ), + ], ), - width: 164, - height: 164, ), - const SizedBox( - height: 40, + ], + ) + : Column( + children: [ + const Spacer( + flex: 4, ), - Text( - "You do not have a MonKey yet. \nFetch yours now!", - style: STextStyles.smallMed14(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, + Center( + child: Column( + children: [ + Opacity( + opacity: 0.2, + child: SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 200, + height: 200, + ), + ), + const SizedBox( + height: 40, + ), + 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, + ), + ], + ), + ), + const Spacer( + flex: 6, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return FetchMonkeyDialog( + onCancel: () async { + Navigator.of(context).pop(); + }, + ); + }, + ); + }, ), - textAlign: TextAlign.center, ), ], ), - ), - const Spacer(), - Padding( - padding: const EdgeInsets.all(16.0), - child: PrimaryButton( - label: "Fetch MonKey", - onPressed: () { - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return FetchMonkeyDialog( - onCancel: () async { - Navigator.of(context).pop(); - }, - ); - }, - ); - }, - ), - ), - ], - ), ), ], ), From c97de6017bec6a8dcf5d51ab6467464e3b2b223f Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 15:42:45 -0600 Subject: [PATCH 070/128] update lelantus coin schema --- lib/db/isar/main_db.dart | 1 - .../models/firo_specific/lelantus_coin.dart | 29 +- .../models/firo_specific/lelantus_coin.g.dart | 663 +++++++++++++++--- lib/services/coins/firo/firo_wallet.dart | 24 +- lib/utilities/db_version_migration.dart | 11 +- test/services/coins/manager_test.mocks.dart | 55 -- .../transaction_card_test.mocks.dart | 54 -- 7 files changed, 634 insertions(+), 203 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 209f13c5d..fa565c0cd 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -14,7 +14,6 @@ import 'package:isar/isar.dart'; 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/firo_specific/lelantus_coin.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart index 3dd48b2dc..9ad5bc5a3 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -23,20 +23,33 @@ class LelantusCoin { final String value; // can't use BigInt in isar :shrug: - final int index; + @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.publicCoin, required this.txid, required this.value, - required this.index, + required this.mintIndex, required this.anonymitySetId, required this.isUsed, + required this.isJMint, + required this.otherData, }); LelantusCoin copyWith({ @@ -44,18 +57,22 @@ class LelantusCoin { String? publicCoin, String? txid, String? value, - int? index, + int? mintIndex, int? anonymitySetId, bool? isUsed, + bool? isJMint, + String? otherData, }) { return LelantusCoin( walletId: walletId ?? this.walletId, publicCoin: publicCoin ?? this.publicCoin, txid: txid ?? this.txid, value: value ?? this.value, - index: index ?? this.index, + mintIndex: mintIndex ?? this.mintIndex, anonymitySetId: anonymitySetId ?? this.anonymitySetId, isUsed: isUsed ?? this.isUsed, + isJMint: isJMint ?? this.isJMint, + otherData: otherData ?? this.otherData, ); } @@ -67,8 +84,10 @@ class LelantusCoin { 'publicCoin: $publicCoin, ' 'txid: $txid, ' 'value: $value, ' - 'index: $index, ' + '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 index 8fafde571..d2e27638d 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -22,33 +22,43 @@ const LelantusCoinSchema = CollectionSchema( name: r'anonymitySetId', type: IsarType.long, ), - r'index': PropertySchema( + r'isJMint': PropertySchema( id: 1, - name: r'index', - type: IsarType.long, + name: r'isJMint', + type: IsarType.bool, ), r'isUsed': PropertySchema( id: 2, name: r'isUsed', type: IsarType.bool, ), - r'publicCoin': PropertySchema( + r'mintIndex': PropertySchema( id: 3, + name: r'mintIndex', + type: IsarType.long, + ), + r'otherData': PropertySchema( + id: 4, + name: r'otherData', + type: IsarType.string, + ), + r'publicCoin': PropertySchema( + id: 5, name: r'publicCoin', type: IsarType.string, ), r'txid': PropertySchema( - id: 4, + id: 6, name: r'txid', type: IsarType.string, ), r'value': PropertySchema( - id: 5, + id: 7, name: r'value', type: IsarType.string, ), r'walletId': PropertySchema( - id: 6, + id: 8, name: r'walletId', type: IsarType.string, ) @@ -94,6 +104,24 @@ const LelantusCoinSchema = CollectionSchema( 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: {}, @@ -110,6 +138,12 @@ int _lelantusCoinEstimateSize( Map> allOffsets, ) { var bytesCount = offsets.last; + { + final value = object.otherData; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } bytesCount += 3 + object.publicCoin.length * 3; bytesCount += 3 + object.txid.length * 3; bytesCount += 3 + object.value.length * 3; @@ -124,12 +158,14 @@ void _lelantusCoinSerialize( Map> allOffsets, ) { writer.writeLong(offsets[0], object.anonymitySetId); - writer.writeLong(offsets[1], object.index); + writer.writeBool(offsets[1], object.isJMint); writer.writeBool(offsets[2], object.isUsed); - writer.writeString(offsets[3], object.publicCoin); - writer.writeString(offsets[4], object.txid); - writer.writeString(offsets[5], object.value); - writer.writeString(offsets[6], object.walletId); + writer.writeLong(offsets[3], object.mintIndex); + writer.writeString(offsets[4], object.otherData); + writer.writeString(offsets[5], object.publicCoin); + writer.writeString(offsets[6], object.txid); + writer.writeString(offsets[7], object.value); + writer.writeString(offsets[8], object.walletId); } LelantusCoin _lelantusCoinDeserialize( @@ -140,12 +176,14 @@ LelantusCoin _lelantusCoinDeserialize( ) { final object = LelantusCoin( anonymitySetId: reader.readLong(offsets[0]), - index: reader.readLong(offsets[1]), + isJMint: reader.readBool(offsets[1]), isUsed: reader.readBool(offsets[2]), - publicCoin: reader.readString(offsets[3]), - txid: reader.readString(offsets[4]), - value: reader.readString(offsets[5]), - walletId: reader.readString(offsets[6]), + mintIndex: reader.readLong(offsets[3]), + otherData: reader.readStringOrNull(offsets[4]), + publicCoin: reader.readString(offsets[5]), + txid: reader.readString(offsets[6]), + value: reader.readString(offsets[7]), + walletId: reader.readString(offsets[8]), ); object.id = id; return object; @@ -161,17 +199,21 @@ P _lelantusCoinDeserializeProp

( case 0: return (reader.readLong(offset)) as P; case 1: - return (reader.readLong(offset)) as P; + return (reader.readBool(offset)) as P; case 2: return (reader.readBool(offset)) as P; case 3: - return (reader.readString(offset)) as P; + return (reader.readLong(offset)) as P; case 4: - return (reader.readString(offset)) as P; + 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; + case 8: + return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -290,6 +332,92 @@ extension LelantusCoinByIndex on IsarCollection { return putAllByIndexSync(r'publicCoin_walletId_txid', objects, saveLinks: saveLinks); } + + 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 @@ -552,6 +680,144 @@ extension LelantusCoinQueryWhere } }); } + + 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 @@ -665,60 +931,16 @@ extension LelantusCoinQueryFilter }); } - QueryBuilder indexEqualTo( - int value) { + QueryBuilder + isJMintEqualTo(bool value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'index', + property: r'isJMint', value: value, )); }); } - QueryBuilder - indexGreaterThan( - int value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'index', - value: value, - )); - }); - } - - QueryBuilder indexLessThan( - int value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'index', - value: value, - )); - }); - } - - QueryBuilder indexBetween( - int lower, - int upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'index', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - QueryBuilder isUsedEqualTo( bool value) { return QueryBuilder.apply(this, (query) { @@ -729,6 +951,216 @@ extension LelantusCoinQueryFilter }); } + 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 publicCoinEqualTo( String value, { @@ -1292,15 +1724,15 @@ extension LelantusCoinQuerySortBy }); } - QueryBuilder sortByIndex() { + QueryBuilder sortByIsJMint() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.asc); + return query.addSortBy(r'isJMint', Sort.asc); }); } - QueryBuilder sortByIndexDesc() { + QueryBuilder sortByIsJMintDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.desc); + return query.addSortBy(r'isJMint', Sort.desc); }); } @@ -1316,6 +1748,30 @@ extension LelantusCoinQuerySortBy }); } + 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 sortByPublicCoin() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'publicCoin', Sort.asc); @@ -1394,15 +1850,15 @@ extension LelantusCoinQuerySortThenBy }); } - QueryBuilder thenByIndex() { + QueryBuilder thenByIsJMint() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.asc); + return query.addSortBy(r'isJMint', Sort.asc); }); } - QueryBuilder thenByIndexDesc() { + QueryBuilder thenByIsJMintDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.desc); + return query.addSortBy(r'isJMint', Sort.desc); }); } @@ -1418,6 +1874,30 @@ extension LelantusCoinQuerySortThenBy }); } + 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 thenByPublicCoin() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'publicCoin', Sort.asc); @@ -1477,9 +1957,9 @@ extension LelantusCoinQueryWhereDistinct }); } - QueryBuilder distinctByIndex() { + QueryBuilder distinctByIsJMint() { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'index'); + return query.addDistinctBy(r'isJMint'); }); } @@ -1489,6 +1969,19 @@ extension LelantusCoinQueryWhereDistinct }); } + 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 distinctByPublicCoin( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -1532,9 +2025,9 @@ extension LelantusCoinQueryProperty }); } - QueryBuilder indexProperty() { + QueryBuilder isJMintProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'index'); + return query.addPropertyName(r'isJMint'); }); } @@ -1544,6 +2037,18 @@ extension LelantusCoinQueryProperty }); } + 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 publicCoinProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'publicCoin'); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 004e5399c..67882a6b6 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -269,18 +269,20 @@ Future> isolateRestore( lelantusCoins.removeWhere((e) => e.txid == txId && - e.index == currentIndex && + e.mintIndex == currentIndex && e.anonymitySetId != setId); lelantusCoins.add( isar_models.LelantusCoin( walletId: walletId, - index: currentIndex, + mintIndex: currentIndex, value: amount.toString(), publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, + isJMint: false, + otherData: null, ), ); Logging.instance.log( @@ -317,18 +319,20 @@ Future> isolateRestore( bool isUsed = usedSerialNumbersSet.contains(serialNumber); lelantusCoins.removeWhere((e) => e.txid == txId && - e.index == currentIndex && + e.mintIndex == currentIndex && e.anonymitySetId != setId); lelantusCoins.add( isar_models.LelantusCoin( walletId: walletId, - index: currentIndex, + mintIndex: currentIndex, value: amount.toString(), publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, + isJMint: true, + otherData: null, ), ); jindexes.add(currentIndex); @@ -2325,7 +2329,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); @@ -2335,7 +2339,7 @@ class FiroWallet extends CoinServiceAPI } final String privateKey = Format.uint8listToString(keyPair.privateKey!); return DartLelantusEntry(coin.isUsed ? 1 : 0, 0, coin.anonymitySetId, - int.parse(coin.value), coin.index, privateKey); + int.parse(coin.value), coin.mintIndex, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); @@ -3054,12 +3058,14 @@ class FiroWallet extends CoinServiceAPI // if a jmint was made add it to the unspent coin index final jmint = isar_models.LelantusCoin( walletId: walletId, - index: index, + mintIndex: nextFreeMintIndex, value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), publicCoin: transactionInfo['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, + isJMint: true, + otherData: null, ); if (int.parse(jmint.value) > 0) { updatedCoins.add(jmint); @@ -3143,12 +3149,14 @@ class FiroWallet extends CoinServiceAPI final index = mintMap['index'] as int; final mint = isar_models.LelantusCoin( walletId: walletId, - index: index, + mintIndex: index, value: (mintMap['value'] as int).toString(), publicCoin: mintMap['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, + isJMint: false, + otherData: null, ); if (int.parse(mint.value) > 0) { updatedCoins.add(mint); diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index dac1dbe82..d2c0adbe8 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -386,19 +386,28 @@ class DbVersionMigrator with WalletDB { ) 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, publicCoin: lcoin.publicCoin, txid: lcoin.txId, value: lcoin.value.toString(), - index: lcoin.index, + mintIndex: lcoin.index, anonymitySetId: lcoin.anonymitySetId, isUsed: lcoin.isUsed, + isJMint: isJMint, + otherData: null, ); coins.add(coin); diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 8b48a38a9..25fd46d9d 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, @@ -1061,51 +1051,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/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 4fc6f8832..29bd7bd29 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1602,15 +1602,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, @@ -2075,51 +2066,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]. From 2fb94444c46965c34d5adcaaa9257fda649a718a Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 16:49:11 -0600 Subject: [PATCH 071/128] no more firo specific hive --- lib/services/coins/firo/firo_wallet.dart | 322 ++++++++++------------- lib/services/mixins/firo_hive.dart | 61 ----- 2 files changed, 145 insertions(+), 238 deletions(-) delete mode 100644 lib/services/mixins/firo_hive.dart diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 67882a6b6..a5a7b3b47 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -35,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'; @@ -752,7 +751,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({ @@ -773,7 +772,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}", @@ -2181,7 +2179,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); @@ -2352,57 +2349,60 @@ class FiroWallet extends CoinServiceAPI } Future> _getUnspentCoins() async { - final jindexes = firoGetJIndex() ?? []; + // final jindexes = firoGetJIndex() ?? []; final lelantusCoinsList = await db.isar.lelantusCoins .where() .walletIdEqualTo(walletId) .filter() + .isUsedEqualTo(false) // TODO add this? .not() .valueEqualTo("0") .findAll(); - List coins = []; + return lelantusCoinsList; - final currentChainHeight = await chainHeight; - - for (int i = 0; i < lelantusCoinsList.length; i++) { - // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - final coin = lelantusCoinsList[i]; - - final tx = await db.getTransaction(walletId, coin.txid); - - // TODO check if sane default - bool isUnconfirmed = false; - - if (tx != null) { - bool isConfirmed = tx.isConfirmed( - currentChainHeight, - MINIMUM_CONFIRMATIONS, - ); - if (!jindexes.contains(coin.index) && - tx.isLelantus == true && - !isConfirmed) { - isUnconfirmed = true; - } else if (!isConfirmed) { - continue; - } - } else { - final txn = await cachedElectrumXClient.getTransaction( - txHash: coin.txid, - verbose: true, - coin: this.coin, - ); - final confirmations = txn["confirmations"]; - isUnconfirmed = confirmations is int && confirmations < 1; - } - if (!coin.isUsed && - coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && - !isUnconfirmed) { - coins.add(coin); - } - } - return coins; + // List coins = []; + // + // final currentChainHeight = await chainHeight; + // + // for (int i = 0; i < lelantusCoinsList.length; i++) { + // // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); + // final coin = lelantusCoinsList[i]; + // + // final tx = await db.getTransaction(walletId, coin.txid); + // + // // TODO check if sane default + // bool isUnconfirmed = false; + // + // if (tx != null) { + // bool isConfirmed = tx.isConfirmed( + // currentChainHeight, + // MINIMUM_CONFIRMATIONS, + // ); + // if (!jindexes.contains(coin.index) && + // tx.isLelantus == true && + // !isConfirmed) { + // isUnconfirmed = true; + // } else if (!isConfirmed) { + // continue; + // } + // } else { + // final txn = await cachedElectrumXClient.getTransaction( + // txHash: coin.txid, + // verbose: true, + // coin: this.coin, + // ); + // final confirmations = txn["confirmations"]; + // isUnconfirmed = confirmations is int && confirmations < 1; + // } + // if (!coin.isUsed && + // coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && + // !isUnconfirmed) { + // coins.add(coin); + // } + // } + // return coins; } // index 0 and 1 for the funds available to spend. @@ -2415,12 +2415,12 @@ class FiroWallet extends CoinServiceAPI .where() .walletIdEqualTo(walletId) .filter() + .isUsedEqualTo(false) .not() .valueEqualTo(0.toString()) .findAll(); final currentChainHeight = await chainHeight; - final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; @@ -2440,33 +2440,17 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Fatal, ); } 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 += int.parse(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 += int.parse(lelantusCoin.value); - } else if (jindexes.contains(lelantusCoin.index) && - !lelantusCoin.isUsed) { + 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 if (!lelantusCoin.isUsed && - (txn.isLelantus == true - ? true - : txn.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += int.parse(lelantusCoin.value); - } else if (!isLelantus && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { + } else { unconfirmedLelantusBalance += int.parse(lelantusCoin.value); } } @@ -2615,10 +2599,16 @@ class FiroWallet extends CoinServiceAPI } Future>> createMintsFromAmount(int total) async { - var tmpTotal = total; - var index = 1; - var mints = >[]; - final nextFreeMintIndex = firoGetMintIndex(); + int tmpTotal = total; + int index = 0; + final mints = >[]; + final lastUsedIndex = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; while (tmpTotal > 0) { final mintValue = min(tmpTotal, MINT_LIMIT); final mint = await _getMintHex( @@ -2783,9 +2773,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 = @@ -2837,45 +2824,39 @@ class FiroWallet extends CoinServiceAPI .where() .walletIdEqualTo(walletId) .filter() + .isUsedEqualTo(false) + .and() + .isJMintEqualTo(true) .not() .valueEqualTo(0.toString()) .findAll(); - final jindexes = firoGetJIndex(); - // Get all joinsplit transaction ids - final listLelantusTxData = await db + final lelantusJoinSplitTxns = await db .getTransactions(walletId) .filter() .isLelantusEqualTo(true) + .and() + .subTypeEqualTo(isar_models.TransactionSubType.join) .findAll(); - List joinsplits = []; - for (final tx in listLelantusTxData) { - if (tx.subType == isar_models.TransactionSubType.join) { - joinsplits.add(tx.txid); - } - } + Set joinSplitTXIDs = {}; + + // for (final tx in lelantusJoinSplitTxns) { + // joinSplitTXIDs.add(tx.txid); + // } for (final coin in lelantusCoins) { - if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txid)) { - joinsplits.add(coin.txid); - } - } + joinSplitTXIDs.add(coin.txid); } - Map> data = - {}; - for (final entry in listLelantusTxData) { - data[entry.txid] = Tuple2(entry.address.value, entry); - } + Map> + updatedData = {}; // Grab the most recent information on all the joinsplits - final updatedJSplit = await getJMintTransactions( cachedElectrumXClient, - joinsplits, + joinSplitTXIDs.toList(), coin, ); @@ -2887,7 +2868,7 @@ class FiroWallet extends CoinServiceAPI try { currentTx = - listLelantusTxData.firstWhere((e) => e.txid == tx.value.txid); + lelantusJoinSplitTxns.firstWhere((e) => e.txid == tx.value.txid); } catch (_) { currentTx = null; } @@ -2895,54 +2876,23 @@ class FiroWallet extends CoinServiceAPI if (currentTx == null) { // this send was accidentally not included in the list tx.value.isLelantus = true; - data[tx.value.txid] = + updatedData[tx.value.txid] = Tuple2(tx.value.address.value ?? tx.key, tx.value); - - continue; - } - if (currentTx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) != + } else if (currentTx.isConfirmed( + currentChainHeight, MINIMUM_CONFIRMATIONS) != tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { tx.value.isLelantus = true; - data[tx.value.txid] = + updatedData[tx.value.txid] = Tuple2(tx.value.address.value ?? tx.key, tx.value); } } - // 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) { + for (final value in updatedData.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 @@ -2967,15 +2917,6 @@ class FiroWallet extends CoinServiceAPI } 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 { @@ -3025,7 +2966,13 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Info); if (txid == transactionInfo['txid']) { - final index = firoGetMintIndex(); + final lastUsedIndex = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { // This is a joinsplit @@ -3041,7 +2988,7 @@ class FiroWallet extends CoinServiceAPI .where() .walletIdEqualTo(walletId) .filter() - .indexEqualTo(index) + .mintIndexEqualTo(index) .findAll(); if (possibleCoins.isNotEmpty) { @@ -3069,22 +3016,26 @@ class FiroWallet extends CoinServiceAPI ); if (int.parse(jmint.value) > 0) { updatedCoins.add(jmint); - final jindexes = firoGetJIndex()!; - jindexes.add(index); - await firoUpdateJIndex(jindexes); - await firoUpdateMintIndex(index + 1); } - await db.isar.writeTxn(() async { - for (final c in updatedCoins) { - await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( - c.publicCoin, - c.walletId, - c.txid, - ); - } - await db.isar.lelantusCoins.putAll(updatedCoins); - }); + try { + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( + c.publicCoin, + c.walletId, + c.txid, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3160,13 +3111,20 @@ class FiroWallet extends CoinServiceAPI ); if (int.parse(mint.value) > 0) { updatedCoins.add(mint); - await firoUpdateMintIndex(index + 1); } } // Logging.instance.log(coins); - await db.isar.writeTxn(() async { - await db.isar.lelantusCoins.putAll(updatedCoins); - }); + 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 { @@ -3933,7 +3891,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, @@ -4586,14 +4543,19 @@ class FiroWallet extends CoinServiceAPI await chainHeight, ); - await Future.wait([ - firoUpdateMintIndex(message['mintIndex'] as int), - db.isar.writeTxn(() async { - await db.isar.lelantusCoins.putAll( - 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, + ); + rethrow; + } final transactionMap = message["newTxMap"] as Map; @@ -4677,7 +4639,13 @@ 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.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); final locktime = await getBlockHead(electrumXClient); @@ -4691,7 +4659,7 @@ class FiroWallet extends CoinServiceAPI "subtractFeeFromAmount": subtractFeeFromAmount, "mnemonic": _mnemonic, "mnemonicPassphrase": _mnemonicPassphrase, - "index": index, + "index": nextFreeMintIndex, // "price": price, "lelantusEntries": lelantusEntry, "locktime": locktime, diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart deleted file mode 100644 index 631aebe6d..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, - ); - } -} From 462b845bd40b1f448d86d75e366363e53d459512 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 16:54:38 -0600 Subject: [PATCH 072/128] clean up unspent coins --- lib/services/coins/firo/firo_wallet.dart | 51 +++--------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index a5a7b3b47..fe1956ce9 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2349,60 +2349,19 @@ class FiroWallet extends CoinServiceAPI } Future> _getUnspentCoins() async { - // final jindexes = firoGetJIndex() ?? []; - final lelantusCoinsList = await db.isar.lelantusCoins .where() .walletIdEqualTo(walletId) .filter() - .isUsedEqualTo(false) // TODO add this? + .isUsedEqualTo(false) .not() - .valueEqualTo("0") + .group((q) => q + .valueEqualTo("0") + .or() + .anonymitySetIdEqualTo(ANONYMITY_SET_EMPTY_ID)) .findAll(); return lelantusCoinsList; - - // List coins = []; - // - // final currentChainHeight = await chainHeight; - // - // for (int i = 0; i < lelantusCoinsList.length; i++) { - // // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - // final coin = lelantusCoinsList[i]; - // - // final tx = await db.getTransaction(walletId, coin.txid); - // - // // TODO check if sane default - // bool isUnconfirmed = false; - // - // if (tx != null) { - // bool isConfirmed = tx.isConfirmed( - // currentChainHeight, - // MINIMUM_CONFIRMATIONS, - // ); - // if (!jindexes.contains(coin.index) && - // tx.isLelantus == true && - // !isConfirmed) { - // isUnconfirmed = true; - // } else if (!isConfirmed) { - // continue; - // } - // } else { - // final txn = await cachedElectrumXClient.getTransaction( - // txHash: coin.txid, - // verbose: true, - // coin: this.coin, - // ); - // final confirmations = txn["confirmations"]; - // isUnconfirmed = confirmations is int && confirmations < 1; - // } - // if (!coin.isUsed && - // coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && - // !isUnconfirmed) { - // coins.add(coin); - // } - // } - // return coins; } // index 0 and 1 for the funds available to spend. From 18f3a2056c382a9a31446d8be81135d367e0c4f1 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 08:55:50 -0600 Subject: [PATCH 073/128] check anon set when creating list of mints --- lib/services/coins/firo/firo_wallet.dart | 89 ++++++++++++++++++++---- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index fe1956ce9..331b5b519 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2559,8 +2559,7 @@ class FiroWallet extends CoinServiceAPI Future>> createMintsFromAmount(int total) async { int tmpTotal = total; - int index = 0; - final mints = >[]; + int counter = 0; final lastUsedIndex = await db.isar.lelantusCoins .where() .walletIdEqualTo(walletId) @@ -2568,20 +2567,82 @@ class FiroWallet extends CoinServiceAPI .mintIndexProperty() .findFirst(); 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, MINT_LIMIT); + final mint = await _getMintHex( + mintValue, + index, + ); + mints.add({ + "value": mintValue, + "script": mint, + "index": index, + "publicCoin": "", + }); + tmpTotal = tmpTotal - MINT_LIMIT; + } + + counter++; } return mints; } From 99a36f158731fad192f7c30bc59ba64dfd671479 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 09:03:26 -0600 Subject: [PATCH 074/128] fix mint limit constant --- lib/services/coins/firo/firo_wallet.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 331b5b519..b02816497 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -58,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; @@ -2628,7 +2628,8 @@ class FiroWallet extends CoinServiceAPI } if (!isUsedMintTag) { - final mintValue = min(tmpTotal, MINT_LIMIT); + final mintValue = min(tmpTotal, + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT)); final mint = await _getMintHex( mintValue, index, @@ -2639,7 +2640,8 @@ class FiroWallet extends CoinServiceAPI "index": index, "publicCoin": "", }); - tmpTotal = tmpTotal - MINT_LIMIT; + tmpTotal = tmpTotal - + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); } counter++; From 140f1468f9d3e243d80a128d65f305b3521b3d07 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 09:04:09 -0600 Subject: [PATCH 075/128] remove unused code --- lib/services/coins/firo/firo_wallet.dart | 69 ------------------------ 1 file changed, 69 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index b02816497..659a6d94b 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -4769,75 +4769,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 { From a0b42226f1f9937e4bbbacc3e8f8e473e41e0636 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 10:39:23 -0600 Subject: [PATCH 076/128] fix test --- test/services/coins/firo/firo_wallet_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index 68db15437..fcd7bd50e 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -72,6 +72,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ); const currentHeight = 100000000000; @@ -133,6 +134,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ), throwsA(isA())); }); From c7f1392734df24cd177324f44cdee12f6af8c789 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 10:49:49 -0600 Subject: [PATCH 077/128] cache boxes hive error fix --- lib/db/hive/db.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 931a1cee4..25efcdb3e 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)); } From a8abd388271f0e6ebfaee51d44490cd374e3e408 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 11:17:30 -0600 Subject: [PATCH 078/128] invalid vsize fix --- lib/services/coins/firo/firo_wallet.dart | 26 +++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 659a6d94b..7b42698dc 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1429,16 +1429,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], From 5f9b1f77f58114eb0f39b2f10911da15b169b791 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 11:17:54 -0600 Subject: [PATCH 079/128] clearer exception --- lib/services/coins/firo/firo_wallet.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 7b42698dc..10cf4473c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -4592,7 +4592,8 @@ class FiroWallet extends CoinServiceAPI "$e\n$s", level: LogLevel.Fatal, ); - rethrow; + // don't just rethrow since isar likes to strip stack traces for some reason + throw Exception("e=$e & s=$s"); } final transactionMap = From e1140e6fa6dc863ca09b43ea2b3596d899be026d Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 11:18:21 -0600 Subject: [PATCH 080/128] clear lelantus coins on full rescan --- lib/db/isar/main_db.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index fa565c0cd..4b60e2cda 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -371,6 +371,7 @@ 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; @@ -404,6 +405,16 @@ 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); + } }); } From 5439ce895433bbf9c9e5dee3cb6e1596954c38b9 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:06:06 -0600 Subject: [PATCH 081/128] show error before data in case dart decides string is too long and cuts it off --- lib/electrumx_rpc/electrumx.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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", ); } From 3c1a3f23e379fb187e40e1738ce34154f17a8b43 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:07:09 -0600 Subject: [PATCH 082/128] clean up --- lib/services/coins/firo/firo_wallet.dart | 47 ------------------------ 1 file changed, 47 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 10cf4473c..a31e0f62c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1224,18 +1224,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 @@ -2409,7 +2397,6 @@ class FiroWallet extends CoinServiceAPI .findFirstSync(); if (txn == null) { - // TODO: ?????????????????????????????????????? Logging.instance.log( "Transaction not found in DB for lelantus coin: $lelantusCoin", level: LogLevel.Fatal, @@ -2686,8 +2673,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 @@ -3966,7 +3951,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); @@ -4816,36 +4800,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 { @@ -4908,7 +4862,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) * From 7d1d7cb8049c08195b146e77e0c77131e2f1c08b Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:07:44 -0600 Subject: [PATCH 083/128] auto format on save --- lib/db/isar/main_db.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 4b60e2cda..93c9052da 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -371,7 +371,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(); + final lelantusCoinCount = + await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); await isar.writeTxn(() async { const paginateLimit = 50; @@ -408,7 +409,9 @@ class MainDB { // lelantusCoins for (int i = 0; i < lelantusCoinCount; i += paginateLimit) { - final lelantusCoinIds = await isar.lelantusCoins.where().walletIdEqualTo(walletId) + final lelantusCoinIds = await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) .offset(i) .limit(paginateLimit) .idProperty() From 590717560e3469ab1b65ea80b355c601285e1023 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:12:10 -0600 Subject: [PATCH 084/128] notif error fix? --- lib/widgets/crypto_notifications.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index f94c74ed9..88a96d422 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/global/prefs_provider.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,7 @@ class _CryptoNotificationsState extends ConsumerState { @override void initState() { + NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on() .listen( From 20c706c7e5558ca6a170b739234f79021b8d4e0d Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:16:08 -0600 Subject: [PATCH 085/128] add todo check --- lib/services/coins/firo/firo_wallet.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index a31e0f62c..ea0a255d4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2637,6 +2637,8 @@ class FiroWallet extends CoinServiceAPI mintValue, index, ); + + // TODO publicCoin prob shouldn't be empty? mints.add({ "value": mintValue, "script": mint, From c9470a5078168f1359f9b771bbb3d7d9e4d86f09 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 13:48:19 -0600 Subject: [PATCH 086/128] desktop notifications fix --- lib/widgets/crypto_notifications.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index 88a96d422..b570ae4f9 100644 --- a/lib/widgets/crypto_notifications.dart +++ b/lib/widgets/crypto_notifications.dart @@ -13,7 +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/global/prefs_provider.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'; @@ -86,6 +86,7 @@ class _CryptoNotificationsState extends ConsumerState { @override void initState() { NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); + NotificationApi.notificationsService = ref.read(notificationsProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on() .listen( From 128fa9db6cb79dc6c5dc8ecfadb2afcc8c6121dc Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 13:48:39 -0600 Subject: [PATCH 087/128] rpc timeout mod --- lib/electrumx_rpc/rpc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); }, From 04658f8eef091b31ba78da10d1a9ab194544fb83 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 13:55:19 -0600 Subject: [PATCH 088/128] null error bandaid --- lib/services/notifications_api.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index 41e95387b..3ef97462a 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -77,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( From 2cca59532e43fc84a0d637ad3bd35032418ed0fc Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 14:14:07 -0600 Subject: [PATCH 089/128] delete by index instead of public Coin since who tf knows what it could be in our code --- lib/services/coins/firo/firo_wallet.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index ea0a255d4..44a29ee96 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3046,10 +3046,9 @@ class FiroWallet extends CoinServiceAPI try { await db.isar.writeTxn(() async { for (final c in updatedCoins) { - await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( - c.publicCoin, + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, c.walletId, - c.txid, ); } await db.isar.lelantusCoins.putAll(updatedCoins); From 3cbde8b5c1b91b77297a7f1d436a63509127df35 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Tue, 25 Jul 2023 14:52:58 -0600 Subject: [PATCH 090/128] able to download monkey.png --- lib/pages/monkey/monkey_view.dart | 79 +++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 10b9c91bf..f109dd5c7 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -3,7 +3,10 @@ import 'dart:io'; 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:stackwallet/pages/monkey/sub_widgets/fetch_monkey_dialog.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -36,11 +39,69 @@ class _MonkeyViewState 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')); + final decodedResponse = SvgPicture.memory(response.bodyBytes); + // final decodedResponse = json.decode(response.body); + // return decodedResponse; + debugPrint("$decodedResponse"); + } + + 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) { + final decodedResponse = response.bodyBytes; + final directory = await getApplicationDocumentsDirectory(); + // Directory appDir = await getTemporaryDirectory(); + final docPath = directory.path; + final filePath = "$docPath/monkey.png"; + + File imgFile = File(filePath); + await imgFile.writeAsBytes(decodedResponse); + print("$imgFile"); + + // final directory = await getApplicationDocumentsDirectory(); + // final docPath = directory.path; + // final filePath = "$do/monkey.png"; + } else { + throw Exception("Failed to get MonKey"); + } + + // final decodedResponse = json.decode(response.body); + // return decodedResponse; + // debugPrint("$decodedResponse"); + } + @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(); } @@ -52,7 +113,7 @@ class _MonkeyViewState extends ConsumerState { @override Widget build(BuildContext context) { final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); - bool isMonkey = false; + bool isMonkey = true; return Background( child: Stack( @@ -116,19 +177,29 @@ class _MonkeyViewState extends ConsumerState { body: isMonkey ? Column( children: [ - Spacer(), + Spacer( + flex: 1, + ), + Image.network( + 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', + ), + Spacer( + flex: 1, + ), Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ SecondaryButton( label: "Download as SVG", - onPressed: () {}, + onPressed: () async {}, ), const SizedBox(height: 12), SecondaryButton( label: "Download as PNG", - onPressed: () {}, + onPressed: () { + getMonkeyPNG(receivingAddress); + }, ), ], ), From ce1dce113062ead4fbd74ef70aa5f08e19ad91ae Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 14:56:31 -0600 Subject: [PATCH 091/128] comment and fix error messages --- lib/services/coins/firo/firo_wallet.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 44a29ee96..861263625 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1138,7 +1138,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; } @@ -1146,7 +1147,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; } @@ -2346,6 +2348,8 @@ class FiroWallet extends CoinServiceAPI 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); } From 759e5624e43d139381027264deebb7b4c444cb0b Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:11:34 -0600 Subject: [PATCH 092/128] stop storing public coin --- .../models/firo_specific/lelantus_coin.dart | 13 - .../models/firo_specific/lelantus_coin.g.dart | 464 +----------------- lib/services/coins/firo/firo_wallet.dart | 14 +- lib/utilities/db_version_migration.dart | 1 - 4 files changed, 16 insertions(+), 476 deletions(-) diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart index 9ad5bc5a3..ca4c11919 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -9,16 +9,6 @@ class LelantusCoin { @Index() final String walletId; - @Index( - unique: true, - composite: [ - CompositeIndex("walletId"), - CompositeIndex("txid"), - ], - replace: false, - ) - final String publicCoin; - final String txid; final String value; // can't use BigInt in isar :shrug: @@ -42,7 +32,6 @@ class LelantusCoin { LelantusCoin({ required this.walletId, - required this.publicCoin, required this.txid, required this.value, required this.mintIndex, @@ -65,7 +54,6 @@ class LelantusCoin { }) { return LelantusCoin( walletId: walletId ?? this.walletId, - publicCoin: publicCoin ?? this.publicCoin, txid: txid ?? this.txid, value: value ?? this.value, mintIndex: mintIndex ?? this.mintIndex, @@ -81,7 +69,6 @@ class LelantusCoin { return 'LelantusCoin{' 'id: $id, ' 'walletId: $walletId, ' - 'publicCoin: $publicCoin, ' 'txid: $txid, ' 'value: $value, ' 'mintIndex: $mintIndex, ' diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart index d2e27638d..4b9214889 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -42,23 +42,18 @@ const LelantusCoinSchema = CollectionSchema( name: r'otherData', type: IsarType.string, ), - r'publicCoin': PropertySchema( - id: 5, - name: r'publicCoin', - type: IsarType.string, - ), r'txid': PropertySchema( - id: 6, + id: 5, name: r'txid', type: IsarType.string, ), r'value': PropertySchema( - id: 7, + id: 6, name: r'value', type: IsarType.string, ), r'walletId': PropertySchema( - id: 8, + id: 7, name: r'walletId', type: IsarType.string, ) @@ -82,29 +77,6 @@ const LelantusCoinSchema = CollectionSchema( ) ], ), - r'publicCoin_walletId_txid': IndexSchema( - id: 5610740154835640070, - name: r'publicCoin_walletId_txid', - unique: true, - replace: false, - properties: [ - IndexPropertySchema( - name: r'publicCoin', - type: IndexType.hash, - caseSensitive: true, - ), - IndexPropertySchema( - name: r'walletId', - type: IndexType.hash, - caseSensitive: true, - ), - IndexPropertySchema( - name: r'txid', - type: IndexType.hash, - caseSensitive: true, - ) - ], - ), r'mintIndex_walletId': IndexSchema( id: -9147309777276196770, name: r'mintIndex_walletId', @@ -144,7 +116,6 @@ int _lelantusCoinEstimateSize( bytesCount += 3 + value.length * 3; } } - bytesCount += 3 + object.publicCoin.length * 3; bytesCount += 3 + object.txid.length * 3; bytesCount += 3 + object.value.length * 3; bytesCount += 3 + object.walletId.length * 3; @@ -162,10 +133,9 @@ void _lelantusCoinSerialize( writer.writeBool(offsets[2], object.isUsed); writer.writeLong(offsets[3], object.mintIndex); writer.writeString(offsets[4], object.otherData); - writer.writeString(offsets[5], object.publicCoin); - writer.writeString(offsets[6], object.txid); - writer.writeString(offsets[7], object.value); - writer.writeString(offsets[8], object.walletId); + writer.writeString(offsets[5], object.txid); + writer.writeString(offsets[6], object.value); + writer.writeString(offsets[7], object.walletId); } LelantusCoin _lelantusCoinDeserialize( @@ -180,10 +150,9 @@ LelantusCoin _lelantusCoinDeserialize( isUsed: reader.readBool(offsets[2]), mintIndex: reader.readLong(offsets[3]), otherData: reader.readStringOrNull(offsets[4]), - publicCoin: reader.readString(offsets[5]), - txid: reader.readString(offsets[6]), - value: reader.readString(offsets[7]), - walletId: reader.readString(offsets[8]), + txid: reader.readString(offsets[5]), + value: reader.readString(offsets[6]), + walletId: reader.readString(offsets[7]), ); object.id = id; return object; @@ -212,8 +181,6 @@ P _lelantusCoinDeserializeProp

( return (reader.readString(offset)) as P; case 7: return (reader.readString(offset)) as P; - case 8: - return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -233,106 +200,6 @@ void _lelantusCoinAttach( } extension LelantusCoinByIndex on IsarCollection { - Future getByPublicCoinWalletIdTxid( - String publicCoin, String walletId, String txid) { - return getByIndex( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - LelantusCoin? getByPublicCoinWalletIdTxidSync( - String publicCoin, String walletId, String txid) { - return getByIndexSync( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - Future deleteByPublicCoinWalletIdTxid( - String publicCoin, String walletId, String txid) { - return deleteByIndex( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - bool deleteByPublicCoinWalletIdTxidSync( - String publicCoin, String walletId, String txid) { - return deleteByIndexSync( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - Future> getAllByPublicCoinWalletIdTxid( - List publicCoinValues, - List walletIdValues, - List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return getAllByIndex(r'publicCoin_walletId_txid', values); - } - - List getAllByPublicCoinWalletIdTxidSync( - List publicCoinValues, - List walletIdValues, - List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return getAllByIndexSync(r'publicCoin_walletId_txid', values); - } - - Future deleteAllByPublicCoinWalletIdTxid(List publicCoinValues, - List walletIdValues, List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return deleteAllByIndex(r'publicCoin_walletId_txid', values); - } - - int deleteAllByPublicCoinWalletIdTxidSync(List publicCoinValues, - List walletIdValues, List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return deleteAllByIndexSync(r'publicCoin_walletId_txid', values); - } - - Future putByPublicCoinWalletIdTxid(LelantusCoin object) { - return putByIndex(r'publicCoin_walletId_txid', object); - } - - Id putByPublicCoinWalletIdTxidSync(LelantusCoin object, - {bool saveLinks = true}) { - return putByIndexSync(r'publicCoin_walletId_txid', object, - saveLinks: saveLinks); - } - - Future> putAllByPublicCoinWalletIdTxid(List objects) { - return putAllByIndex(r'publicCoin_walletId_txid', objects); - } - - List putAllByPublicCoinWalletIdTxidSync(List objects, - {bool saveLinks = true}) { - return putAllByIndexSync(r'publicCoin_walletId_txid', objects, - saveLinks: saveLinks); - } - Future getByMintIndexWalletId(int mintIndex, String walletId) { return getByIndex(r'mintIndex_walletId', [mintIndex, walletId]); } @@ -543,144 +410,6 @@ extension LelantusCoinQueryWhere }); } - QueryBuilder - publicCoinEqualToAnyWalletIdTxid(String publicCoin) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'publicCoin_walletId_txid', - value: [publicCoin], - )); - }); - } - - QueryBuilder - publicCoinNotEqualToAnyWalletIdTxid(String publicCoin) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [], - upper: [publicCoin], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - includeLower: false, - upper: [], - )); - } else { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [], - upper: [publicCoin], - includeUpper: false, - )); - } - }); - } - - QueryBuilder - publicCoinWalletIdEqualToAnyTxid(String publicCoin, String walletId) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'publicCoin_walletId_txid', - value: [publicCoin, walletId], - )); - }); - } - - QueryBuilder - publicCoinEqualToWalletIdNotEqualToAnyTxid( - String publicCoin, String walletId) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - upper: [publicCoin, walletId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - includeLower: false, - upper: [publicCoin], - )); - } else { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - includeLower: false, - upper: [publicCoin], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - upper: [publicCoin, walletId], - includeUpper: false, - )); - } - }); - } - - QueryBuilder - publicCoinWalletIdTxidEqualTo( - String publicCoin, String walletId, String txid) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'publicCoin_walletId_txid', - value: [publicCoin, walletId, txid], - )); - }); - } - - QueryBuilder - publicCoinWalletIdEqualToTxidNotEqualTo( - String publicCoin, String walletId, String txid) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - upper: [publicCoin, walletId, txid], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId, txid], - includeLower: false, - upper: [publicCoin, walletId], - )); - } else { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId, txid], - includeLower: false, - upper: [publicCoin, walletId], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - upper: [publicCoin, walletId, txid], - includeUpper: false, - )); - } - }); - } - QueryBuilder mintIndexEqualToAnyWalletId(int mintIndex) { return QueryBuilder.apply(this, (query) { @@ -1161,142 +890,6 @@ extension LelantusCoinQueryFilter }); } - QueryBuilder - publicCoinEqualTo( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinGreaterThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinBetween( - 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'publicCoin', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'publicCoin', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'publicCoin', - value: '', - )); - }); - } - - QueryBuilder - publicCoinIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'publicCoin', - value: '', - )); - }); - } - QueryBuilder txidEqualTo( String value, { bool caseSensitive = true, @@ -1772,19 +1365,6 @@ extension LelantusCoinQuerySortBy }); } - QueryBuilder sortByPublicCoin() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.asc); - }); - } - - QueryBuilder - sortByPublicCoinDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.desc); - }); - } - QueryBuilder sortByTxid() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'txid', Sort.asc); @@ -1898,19 +1478,6 @@ extension LelantusCoinQuerySortThenBy }); } - QueryBuilder thenByPublicCoin() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.asc); - }); - } - - QueryBuilder - thenByPublicCoinDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.desc); - }); - } - QueryBuilder thenByTxid() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'txid', Sort.asc); @@ -1982,13 +1549,6 @@ extension LelantusCoinQueryWhereDistinct }); } - QueryBuilder distinctByPublicCoin( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'publicCoin', caseSensitive: caseSensitive); - }); - } - QueryBuilder distinctByTxid( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -2049,12 +1609,6 @@ extension LelantusCoinQueryProperty }); } - QueryBuilder publicCoinProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'publicCoin'); - }); - } - QueryBuilder txidProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'txid'); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 861263625..3b67d96c3 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -276,7 +276,7 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - publicCoin: publicCoin, + // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, @@ -326,7 +326,7 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - publicCoin: publicCoin, + // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, @@ -682,7 +682,7 @@ Future isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - "publicCoin": "jmintData.publicCoin", + // "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -2647,7 +2647,7 @@ class FiroWallet extends CoinServiceAPI "value": mintValue, "script": mint, "index": index, - "publicCoin": "", + // "publicCoin": "", }); tmpTotal = tmpTotal - (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); @@ -2834,7 +2834,7 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - "publicCoin": "", + // "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -3036,7 +3036,7 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: nextFreeMintIndex, value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), - publicCoin: transactionInfo['publicCoin'] as String, + // publicCoin: transactionInfo['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, @@ -3130,7 +3130,7 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: index, value: (mintMap['value'] as int).toString(), - publicCoin: mintMap['publicCoin'] as String, + // publicCoin: mintMap['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index d2c0adbe8..f54c2b85a 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -400,7 +400,6 @@ class DbVersionMigrator with WalletDB { final coin = isar_models.LelantusCoin( walletId: walletId, - publicCoin: lcoin.publicCoin, txid: lcoin.txId, value: lcoin.value.toString(), mintIndex: lcoin.index, From 7919353881c132684d23cc74a033be91836210e0 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:11:56 -0600 Subject: [PATCH 093/128] update mocks --- test/services/coins/manager_test.mocks.dart | 9 --------- test/widget_tests/transaction_card_test.mocks.dart | 9 --------- 2 files changed, 18 deletions(-) diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 25fd46d9d..5656feb88 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -745,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( diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 29bd7bd29..74fa6e89e 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1759,15 +1759,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( From 90b16c576e2cacc9437230ecd9b8d066b814f98a Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:36:37 -0600 Subject: [PATCH 094/128] clean up --- lib/services/coins/firo/firo_wallet.dart | 84 +++--------------------- 1 file changed, 10 insertions(+), 74 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 3b67d96c3..7dedf2958 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -276,12 +276,12 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, isJMint: false, - otherData: null, + otherData: + publicCoin, // not really needed but saved just in case ), ); Logging.instance.log( @@ -326,12 +326,12 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, isJMint: true, - otherData: null, + otherData: + publicCoin, // not really needed but saved just in case ), ); jindexes.add(currentIndex); @@ -370,7 +370,6 @@ Future> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['mintIndex'] = lastFoundIndex + 1; result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; @@ -682,7 +681,6 @@ Future isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - // "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -1241,53 +1239,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) { @@ -2642,12 +2593,10 @@ class FiroWallet extends CoinServiceAPI index, ); - // TODO publicCoin prob shouldn't be empty? mints.add({ "value": mintValue, "script": mint, "index": index, - // "publicCoin": "", }); tmpTotal = tmpTotal - (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); @@ -2834,7 +2783,6 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - // "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -2848,6 +2796,7 @@ class FiroWallet extends CoinServiceAPI }; } + // TODO: verify this function does what we think it does Future _refreshLelantusData() async { final lelantusCoins = await db.isar.lelantusCoins .where() @@ -2916,8 +2865,6 @@ class FiroWallet extends CoinServiceAPI } } - // TODO: optimize this whole lelantus process - final List> txnsData = []; @@ -3013,21 +2960,13 @@ class FiroWallet extends CoinServiceAPI // Update all of the coins that have been spent. for (final index in spentCoinIndexes) { - final possibleCoins = await db.isar.lelantusCoins + final possibleCoin = await db.isar.lelantusCoins .where() - .walletIdEqualTo(walletId) - .filter() - .mintIndexEqualTo(index) - .findAll(); + .mintIndexWalletIdEqualTo(index, walletId) + .findFirst(); - if (possibleCoins.isNotEmpty) { - if (possibleCoins.length > 1) { - print( - "======================= possibleCoins.length > 1 !!! ================================="); - } else { - final spentCoin = possibleCoins.first; - updatedCoins.add(spentCoin.copyWith(isUsed: true)); - } + if (possibleCoin != null) { + updatedCoins.add(possibleCoin.copyWith(isUsed: true)); } } @@ -3036,7 +2975,6 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: nextFreeMintIndex, value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), - // publicCoin: transactionInfo['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, @@ -3122,7 +3060,6 @@ class FiroWallet extends CoinServiceAPI final List updatedCoins = []; - // TODO: transactionInfo['mintsMap'] for (final mintMap in transactionInfo['mintsMap'] as List>) { final index = mintMap['index'] as int; @@ -3130,7 +3067,6 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: index, value: (mintMap['value'] as int).toString(), - // publicCoin: mintMap['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, From c1f73a89be16827fddd36343029c60212061f948 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:41:06 -0600 Subject: [PATCH 095/128] store jmint separately from updated coins --- lib/services/coins/firo/firo_wallet.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 7dedf2958..2a17d0849 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2981,9 +2981,6 @@ class FiroWallet extends CoinServiceAPI isJMint: true, otherData: null, ); - if (int.parse(jmint.value) > 0) { - updatedCoins.add(jmint); - } try { await db.isar.writeTxn(() async { @@ -2994,6 +2991,10 @@ class FiroWallet extends CoinServiceAPI ); } await db.isar.lelantusCoins.putAll(updatedCoins); + + if (int.parse(jmint.value) > 0) { + await db.isar.lelantusCoins.put(jmint); + } }); } catch (e, s) { Logging.instance.log( From 5d3e976601e7b2e9f63f456931a26301107d48e3 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 16:08:54 -0600 Subject: [PATCH 096/128] always add lelantus coin to locale db --- lib/services/coins/firo/firo_wallet.dart | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 2a17d0849..37c31dce4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -370,7 +370,6 @@ Future> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; return result; @@ -2992,9 +2991,7 @@ class FiroWallet extends CoinServiceAPI } await db.isar.lelantusCoins.putAll(updatedCoins); - if (int.parse(jmint.value) > 0) { - await db.isar.lelantusCoins.put(jmint); - } + await db.isar.lelantusCoins.put(jmint); }); } catch (e, s) { Logging.instance.log( @@ -3074,9 +3071,8 @@ class FiroWallet extends CoinServiceAPI isJMint: false, otherData: null, ); - if (int.parse(mint.value) > 0) { - updatedCoins.add(mint); - } + + updatedCoins.add(mint); } // Logging.instance.log(coins); try { From b2ec2763fb6aaa8b07db166dee18523a10792189 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Tue, 25 Jul 2023 16:51:21 -0600 Subject: [PATCH 097/128] monkey.png downloads to user device --- lib/pages/monkey/monkey_view.dart | 55 +++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index f109dd5c7..5c1dbede1 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -5,6 +5,7 @@ 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/monkey/sub_widgets/fetch_monkey_dialog.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/services/coins/manager.dart'; @@ -49,10 +50,19 @@ class _MonkeyViewState extends ConsumerState { final http.Response response = await http .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); - final decodedResponse = SvgPicture.memory(response.bodyBytes); - // final decodedResponse = json.decode(response.body); - // return decodedResponse; - debugPrint("$decodedResponse"); + if (response.statusCode == 200) { + final decodedResponse = response.bodyBytes; + final directory = await getApplicationDocumentsDirectory(); + // Directory appDir = await getTemporaryDirectory(); + final docPath = directory.path; + final filePath = "$docPath/monkey.svg"; + + File imgFile = File(filePath); + await imgFile.writeAsBytes(decodedResponse); + print("$imgFile"); + } else { + throw Exception("Failed to get MonKey"); + } } void getMonkeyPNG(String address) async { @@ -65,10 +75,29 @@ class _MonkeyViewState extends ConsumerState { '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; - final directory = await getApplicationDocumentsDirectory(); - // Directory appDir = await getTemporaryDirectory(); - final docPath = directory.path; + Directory directory = await getApplicationDocumentsDirectory(); + late Directory sampleFolder; + + if (Platform.isAndroid) { + directory = Directory("/storage/emulated/0/"); + 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); @@ -81,10 +110,6 @@ class _MonkeyViewState extends ConsumerState { } else { throw Exception("Failed to get MonKey"); } - - // final decodedResponse = json.decode(response.body); - // return decodedResponse; - // debugPrint("$decodedResponse"); } @override @@ -177,13 +202,13 @@ class _MonkeyViewState extends ConsumerState { body: isMonkey ? Column( children: [ - Spacer( + const Spacer( flex: 1, ), Image.network( 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', ), - Spacer( + const Spacer( flex: 1, ), Padding( @@ -192,7 +217,9 @@ class _MonkeyViewState extends ConsumerState { children: [ SecondaryButton( label: "Download as SVG", - onPressed: () async {}, + onPressed: () async { + getMonkeySVG(receivingAddress); + }, ), const SizedBox(height: 12), SecondaryButton( From c6c2b429238c120ca19a4f726b8e5935e888cd35 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Tue, 25 Jul 2023 17:06:32 -0600 Subject: [PATCH 098/128] monkey.svg download to user device --- lib/pages/monkey/monkey_view.dart | 38 ++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 5c1dbede1..df449a91d 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -50,11 +50,35 @@ class _MonkeyViewState extends ConsumerState { 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; - final directory = await getApplicationDocumentsDirectory(); - // Directory appDir = await getTemporaryDirectory(); - final docPath = directory.path; + 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); @@ -86,6 +110,14 @@ class _MonkeyViewState extends ConsumerState { 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 { From 512960d9b92cf23c1936337927651a9915be9d81 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Tue, 25 Jul 2023 17:29:05 -0600 Subject: [PATCH 099/128] changed monkey files around --- lib/pages/monkey/monkey_loaded_view.dart | 272 +++++++++++++++++++++++ lib/pages/monkey/monkey_view.dart | 253 ++++++--------------- lib/route_generator.dart | 16 ++ 3 files changed, 350 insertions(+), 191 deletions(-) create mode 100644 lib/pages/monkey/monkey_loaded_view.dart diff --git a/lib/pages/monkey/monkey_loaded_view.dart b/lib/pages/monkey/monkey_loaded_view.dart new file mode 100644 index 000000000..0b49fc738 --- /dev/null +++ b/lib/pages/monkey/monkey_loaded_view.dart @@ -0,0 +1,272 @@ +import 'dart:io'; + +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/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); + print("$imgFile"); + } 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); + print("$imgFile"); + + // final directory = await getApplicationDocumentsDirectory(); + // final docPath = directory.path; + // final filePath = "$do/monkey.png"; + } 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)); + bool isMonkey = false; + + 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, + ), + Image.network( + 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', + ), + 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 index df449a91d..17758b9f7 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -3,9 +3,7 @@ import 'dart:io'; 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/monkey/monkey_loaded_view.dart'; import 'package:stackwallet/pages/monkey/sub_widgets/fetch_monkey_dialog.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/services/coins/manager.dart'; @@ -17,7 +15,7 @@ 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:tuple/tuple.dart'; class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ @@ -42,108 +40,6 @@ class _MonkeyViewState extends ConsumerState { 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); - print("$imgFile"); - } 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); - print("$imgFile"); - - // final directory = await getApplicationDocumentsDirectory(); - // final docPath = directory.path; - // final filePath = "$do/monkey.png"; - } else { - throw Exception("Failed to get MonKey"); - } - } - @override void initState() { walletId = widget.walletId; @@ -170,7 +66,6 @@ class _MonkeyViewState extends ConsumerState { @override Widget build(BuildContext context) { final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); - bool isMonkey = true; return Background( child: Stack( @@ -231,98 +126,74 @@ class _MonkeyViewState extends ConsumerState { ) ], ), - body: isMonkey - ? Column( + body: Column( + children: [ + const Spacer( + flex: 4, + ), + Center( + child: Column( children: [ - const Spacer( - flex: 1, - ), - Image.network( - 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', - ), - 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); - }, - ), - ], + Opacity( + opacity: 0.2, + child: SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 200, + height: 200, ), ), - ], - ) - : Column( - children: [ - const Spacer( - flex: 4, + const SizedBox( + height: 40, ), - Center( - child: Column( - children: [ - Opacity( - opacity: 0.2, - child: SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), - ), - width: 200, - height: 200, - ), - ), - const SizedBox( - height: 40, - ), - 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, - ), - ], - ), - ), - const Spacer( - flex: 6, - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: PrimaryButton( - label: "Fetch MonKey", - onPressed: () { - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return FetchMonkeyDialog( - onCancel: () async { - Navigator.of(context).pop(); - }, - ); - }, - ); - }, + 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, ), ], ), + ), + const Spacer( + flex: 6, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () async { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return FetchMonkeyDialog( + onCancel: () async { + Navigator.of(context).pop(); + }, + ); + }, + ); + + await Future.delayed(const Duration(seconds: 2)); + + Navigator.of(context).pushNamed( + MonkeyLoadedView.routeName, + arguments: Tuple2( + widget.walletId, + widget.managerProvider, + ), + ); + }, + ), + ), + ], + ), ), ], ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 4513fd1d6..b02638082 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -56,6 +56,7 @@ 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_loaded_view.dart'; import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart'; @@ -391,6 +392,21 @@ class RouteGenerator { } 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( From 78e4cd463172258cdde6fd2ca98a709ca2b0f7e1 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Tue, 25 Jul 2023 17:42:36 -0600 Subject: [PATCH 100/128] removed unnecessary lines --- lib/pages/monkey/monkey_loaded_view.dart | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/pages/monkey/monkey_loaded_view.dart b/lib/pages/monkey/monkey_loaded_view.dart index 0b49fc738..32570e6a7 100644 --- a/lib/pages/monkey/monkey_loaded_view.dart +++ b/lib/pages/monkey/monkey_loaded_view.dart @@ -81,7 +81,6 @@ class _MonkeyLoadedViewState extends ConsumerState { File imgFile = File(filePath); await imgFile.writeAsBytes(decodedResponse); - print("$imgFile"); } else { throw Exception("Failed to get MonKey"); } @@ -132,11 +131,6 @@ class _MonkeyLoadedViewState extends ConsumerState { File imgFile = File(filePath); await imgFile.writeAsBytes(decodedResponse); - print("$imgFile"); - - // final directory = await getApplicationDocumentsDirectory(); - // final docPath = directory.path; - // final filePath = "$do/monkey.png"; } else { throw Exception("Failed to get MonKey"); } @@ -168,7 +162,6 @@ class _MonkeyLoadedViewState extends ConsumerState { @override Widget build(BuildContext context) { final Coin coin = ref.watch(managerProvider.select((value) => value.coin)); - bool isMonkey = false; return Background( child: Stack( From c3054ca753d314ad5eab3752a76804a4e105d1ab Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:28:27 -0600 Subject: [PATCH 101/128] fix broken used serials call and optimize cache a bit --- lib/electrumx_rpc/cached_electrumx.dart | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) 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", From c9da22601ef0d92057d5ebe0ad58dd5d69dab5b2 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:28:51 -0600 Subject: [PATCH 102/128] clean up spam logs a bit --- lib/services/coins/firo/firo_wallet.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 37c31dce4..6b2bf733f 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -349,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, - ); } } From 57839c2d181a06461b8a8640968af0269802220e Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:29:44 -0600 Subject: [PATCH 103/128] WIP fixing _refreshLelantusData --- lib/services/coins/firo/firo_wallet.dart | 116 +++++++++-------------- 1 file changed, 44 insertions(+), 72 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 6b2bf733f..da0919570 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2797,96 +2797,68 @@ class FiroWallet extends CoinServiceAPI .walletIdEqualTo(walletId) .filter() .isUsedEqualTo(false) - .and() - .isJMintEqualTo(true) .not() .valueEqualTo(0.toString()) .findAll(); - // Get all joinsplit transaction ids + final List updatedCoins = []; - final lelantusJoinSplitTxns = await db - .getTransactions(walletId) - .filter() - .isLelantusEqualTo(true) - .and() - .subTypeEqualTo(isar_models.TransactionSubType.join) - .findAll(); + final usedSerialNumbersSet = (await getUsedCoinSerials()).toSet(); - Set joinSplitTXIDs = {}; - - // for (final tx in lelantusJoinSplitTxns) { - // joinSplitTXIDs.add(tx.txid); - // } - for (final coin in lelantusCoins) { - joinSplitTXIDs.add(coin.txid); - } - - Map> - updatedData = {}; - - // Grab the most recent information on all the joinsplits - final updatedJSplit = await getJMintTransactions( - cachedElectrumXClient, - joinSplitTXIDs.toList(), - 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); - try { - currentTx = - lelantusJoinSplitTxns.firstWhere((e) => e.txid == tx.value.txid); - } catch (_) { - currentTx = null; + if (isUsed) { + updatedCoins.add(coin.copyWith(isUsed: isUsed)); } - if (currentTx == null) { - // this send was accidentally not included in the list - tx.value.isLelantus = true; - updatedData[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); - } else if (currentTx.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - tx.value.isLelantus = true; - updatedData[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); + final tx = await db.getTransaction(walletId, coin.txid); + if (tx == null) { + print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); } } - final List> txnsData = - []; - - for (final value in updatedData.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; + if (updatedCoins.isNotEmpty) { try { - transactionAddress = - value.item2.subType == isar_models.TransactionSubType.mint - ? value.item1 - : value.item1!; - } catch (_) { - Logging.instance - .log("_refreshLelantusData value: $value", level: LogLevel.Fatal); + 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; } - 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); } Future _getMintHex(int amount, int index) async { From 7b8f26206e7c5883a363d2f76976fef74b0f197c Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:48:03 -0600 Subject: [PATCH 104/128] show sent to self jmint transactions correctly --- lib/services/coins/firo/firo_wallet.dart | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index da0919570..0ccc6436c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3538,11 +3538,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 @@ -3569,7 +3579,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, From 683364750adfab6c04ae7509a332defb77c761b1 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 11:13:48 -0600 Subject: [PATCH 105/128] structure change to add monkey image --- .../sub_widgets/wallet_summary_info.dart | 160 +++++++++++------- 1 file changed, 95 insertions(+), 65 deletions(-) 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..57658498c 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary_info.dart @@ -31,6 +31,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 +50,8 @@ class WalletSummaryInfo extends ConsumerStatefulWidget { class _WalletSummaryInfoState extends ConsumerState { late StreamSubscription _balanceUpdated; + String receivingAddress = ""; + void showSheet() { showModalBottomSheet( backgroundColor: Colors.transparent, @@ -72,6 +75,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,6 +99,8 @@ class _WalletSummaryInfoState extends ConsumerState { Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + bool isMonkey = true; + final externalCalls = ref.watch( prefsChangeNotifierProvider.select((value) => value.externalCalls)); final coin = ref.watch(walletsChangeNotifierProvider @@ -125,84 +141,98 @@ 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( + return ConditionalParent( + condition: isMonkey && Coin == Coin.banano, + builder: (child) => Container( + decoration: BoxDecoration( + image: DecorationImage( + alignment: Alignment.centerRight, + image: NetworkImage( + 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', + ), + ), + ), + child: 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, - ), - ], - ) - ], + ) + ], + ), ); } } From 6733a367e3461ab40a14ac0a6fb53b22de65701b Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 11:47:49 -0600 Subject: [PATCH 106/128] create mockable wrapper function --- lib/db/isar/main_db.dart | 11 +++++++++++ lib/services/coins/firo/firo_wallet.dart | 22 ++++------------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 93c9052da..c7a593d03 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -512,4 +512,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/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 0ccc6436c..079b96ff4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2512,12 +2512,7 @@ class FiroWallet extends CoinServiceAPI Future>> createMintsFromAmount(int total) async { int tmpTotal = total; int counter = 0; - final lastUsedIndex = await db.isar.lelantusCoins - .where() - .walletIdEqualTo(walletId) - .sortByMintIndexDesc() - .mintIndexProperty() - .findFirst(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final root = await Bip32Utils.getBip32Root( @@ -2908,12 +2903,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Info); if (txid == transactionInfo['txid']) { - final lastUsedIndex = await db.isar.lelantusCoins - .where() - .walletIdEqualTo(walletId) - .sortByMintIndexDesc() - .mintIndexProperty() - .findFirst(); + final lastUsedIndex = + await db.getHighestUsedMintIndex(walletId: walletId); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { @@ -4577,12 +4568,7 @@ class FiroWallet extends CoinServiceAPI int spendAmount, String address, bool subtractFeeFromAmount) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; - final lastUsedIndex = await db.isar.lelantusCoins - .where() - .walletIdEqualTo(walletId) - .sortByMintIndexDesc() - .mintIndexProperty() - .findFirst(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); From d169ea0b109b87c289d77fb996daf73d728be20a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 26 Jul 2023 12:53:19 -0500 Subject: [PATCH 107/128] fix firo test --- test/services/coins/firo/firo_wallet_test.mocks.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 95d1750e9..dc9bb266f 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, From 2eb10e249f1ee4d9543aea4e583b4f1e0c0bebff Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 11:54:10 -0600 Subject: [PATCH 108/128] add monkey image to hive and display on wallet card --- .../sub_widgets/wallet_summary_info.dart | 32 ++++++++++++------- lib/services/coins/banano/banano_wallet.dart | 16 ++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) 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 57658498c..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'; @@ -101,10 +103,12 @@ class _WalletSummaryInfoState extends ConsumerState { 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)); @@ -141,18 +145,24 @@ class _WalletSummaryInfoState extends ConsumerState { title = _showAvailable ? "Available balance" : "Full balance"; } + List? imageBytes; + + if (coin == Coin.banano) { + imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); + } + return ConditionalParent( - condition: isMonkey && Coin == Coin.banano, - builder: (child) => Container( - decoration: BoxDecoration( - image: DecorationImage( - alignment: Alignment.centerRight, - image: NetworkImage( - 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', + condition: imageBytes != null, + builder: (child) => Stack( + children: [ + Positioned.fill( + left: 150.0, + child: SvgPicture.memory( + Uint8List.fromList(imageBytes!), ), ), - ), - child: child, + child, + ], ), child: Row( children: [ diff --git a/lib/services/coins/banano/banano_wallet.dart b/lib/services/coins/banano/banano_wallet.dart index 8511f5599..be5d20889 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'; @@ -925,4 +926,19 @@ 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?; + } } From dbcb567d6bcdff7c7905023b2479d9955d6440d4 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 11:54:46 -0600 Subject: [PATCH 109/128] fix tests --- .../services/coins/firo/firo_wallet_test.dart | 37 +++++++++++-------- .../coins/firo/firo_wallet_test.mocks.dart | 10 +++++ .../transaction_card_test.mocks.dart | 10 +++++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index fcd7bd50e..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'; @@ -114,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>()); }); @@ -532,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); }); @@ -1204,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..2365f83b1 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -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/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 74fa6e89e..35e8a2560 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -3496,4 +3496,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); } From ba4e83968d4282255a08507014fe7fa9e9e5645c Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 26 Jul 2023 13:02:56 -0500 Subject: [PATCH 110/128] show more button on desktop wallet home page if wallet ordinalsinterface --- .../wallet_view/sub_widgets/desktop_wallet_features.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 f116e6656..44b1853db 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 @@ -19,10 +19,10 @@ import 'package:stackwallet/notifications/show_flush_bar.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/ordinals/desktop_ordinals_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'; @@ -341,7 +341,8 @@ class _DesktopWalletFeaturesState extends ConsumerState { )) || manager.coin == Coin.firo || manager.coin == Coin.firoTestNet || - manager.hasWhirlpoolSupport; + manager.hasWhirlpoolSupport || + manager.hasOrdinalsSupport; return Row( children: [ From 032061fa1912e536fc72dae0691f9bcd890d8415 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 12:03:01 -0600 Subject: [PATCH 111/128] changed generate monkey structure --- lib/pages/monkey/monkey_loaded_view.dart | 540 ++++++++++++----------- lib/pages/monkey/monkey_view.dart | 296 ++++++++++--- lib/route_generator.dart | 29 +- 3 files changed, 520 insertions(+), 345 deletions(-) diff --git a/lib/pages/monkey/monkey_loaded_view.dart b/lib/pages/monkey/monkey_loaded_view.dart index 32570e6a7..93c28b39a 100644 --- a/lib/pages/monkey/monkey_loaded_view.dart +++ b/lib/pages/monkey/monkey_loaded_view.dart @@ -1,265 +1,275 @@ -import 'dart:io'; - -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/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)); - - 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, - ), - Image.network( - 'https://monkey.banano.cc/api/v1/monkey/$receivingAddress?format=png&size=512', - ), - 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); - }, - ), - ], - ), - ), - ], - ), - ), - ], - ), - ); - } -} +// 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 index 17758b9f7..b631b0703 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -1,11 +1,16 @@ 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:stackwallet/pages/monkey/monkey_loaded_view.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/monkey/sub_widgets/fetch_monkey_dialog.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/coin_icon_provider.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -13,9 +18,10 @@ 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/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart'; -import 'package:tuple/tuple.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ @@ -40,6 +46,123 @@ class _MonkeyViewState extends ConsumerState { String receivingAddress = ""; + void getMonkeyImage(String address) async { + if (address.isEmpty) { + //address shouldn't be empty + return; + } + + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(walletId))); + + 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; + 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"); + } + } + + 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; @@ -66,6 +189,11 @@ class _MonkeyViewState extends ConsumerState { @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( @@ -123,76 +251,114 @@ class _MonkeyViewState extends ConsumerState { ); }); }), - ) + ), ], ), - body: Column( - children: [ - const Spacer( - flex: 4, - ), - Center( - child: Column( - children: [ - Opacity( - opacity: 0.2, - child: SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), + body: ConditionalParent( + condition: imageBytes != null, + builder: (child) => 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); + }, + ), + ], + ), + ), + // child, + ], + ), + child: Column( + children: [ + const Spacer( + flex: 4, + ), + Center( + child: Column( + children: [ + Opacity( + opacity: 0.2, + child: SvgPicture.file( + File( + ref.watch(coinIconProvider(coin)), + ), + width: 200, + height: 200, ), - width: 200, - height: 200, ), - ), - const SizedBox( - height: 40, - ), - Text( - "You do not have a MonKey yet. \nFetch yours now!", - style: STextStyles.smallMed14(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, + const SizedBox( + height: 40, ), - textAlign: TextAlign.center, - ), - ], + 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, + ), + ], + ), ), - ), - const Spacer( - flex: 6, - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: PrimaryButton( - label: "Fetch MonKey", - onPressed: () async { - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return FetchMonkeyDialog( - onCancel: () async { - Navigator.of(context).pop(); - }, - ); - }, - ); - - await Future.delayed(const Duration(seconds: 2)); - - Navigator.of(context).pushNamed( - MonkeyLoadedView.routeName, - arguments: Tuple2( - widget.walletId, - widget.managerProvider, - ), - ); - }, + const Spacer( + flex: 6, ), - ), - ], + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () async { + getMonkeyImage(receivingAddress); + + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return FetchMonkeyDialog( + onCancel: () async { + Navigator.of(context).pop(); + }, + ); + }, + ); + + await Future.delayed(const Duration(seconds: 2)); + + Navigator.of(context).popUntil( + ModalRoute.withName(WalletView.routeName), + ); + }, + ), + ), + ], + ), ), ), ], diff --git a/lib/route_generator.dart b/lib/route_generator.dart index b02638082..64c2c86d4 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -56,7 +56,6 @@ 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_loaded_view.dart'; import 'package:stackwallet/pages/monkey/monkey_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart'; @@ -392,20 +391,20 @@ class RouteGenerator { } 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 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) { From 86d3258910f3d138d3859159d344a6a888258165 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 13:18:18 -0600 Subject: [PATCH 112/128] Monkey help message --- crypto_plugins/flutter_libmonero | 2 +- lib/pages/monkey/monkey_view.dart | 33 +++--------------- lib/pages/wallet_view/wallet_view.dart | 46 +++++++++++--------------- 3 files changed, 25 insertions(+), 56 deletions(-) diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index e48952185..407425c9f 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit e48952185556a10f182184fd572bcb04365f5831 +Subproject commit 407425c9fcf7a30c81f1345246c7225bc18b5cd5 diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index b631b0703..075191235 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -22,6 +22,7 @@ import 'package:stackwallet/widgets/conditional_parent.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/stack_dialog.dart'; class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ @@ -220,34 +221,10 @@ class _MonkeyViewState extends ConsumerState { 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), - ), - ) - ], - ), - ), - ), + return const StackOkDialog( + title: "About MonKeys", + message: + "A MonKey is a visual representation of your Banano address.", ); }); }), diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 0dcfe18dc..b2d5de8e6 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -925,33 +925,25 @@ class _WalletViewState extends ConsumerState { ); }, ), - if (ref.watch( - walletsChangeNotifierProvider.select( - (value) => value - .getManager(widget.walletId) - .hasCoinControlSupport, - ), - ) && - ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.enableCoinControl, - ), - )) - 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: Tuple2( - widget.walletId, - widget.managerProvider, - ), - ); - }), + 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: Tuple2( + widget.walletId, + widget.managerProvider, + ), + ); + }), if (ref.watch( walletsChangeNotifierProvider.select( (value) => value From 3cb8a6a173d834e633969ab0f4035918b0aed72f Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 14:12:06 -0600 Subject: [PATCH 113/128] WIP: adding monkey to desktop --- .../sub_widgets/desktop_wallet_features.dart | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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..1ee1b664b 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,6 +16,7 @@ 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'; @@ -41,6 +42,7 @@ import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; +import 'package:tuple/tuple.dart'; class DesktopWalletFeatures extends ConsumerStatefulWidget { const DesktopWalletFeatures({ @@ -80,6 +82,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { onCoinControlPressed: _onCoinControlPressed, onAnonymizeAllPressed: _onAnonymizeAllPressed, onWhirlpoolPressed: _onWhirlpoolPressed, + onMonkeyPressed: _onMonkeyPressed, ), ); } @@ -313,6 +316,21 @@ class _DesktopWalletFeaturesState extends ConsumerState { } } + Future _onMonkeyPressed() async { + Navigator.of(context, rootNavigator: true).pop(); + final managerProvider = ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(widget.walletId); + + await (Navigator.of(context).pushNamed( + MonkeyView.routeName, + arguments: Tuple2( + widget.walletId, + managerProvider, + ), + )); + } + @override Widget build(BuildContext context) { final manager = ref.watch( @@ -330,8 +348,8 @@ class _DesktopWalletFeaturesState extends ConsumerState { )) || manager.coin == Coin.firo || manager.coin == Coin.firoTestNet || + manager.coin == Coin.banano || manager.hasWhirlpoolSupport; - return Row( children: [ if (Constants.enableExchange) From 5668b045e5ec63a72526263eb408d996758ab21e Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 14:12:19 -0600 Subject: [PATCH 114/128] WIP: adding monkey to desktop --- .../sub_widgets/more_features/more_features_dialog.dart | 9 +++++++++ 1 file changed, 9 insertions(+) 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..3aaa6b5df 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,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { required this.onCoinControlPressed, required this.onAnonymizeAllPressed, required this.onWhirlpoolPressed, + required this.onMonkeyPressed, }) : super(key: key); final String walletId; @@ -36,6 +37,7 @@ class MoreFeaturesDialog extends ConsumerStatefulWidget { final VoidCallback? onCoinControlPressed; final VoidCallback? onAnonymizeAllPressed; final VoidCallback? onWhirlpoolPressed; + final VoidCallback? onMonkeyPressed; @override ConsumerState createState() => _MoreFeaturesDialogState(); @@ -103,6 +105,13 @@ class _MoreFeaturesDialogState extends ConsumerState { iconAsset: Assets.svg.robotHead, onPressed: () => widget.onPaynymPressed?.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, ), From 36747ca479da7ad04b35e2fd7434ce931255576d Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 14:50:38 -0600 Subject: [PATCH 115/128] another bandaid fix for hive box isn't open --- lib/db/hive/db.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 25efcdb3e..9d3f73abd 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -274,8 +274,15 @@ 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 { + Box box = Hive.box(boxName); + if (!box.isOpen) { + box = await Hive.openBox(boxName); + } + await box.clear(); + }); + } Future deleteBoxFromDisk({required String boxName}) async => await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName)); From 66e1db9b52021689ce208b86c593c9cf96d0daa1 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 15:10:53 -0600 Subject: [PATCH 116/128] WIP: scaling monkey view for desktop --- lib/pages/monkey/monkey_view.dart | 315 ++++++++++++++++++++---------- 1 file changed, 207 insertions(+), 108 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 075191235..83901fbca 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -9,6 +9,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:stackwallet/pages/monkey/sub_widgets/fetch_monkey_dialog.dart'; import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; +import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_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'; @@ -17,6 +18,7 @@ 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/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'; @@ -24,6 +26,9 @@ import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; +import '../../widgets/desktop/desktop_app_bar.dart'; +import '../../widgets/desktop/desktop_scaffold.dart'; + class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ Key? key, @@ -193,25 +198,57 @@ class _MonkeyViewState extends ConsumerState { final manager = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManager(widget.walletId))); + final bool isDesktop = Util.isDesktop; + List? imageBytes; imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); + //edit for desktop return Background( child: Stack( children: [ - Scaffold( - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: Text( - "MonKey", - style: STextStyles.navBarTitle(context), - ), - actions: [ - AspectRatio( + 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, + ), + Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + ], + ), + ), + trailing: AspectRatio( aspectRatio: 1, child: AppBarIconButton( icon: SvgPicture.asset(Assets.svg.circleQuestion), @@ -221,7 +258,7 @@ class _MonkeyViewState extends ConsumerState { useSafeArea: false, barrierDismissible: true, builder: (context) { - return const StackOkDialog( + return const StackDialog( title: "About MonKeys", message: "A MonKey is a visual representation of your Banano address.", @@ -229,112 +266,174 @@ class _MonkeyViewState extends ConsumerState { }); }), ), - ], - ), - body: ConditionalParent( - condition: imageBytes != null, - builder: (child) => 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); - }, - ), - ], - ), - ), - // child, - ], + useSpacers: false, + isCompactHeight: true, ), - child: Column( - children: [ - const Spacer( - flex: 4, + body: child, + ), + child: ConditionalParent( + condition: !isDesktop, + builder: (child) => Scaffold( + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, ), - Center( - child: Column( - children: [ - Opacity( - opacity: 0.2, - child: SvgPicture.file( - File( - ref.watch(coinIconProvider(coin)), - ), - width: 200, - height: 200, - ), - ), - const SizedBox( - height: 40, - ), - Text( - "You do not have a MonKey yet. \nFetch yours now!", - style: STextStyles.smallMed14(context).copyWith( + title: Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset( + Assets.svg.circleQuestion, color: Theme.of(context) .extension()! - .textDark3, + .infoItemText, ), - textAlign: TextAlign.center, - ), - ], + 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: 300, + height: 300, + child: child, + ), + child: ConditionalParent( + condition: imageBytes != null, + builder: (child) => 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); + }, + ), + ], + ), + ), + // child, + ], ), - const Spacer( - flex: 6, - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: PrimaryButton( - label: "Fetch MonKey", - onPressed: () async { - getMonkeyImage(receivingAddress); + child: Column( + children: [ + 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: 40, + ), + 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, + ), + ], + ), + ), + const Spacer( + flex: 6, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: PrimaryButton( + label: "Fetch MonKey", + onPressed: () async { + getMonkeyImage(receivingAddress); - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return FetchMonkeyDialog( - onCancel: () async { - Navigator.of(context).pop(); + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return FetchMonkeyDialog( + onCancel: () async { + Navigator.of(context).pop(); + }, + ); }, ); + + await Future.delayed( + const Duration(seconds: 2)); + + if (isDesktop) { + Navigator.of(context).popUntil( + ModalRoute.withName( + DesktopWalletView.routeName), + ); + } else { + Navigator.of(context).popUntil( + ModalRoute.withName(WalletView.routeName), + ); + } }, - ); - - await Future.delayed(const Duration(seconds: 2)); - - Navigator.of(context).popUntil( - ModalRoute.withName(WalletView.routeName), - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), From b2aa4272b445433bff9dd5232a675d4934e616ce Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 15:32:00 -0600 Subject: [PATCH 117/128] reorg and loading indicator --- lib/pages/monkey/monkey_view.dart | 66 ++++++++----------- .../sub_widgets/desktop_wallet_features.dart | 9 +-- lib/route_generator.dart | 5 +- 3 files changed, 29 insertions(+), 51 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 83901fbca..ce4e951d3 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -7,40 +7,34 @@ 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/monkey/sub_widgets/fetch_monkey_dialog.dart'; -import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; -import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_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/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'; -import '../../widgets/desktop/desktop_app_bar.dart'; -import '../../widgets/desktop/desktop_scaffold.dart'; - class MonkeyView extends ConsumerStatefulWidget { const MonkeyView({ Key? key, required this.walletId, - required this.managerProvider, }) : super(key: key); static const String routeName = "/monkey"; static const double navBarHeight = 65.0; final String walletId; - final ChangeNotifierProvider managerProvider; @override ConsumerState createState() => _MonkeyViewState(); @@ -48,23 +42,21 @@ class MonkeyView extends ConsumerStatefulWidget { class _MonkeyViewState extends ConsumerState { late final String walletId; - late final ChangeNotifierProvider managerProvider; String receivingAddress = ""; - void getMonkeyImage(String address) async { + Future getMonkeyImage(String address) async { if (address.isEmpty) { //address shouldn't be empty return; } - final manager = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(walletId))); - 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); @@ -172,7 +164,6 @@ class _MonkeyViewState extends ConsumerState { @override void initState() { walletId = widget.walletId; - managerProvider = widget.managerProvider; WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { final address = await ref @@ -194,9 +185,9 @@ class _MonkeyViewState extends ConsumerState { @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))); + final Coin coin = manager.coin; final bool isDesktop = Util.isDesktop; @@ -400,34 +391,29 @@ class _MonkeyViewState extends ConsumerState { child: PrimaryButton( label: "Fetch MonKey", onPressed: () async { - getMonkeyImage(receivingAddress); + final future = Future.wait([ + getMonkeyImage(receivingAddress), + Future.delayed(const Duration(seconds: 2)), + ]); - showDialog( + await showLoading( + whileFuture: future, context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return FetchMonkeyDialog( - onCancel: () async { - Navigator.of(context).pop(); - }, - ); - }, + isDesktop: Util.isDesktop, + message: "Fetching MonKey", + subMessage: "We are fetching your MonKey", ); - await Future.delayed( - const Duration(seconds: 2)); - - if (isDesktop) { - Navigator.of(context).popUntil( - ModalRoute.withName( - DesktopWalletView.routeName), - ); - } else { - Navigator.of(context).popUntil( - ModalRoute.withName(WalletView.routeName), - ); - } + // if (isDesktop) { + // Navigator.of(context).popUntil( + // ModalRoute.withName( + // DesktopWalletView.routeName), + // ); + // } else { + // Navigator.of(context).popUntil( + // ModalRoute.withName(WalletView.routeName), + // ); + // } }, ), ), 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 1ee1b664b..afeec5094 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 @@ -42,7 +42,6 @@ import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; -import 'package:tuple/tuple.dart'; class DesktopWalletFeatures extends ConsumerStatefulWidget { const DesktopWalletFeatures({ @@ -318,16 +317,10 @@ class _DesktopWalletFeaturesState extends ConsumerState { Future _onMonkeyPressed() async { Navigator.of(context, rootNavigator: true).pop(); - final managerProvider = ref - .read(walletsChangeNotifierProvider) - .getManagerProvider(widget.walletId); await (Navigator.of(context).pushNamed( MonkeyView.routeName, - arguments: Tuple2( - widget.walletId, - managerProvider, - ), + arguments: widget.walletId, )); } diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 368d23397..edd3b15fc 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -378,12 +378,11 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case MonkeyView.routeName: - if (args is Tuple2>) { + if (args is String) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => MonkeyView( - walletId: args.item1, - managerProvider: args.item2, + walletId: args, ), settings: RouteSettings( name: settings.name, From 4e9ba505723970d216cbbc6f2392cfaad59cd9bd Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 15:37:10 -0600 Subject: [PATCH 118/128] Desktop what is monkey button --- lib/pages/monkey/monkey_view.dart | 40 +++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index ce4e951d3..07466fe1b 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -239,11 +239,12 @@ class _MonkeyViewState extends ConsumerState { ], ), ), - trailing: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - icon: SvgPicture.asset(Assets.svg.circleQuestion), - onPressed: () { + trailing: Padding( + padding: const EdgeInsets.all(8.0), + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { showDialog( context: context, useSafeArea: false, @@ -255,7 +256,30 @@ class _MonkeyViewState extends ConsumerState { "A MonKey is a visual representation of your Banano address.", ); }); - }), + }, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.circleQuestion, + color: Colors.blue[800], + ), + 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, @@ -281,9 +305,6 @@ class _MonkeyViewState extends ConsumerState { child: AppBarIconButton( icon: SvgPicture.asset( Assets.svg.circleQuestion, - color: Theme.of(context) - .extension()! - .infoItemText, ), onPressed: () { showDialog( @@ -403,7 +424,6 @@ class _MonkeyViewState extends ConsumerState { message: "Fetching MonKey", subMessage: "We are fetching your MonKey", ); - // if (isDesktop) { // Navigator.of(context).popUntil( // ModalRoute.withName( From 0175939d6383526b55f8bb80e84ef7ff6a34d363 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 15:52:52 -0600 Subject: [PATCH 119/128] download/save image on linux --- lib/pages/monkey/monkey_view.dart | 170 +++++++++++++++++------------- 1 file changed, 96 insertions(+), 74 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index ce4e951d3..56cd8cf1d 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -65,60 +65,73 @@ class _MonkeyViewState extends ConsumerState { } } - void getMonkeySVG(String address) async { - if (address.isEmpty) { - //address shouldn't be empty - return; - } + // 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"); + // } + // } - 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 { + return await getApplicationDocumentsDirectory(); + } catch (_) { + return null; } } - void getMonkeyPNG(String address) async { + Future downloadMonkey(String address, bool isPNG) 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')); + 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) { @@ -126,33 +139,30 @@ class _MonkeyViewState extends ConsumerState { } final decodedResponse = response.bodyBytes; - Directory directory = await getApplicationDocumentsDirectory(); - late Directory sampleFolder; + final Directory? sampleFolder = await getDocsDir(); - 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'); + 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"); - } + // 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"; + String filePath = "$docPath/monkey_$address"; + + filePath += isPNG ? ".png" : ".svg"; + + // todo check if monkey.png exists File imgFile = File(filePath); await imgFile.writeAsBytes(decodedResponse); @@ -318,11 +328,11 @@ class _MonkeyViewState extends ConsumerState { flex: 1, ), if (imageBytes != null) - Container( - child: SvgPicture.memory( - Uint8List.fromList(imageBytes!)), + SizedBox( width: 300, height: 300, + child: + SvgPicture.memory(Uint8List.fromList(imageBytes)), ), const Spacer( flex: 1, @@ -332,16 +342,28 @@ class _MonkeyViewState extends ConsumerState { child: Column( children: [ SecondaryButton( - label: "Download as SVG", + label: "Save as SVG", onPressed: () async { - getMonkeySVG(receivingAddress); + 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: () { - getMonkeyPNG(receivingAddress); + onPressed: () async { + await showLoading( + whileFuture: + downloadMonkey(receivingAddress, true), + context: context, + isDesktop: Util.isDesktop, + message: "Downloading MonKey png", + ); }, ), ], From 39882b0d46bf37d15f05ceb84f60da41aaa1997b Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 16:07:54 -0600 Subject: [PATCH 120/128] monkey icon in app bar --- lib/pages/monkey/monkey_view.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 07466fe1b..940b41fa4 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -232,6 +232,10 @@ class _MonkeyViewState extends ConsumerState { const SizedBox( width: 15, ), + SvgPicture.asset(Assets.svg.monkey), + const SizedBox( + width: 12, + ), Text( "MonKey", style: STextStyles.navBarTitle(context), From 9d2b315bd9a6af38a47addd5f3c3853e79ce5401 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 16:08:01 -0600 Subject: [PATCH 121/128] desktop layout --- lib/pages/monkey/monkey_view.dart | 428 +++++++++++++++--------------- 1 file changed, 219 insertions(+), 209 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 56cd8cf1d..02947a5ce 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -206,60 +206,100 @@ class _MonkeyViewState extends ConsumerState { //edit for desktop return Background( - child: Stack( - children: [ - 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, - ), - Text( - "MonKey", - style: STextStyles.navBarTitle(context), - ), - ], + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopScaffold( + appBar: DesktopAppBar( + background: Theme.of(context).extension()!.popupBG, + leading: Expanded( + child: Row( + children: [ + const SizedBox( + width: 32, ), - ), - trailing: AspectRatio( + 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, + ), + Text( + "MonKey", + style: STextStyles.navBarTitle(context), + ), + ], + ), + ), + trailing: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset(Assets.svg.circleQuestion), + onPressed: () { + 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.", + ); + }); + }), + ), + 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), + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + color: Theme.of(context) + .extension()! + .infoItemText, + ), onPressed: () { showDialog( context: context, useSafeArea: false, barrierDismissible: true, builder: (context) { - return const StackDialog( + return const StackOkDialog( title: "About MonKeys", message: "A MonKey is a visual representation of your Banano address.", @@ -267,185 +307,155 @@ class _MonkeyViewState extends ConsumerState { }); }), ), - useSpacers: false, - isCompactHeight: true, - ), - body: child, + ], + ), + body: child, + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => SizedBox( + width: 318, + child: 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, - color: Theme.of(context) - .extension()! - .infoItemText, - ), - 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.", - ); - }); - }), + 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)), ), - ], - ), - body: child, - ), - child: ConditionalParent( - condition: isDesktop, - builder: (child) => SizedBox( - width: 300, - height: 300, - child: child, - ), - child: ConditionalParent( - condition: imageBytes != null, - builder: (child) => Column( - children: [ - 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, ), - 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: [ - 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: 40, - ), - 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, - ), - ], - ), - ), - const Spacer( - flex: 6, - ), - Padding( - padding: const EdgeInsets.all(16.0), - child: PrimaryButton( - label: "Fetch MonKey", + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + SecondaryButton( + label: "Save as SVG", onPressed: () async { - final future = Future.wait([ - getMonkeyImage(receivingAddress), - Future.delayed(const Duration(seconds: 2)), - ]); - await showLoading( - whileFuture: future, + whileFuture: + downloadMonkey(receivingAddress, false), context: context, isDesktop: Util.isDesktop, - message: "Fetching MonKey", - subMessage: "We are fetching your MonKey", + message: "Saving MonKey svg", ); - - // if (isDesktop) { - // Navigator.of(context).popUntil( - // ModalRoute.withName( - // DesktopWalletView.routeName), - // ); - // } else { - // Navigator.of(context).popUntil( - // ModalRoute.withName(WalletView.routeName), - // ); - // } }, ), - ), - ], + 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", + ); + + // if (isDesktop) { + // Navigator.of(context).popUntil( + // ModalRoute.withName( + // DesktopWalletView.routeName), + // ); + // } else { + // Navigator.of(context).popUntil( + // ModalRoute.withName(WalletView.routeName), + // ); + // } + }, + ), + ), + ], ), ), ), - ], + ), ), ); } From 2f5a18b61574298b223d5437d19dd4b7bc043db2 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 16:10:27 -0600 Subject: [PATCH 122/128] merge conflict clean things --- lib/pages/monkey/monkey_view.dart | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index c34d4ed95..1dff1fd4d 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -241,9 +241,10 @@ class _MonkeyViewState extends ConsumerState { width: 15, ), SvgPicture.asset(Assets.svg.monkey), - const SizedBox( - width: 12, - ),Text( + const SizedBox( + width: 12, + ), + Text( "MonKey", style: STextStyles.navBarTitle(context), ), @@ -253,8 +254,8 @@ class _MonkeyViewState extends ConsumerState { trailing: Padding( padding: const EdgeInsets.all(8.0), child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( + cursor: SystemMouseCursors.click, + child: GestureDetector( onTap: () { showDialog( context: context, @@ -268,28 +269,28 @@ class _MonkeyViewState extends ConsumerState { ); }); }, - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.circleQuestion, + 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], ), - 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, @@ -315,7 +316,6 @@ class _MonkeyViewState extends ConsumerState { child: AppBarIconButton( icon: SvgPicture.asset( Assets.svg.circleQuestion, - ), onPressed: () { showDialog( From d8e6f3cac691443f4167205a35b8747450282947 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 16:15:29 -0600 Subject: [PATCH 123/128] fix mobile nav error --- lib/pages/wallet_view/wallet_view.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index b2d5de8e6..b3552807f 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -938,10 +938,7 @@ class _WalletViewState extends ConsumerState { onTap: () { Navigator.of(context).pushNamed( MonkeyView.routeName, - arguments: Tuple2( - widget.walletId, - widget.managerProvider, - ), + arguments: widget.walletId, ); }), if (ref.watch( From 8754142694e81e958b25c8e010cfb36661db434b Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 16:21:20 -0600 Subject: [PATCH 124/128] android file path --- lib/pages/monkey/monkey_view.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 1dff1fd4d..554d6d9e0 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -113,6 +113,10 @@ class _MonkeyViewState extends ConsumerState { Future getDocsDir() async { try { + if (Platform.isAndroid) { + return Directory("/storage/emulated/0/"); + } + return await getApplicationDocumentsDirectory(); } catch (_) { return null; From 04b9cddb1994b23208b108f0f73f130298b1c5ab Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Wed, 26 Jul 2023 16:23:34 -0600 Subject: [PATCH 125/128] fixed path --- lib/pages/monkey/monkey_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 554d6d9e0..af2fd91fd 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -114,7 +114,7 @@ class _MonkeyViewState extends ConsumerState { Future getDocsDir() async { try { if (Platform.isAndroid) { - return Directory("/storage/emulated/0/"); + return Directory("/storage/emulated/0/Documents"); } return await getApplicationDocumentsDirectory(); From 7390c498c732f336b7c047827e8c17eb616d1f64 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 16:29:09 -0600 Subject: [PATCH 126/128] set state on download --- lib/pages/monkey/monkey_view.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index af2fd91fd..2ad4b18eb 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -42,6 +42,7 @@ class MonkeyView extends ConsumerStatefulWidget { class _MonkeyViewState extends ConsumerState { late final String walletId; + List? imageBytes; String receivingAddress = ""; @@ -205,8 +206,7 @@ class _MonkeyViewState extends ConsumerState { final bool isDesktop = Util.isDesktop; - List? imageBytes; - imageBytes = (manager.wallet as BananoWallet).getMonkeyImageBytes(); + imageBytes ??= (manager.wallet as BananoWallet).getMonkeyImageBytes(); //edit for desktop return Background( @@ -360,7 +360,7 @@ class _MonkeyViewState extends ConsumerState { SizedBox( width: 300, height: 300, - child: SvgPicture.memory(Uint8List.fromList(imageBytes)), + child: SvgPicture.memory(Uint8List.fromList(imageBytes!)), ), isDesktop ? const SizedBox( @@ -466,6 +466,13 @@ class _MonkeyViewState extends ConsumerState { subMessage: "We are fetching your MonKey", ); + imageBytes = (manager.wallet as BananoWallet) + .getMonkeyImageBytes(); + + if (imageBytes != null) { + setState(() {}); + } + // if (isDesktop) { // Navigator.of(context).popUntil( // ModalRoute.withName( From e52072649594896012f797805848f985d355b3ec Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 14:50:38 -0600 Subject: [PATCH 127/128] fix the fix --- lib/db/hive/db.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 9d3f73abd..2fb82c806 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -276,10 +276,7 @@ class DB { Future deleteAll({required String boxName}) async { await mutex.protect(() async { - Box box = Hive.box(boxName); - if (!box.isOpen) { - box = await Hive.openBox(boxName); - } + final box = await Hive.openBox(boxName); await box.clear(); }); } From 4d870963245e00e6486129c94b522e58617e2d72 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 17:01:24 -0600 Subject: [PATCH 128/128] temp disable some ordinals stuff --- lib/pages/ordinals/ordinals_view.dart | 177 +++++++++--------- .../desktop_ordinal_details_view.dart | 35 ++-- .../ordinals/desktop_ordinals_view.dart | 113 +++++------ 3 files changed, 160 insertions(+), 165 deletions(-) diff --git a/lib/pages/ordinals/ordinals_view.dart b/lib/pages/ordinals/ordinals_view.dart index 059c81c41..45b30655a 100644 --- a/lib/pages/ordinals/ordinals_view.dart +++ b/lib/pages/ordinals/ordinals_view.dart @@ -11,21 +11,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/pages/ordinals/ordinals_filter_view.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/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/background.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_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 OrdinalsView extends ConsumerStatefulWidget { const OrdinalsView({ @@ -93,38 +87,39 @@ class _OrdinalsViewState extends ConsumerState { 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, + 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( @@ -135,63 +130,63 @@ class _OrdinalsViewState extends ConsumerState { ), 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, - ), + // 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_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index cb76e036d..2f89b7cd6 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -19,7 +19,6 @@ 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/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; class DesktopOrdinalDetailsView extends ConsumerStatefulWidget { @@ -175,23 +174,23 @@ class _DesktopOrdinalDetailsViewState // 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 - }, - ), + // 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 + // }, + // ), ], ), ), diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart index 414ece75d..ec2748560 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinals_view.dart @@ -121,62 +121,63 @@ class _DesktopOrdinals extends ConsumerState { children: [ Row( children: [ - 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 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, // ),