diff --git a/assets/svg/arrow-down.svg b/assets/svg/arrow-down.svg
new file mode 100644
index 0000000000..c96e43ce3d
--- /dev/null
+++ b/assets/svg/arrow-down.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/svg/keys.svg b/assets/svg/keys.svg
new file mode 100644
index 0000000000..cf86daea52
--- /dev/null
+++ b/assets/svg/keys.svg
@@ -0,0 +1,23 @@
+
diff --git a/lib/models/transaction_filter.dart b/lib/models/transaction_filter.dart
index 119ac70159..7ef5f0bff8 100644
--- a/lib/models/transaction_filter.dart
+++ b/lib/models/transaction_filter.dart
@@ -1,14 +1,16 @@
class TransactionFilter {
final bool sent;
final bool received;
- final DateTime from;
- final DateTime to;
+ final bool trade;
+ final DateTime? from;
+ final DateTime? to;
final int? amount;
final String keyword;
TransactionFilter({
required this.sent,
required this.received,
+ required this.trade,
required this.from,
required this.to,
required this.amount,
@@ -18,6 +20,7 @@ class TransactionFilter {
TransactionFilter copyWith({
bool? sent,
bool? received,
+ bool? trade,
DateTime? from,
DateTime? to,
int? amount,
@@ -26,6 +29,7 @@ class TransactionFilter {
return TransactionFilter(
sent: sent ?? this.sent,
received: received ?? this.received,
+ trade: trade ?? this.trade,
from: from ?? this.from,
to: to ?? this.to,
amount: amount ?? this.amount,
@@ -35,6 +39,6 @@ class TransactionFilter {
@override
String toString() {
- return "TxFilter { sent: $sent, received: $received, from: $from, to: $to, amount: $amount, keyword: $keyword }";
+ return "TxFilter { sent: $sent, received: $received, trade: $trade, from: $from, to: $to, amount: $amount, keyword: $keyword }";
}
}
diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart
index 946f54d4af..7d21dfec49 100644
--- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart
+++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart
@@ -6,10 +6,12 @@ class MnemonicTable extends StatelessWidget {
Key? key,
required this.words,
required this.isDesktop,
+ this.itemBorderColor,
}) : super(key: key);
final List words;
final bool isDesktop;
+ final Color? itemBorderColor;
@override
Widget build(BuildContext context) {
@@ -40,6 +42,7 @@ class MnemonicTable extends StatelessWidget {
number: ++index,
word: words[index - 1],
isDesktop: isDesktop,
+ borderColor: itemBorderColor,
),
),
],
@@ -61,6 +64,7 @@ class MnemonicTable extends StatelessWidget {
number: i + 1,
word: words[i],
isDesktop: isDesktop,
+ borderColor: itemBorderColor,
),
),
],
diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart
index 8928ff3a6b..ec103dfc69 100644
--- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart
+++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart
@@ -9,16 +9,19 @@ class MnemonicTableItem extends StatelessWidget {
required this.number,
required this.word,
required this.isDesktop,
+ this.borderColor,
}) : super(key: key);
final int number;
final String word;
final bool isDesktop;
+ final Color? borderColor;
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
return RoundedWhiteContainer(
+ borderColor: borderColor,
padding: isDesktop
? const EdgeInsets.symmetric(horizontal: 12, vertical: 9)
: const EdgeInsets.all(8),
diff --git a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart
index 3e29612d1e..4c3c4c968a 100644
--- a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart
+++ b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart
@@ -20,7 +20,10 @@ import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.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: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_dialog.dart';
@@ -51,6 +54,10 @@ class _GenerateUriQrCodeViewState extends State {
late TextEditingController amountController;
late TextEditingController noteController;
+ late final bool isDesktop;
+ late String _uriString;
+ bool didGenerate = false;
+
final _amountFocusNode = FocusNode();
final _noteFocusNode = FocusNode();
@@ -81,8 +88,151 @@ class _GenerateUriQrCodeViewState extends State {
}
}
+ String? _generateURI() {
+ final amountString = amountController.text;
+ final noteString = noteController.text;
+
+ if (amountString.isNotEmpty && Decimal.tryParse(amountString) == null) {
+ showFloatingFlushBar(
+ type: FlushBarType.warning,
+ message: "Invalid amount",
+ context: context,
+ );
+ return null;
+ }
+
+ String query = "";
+
+ if (amountString.isNotEmpty) {
+ query += "amount=$amountString";
+ }
+ if (noteString.isNotEmpty) {
+ if (query.isNotEmpty) {
+ query += "&";
+ }
+ query += "message=$noteString";
+ }
+
+ final uri = Uri(
+ scheme: widget.coin.uriScheme,
+ host: widget.receivingAddress,
+ query: query.isNotEmpty ? query : null,
+ );
+
+ final uriString = uri.toString().replaceFirst("://", ":");
+
+ Logging.instance.log("Generated receiving QR code for: $uriString",
+ level: LogLevel.Info);
+
+ return uriString;
+ }
+
+ void onGeneratePressed() {
+ final uriString = _generateURI();
+
+ if (uriString == null) {
+ return;
+ }
+
+ showDialog(
+ context: context,
+ useSafeArea: false,
+ barrierDismissible: true,
+ builder: (_) {
+ final width = MediaQuery.of(context).size.width / 2;
+ return StackDialogBase(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Center(
+ child: Text(
+ "New QR code",
+ style: STextStyles.pageTitleH2(context),
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ Center(
+ child: RepaintBoundary(
+ key: _qrKey,
+ child: SizedBox(
+ width: width + 20,
+ height: width + 20,
+ child: QrImage(
+ data: uriString,
+ size: width,
+ backgroundColor:
+ Theme.of(context).extension()!.popupBG,
+ foregroundColor: Theme.of(context)
+ .extension()!
+ .accentColorDark),
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ Center(
+ child: SizedBox(
+ width: width,
+ child: TextButton(
+ onPressed: () async {
+ // TODO: add save button as well
+ await _capturePng(true);
+ },
+ style: Theme.of(context)
+ .extension()!
+ .getSecondaryEnabledButtonColor(context),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Center(
+ child: SvgPicture.asset(
+ Assets.svg.share,
+ width: 14,
+ height: 14,
+ ),
+ ),
+ const SizedBox(
+ width: 4,
+ ),
+ Column(
+ children: [
+ Text(
+ "Share",
+ textAlign: TextAlign.center,
+ style: STextStyles.button(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextSecondary,
+ ),
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ }
+
@override
void initState() {
+ isDesktop = Util.isDesktop;
+ _uriString = Uri(
+ scheme: widget.coin.uriScheme,
+ host: widget.receivingAddress,
+ ).toString().replaceFirst("://", ":");
amountController = TextEditingController();
noteController = TextEditingController();
super.initState();
@@ -101,315 +251,330 @@ class _GenerateUriQrCodeViewState extends State {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
- return Scaffold(
- backgroundColor: Theme.of(context).extension()!.background,
- appBar: AppBar(
- leading: AppBarBackButton(
- onPressed: () async {
- if (FocusScope.of(context).hasFocus) {
- FocusScope.of(context).unfocus();
- await Future.delayed(const Duration(milliseconds: 70));
- }
- if (mounted) {
- Navigator.of(context).pop();
- }
- },
+
+ return ConditionalParent(
+ condition: !isDesktop,
+ builder: (child) => Scaffold(
+ backgroundColor: Theme.of(context).extension()!.background,
+ appBar: AppBar(
+ leading: AppBarBackButton(
+ onPressed: () async {
+ if (FocusScope.of(context).hasFocus) {
+ FocusScope.of(context).unfocus();
+ await Future.delayed(const Duration(milliseconds: 70));
+ }
+ if (mounted) {
+ Navigator.of(context).pop();
+ }
+ },
+ ),
+ title: Text(
+ "Generate QR code",
+ style: STextStyles.navBarTitle(context),
+ ),
),
- title: Text(
- "Generate QR code",
- style: STextStyles.navBarTitle(context),
+ body: LayoutBuilder(
+ builder: (buildContext, constraints) {
+ return Padding(
+ padding: const EdgeInsets.only(
+ left: 12,
+ top: 12,
+ right: 12,
+ ),
+ child: SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: constraints.maxHeight - 24,
+ ),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: const EdgeInsets.all(4),
+ child: child,
+ ),
+ ),
+ ),
+ ),
+ );
+ },
),
),
- body: LayoutBuilder(
- builder: (buildContext, constraints) {
- return Padding(
- padding: const EdgeInsets.only(
- left: 12,
- top: 12,
- right: 12,
- ),
- child: SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight - 24,
+ child: Padding(
+ padding: isDesktop
+ ? const EdgeInsets.only(
+ top: 12,
+ left: 32,
+ right: 32,
+ bottom: 32,
+ )
+ : const EdgeInsets.all(0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
+ children: [
+ if (!isDesktop)
+ RoundedWhiteContainer(
+ child: Text(
+ "The new QR code with your address, amount and note will appear in the pop up window.",
+ style: STextStyles.itemSubtitle(context),
),
- child: IntrinsicHeight(
- child: Padding(
- padding: const EdgeInsets.all(4),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- RoundedWhiteContainer(
- child: Text(
- "The new QR code with your address, amount and note will appear in the pop up window.",
- style: STextStyles.itemSubtitle(context),
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- Text(
- "Amount (Optional)",
- style: STextStyles.smallMed12(context),
- textAlign: TextAlign.left,
- ),
- const SizedBox(
- height: 8,
- ),
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- controller: amountController,
- focusNode: _amountFocusNode,
- style: STextStyles.field(context),
- keyboardType: const TextInputType.numberWithOptions(
- decimal: true),
- onChanged: (_) => setState(() {}),
- decoration: standardInputDecoration(
- "Amount",
- _amountFocusNode,
- context,
- ).copyWith(
- suffixIcon: amountController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- amountController.text = "";
- });
- },
- ),
- ],
- ),
- ),
- )
- : null,
+ ),
+ if (!isDesktop)
+ const SizedBox(
+ height: 12,
+ ),
+ Text(
+ "Amount (Optional)",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldActiveSearchIconRight,
+ )
+ : STextStyles.smallMed12(context),
+ textAlign: TextAlign.left,
+ ),
+ SizedBox(
+ height: isDesktop ? 10 : 8,
+ ),
+ ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ autocorrect: Util.isDesktop ? false : true,
+ enableSuggestions: Util.isDesktop ? false : true,
+ controller: amountController,
+ focusNode: _amountFocusNode,
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultText,
+ height: 1.8,
+ )
+ : STextStyles.field(context),
+ keyboardType:
+ const TextInputType.numberWithOptions(decimal: true),
+ onChanged: (_) => setState(() {}),
+ decoration: standardInputDecoration(
+ "Amount",
+ _amountFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: isDesktop
+ ? const EdgeInsets.only(
+ left: 16,
+ top: 11,
+ bottom: 12,
+ right: 5,
+ )
+ : null,
+ suffixIcon: amountController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ amountController.text = "";
+ });
+ },
+ ),
+ ],
),
),
- ),
- const SizedBox(
- height: 12,
- ),
- Text(
- "Note (Optional)",
- style: STextStyles.smallMed12(context),
- textAlign: TextAlign.left,
- ),
- const SizedBox(
- height: 8,
- ),
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- controller: noteController,
- focusNode: _noteFocusNode,
- style: STextStyles.field(context),
- onChanged: (_) => setState(() {}),
- decoration: standardInputDecoration(
- "Note",
- _noteFocusNode,
- context,
- ).copyWith(
- suffixIcon: noteController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- noteController.text = "";
- });
- },
- ),
- ],
- ),
- ),
- )
- : null,
+ )
+ : null,
+ ),
+ ),
+ ),
+ SizedBox(
+ height: isDesktop ? 20 : 12,
+ ),
+ Text(
+ "Note (Optional)",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldActiveSearchIconRight,
+ )
+ : STextStyles.smallMed12(context),
+ textAlign: TextAlign.left,
+ ),
+ SizedBox(
+ height: isDesktop ? 10 : 8,
+ ),
+ ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ autocorrect: Util.isDesktop ? false : true,
+ enableSuggestions: Util.isDesktop ? false : true,
+ controller: noteController,
+ focusNode: _noteFocusNode,
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultText,
+ height: 1.8,
+ )
+ : STextStyles.field(context),
+ onChanged: (_) => setState(() {}),
+ decoration: standardInputDecoration(
+ "Note",
+ _noteFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: isDesktop
+ ? const EdgeInsets.only(
+ left: 16,
+ top: 11,
+ bottom: 12,
+ right: 5,
+ )
+ : null,
+ suffixIcon: noteController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ noteController.text = "";
+ });
+ },
+ ),
+ ],
),
),
- ),
- // SizedBox()
- // Spacer(),
- const SizedBox(
- height: 8,
- ),
- TextButton(
- style: Theme.of(context)
- .extension()!
- .getPrimaryEnabledButtonColor(context),
- onPressed: () {
- final amountString = amountController.text;
- final noteString = noteController.text;
-
- if (amountString.isNotEmpty &&
- Decimal.tryParse(amountString) == null) {
- showFloatingFlushBar(
- type: FlushBarType.warning,
- message: "Invalid amount",
- context: context,
- );
- return;
- }
-
- String query = "";
-
- if (amountString.isNotEmpty) {
- query += "amount=$amountString";
- }
- if (noteString.isNotEmpty) {
- if (query.isNotEmpty) {
- query += "&";
- }
- query += "message=$noteString";
- }
-
- final uri = Uri(
- scheme: widget.coin.uriScheme,
- host: widget.receivingAddress,
- query: query.isNotEmpty ? query : null,
- );
-
- final uriString =
- uri.toString().replaceFirst("://", ":");
-
- Logging.instance.log(
- "Generated receiving QR code for: $uriString",
- level: LogLevel.Info);
+ )
+ : null,
+ ),
+ ),
+ ),
+ SizedBox(
+ height: isDesktop ? 20 : 8,
+ ),
+ PrimaryButton(
+ label: "Generate QR code",
+ onPressed: isDesktop
+ ? () {
+ final uriString = _generateURI();
+ if (uriString == null) {
+ return;
+ }
- showDialog(
- context: context,
- useSafeArea: false,
- barrierDismissible: true,
- builder: (_) {
- final width =
- MediaQuery.of(context).size.width / 2;
- return StackDialogBase(
- child: Column(
- crossAxisAlignment:
- CrossAxisAlignment.stretch,
- children: [
- Center(
- child: Text(
- "New QR code",
- style:
- STextStyles.pageTitleH2(context),
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- Center(
- child: RepaintBoundary(
- key: _qrKey,
- child: SizedBox(
- width: width + 20,
- height: width + 20,
- child: QrImage(
- data: uriString,
- size: width,
- backgroundColor: Theme.of(
- context)
- .extension()!
- .popupBG,
- foregroundColor: Theme.of(
- context)
- .extension()!
- .accentColorDark),
- ),
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- Center(
- child: SizedBox(
- width: width,
- child: TextButton(
- onPressed: () async {
- // TODO: add save button as well
- await _capturePng(true);
- },
- style: Theme.of(context)
- .extension()!
- .getSecondaryEnabledButtonColor(
- context),
- child: Row(
- mainAxisAlignment:
- MainAxisAlignment.center,
- crossAxisAlignment:
- CrossAxisAlignment.center,
- children: [
- Center(
- child: SvgPicture.asset(
- Assets.svg.share,
- width: 14,
- height: 14,
- ),
- ),
- const SizedBox(
- width: 4,
- ),
- Column(
- children: [
- Text(
- "Share",
- textAlign:
- TextAlign.center,
- style: STextStyles.button(
- context)
- .copyWith(
- color: Theme.of(context)
- .extension<
- StackColors>()!
- .buttonTextSecondary,
- ),
- ),
- const SizedBox(
- height: 2,
- ),
- ],
- ),
- ],
- ),
- ),
- ),
- ),
- ],
+ setState(() {
+ didGenerate = true;
+ _uriString = uriString;
+ });
+ }
+ : onGeneratePressed,
+ desktopMed: true,
+ ),
+ if (isDesktop && didGenerate)
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const SizedBox(
+ height: 20,
+ ),
+ RoundedWhiteContainer(
+ borderColor: Theme.of(context)
+ .extension()!
+ .background,
+ child: Column(
+ children: [
+ Text(
+ "New QR Code",
+ style: STextStyles.desktopTextMedium(context),
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ Center(
+ child: RepaintBoundary(
+ key: _qrKey,
+ child: SizedBox(
+ width: 234,
+ height: 234,
+ child: QrImage(
+ data: _uriString,
+ size: 220,
+ backgroundColor: Theme.of(context)
+ .extension()!
+ .popupBG,
+ foregroundColor: Theme.of(context)
+ .extension()!
+ .accentColorDark),
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ Row(
+ children: [
+ SecondaryButton(
+ width: 170,
+ desktopMed: true,
+ onPressed: () async {
+ await _capturePng(false);
+ },
+ label: "Share",
+ icon: SvgPicture.asset(
+ Assets.svg.share,
+ width: 20,
+ height: 20,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextSecondary,
),
- );
- },
- );
- },
- child: Text(
- "Generate QR Code",
- style: STextStyles.button(context),
- ),
+ ),
+ const SizedBox(
+ width: 16,
+ ),
+ PrimaryButton(
+ width: 170,
+ desktopMed: true,
+ onPressed: () async {
+ // TODO: add save functionality instead of share
+ await _capturePng(true);
+ },
+ label: "Save",
+ icon: SvgPicture.asset(
+ Assets.svg.arrowDown,
+ width: 20,
+ height: 20,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextPrimary,
+ ),
+ ),
+ ],
+ )
+ ],
),
- ],
- ),
+ ),
+ ],
),
- ),
+ ],
),
- ),
- );
- },
+ ],
+ ),
),
);
}
diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart
index 65537f20ee..81d5a3da2f 100644
--- a/lib/pages/send_view/confirm_transaction_view.dart
+++ b/lib/pages/send_view/confirm_transaction_view.dart
@@ -1,7 +1,9 @@
import 'dart:async';
+import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
@@ -11,12 +13,17 @@ import 'package:stackwallet/providers/wallet/public_private_balance_state_provid
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.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/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.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:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
@@ -45,6 +52,14 @@ class _ConfirmTransactionViewState
late final Map transactionInfo;
late final String walletId;
late final String routeOnSuccessName;
+ late final bool isDesktop;
+
+ int _fee = 12;
+ final List _dropDownItems = [
+ 12,
+ 22,
+ 234,
+ ];
Future _attemptSend(BuildContext context) async {
unawaited(showDialog(
@@ -133,6 +148,7 @@ class _ConfirmTransactionViewState
@override
void initState() {
+ isDesktop = Util.isDesktop;
transactionInfo = widget.transactionInfo;
walletId = widget.walletId;
routeOnSuccessName = widget.routeOnSuccessName;
@@ -143,234 +159,553 @@ class _ConfirmTransactionViewState
Widget build(BuildContext context) {
final managerProvider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
- return Scaffold(
- backgroundColor: Theme.of(context).extension()!.background,
- appBar: AppBar(
+
+ return ConditionalParent(
+ condition: !isDesktop,
+ builder: (child) => Scaffold(
backgroundColor: Theme.of(context).extension()!.background,
- leading: AppBarBackButton(
- onPressed: () async {
- // if (FocusScope.of(context).hasFocus) {
- // FocusScope.of(context).unfocus();
- // await Future.delayed(Duration(milliseconds: 50));
- // }
- Navigator.of(context).pop();
- },
+ appBar: AppBar(
+ backgroundColor:
+ Theme.of(context).extension()!.background,
+ leading: AppBarBackButton(
+ onPressed: () async {
+ // if (FocusScope.of(context).hasFocus) {
+ // FocusScope.of(context).unfocus();
+ // await Future.delayed(Duration(milliseconds: 50));
+ // }
+ Navigator.of(context).pop();
+ },
+ ),
+ title: Text(
+ "Confirm transaction",
+ style: STextStyles.navBarTitle(context),
+ ),
),
- title: Text(
- "Confirm transaction",
- style: STextStyles.navBarTitle(context),
+ body: LayoutBuilder(
+ builder: (builderContext, constraints) {
+ return Padding(
+ padding: const EdgeInsets.only(
+ left: 12,
+ top: 12,
+ right: 12,
+ ),
+ child: SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: constraints.maxHeight - 24,
+ ),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: const EdgeInsets.all(4),
+ child: child,
+ ),
+ ),
+ ),
+ ),
+ );
+ },
),
),
- body: LayoutBuilder(
- builder: (builderContext, constraints) {
- return Padding(
- padding: const EdgeInsets.only(
- left: 12,
- top: 12,
- right: 12,
- ),
- child: SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight - 24,
+ child: ConditionalParent(
+ condition: isDesktop,
+ builder: (child) => Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ children: [
+ AppBarBackButton(
+ size: 40,
+ iconSize: 24,
+ onPressed: () => Navigator.of(
+ context,
+ rootNavigator: true,
+ ).pop(),
),
- child: IntrinsicHeight(
- child: Padding(
- padding: const EdgeInsets.all(4),
+ Text(
+ "Confirm ${ref.watch(managerProvider.select((value) => value.coin.ticker.toUpperCase()))} transaction",
+ style: STextStyles.desktopH3(context),
+ ),
+ ],
+ ),
+ child,
+ ],
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
+ children: [
+ if (!isDesktop)
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ "Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
+ style: STextStyles.pageTitleH1(context),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
- "Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}",
- style: STextStyles.pageTitleH1(context),
+ "Recipient",
+ style: STextStyles.smallMed12(context),
),
const SizedBox(
- height: 12,
+ height: 4,
),
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- Text(
- "Recipient",
- style: STextStyles.smallMed12(context),
- ),
- const SizedBox(
- height: 4,
- ),
- Text(
- "${transactionInfo["address"] ?? "ERROR"}",
- style: STextStyles.itemSubtitle12(context),
- ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
+ Text(
+ "${transactionInfo["address"] ?? "ERROR"}",
+ style: STextStyles.itemSubtitle12(context),
),
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Amount",
- style: STextStyles.smallMed12(context),
- ),
- Text(
- "${Format.satoshiAmountToPrettyString(
- transactionInfo["recipientAmt"] as int,
- ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale),
- ),
- )} ${ref.watch(
- managerProvider
- .select((value) => value.coin),
- ).ticker}",
- style: STextStyles.itemSubtitle12(context),
- textAlign: TextAlign.right,
- ),
- ],
- ),
+ ],
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Amount",
+ style: STextStyles.smallMed12(context),
),
- const SizedBox(
- height: 12,
+ Text(
+ "${Format.satoshiAmountToPrettyString(
+ transactionInfo["recipientAmt"] as int,
+ ref.watch(
+ localeServiceChangeNotifierProvider
+ .select((value) => value.locale),
+ ),
+ )} ${ref.watch(
+ managerProvider.select((value) => value.coin),
+ ).ticker}",
+ style: STextStyles.itemSubtitle12(context),
+ textAlign: TextAlign.right,
),
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Transaction fee",
- style: STextStyles.smallMed12(context),
- ),
- Text(
- "${Format.satoshiAmountToPrettyString(
- transactionInfo["fee"] as int,
- ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale),
- ),
- )} ${ref.watch(
- managerProvider
- .select((value) => value.coin),
- ).ticker}",
- style: STextStyles.itemSubtitle12(context),
- textAlign: TextAlign.right,
- ),
- ],
- ),
+ ],
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Transaction fee",
+ style: STextStyles.smallMed12(context),
),
- const SizedBox(
- height: 12,
+ Text(
+ "${Format.satoshiAmountToPrettyString(
+ transactionInfo["fee"] as int,
+ ref.watch(
+ localeServiceChangeNotifierProvider
+ .select((value) => value.locale),
+ ),
+ )} ${ref.watch(
+ managerProvider.select((value) => value.coin),
+ ).ticker}",
+ style: STextStyles.itemSubtitle12(context),
+ textAlign: TextAlign.right,
),
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- Text(
- "Note",
- style: STextStyles.smallMed12(context),
- ),
- const SizedBox(
- height: 4,
- ),
- Text(
- transactionInfo["note"] as String,
- style: STextStyles.itemSubtitle12(context),
- ),
- ],
- ),
+ ],
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ "Note",
+ style: STextStyles.smallMed12(context),
),
- const Spacer(),
const SizedBox(
- height: 12,
+ height: 4,
+ ),
+ Text(
+ transactionInfo["note"] as String,
+ style: STextStyles.itemSubtitle12(context),
),
- RoundedContainer(
+ ],
+ ),
+ ),
+ ],
+ ),
+ if (isDesktop)
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 16,
+ left: 32,
+ right: 32,
+ bottom: 50,
+ ),
+ child: RoundedWhiteContainer(
+ padding: const EdgeInsets.all(0),
+ borderColor:
+ Theme.of(context).extension()!.background,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Container(
+ decoration: BoxDecoration(
color: Theme.of(context)
.extension()!
- .snackBarBackSuccess,
+ .background,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ topRight: Radius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: 22,
+ ),
child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Text(
- "Total amount",
- style:
- STextStyles.titleBold12(context).copyWith(
- color: Theme.of(context)
- .extension()!
- .textConfirmTotalAmount,
- ),
+ SvgPicture.asset(
+ Assets.svg.send(context),
+ width: 32,
+ height: 32,
+ ),
+ const SizedBox(
+ width: 16,
),
Text(
- "${Format.satoshiAmountToPrettyString(
- (transactionInfo["fee"] as int) +
- (transactionInfo["recipientAmt"] as int),
- ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale),
- ),
- )} ${ref.watch(
+ "Send ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
- style: STextStyles.itemSubtitle12(context)
- .copyWith(
- color: Theme.of(context)
- .extension()!
- .textConfirmTotalAmount,
- ),
- textAlign: TextAlign.right,
+ style: STextStyles.desktopTextMedium(context),
),
],
),
),
- const SizedBox(
- height: 16,
+ ),
+ Padding(
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Amount",
+ style: STextStyles.desktopTextExtraExtraSmall(
+ context),
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ Builder(
+ builder: (context) {
+ final amount =
+ transactionInfo["recipientAmt"] as int;
+ final coin = ref.watch(
+ managerProvider.select(
+ (value) => value.coin,
+ ),
+ );
+ final externalCalls = ref.watch(
+ prefsChangeNotifierProvider.select(
+ (value) => value.externalCalls));
+ String fiatAmount = "N/A";
+
+ if (externalCalls) {
+ final price = ref
+ .read(priceAnd24hChangeNotifierProvider)
+ .getPrice(coin)
+ .item1;
+ if (price > Decimal.zero) {
+ fiatAmount = Format.localizedStringAsFixed(
+ value: Format.satoshisToAmount(amount,
+ coin: coin) *
+ price,
+ locale: ref
+ .read(
+ localeServiceChangeNotifierProvider)
+ .locale,
+ decimalPlaces: 2,
+ );
+ }
+ }
+
+ return Row(
+ children: [
+ Text(
+ "${Format.satoshiAmountToPrettyString(
+ amount,
+ ref.watch(
+ localeServiceChangeNotifierProvider
+ .select((value) => value.locale),
+ ),
+ )} ${coin.ticker}",
+ style: STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ ),
+ ),
+ if (externalCalls)
+ Text(
+ " | ",
+ style: STextStyles
+ .desktopTextExtraExtraSmall(
+ context),
+ ),
+ if (externalCalls)
+ Text(
+ "~$fiatAmount ${ref.watch(prefsChangeNotifierProvider.select(
+ (value) => value.currency,
+ ))}",
+ style: STextStyles
+ .desktopTextExtraExtraSmall(
+ context),
+ ),
+ ],
+ );
+ },
+ ),
+ ],
),
- TextButton(
- style: Theme.of(context)
- .extension()!
- .getPrimaryEnabledButtonColor(context),
- onPressed: () async {
- final unlocked = await Navigator.push(
- context,
- RouteGenerator.getRoute(
- shouldUseMaterialRoute:
- RouteGenerator.useMaterialPageRoute,
- builder: (_) => const LockscreenView(
- showBackButton: true,
- popOnSuccess: true,
- routeOnSuccessArguments: true,
- routeOnSuccess: "",
- biometricsCancelButtonString: "CANCEL",
- biometricsLocalizedReason:
- "Authenticate to send transaction",
- biometricsAuthenticationTitle:
- "Confirm Transaction",
- ),
- settings: const RouteSettings(
- name: "/confirmsendlockscreen"),
+ ),
+ Container(
+ height: 1,
+ color: Theme.of(context)
+ .extension()!
+ .background,
+ ),
+ Padding(
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Send to",
+ style: STextStyles.desktopTextExtraExtraSmall(
+ context),
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ Text(
+ "${transactionInfo["address"] ?? "ERROR"}",
+ style: STextStyles.desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
),
- );
-
- if (unlocked is bool && unlocked && mounted) {
- unawaited(_attemptSend(context));
- }
- },
+ )
+ ],
+ ),
+ ),
+ Container(
+ height: 1,
+ color: Theme.of(context)
+ .extension()!
+ .background,
+ ),
+ Padding(
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Note",
+ style: STextStyles.desktopTextExtraExtraSmall(
+ context),
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ Text(
+ transactionInfo["note"] as String,
+ style: STextStyles.desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ ),
+ )
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ if (isDesktop)
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ ),
+ child: Text(
+ "Transaction fee (estimated)",
+ style: STextStyles.desktopTextExtraExtraSmall(context),
+ ),
+ ),
+ if (isDesktop)
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 10,
+ left: 32,
+ right: 32,
+ ),
+ child: DropdownButtonFormField(
+ value: _fee,
+ items: _dropDownItems
+ .map(
+ (e) => DropdownMenuItem(
+ value: e,
child: Text(
- "Send",
- style: STextStyles.button(context),
+ e.toString(),
),
),
- ],
+ )
+ .toList(),
+ onChanged: (value) {
+ if (value is int) {
+ setState(() {
+ _fee = value;
+ });
+ }
+ },
+ ),
+ ),
+ if (!isDesktop) const Spacer(),
+ SizedBox(
+ height: isDesktop ? 23 : 12,
+ ),
+ Padding(
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(
+ horizontal: 32,
+ )
+ : const EdgeInsets.all(0),
+ child: RoundedContainer(
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 18,
+ )
+ : const EdgeInsets.all(12),
+ color: Theme.of(context)
+ .extension()!
+ .snackBarBackSuccess,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ isDesktop ? "Total amount to send" : "Total amount",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textConfirmTotalAmount,
+ )
+ : STextStyles.titleBold12(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textConfirmTotalAmount,
+ ),
),
- ),
+ Text(
+ "${Format.satoshiAmountToPrettyString(
+ (transactionInfo["fee"] as int) +
+ (transactionInfo["recipientAmt"] as int),
+ ref.watch(
+ localeServiceChangeNotifierProvider
+ .select((value) => value.locale),
+ ),
+ )} ${ref.watch(
+ managerProvider.select((value) => value.coin),
+ ).ticker}",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textConfirmTotalAmount,
+ )
+ : STextStyles.itemSubtitle12(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textConfirmTotalAmount,
+ ),
+ textAlign: TextAlign.right,
+ ),
+ ],
),
),
),
- );
- },
+ SizedBox(
+ height: isDesktop ? 28 : 16,
+ ),
+ Padding(
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(
+ horizontal: 32,
+ )
+ : const EdgeInsets.all(0),
+ child: PrimaryButton(
+ label: "Send",
+ desktopMed: true,
+ onPressed: () async {
+ final unlocked = await Navigator.push(
+ context,
+ RouteGenerator.getRoute(
+ shouldUseMaterialRoute:
+ RouteGenerator.useMaterialPageRoute,
+ builder: (_) => const LockscreenView(
+ showBackButton: true,
+ popOnSuccess: true,
+ routeOnSuccessArguments: true,
+ routeOnSuccess: "",
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason:
+ "Authenticate to send transaction",
+ biometricsAuthenticationTitle: "Confirm Transaction",
+ ),
+ settings:
+ const RouteSettings(name: "/confirmsendlockscreen"),
+ ),
+ );
+
+ if (unlocked is bool && unlocked && mounted) {
+ unawaited(_attemptSend(context));
+ }
+ },
+ ),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 32,
+ ),
+ ],
+ ),
),
);
}
diff --git a/lib/pages/send_view/sub_widgets/building_transaction_dialog.dart b/lib/pages/send_view/sub_widgets/building_transaction_dialog.dart
index 0b67869156..045218e54a 100644
--- a/lib/pages/send_view/sub_widgets/building_transaction_dialog.dart
+++ b/lib/pages/send_view/sub_widgets/building_transaction_dialog.dart
@@ -3,6 +3,8 @@ import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.dart';
+import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class BuildingTransactionDialog extends StatefulWidget {
@@ -50,37 +52,73 @@ class _RestoringDialogState extends State
@override
Widget build(BuildContext context) {
- return WillPopScope(
- onWillPop: () async {
- return false;
- },
- child: StackDialog(
- title: "Generating transaction",
- // // TODO get message from design team
- // message: "",
- icon: RotationTransition(
- turns: _spinAnimation,
- child: SvgPicture.asset(
- Assets.svg.arrowRotate,
- color: Theme.of(context).extension()!.accentColorDark,
- width: 24,
- height: 24,
+ if (Util.isDesktop) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ "Generating transaction",
+ style: STextStyles.desktopH3(context),
),
- ),
- rightButton: TextButton(
- style: Theme.of(context)
- .extension()!
- .getSecondaryEnabledButtonColor(context),
- child: Text(
- "Cancel",
- style: STextStyles.itemSubtitle12(context),
+ const SizedBox(
+ height: 40,
+ ),
+ RotationTransition(
+ turns: _spinAnimation,
+ child: SvgPicture.asset(
+ Assets.svg.arrowRotate,
+ color:
+ Theme.of(context).extension()!.accentColorDark,
+ width: 24,
+ height: 24,
+ ),
+ ),
+ const SizedBox(
+ height: 40,
+ ),
+ SecondaryButton(
+ desktopMed: true,
+ label: "Cancel",
+ onPressed: () {
+ onCancel.call();
+ },
+ )
+ ],
+ );
+ } else {
+ return WillPopScope(
+ onWillPop: () async {
+ return false;
+ },
+ child: StackDialog(
+ title: "Generating transaction",
+ // // TODO get message from design team
+ // message: "",
+ icon: RotationTransition(
+ turns: _spinAnimation,
+ child: SvgPicture.asset(
+ Assets.svg.arrowRotate,
+ color:
+ Theme.of(context).extension()!.accentColorDark,
+ width: 24,
+ height: 24,
+ ),
+ ),
+ rightButton: TextButton(
+ style: Theme.of(context)
+ .extension()!
+ .getSecondaryEnabledButtonColor(context),
+ child: Text(
+ "Cancel",
+ style: STextStyles.itemSubtitle12(context),
+ ),
+ onPressed: () {
+ Navigator.of(context).pop();
+ onCancel.call();
+ },
),
- onPressed: () {
- Navigator.of(context).pop();
- onCancel.call();
- },
),
- ),
- );
+ );
+ }
}
}
diff --git a/lib/pages/stack_privacy_calls.dart b/lib/pages/stack_privacy_calls.dart
index d271b239f6..7e981b91a4 100644
--- a/lib/pages/stack_privacy_calls.dart
+++ b/lib/pages/stack_privacy_calls.dart
@@ -3,21 +3,23 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
+import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/pages/pinpad_views/create_pin_view.dart';
import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
+import 'package:stackwallet/providers/global/price_provider.dart';
+import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.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/primary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
-import '../hive/db.dart';
-import '../providers/global/price_provider.dart';
-import '../services/exchange/exchange_data_loading_service.dart';
-
class StackPrivacyCalls extends ConsumerStatefulWidget {
const StackPrivacyCalls({
Key? key,
@@ -52,155 +54,197 @@ class _StackPrivacyCalls extends ConsumerState {
@override
Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Theme.of(context).extension()!.background,
- appBar: AppBar(
- leading: AppBarBackButton(
- onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- ),
+ return MasterScaffold(
+ background: Theme.of(context).extension()!.background,
+ isDesktop: isDesktop,
+ appBar: isDesktop
+ ? const DesktopAppBar(
+ isCompactHeight: false,
+ leading: AppBarBackButton(),
+ )
+ : AppBar(
+ leading: AppBarBackButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ),
body: SafeArea(
child: Padding(
- padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- "Choose your Stack experience",
- style: STextStyles.pageTitleH1(context),
- ),
- const SizedBox(
- height: 8,
- ),
- Text(
- !widget.isSettings ? "You can change it later in Settings" : "",
- style: STextStyles.subtitle(context),
- ),
- const SizedBox(
- height: 36,
- ),
- Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
+ padding: EdgeInsets.fromLTRB(0, isDesktop ? 0 : 40, 0, 0),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth: isDesktop ? 480 : double.infinity,
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ "Choose your Stack experience",
+ style: isDesktop
+ ? STextStyles.desktopH2(context)
+ : STextStyles.pageTitleH1(context),
),
- child: PrivacyToggle(
- externalCallsEnabled: isEasy,
- onChanged: (externalCalls) {
- isEasy = externalCalls;
- setState(() {
- infoToggle = isEasy;
- });
- },
+ SizedBox(
+ height: isDesktop ? 16 : 8,
),
- ),
- const SizedBox(
- height: 36,
- ),
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: RoundedWhiteContainer(
- child: Center(
- child: RichText(
- textAlign: TextAlign.left,
- text: TextSpan(
- style:
- STextStyles.label(context).copyWith(fontSize: 12.0),
- children: infoToggle
- ? [
- const TextSpan(
- text:
- "Exchange data preloaded for a seamless experience."),
- const TextSpan(
- text:
- "\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."),
- TextSpan(
- text:
- "\n\nRecommended for most crypto users.",
- style: TextStyle(
- color: Theme.of(context)
- .extension()!
- .textDark,
- fontWeight: FontWeight.w600,
- ),
+ Text(
+ !widget.isSettings
+ ? "You can change it later in Settings"
+ : "",
+ style: isDesktop
+ ? STextStyles.desktopSubtitleH2(context)
+ : STextStyles.subtitle(context),
+ ),
+ SizedBox(
+ height: isDesktop ? 32 : 36,
+ ),
+ Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: isDesktop ? 0 : 16,
+ ),
+ child: PrivacyToggle(
+ externalCallsEnabled: isEasy,
+ onChanged: (externalCalls) {
+ isEasy = externalCalls;
+ setState(() {
+ infoToggle = isEasy;
+ });
+ },
+ ),
+ ),
+ SizedBox(
+ height: isDesktop ? 16 : 36,
+ ),
+ Padding(
+ padding: isDesktop
+ ? const EdgeInsets.all(0)
+ : const EdgeInsets.all(16.0),
+ child: RoundedWhiteContainer(
+ child: Center(
+ child: RichText(
+ textAlign: TextAlign.left,
+ text: TextSpan(
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.label(context).copyWith(
+ fontSize: 12.0,
),
- ]
- : [
- const TextSpan(
+ children: infoToggle
+ ? [
+ const TextSpan(
+ text:
+ "Exchange data preloaded for a seamless experience."),
+ const TextSpan(
+ text:
+ "\n\nCoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency)."),
+ TextSpan(
text:
- "Exchange data not preloaded (slower experience)."),
- const TextSpan(
+ "\n\nRecommended for most crypto users.",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall600(
+ context)
+ : TextStyle(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ]
+ : [
+ const TextSpan(
+ text:
+ "Exchange data not preloaded (slower experience)."),
+ const TextSpan(
+ text:
+ "\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."),
+ TextSpan(
text:
- "\n\nCoinGecko disabled (price changes not shown, no wallet value shown in other currencies)."),
- TextSpan(
- text:
- "\n\nRecommended for the privacy conscious.",
- style: TextStyle(
- color: Theme.of(context)
- .extension()!
- .textDark,
- fontWeight: FontWeight.w600,
+ "\n\nRecommended for the privacy conscious.",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall600(
+ context)
+ : TextStyle(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ fontWeight: FontWeight.w600,
+ ),
),
- ),
- ],
+ ],
+ ),
),
),
),
),
- ),
- const Spacer(
- flex: 4,
- ),
- Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 16,
- ),
- child: Row(
- children: [
- Expanded(
- child: ContinueButton(
- isDesktop: isDesktop,
- label: !widget.isSettings ? "Continue" : "Save changes",
- onPressed: () {
- ref.read(prefsChangeNotifierProvider).externalCalls =
- isEasy;
+ if (!isDesktop)
+ const Spacer(
+ flex: 4,
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 32,
+ ),
+ Padding(
+ padding: isDesktop
+ ? const EdgeInsets.all(0)
+ : const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 16,
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: PrimaryButton(
+ label:
+ !widget.isSettings ? "Continue" : "Save changes",
+ onPressed: () {
+ ref
+ .read(prefsChangeNotifierProvider)
+ .externalCalls = isEasy;
- DB.instance
- .put(
- boxName: DB.boxNamePrefs,
- key: "externalCalls",
- value: isEasy)
- .then((_) {
- if (isEasy) {
- unawaited(
- ExchangeDataLoadingService().loadAll(ref));
- ref
- .read(priceAnd24hChangeNotifierProvider)
- .start(true);
- }
- });
- if (!widget.isSettings) {
- if (isDesktop) {
- Navigator.of(context).pushNamed(
- CreatePasswordView.routeName,
- );
+ DB.instance
+ .put(
+ boxName: DB.boxNamePrefs,
+ key: "externalCalls",
+ value: isEasy)
+ .then((_) {
+ if (isEasy) {
+ unawaited(
+ ExchangeDataLoadingService().loadAll(ref));
+ ref
+ .read(priceAnd24hChangeNotifierProvider)
+ .start(true);
+ }
+ });
+ if (!widget.isSettings) {
+ if (isDesktop) {
+ Navigator.of(context).pushNamed(
+ CreatePasswordView.routeName,
+ );
+ } else {
+ Navigator.of(context).pushNamed(
+ CreatePinView.routeName,
+ );
+ }
} else {
- Navigator.of(context).pushNamed(
- CreatePinView.routeName,
- );
+ Navigator.pop(context);
}
- } else {
- Navigator.pop(context);
- }
- },
+ },
+ ),
),
- ),
- ],
+ ],
+ ),
),
- ),
- ],
+ if (isDesktop)
+ const SizedBox(
+ height: kDesktopAppBarHeight,
+ ),
+ ],
+ ),
),
),
),
@@ -225,8 +269,11 @@ class PrivacyToggle extends StatefulWidget {
class _PrivacyToggleState extends State {
late bool externalCallsEnabled;
+ late final bool isDesktop;
+
@override
void initState() {
+ isDesktop = Util.isDesktop;
// initial toggle state
externalCallsEnabled = widget.externalCallsEnabled;
super.initState();
@@ -269,24 +316,39 @@ class _PrivacyToggleState extends State {
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
+ if (isDesktop)
+ const SizedBox(
+ height: 10,
+ ),
SvgPicture.asset(
Assets.svg.personaEasy,
- width: 140,
- height: 140,
+ width: isDesktop ? 120 : 140,
+ height: isDesktop ? 120 : 140,
),
+ if (isDesktop)
+ const SizedBox(
+ height: 12,
+ ),
Center(
- child: Text(
- "Easy Crypto",
- style: STextStyles.label(context).copyWith(
- fontWeight: FontWeight.bold,
+ child: Text(
+ "Easy Crypto",
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.label700(context),
),
- )),
+ ),
Center(
child: Text(
"Recommended",
- style: STextStyles.label(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.label(context),
),
),
+ if (isDesktop)
+ const SizedBox(
+ height: 12,
+ ),
],
),
if (externalCallsEnabled)
@@ -359,25 +421,39 @@ class _PrivacyToggleState extends State {
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
+ if (isDesktop)
+ const SizedBox(
+ height: 10,
+ ),
SvgPicture.asset(
Assets.svg.personaIncognito,
- width: 140,
- height: 140,
+ width: isDesktop ? 120 : 140,
+ height: isDesktop ? 120 : 140,
),
+ if (isDesktop)
+ const SizedBox(
+ height: 12,
+ ),
Center(
child: Text(
"Incognito",
- style: STextStyles.label(context).copyWith(
- fontWeight: FontWeight.bold,
- ),
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.label700(context),
),
),
Center(
child: Text(
"Privacy conscious",
- style: STextStyles.label(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.label(context),
),
),
+ if (isDesktop)
+ const SizedBox(
+ height: 12,
+ ),
],
),
if (!externalCallsEnabled)
@@ -418,49 +494,49 @@ class _PrivacyToggleState extends State {
}
}
-class ContinueButton extends ConsumerWidget {
- const ContinueButton({
- Key? key,
- required this.isDesktop,
- required this.onPressed,
- required this.label,
- }) : super(key: key);
-
- final String label;
- final bool isDesktop;
- final VoidCallback onPressed;
-
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- if (isDesktop) {
- return SizedBox(
- width: 328,
- height: 70,
- child: TextButton(
- style: Theme.of(context)
- .extension()!
- .getPrimaryEnabledButtonColor(context),
- onPressed: onPressed,
- child: Text(
- label,
- style: STextStyles.button(context).copyWith(fontSize: 20),
- ),
- ),
- );
- } else {
- return TextButton(
- style: Theme.of(context)
- .extension()!
- .getPrimaryEnabledButtonColor(context),
- onPressed: onPressed,
- child: Text(
- label,
- style: STextStyles.button(context),
- ),
- );
- }
- }
-}
+// class ContinueButton extends ConsumerWidget {
+// const ContinueButton({
+// Key? key,
+// required this.isDesktop,
+// required this.onPressed,
+// required this.label,
+// }) : super(key: key);
+//
+// final String label;
+// final bool isDesktop;
+// final VoidCallback onPressed;
+//
+// @override
+// Widget build(BuildContext context, WidgetRef ref) {
+// if (isDesktop) {
+// return SizedBox(
+// width: 328,
+// height: 70,
+// child: TextButton(
+// style: Theme.of(context)
+// .extension()!
+// .getPrimaryEnabledButtonColor(context),
+// onPressed: onPressed,
+// child: Text(
+// label,
+// style: STextStyles.button(context).copyWith(fontSize: 20),
+// ),
+// ),
+// );
+// } else {
+// return TextButton(
+// style: Theme.of(context)
+// .extension()!
+// .getPrimaryEnabledButtonColor(context),
+// onPressed: onPressed,
+// child: Text(
+// label,
+// style: STextStyles.button(context),
+// ),
+// );
+// }
+// }
+// }
// class CustomRadio extends StatefulWidget {
// CustomRadio(this.upperCall, {Key? key}) : super(key: key);
diff --git a/lib/pages/wallet_view/sub_widgets/transactions_list.dart b/lib/pages/wallet_view/sub_widgets/transactions_list.dart
index 2246882ab4..d23d3082f6 100644
--- a/lib/pages/wallet_view/sub_widgets/transactions_list.dart
+++ b/lib/pages/wallet_view/sub_widgets/transactions_list.dart
@@ -10,6 +10,7 @@ import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
@@ -67,6 +68,65 @@ class _TransactionsListState extends ConsumerState {
);
}
+ Widget itemBuilder(
+ BuildContext context, Transaction tx, BorderRadius? radius) {
+ final matchingTrades = ref
+ .read(tradesServiceProvider)
+ .trades
+ .where((e) => e.payInTxid == tx.txid || e.payOutTxid == tx.txid);
+ if (tx.txType == "Sent" && matchingTrades.isNotEmpty) {
+ final trade = matchingTrades.first;
+ return Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).extension()!.popupBG,
+ borderRadius: radius,
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ TransactionCard(
+ // this may mess with combined firo transactions
+ key: Key(tx.toString()), //
+ transaction: tx,
+ walletId: widget.walletId,
+ ),
+ TradeCard(
+ // this may mess with combined firo transactions
+ key: Key(tx.toString() + trade.uuid), //
+ trade: trade,
+ onTap: () {
+ unawaited(
+ Navigator.of(context).pushNamed(
+ TradeDetailsView.routeName,
+ arguments: Tuple4(
+ trade.tradeId,
+ tx,
+ widget.walletId,
+ ref.read(managerProvider).walletName,
+ ),
+ ),
+ );
+ },
+ )
+ ],
+ ),
+ );
+ } else {
+ return Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).extension()!.popupBG,
+ borderRadius: radius,
+ ),
+ child: TransactionCard(
+ // this may mess with combined firo transactions
+ key: Key(tx.toString()), //
+ transaction: tx,
+ walletId: widget.walletId,
+ ),
+ );
+ }
+ }
+
@override
void initState() {
managerProvider = widget.managerProvider;
@@ -119,77 +179,42 @@ class _TransactionsListState extends ConsumerState {
unawaited(ref.read(managerProvider).refresh());
}
},
- child: ListView.builder(
- itemCount: list.length,
- itemBuilder: (context, index) {
- BorderRadius? radius;
- if (index == list.length - 1) {
- radius = _borderRadiusLast;
- } else if (index == 0) {
- radius = _borderRadiusFirst;
- }
- final tx = list[index];
-
- final matchingTrades = ref
- .read(tradesServiceProvider)
- .trades
- .where((e) =>
- e.payInTxid == tx.txid || e.payOutTxid == tx.txid);
- if (tx.txType == "Sent" && matchingTrades.isNotEmpty) {
- final trade = matchingTrades.first;
- return Container(
- decoration: BoxDecoration(
- color:
- Theme.of(context).extension()!.popupBG,
- borderRadius: radius,
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TransactionCard(
- // this may mess with combined firo transactions
- key: Key(tx.toString()), //
- transaction: tx,
- walletId: widget.walletId,
- ),
- TradeCard(
- // this may mess with combined firo transactions
- key: Key(tx.toString() + trade.uuid), //
- trade: trade,
- onTap: () {
- unawaited(
- Navigator.of(context).pushNamed(
- TradeDetailsView.routeName,
- arguments: Tuple4(
- trade.tradeId,
- tx,
- widget.walletId,
- ref.read(managerProvider).walletName,
- ),
- ),
- );
- },
- )
- ],
- ),
- );
- } else {
- return Container(
- decoration: BoxDecoration(
- color:
- Theme.of(context).extension()!.popupBG,
- borderRadius: radius,
- ),
- child: TransactionCard(
- // this may mess with combined firo transactions
- key: Key(tx.toString()), //
- transaction: tx,
- walletId: widget.walletId,
- ),
- );
- }
- },
- ),
+ child: Util.isDesktop
+ ? ListView.separated(
+ itemBuilder: (context, index) {
+ BorderRadius? radius;
+ if (index == list.length - 1) {
+ radius = _borderRadiusLast;
+ } else if (index == 0) {
+ radius = _borderRadiusFirst;
+ }
+ final tx = list[index];
+ return itemBuilder(context, tx, radius);
+ },
+ separatorBuilder: (context, index) {
+ return Container(
+ width: double.infinity,
+ height: 2,
+ color: Theme.of(context)
+ .extension()!
+ .background,
+ );
+ },
+ itemCount: list.length,
+ )
+ : ListView.builder(
+ itemCount: list.length,
+ itemBuilder: (context, index) {
+ BorderRadius? radius;
+ if (index == list.length - 1) {
+ radius = _borderRadiusLast;
+ } else if (index == 0) {
+ radius = _borderRadiusFirst;
+ }
+ final tx = list[index];
+ return itemBuilder(context, tx, radius);
+ },
+ ),
);
}
},
diff --git a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart
index 7faae106f0..603b72338f 100644
--- a/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart
+++ b/lib/pages/wallet_view/sub_widgets/wallet_refresh_button.dart
@@ -10,6 +10,7 @@ import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.dart';
/// [eventBus] should only be set during testing
class WalletRefreshButton extends ConsumerStatefulWidget {
@@ -70,7 +71,7 @@ class _RefreshButtonState extends ConsumerState
_spinController?.stop();
break;
case WalletSyncStatus.syncing:
- _spinController?.repeat();
+ unawaited(_spinController?.repeat());
break;
}
}
@@ -92,10 +93,15 @@ class _RefreshButtonState extends ConsumerState
@override
Widget build(BuildContext context) {
+ final isDesktop = Util.isDesktop;
+
return SizedBox(
- height: 36,
- width: 36,
+ height: isDesktop ? 22 : 36,
+ width: isDesktop ? 22 : 36,
child: MaterialButton(
+ color: isDesktop
+ ? Theme.of(context).extension()!.buttonBackSecondary
+ : null,
splashColor: Theme.of(context).extension()!.highlight,
onPressed: () {
final managerProvider = ref
@@ -110,6 +116,9 @@ class _RefreshButtonState extends ConsumerState
.then((_) => _spinController?.stop());
}
},
+ elevation: 0,
+ highlightElevation: 0,
+ hoverElevation: 0,
padding: EdgeInsets.zero,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
@@ -121,9 +130,13 @@ class _RefreshButtonState extends ConsumerState
turns: _spinAnimation,
child: SvgPicture.asset(
Assets.svg.arrowRotate,
- width: 24,
- height: 24,
- color: Theme.of(context).extension()!.textFavoriteCard,
+ width: isDesktop ? 12 : 24,
+ height: isDesktop ? 12 : 24,
+ color: isDesktop
+ ? Theme.of(context)
+ .extension()!
+ .textFieldDefaultSearchIconRight
+ : Theme.of(context).extension()!.textFavoriteCard,
),
),
),
diff --git a/lib/pages/wallet_view/transaction_views/all_transactions_view.dart b/lib/pages/wallet_view/transaction_views/all_transactions_view.dart
index 4194a7307a..8604ae721f 100644
--- a/lib/pages/wallet_view/transaction_views/all_transactions_view.dart
+++ b/lib/pages/wallet_view/transaction_views/all_transactions_view.dart
@@ -1,18 +1,31 @@
+import 'dart:async';
+
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/transaction_filter.dart';
+import 'package:stackwallet/notifications/show_flush_bar.dart';
+import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
+import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_search_filter_view.dart';
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.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/enums/flush_bar_type.dart';
+import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.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_dialog.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/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@@ -21,8 +34,6 @@ import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
import 'package:tuple/tuple.dart';
-import 'package:stackwallet/utilities/util.dart';
-
class AllTransactionsView extends ConsumerStatefulWidget {
const AllTransactionsView({
Key? key,
@@ -86,8 +97,12 @@ class _TransactionDetailsViewState extends ConsumerState {
}
final date = DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000);
- if (date.millisecondsSinceEpoch > filter.to.millisecondsSinceEpoch ||
- date.millisecondsSinceEpoch < filter.from.millisecondsSinceEpoch) {
+ if ((filter.to != null &&
+ date.millisecondsSinceEpoch >
+ filter.to!.millisecondsSinceEpoch) ||
+ (filter.from != null &&
+ date.millisecondsSinceEpoch <
+ filter.from!.millisecondsSinceEpoch)) {
return false;
}
@@ -115,13 +130,25 @@ class _TransactionDetailsViewState extends ConsumerState {
.isNotEmpty;
// check if address contains
- contains |= tx.address.contains(keyword);
+ contains |= tx.address.toLowerCase().contains(keyword);
// check if note contains
- contains |= notes[tx.txid] != null && notes[tx.txid]!.contains(keyword);
+ contains |= notes[tx.txid] != null &&
+ notes[tx.txid]!.toLowerCase().contains(keyword);
// check if txid contains
- contains |= tx.txid.contains(keyword);
+ contains |= tx.txid.toLowerCase().contains(keyword);
+
+ // check if subType contains
+ contains |=
+ tx.subType.isNotEmpty && tx.subType.toLowerCase().contains(keyword);
+
+ // check if txType contains
+ contains |= tx.txType.toLowerCase().contains(keyword);
+
+ // check if date contains
+ contains |=
+ Format.extractDateFrom(tx.timestamp).toLowerCase().contains(keyword);
return contains;
}
@@ -166,125 +193,239 @@ class _TransactionDetailsViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
- return 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(
- "Transactions",
- style: STextStyles.navBarTitle(context),
- ),
- actions: [
- Padding(
- padding: const EdgeInsets.only(
- top: 10,
- bottom: 10,
- right: 20,
- ),
- child: AspectRatio(
- aspectRatio: 1,
- child: AppBarIconButton(
- key: const Key("transactionSearchFilterViewButton"),
- size: 36,
- shadows: const [],
- color: Theme.of(context).extension()!.background,
- icon: SvgPicture.asset(
- Assets.svg.filter,
- color: Theme.of(context)
- .extension()!
- .accentColorDark,
- width: 20,
- height: 20,
- ),
- onPressed: () {
- Navigator.of(context).pushNamed(
- TransactionSearchFilterView.routeName,
- arguments: ref
- .read(walletsChangeNotifierProvider)
- .getManager(walletId)
- .coin,
- );
+ final isDesktop = Util.isDesktop;
+
+ return MasterScaffold(
+ background: Theme.of(context).extension()!.background,
+ isDesktop: isDesktop,
+ appBar: isDesktop
+ ? DesktopAppBar(
+ isCompactHeight: true,
+ background: Theme.of(context).extension()!.popupBG,
+ leading: 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: 12,
+ ),
+ Text(
+ "Transactions",
+ style: STextStyles.desktopH3(context),
+ ),
+ ],
+ ),
+ )
+ : 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(
+ "Transactions",
+ style: STextStyles.navBarTitle(context),
+ ),
+ actions: [
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 10,
+ bottom: 10,
+ right: 20,
+ ),
+ child: AspectRatio(
+ aspectRatio: 1,
+ child: AppBarIconButton(
+ key: const Key("transactionSearchFilterViewButton"),
+ size: 36,
+ shadows: const [],
+ color: Theme.of(context)
+ .extension()!
+ .background,
+ icon: SvgPicture.asset(
+ Assets.svg.filter,
+ color: Theme.of(context)
+ .extension()!
+ .accentColorDark,
+ width: 20,
+ height: 20,
+ ),
+ onPressed: () {
+ Navigator.of(context).pushNamed(
+ TransactionSearchFilterView.routeName,
+ arguments: ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(walletId)
+ .coin,
+ );
+ },
+ ),
+ ),
+ ),
+ ],
),
- ),
- ],
- ),
body: Padding(
- padding: const EdgeInsets.only(
- left: 12,
- top: 12,
- right: 12,
+ padding: EdgeInsets.only(
+ left: isDesktop ? 20 : 12,
+ top: isDesktop ? 20 : 12,
+ right: isDesktop ? 20 : 12,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(4),
- child: ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- controller: _searchController,
- focusNode: searchFieldFocusNode,
- onChanged: (value) {
- setState(() {
- _searchString = value;
- });
- },
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Search",
- searchFieldFocusNode,
- context,
- ).copyWith(
- prefixIcon: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 10,
- vertical: 16,
+ child: Row(
+ children: [
+ SizedBox(
+ width: isDesktop ? 570 : null,
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
),
- child: SvgPicture.asset(
- Assets.svg.search,
- width: 16,
- height: 16,
+ child: TextField(
+ autocorrect: !isDesktop,
+ enableSuggestions: !isDesktop,
+ controller: _searchController,
+ focusNode: searchFieldFocusNode,
+ onChanged: (value) {
+ setState(() {
+ _searchString = value;
+ });
+ },
+ style: isDesktop
+ ? STextStyles.desktopTextExtraSmall(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldActiveText,
+ height: 1.8,
+ )
+ : STextStyles.field(context),
+ decoration: standardInputDecoration(
+ "Search...",
+ searchFieldFocusNode,
+ context,
+ desktopMed: isDesktop,
+ ).copyWith(
+ prefixIcon: Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: isDesktop ? 12 : 10,
+ vertical: isDesktop ? 18 : 16,
+ ),
+ child: SvgPicture.asset(
+ Assets.svg.search,
+ width: isDesktop ? 20 : 16,
+ height: isDesktop ? 20 : 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 = "";
+ _searchString = "";
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ )
+ : 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 = "";
- });
- },
- ),
- ],
- ),
- ),
- )
- : null,
),
- ),
+ if (isDesktop)
+ const SizedBox(
+ width: 20,
+ ),
+ if (isDesktop)
+ SecondaryButton(
+ desktopMed: isDesktop,
+ width: 200,
+ label: "Filter",
+ icon: SvgPicture.asset(
+ Assets.svg.filter,
+ color: Theme.of(context)
+ .extension()!
+ .accentColorDark,
+ width: 20,
+ height: 20,
+ ),
+ onPressed: () {
+ final coin = ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(walletId)
+ .coin;
+ if (isDesktop) {
+ showDialog(
+ context: context,
+ builder: (context) {
+ return TransactionSearchFilterView(
+ coin: coin,
+ );
+ },
+ );
+ } else {
+ Navigator.of(context).pushNamed(
+ TransactionSearchFilterView.routeName,
+ arguments: coin,
+ );
+ }
+ },
+ ),
+ ],
),
),
+ if (isDesktop)
+ const SizedBox(
+ height: 8,
+ ),
+ if (isDesktop &&
+ ref.watch(transactionFilterProvider.state).state != null)
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 8,
+ ),
+ child: Row(
+ children: const [
+ TransactionFilterOptionBar(),
+ ],
+ ),
+ ),
const SizedBox(
height: 8,
),
@@ -317,6 +458,7 @@ class _TransactionDetailsViewState extends ConsumerState {
final monthlyList = groupTransactionsByMonth(searched);
return ListView.builder(
+ primary: isDesktop ? false : null,
itemCount: monthlyList.length,
itemBuilder: (_, index) {
final month = monthlyList[index];
@@ -336,21 +478,48 @@ class _TransactionDetailsViewState extends ConsumerState {
const SizedBox(
height: 12,
),
- RoundedWhiteContainer(
- padding: const EdgeInsets.all(0),
- child: Column(
- children: [
- ...month.item2.map(
- (tx) => TransactionCard(
+ if (isDesktop)
+ RoundedWhiteContainer(
+ padding: const EdgeInsets.all(0),
+ child: ListView.separated(
+ shrinkWrap: true,
+ primary: false,
+ separatorBuilder: (context, _) =>
+ Container(
+ height: 1,
+ color: Theme.of(context)
+ .extension()!
+ .background,
+ ),
+ itemCount: month.item2.length,
+ itemBuilder: (context, index) =>
+ Padding(
+ padding: const EdgeInsets.all(4),
+ child: DesktopTransactionCardRow(
key: Key(
- "transactionCard_key_${tx.txid}"),
- transaction: tx,
+ "transactionCard_key_${month.item2[index].txid}"),
+ transaction: month.item2[index],
walletId: walletId,
),
),
- ],
+ ),
+ ),
+ if (!isDesktop)
+ RoundedWhiteContainer(
+ padding: const EdgeInsets.all(0),
+ child: Column(
+ children: [
+ ...month.item2.map(
+ (tx) => TransactionCard(
+ key: Key(
+ "transactionCard_key_${tx.txid}"),
+ transaction: tx,
+ walletId: walletId,
+ ),
+ ),
+ ],
+ ),
),
- ),
],
),
);
@@ -371,3 +540,445 @@ class _TransactionDetailsViewState extends ConsumerState {
);
}
}
+
+class TransactionFilterOptionBar extends ConsumerStatefulWidget {
+ const TransactionFilterOptionBar({Key? key}) : super(key: key);
+
+ @override
+ ConsumerState createState() =>
+ _TransactionFilterOptionBarState();
+}
+
+class _TransactionFilterOptionBarState
+ extends ConsumerState {
+ final List items = [];
+ TransactionFilter? _filter;
+
+ @override
+ void initState() {
+ _filter = ref.read(transactionFilterProvider.state).state;
+
+ if (_filter != null) {
+ if (_filter!.sent) {
+ const label = "Sent";
+ final item = TransactionFilterOptionBarItem(
+ label: label,
+ onPressed: (s) {
+ items.removeWhere((e) => e.label == label);
+ if (items.isEmpty) {
+ ref.read(transactionFilterProvider.state).state = null;
+ } else {
+ ref.read(transactionFilterProvider.state).state =
+ ref.read(transactionFilterProvider.state).state?.copyWith(
+ sent: false,
+ );
+ setState(() {});
+ }
+ },
+ );
+ items.add(item);
+ }
+ if (_filter!.received) {
+ const label = ("Received");
+ final item = TransactionFilterOptionBarItem(
+ label: label,
+ onPressed: (s) {
+ items.removeWhere((e) => e.label == label);
+ if (items.isEmpty) {
+ ref.read(transactionFilterProvider.state).state = null;
+ } else {
+ ref.read(transactionFilterProvider.state).state =
+ ref.read(transactionFilterProvider.state).state?.copyWith(
+ received: false,
+ );
+ setState(() {});
+ }
+ },
+ );
+ items.add(item);
+ }
+
+ if (_filter!.to != null) {
+ final label = _filter!.from.toString();
+ final item = TransactionFilterOptionBarItem(
+ label: label,
+ onPressed: (s) {
+ items.removeWhere((e) => e.label == label);
+ if (items.isEmpty) {
+ ref.read(transactionFilterProvider.state).state = null;
+ } else {
+ ref.read(transactionFilterProvider.state).state =
+ ref.read(transactionFilterProvider.state).state?.copyWith(
+ to: null,
+ );
+ setState(() {});
+ }
+ },
+ );
+ items.add(item);
+ }
+ if (_filter!.from != null) {
+ final label2 = _filter!.to.toString();
+ final item2 = TransactionFilterOptionBarItem(
+ label: label2,
+ onPressed: (s) {
+ items.removeWhere((e) => e.label == label2);
+ if (items.isEmpty) {
+ ref.read(transactionFilterProvider.state).state = null;
+ } else {
+ ref.read(transactionFilterProvider.state).state =
+ ref.read(transactionFilterProvider.state).state?.copyWith(
+ from: null,
+ );
+ setState(() {});
+ }
+ },
+ );
+ items.add(item2);
+ }
+
+ if (_filter!.amount != null) {
+ final label = _filter!.amount!.toString();
+ final item = TransactionFilterOptionBarItem(
+ label: label,
+ onPressed: (s) {
+ items.removeWhere((e) => e.label == label);
+ if (items.isEmpty) {
+ ref.read(transactionFilterProvider.state).state = null;
+ } else {
+ ref.read(transactionFilterProvider.state).state =
+ ref.read(transactionFilterProvider.state).state?.copyWith(
+ amount: null,
+ );
+ setState(() {});
+ }
+ },
+ );
+ items.add(item);
+ }
+ if (_filter!.keyword.isNotEmpty) {
+ final label = _filter!.keyword;
+ final item = TransactionFilterOptionBarItem(
+ label: label,
+ onPressed: (s) {
+ items.removeWhere((e) => e.label == label);
+ if (items.isEmpty) {
+ ref.read(transactionFilterProvider.state).state = null;
+ } else {
+ ref.read(transactionFilterProvider.state).state =
+ ref.read(transactionFilterProvider.state).state?.copyWith(
+ keyword: "",
+ );
+ setState(() {});
+ }
+ },
+ );
+ items.add(item);
+ }
+ }
+
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 32,
+ child: ListView.separated(
+ primary: false,
+ scrollDirection: Axis.horizontal,
+ shrinkWrap: true,
+ itemCount: items.length,
+ separatorBuilder: (_, __) => const SizedBox(
+ width: 16,
+ ),
+ itemBuilder: (context, index) => items[index],
+ ),
+ );
+ }
+}
+
+class TransactionFilterOptionBarItem extends StatelessWidget {
+ const TransactionFilterOptionBarItem({
+ Key? key,
+ required this.label,
+ this.onPressed,
+ }) : super(key: key);
+
+ final String label;
+ final void Function(String)? onPressed;
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: () => onPressed?.call(label),
+ child: Container(
+ height: 32,
+ decoration: BoxDecoration(
+ color:
+ Theme.of(context).extension()!.buttonBackSecondary,
+ borderRadius: BorderRadius.circular(1000)),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 14,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Center(
+ child: Padding(
+ padding: const EdgeInsets.only(top: 2),
+ child: Text(
+ label,
+ textAlign: TextAlign.center,
+ style: STextStyles.labelExtraExtraSmall(context).copyWith(
+ color:
+ Theme.of(context).extension()!.textDark,
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ XIcon(
+ width: 16,
+ height: 16,
+ color: Theme.of(context).extension()!.textDark,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class DesktopTransactionCardRow extends ConsumerStatefulWidget {
+ const DesktopTransactionCardRow({
+ Key? key,
+ required this.transaction,
+ required this.walletId,
+ }) : super(key: key);
+
+ final Transaction transaction;
+ final String walletId;
+
+ @override
+ ConsumerState createState() =>
+ _DesktopTransactionCardRowState();
+}
+
+class _DesktopTransactionCardRowState
+ extends ConsumerState {
+ late final Transaction _transaction;
+ late final String walletId;
+
+ String whatIsIt(String type, Coin coin) {
+ if (coin == Coin.epicCash && _transaction.slateId == null) {
+ return "Restored Funds";
+ }
+
+ if (_transaction.subType == "mint") {
+ if (_transaction.confirmedStatus) {
+ return "Anonymized";
+ } else {
+ return "Anonymizing";
+ }
+ }
+
+ if (type == "Received") {
+ if (_transaction.confirmedStatus) {
+ return "Received";
+ } else {
+ return "Receiving";
+ }
+ } else if (type == "Sent") {
+ if (_transaction.confirmedStatus) {
+ return "Sent";
+ } else {
+ return "Sending";
+ }
+ } else {
+ return type;
+ }
+ }
+
+ @override
+ void initState() {
+ walletId = widget.walletId;
+ _transaction = widget.transaction;
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final locale = ref.watch(
+ localeServiceChangeNotifierProvider.select((value) => value.locale));
+ final manager = ref.watch(walletsChangeNotifierProvider
+ .select((value) => value.getManager(walletId)));
+
+ final baseCurrency = ref
+ .watch(prefsChangeNotifierProvider.select((value) => value.currency));
+
+ final coin = manager.coin;
+
+ final price = ref
+ .watch(priceAnd24hChangeNotifierProvider
+ .select((value) => value.getPrice(coin)))
+ .item1;
+
+ late final String prefix;
+ if (Util.isDesktop) {
+ if (_transaction.txType == "Sent") {
+ prefix = "-";
+ } else if (_transaction.txType == "Received") {
+ prefix = "+";
+ }
+ } else {
+ prefix = "";
+ }
+
+ return Material(
+ color: Theme.of(context).extension()!.popupBG,
+ elevation: 0,
+ shape: RoundedRectangleBorder(
+ borderRadius:
+ BorderRadius.circular(Constants.size.circularBorderRadius),
+ ),
+ child: RawMaterialButton(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ onPressed: () async {
+ if (coin == Coin.epicCash && _transaction.slateId == null) {
+ unawaited(
+ showFloatingFlushBar(
+ context: context,
+ message:
+ "Restored Epic funds from your Seed have no Data.\nUse Stack Backup to keep your transaction history.",
+ type: FlushBarType.warning,
+ duration: const Duration(seconds: 5),
+ ),
+ );
+ return;
+ }
+ if (Util.isDesktop) {
+ await showDialog(
+ context: context,
+ builder: (context) => DesktopDialog(
+ maxHeight: MediaQuery.of(context).size.height - 64,
+ maxWidth: 580,
+ child: TransactionDetailsView(
+ transaction: _transaction,
+ coin: coin,
+ walletId: walletId,
+ ),
+ ),
+ );
+ } else {
+ unawaited(
+ Navigator.of(context).pushNamed(
+ TransactionDetailsView.routeName,
+ arguments: Tuple3(
+ _transaction,
+ coin,
+ walletId,
+ ),
+ ),
+ );
+ }
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 10,
+ horizontal: 16,
+ ),
+ child: Row(
+ children: [
+ TxIcon(transaction: _transaction),
+ const SizedBox(
+ width: 12,
+ ),
+ Expanded(
+ flex: 3,
+ child: Text(
+ _transaction.isCancelled
+ ? "Cancelled"
+ : whatIsIt(_transaction.txType, coin),
+ style:
+ STextStyles.desktopTextExtraExtraSmall(context).copyWith(
+ color: Theme.of(context).extension()!.textDark,
+ ),
+ ),
+ ),
+ Expanded(
+ flex: 4,
+ child: Text(
+ Format.extractDateFrom(_transaction.timestamp),
+ style: STextStyles.label(context),
+ ),
+ ),
+ Expanded(
+ flex: 6,
+ child: Builder(
+ builder: (_) {
+ final amount = coin == Coin.monero
+ ? (_transaction.amount ~/ 10000)
+ : coin == Coin.wownero
+ ? (_transaction.amount ~/ 1000)
+ : _transaction.amount;
+ return Text(
+ "$prefix${Format.satoshiAmountToPrettyString(amount, locale)} ${coin.ticker}",
+ style: STextStyles.desktopTextExtraExtraSmall(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ ),
+ );
+ },
+ ),
+ ),
+ if (ref.watch(prefsChangeNotifierProvider
+ .select((value) => value.externalCalls)))
+ Expanded(
+ flex: 4,
+ child: Builder(
+ builder: (_) {
+ // TODO: modify Format. to take optional Coin parameter so this type oif check isn't done in ui
+ int value = _transaction.amount;
+ if (coin == Coin.monero) {
+ value = (value ~/ 10000);
+ } else if (coin == Coin.wownero) {
+ value = (value ~/ 1000);
+ }
+
+ return Text(
+ "$prefix${Format.localizedStringAsFixed(
+ value: Format.satoshisToAmount(value) * price,
+ locale: locale,
+ decimalPlaces: 2,
+ )} $baseCurrency",
+ style: STextStyles.desktopTextExtraExtraSmall(context),
+ );
+ },
+ ),
+ ),
+ SvgPicture.asset(
+ Assets.svg.circleInfo,
+ width: 20,
+ height: 20,
+ color:
+ Theme.of(context).extension()!.textSubtitle2,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pages/wallet_view/transaction_views/edit_note_view.dart b/lib/pages/wallet_view/transaction_views/edit_note_view.dart
index b811dc62db..4baaaffc9b 100644
--- a/lib/pages/wallet_view/transaction_views/edit_note_view.dart
+++ b/lib/pages/wallet_view/transaction_views/edit_note_view.dart
@@ -4,13 +4,14 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
+import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
+import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
-import 'package:stackwallet/utilities/util.dart';
-
class EditNoteView extends ConsumerStatefulWidget {
const EditNoteView({
Key? key,
@@ -33,8 +34,11 @@ class _EditNoteViewState extends ConsumerState {
late final TextEditingController _noteController;
final noteFieldFocusNode = FocusNode();
+ late final bool isDesktop;
+
@override
void initState() {
+ isDesktop = Util.isDesktop;
_noteController = TextEditingController();
_noteController.text = widget.note;
super.initState();
@@ -50,29 +54,178 @@ class _EditNoteViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
return 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(
- "Edit note",
- style: STextStyles.navBarTitle(context),
- ),
+ backgroundColor: isDesktop
+ ? Colors.transparent
+ : Theme.of(context).extension()!.background,
+ appBar: isDesktop
+ ? null
+ : 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(
+ "Edit note",
+ style: STextStyles.navBarTitle(context),
+ ),
+ ),
+ body: MobileEditNoteScaffold(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ if (isDesktop)
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ bottom: 12,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Edit note",
+ style: STextStyles.desktopH3(context),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ ),
+ Padding(
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(
+ horizontal: 32,
+ )
+ : const EdgeInsets.all(0),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ autocorrect: Util.isDesktop ? false : true,
+ enableSuggestions: Util.isDesktop ? false : true,
+ controller: _noteController,
+ style: isDesktop
+ ? STextStyles.desktopTextExtraSmall(context).copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldActiveText,
+ height: 1.8,
+ )
+ : STextStyles.field(context),
+ focusNode: noteFieldFocusNode,
+ decoration: standardInputDecoration(
+ "Note",
+ noteFieldFocusNode,
+ context,
+ desktopMed: isDesktop,
+ ).copyWith(
+ contentPadding: isDesktop
+ ? const EdgeInsets.only(
+ left: 16,
+ top: 11,
+ bottom: 12,
+ right: 5,
+ )
+ : null,
+ suffixIcon: _noteController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ _noteController.text = "";
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ )
+ : null,
+ ),
+ ),
+ ),
+ ),
+ // if (!isDesktop)
+ const Spacer(),
+ if (isDesktop)
+ Padding(
+ padding: const EdgeInsets.all(32),
+ child: PrimaryButton(
+ label: "Save",
+ onPressed: () async {
+ await ref
+ .read(
+ notesServiceChangeNotifierProvider(widget.walletId))
+ .editOrAddNote(
+ txid: widget.txid,
+ note: _noteController.text,
+ );
+ if (mounted) {
+ Navigator.of(context).pop();
+ }
+ },
+ ),
+ ),
+ if (!isDesktop)
+ TextButton(
+ onPressed: () async {
+ await ref
+ .read(notesServiceChangeNotifierProvider(widget.walletId))
+ .editOrAddNote(
+ txid: widget.txid,
+ note: _noteController.text,
+ );
+ if (mounted) {
+ Navigator.of(context).pop();
+ }
+ },
+ style: Theme.of(context)
+ .extension()!
+ .getPrimaryEnabledButtonColor(context),
+ child: Text(
+ "Save",
+ style: STextStyles.button(context),
+ ),
+ )
+ ],
),
- body: Padding(
- padding: const EdgeInsets.all(12),
- child: LayoutBuilder(builder: (context, constraints) {
+ ),
+ );
+ }
+}
+
+class MobileEditNoteScaffold extends StatelessWidget {
+ const MobileEditNoteScaffold({
+ Key? key,
+ required this.child,
+ }) : super(key: key);
+
+ final Widget child;
+
+ @override
+ Widget build(BuildContext context) {
+ if (Util.isDesktop) {
+ return child;
+ } else {
+ return Padding(
+ padding: const EdgeInsets.all(12),
+ child: LayoutBuilder(
+ builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
@@ -81,75 +234,14 @@ class _EditNoteViewState extends ConsumerState {
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- controller: _noteController,
- style: STextStyles.field(context),
- focusNode: noteFieldFocusNode,
- decoration: standardInputDecoration(
- "Note",
- noteFieldFocusNode,
- context,
- ).copyWith(
- suffixIcon: _noteController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- _noteController.text = "";
- });
- },
- ),
- ],
- ),
- ),
- )
- : null,
- ),
- ),
- ),
- const Spacer(),
- TextButton(
- onPressed: () async {
- await ref
- .read(notesServiceChangeNotifierProvider(
- widget.walletId))
- .editOrAddNote(
- txid: widget.txid,
- note: _noteController.text,
- );
- if (mounted) {
- Navigator.of(context).pop();
- }
- },
- style: Theme.of(context)
- .extension()!
- .getPrimaryEnabledButtonColor(context),
- child: Text(
- "Save",
- style: STextStyles.button(context),
- ),
- )
- ],
- ),
+ child: child,
),
),
),
);
- }),
- ));
+ },
+ ),
+ );
+ }
}
}
diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart
index 8d7aac85f3..d1e415b261 100644
--- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart
+++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:decimal/decimal.dart';
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/models.dart';
@@ -23,8 +24,13 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
+import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
+import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
+import 'package:stackwallet/widgets/icon_widgets/copy_icon.dart';
+import 'package:stackwallet/widgets/icon_widgets/pencil_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:tuple/tuple.dart';
@@ -51,6 +57,7 @@ class TransactionDetailsView extends ConsumerStatefulWidget {
class _TransactionDetailsViewState
extends ConsumerState {
+ late final bool isDesktop;
late Transaction _transaction;
late final String walletId;
@@ -63,6 +70,7 @@ class _TransactionDetailsViewState
@override
void initState() {
+ isDesktop = Util.isDesktop;
_transaction = widget.transaction;
walletId = widget.walletId;
@@ -74,7 +82,7 @@ class _TransactionDetailsViewState
_transaction.subType == "mint") {
amountPrefix = "";
} else {
- amountPrefix = _transaction.txType.toLowerCase() == "sent" ? "- " : "+ ";
+ amountPrefix = _transaction.txType.toLowerCase() == "sent" ? "-" : "+";
}
// if (coin == Coin.firo || coin == Coin.firoTestNet) {
@@ -205,521 +213,961 @@ class _TransactionDetailsViewState
@override
Widget build(BuildContext context) {
return 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(Duration(milliseconds: 50));
- // }
- Navigator.of(context).pop();
- },
- ),
- title: Text(
- "Transaction details",
- style: STextStyles.navBarTitle(context),
- ),
- ),
+ backgroundColor: isDesktop
+ ? Colors.transparent
+ : Theme.of(context).extension()!.background,
+ appBar: isDesktop
+ ? null
+ : AppBar(
+ backgroundColor:
+ Theme.of(context).extension()!.background,
+ leading: AppBarBackButton(
+ onPressed: () async {
+ // if (FocusScope.of(context).hasFocus) {
+ // FocusScope.of(context).unfocus();
+ // await Future.delayed(Duration(milliseconds: 50));
+ // }
+ Navigator.of(context).pop();
+ },
+ ),
+ title: Text(
+ "Transaction details",
+ style: STextStyles.navBarTitle(context),
+ ),
+ ),
body: Padding(
- padding: const EdgeInsets.all(12),
- child: SingleChildScrollView(
- child: Padding(
- padding: const EdgeInsets.all(4),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ padding: isDesktop
+ ? const EdgeInsets.only(left: 32)
+ : const EdgeInsets.all(12),
+ child: Column(
+ children: [
+ if (isDesktop)
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Transaction details",
+ style: STextStyles.desktopH3(context),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Expanded(
+ child: Padding(
+ padding: isDesktop
+ ? const EdgeInsets.only(
+ right: 32,
+ bottom: 32,
+ )
+ : const EdgeInsets.all(0),
+ child: RoundedWhiteContainer(
+ borderColor: isDesktop
+ ? Theme.of(context).extension()!.background
+ : null,
+ padding: const EdgeInsets.all(0),
+ child: SingleChildScrollView(
+ primary: isDesktop ? false : null,
+ child: Padding(
+ padding: isDesktop
+ ? const EdgeInsets.all(0)
+ : const EdgeInsets.all(4),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
- SelectableText(
- "$amountPrefix${Format.localizedStringAsFixed(
- value: coin == Coin.monero
- ? (amount / 10000.toDecimal()).toDecimal()
- : coin == Coin.wownero
- ? (amount / 1000.toDecimal()).toDecimal()
- : amount,
- locale: ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale),
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(0)
+ : const EdgeInsets.all(12),
+ child: Container(
+ decoration: isDesktop
+ ? BoxDecoration(
+ color: Theme.of(context)
+ .extension()!
+ .background,
+ borderRadius: BorderRadius.vertical(
+ top: Radius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ )
+ : null,
+ child: Padding(
+ padding: isDesktop
+ ? const EdgeInsets.all(12)
+ : const EdgeInsets.all(0),
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ if (isDesktop)
+ Row(
+ children: [
+ TxIcon(
+ transaction: _transaction,
+ ),
+ const SizedBox(
+ width: 16,
+ ),
+ SelectableText(
+ _transaction.isCancelled
+ ? "Cancelled"
+ : whatIsIt(_transaction.txType),
+ style:
+ STextStyles.desktopTextMedium(
+ context),
+ ),
+ ],
+ ),
+ Column(
+ crossAxisAlignment: isDesktop
+ ? CrossAxisAlignment.end
+ : CrossAxisAlignment.start,
+ children: [
+ SelectableText(
+ "$amountPrefix${Format.localizedStringAsFixed(
+ value: coin == Coin.monero
+ ? (amount / 10000.toDecimal())
+ .toDecimal()
+ : coin == Coin.wownero
+ ? (amount /
+ 1000.toDecimal())
+ .toDecimal()
+ : amount,
+ locale: ref.watch(
+ localeServiceChangeNotifierProvider
+ .select(
+ (value) => value.locale),
+ ),
+ decimalPlaces:
+ Constants.decimalPlaces,
+ )} ${coin.ticker}",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.titleBold12(
+ context),
+ ),
+ const SizedBox(
+ height: 2,
+ ),
+ if (ref.watch(
+ prefsChangeNotifierProvider.select(
+ (value) =>
+ value.externalCalls)))
+ SelectableText(
+ "$amountPrefix${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : coin == Coin.wownero ? (amount / 1000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch(
+ localeServiceChangeNotifierProvider
+ .select((value) =>
+ value.locale),
+ ), decimalPlaces: 2)} ${ref.watch(
+ prefsChangeNotifierProvider
+ .select(
+ (value) => value.currency,
+ ),
+ )}",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(
+ context),
+ ),
+ ],
+ ),
+ if (!isDesktop)
+ TxIcon(
+ transaction: _transaction,
+ ),
+ ],
+ ),
),
- decimalPlaces: Constants.decimalPlaces,
- )} ${coin.ticker}",
- style: STextStyles.titleBold12(context),
- ),
- const SizedBox(
- height: 2,
+ ),
),
- if (ref.watch(prefsChangeNotifierProvider
- .select((value) => value.externalCalls)))
- SelectableText(
- "${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : coin == Coin.wownero ? (amount / 1000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale),
- ), decimalPlaces: 2)} ${ref.watch(
- prefsChangeNotifierProvider.select(
- (value) => value.currency,
+
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Status",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ // Flexible(
+ // child: FittedBox(
+ // fit: BoxFit.scaleDown,
+ // child:
+ SelectableText(
+ _transaction.isCancelled
+ ? "Cancelled"
+ : whatIsIt(_transaction.txType),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: _transaction.txType == "Sent"
+ ? Theme.of(context)
+ .extension()!
+ .accentColorOrange
+ : Theme.of(context)
+ .extension()!
+ .accentColorGreen,
+ )
+ : STextStyles.itemSubtitle12(context),
),
- )}",
- style: STextStyles.itemSubtitle(context),
+ // ),
+ // ),
+ ],
),
- ],
- ),
- TxIcon(
- transaction: _transaction,
- ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Status",
- style: STextStyles.itemSubtitle(context),
- ),
- // Flexible(
- // child: FittedBox(
- // fit: BoxFit.scaleDown,
- // child:
- SelectableText(
- _transaction.isCancelled
- ? "Cancelled"
- : whatIsIt(_transaction.txType),
- style: STextStyles.itemSubtitle12(context),
- ),
- // ),
- // ),
- ],
- ),
- ),
- if (!((coin == Coin.monero || coin == Coin.wownero) &&
- _transaction.txType.toLowerCase() == "sent") &&
- !((coin == Coin.firo || coin == Coin.firoTestNet) &&
- _transaction.subType == "mint"))
- const SizedBox(
- height: 12,
- ),
- if (!((coin == Coin.monero || coin == Coin.wownero) &&
- _transaction.txType.toLowerCase() == "sent") &&
- !((coin == Coin.firo || coin == Coin.firoTestNet) &&
- _transaction.subType == "mint"))
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- _transaction.txType.toLowerCase() == "sent"
- ? "Sent to"
- : "Received on",
- style: STextStyles.itemSubtitle(context),
- ),
- const SizedBox(
- height: 8,
- ),
- _transaction.txType.toLowerCase() == "received"
- ? FutureBuilder(
- future:
- fetchContactNameFor(_transaction.address),
- builder: (builderContext,
- AsyncSnapshot snapshot) {
- String addressOrContactName =
- _transaction.address;
- if (snapshot.connectionState ==
- ConnectionState.done &&
- snapshot.hasData) {
- addressOrContactName = snapshot.data!;
- }
- return SelectableText(
- addressOrContactName,
- style: STextStyles.itemSubtitle12(context),
- );
- },
- )
- : SelectableText(
- _transaction.address,
- style: STextStyles.itemSubtitle12(context),
- ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Note",
- style: STextStyles.itemSubtitle(context),
),
- GestureDetector(
- onTap: () {
- Navigator.of(context).pushNamed(
- EditNoteView.routeName,
- arguments: Tuple3(
- _transaction.txid,
- walletId,
- _note,
+ if (!((coin == Coin.monero || coin == Coin.wownero) &&
+ _transaction.txType.toLowerCase() ==
+ "sent") &&
+ !((coin == Coin.firo ||
+ coin == Coin.firoTestNet) &&
+ _transaction.subType == "mint"))
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
+ ),
+ if (!((coin == Coin.monero || coin == Coin.wownero) &&
+ _transaction.txType.toLowerCase() ==
+ "sent") &&
+ !((coin == Coin.firo ||
+ coin == Coin.firoTestNet) &&
+ _transaction.subType == "mint"))
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Column(
+ crossAxisAlignment:
+ CrossAxisAlignment.start,
+ children: [
+ Text(
+ _transaction.txType.toLowerCase() ==
+ "sent"
+ ? "Sent to"
+ : "Receiving address",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ _transaction.txType.toLowerCase() ==
+ "received"
+ ? FutureBuilder(
+ future: fetchContactNameFor(
+ _transaction.address),
+ builder: (builderContext,
+ AsyncSnapshot
+ snapshot) {
+ String addressOrContactName =
+ _transaction.address;
+ if (snapshot.connectionState ==
+ ConnectionState.done &&
+ snapshot.hasData) {
+ addressOrContactName =
+ snapshot.data!;
+ }
+ return SelectableText(
+ addressOrContactName,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(
+ context)
+ .extension<
+ StackColors>()!
+ .textDark,
+ )
+ : STextStyles
+ .itemSubtitle12(
+ context),
+ );
+ },
+ )
+ : SelectableText(
+ _transaction.address,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension<
+ StackColors>()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(
+ context),
+ ),
+ ],
+ ),
+ if (isDesktop)
+ IconCopyButton(
+ data: _transaction.address,
+ ),
+ ],
+ ),
+ ),
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
),
- );
- },
- child: Row(
+
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- SvgPicture.asset(
- Assets.svg.pencil,
- width: 10,
- height: 10,
- color: Theme.of(context)
- .extension()!
- .infoItemIcons,
+ Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Note",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ isDesktop
+ ? IconPencilButton(
+ onPressed: () {
+ showDialog(
+ context: context,
+ builder: (context) {
+ return DesktopDialog(
+ maxWidth: 580,
+ maxHeight: 360,
+ child: EditNoteView(
+ txid: _transaction.txid,
+ walletId: walletId,
+ note: _note,
+ ),
+ );
+ },
+ );
+ },
+ )
+ : GestureDetector(
+ onTap: () {
+ Navigator.of(context).pushNamed(
+ EditNoteView.routeName,
+ arguments: Tuple3(
+ _transaction.txid,
+ walletId,
+ _note,
+ ),
+ );
+ },
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ Assets.svg.pencil,
+ width: 10,
+ height: 10,
+ color: Theme.of(context)
+ .extension()!
+ .infoItemIcons,
+ ),
+ const SizedBox(
+ width: 4,
+ ),
+ Text(
+ "Edit",
+ style: STextStyles.link2(
+ context),
+ ),
+ ],
+ ),
+ ),
+ ],
),
const SizedBox(
- width: 4,
+ height: 8,
),
- Text(
- "Edit",
- style: STextStyles.link2(context),
+ FutureBuilder(
+ future: ref.watch(
+ notesServiceChangeNotifierProvider(
+ walletId)
+ .select((value) => value.getNoteFor(
+ txid: _transaction.txid))),
+ builder: (builderContext,
+ AsyncSnapshot snapshot) {
+ if (snapshot.connectionState ==
+ ConnectionState.done &&
+ snapshot.hasData) {
+ _note = snapshot.data ?? "";
+ }
+ return SelectableText(
+ _note,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(context),
+ );
+ },
),
],
),
),
- ],
- ),
- const SizedBox(
- height: 8,
- ),
- FutureBuilder(
- future: ref.watch(
- notesServiceChangeNotifierProvider(walletId).select(
- (value) =>
- value.getNoteFor(txid: _transaction.txid))),
- builder:
- (builderContext, AsyncSnapshot snapshot) {
- if (snapshot.connectionState ==
- ConnectionState.done &&
- snapshot.hasData) {
- _note = snapshot.data ?? "";
- }
- return SelectableText(
- _note,
- style: STextStyles.itemSubtitle12(context),
- );
- },
- ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Date",
- style: STextStyles.itemSubtitle(context),
- ),
- // Flexible(
- // child: FittedBox(
- // fit: BoxFit.scaleDown,
- // child:
- SelectableText(
- Format.extractDateFrom(_transaction.timestamp),
- style: STextStyles.itemSubtitle12(context),
- ),
- // ),
- // ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Transaction fee",
- style: STextStyles.itemSubtitle(context),
- ),
- // Flexible(
- // child: FittedBox(
- // fit: BoxFit.scaleDown,
- // child:
- SelectableText(
- showFeePending
- ? _transaction.confirmedStatus
- ? Format.localizedStringAsFixed(
- value: coin == Coin.monero
- ? (fee / 10000.toDecimal()).toDecimal()
- : coin == Coin.wownero
- ? (fee / 1000.toDecimal())
- .toDecimal()
- : fee,
- locale: ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale)),
- decimalPlaces: Constants.decimalPlaces)
- : "Pending"
- : Format.localizedStringAsFixed(
- value: coin == Coin.monero
- ? (fee / 10000.toDecimal()).toDecimal()
- : coin == Coin.wownero
- ? (fee / 1000.toDecimal()).toDecimal()
- : fee,
- locale: ref.watch(
- localeServiceChangeNotifierProvider
- .select((value) => value.locale)),
- decimalPlaces: Constants.decimalPlaces),
- style: STextStyles.itemSubtitle12(context),
- ),
- // ),
- // ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Block height",
- style: STextStyles.itemSubtitle(context),
- ),
- // Flexible(
- // child: FittedBox(
- // fit: BoxFit.scaleDown,
- // child:
- SelectableText(
- widget.coin != Coin.epicCash &&
- _transaction.confirmedStatus
- ? "${_transaction.height == 0 ? "Unknown" : _transaction.height}"
- : _transaction.confirmations > 0
- ? "${_transaction.height}"
- : "Pending",
- style: STextStyles.itemSubtitle12(context),
- ),
- // ),
- // ),
- ],
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Transaction ID",
- style: STextStyles.itemSubtitle(context),
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Date",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 2,
+ ),
+ if (isDesktop)
+ SelectableText(
+ Format.extractDateFrom(
+ _transaction.timestamp,
+ ),
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(
+ context),
+ ),
+ ],
+ ),
+ if (!isDesktop)
+ SelectableText(
+ Format.extractDateFrom(
+ _transaction.timestamp,
+ ),
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(context),
+ ),
+ if (isDesktop)
+ IconCopyButton(
+ data: Format.extractDateFrom(
+ _transaction.timestamp,
+ ),
+ ),
+ ],
+ ),
),
- ],
- ),
- const SizedBox(
- height: 8,
- ),
- // Flexible(
- // child: FittedBox(
- // fit: BoxFit.scaleDown,
- // child:
- SelectableText(
- _transaction.txid,
- style: STextStyles.itemSubtitle12(context),
- ),
- if (coin != Coin.epicCash)
- const SizedBox(
- height: 8,
- ),
- if (coin != Coin.epicCash)
- BlueTextButton(
- text: "Open in block explorer",
- onTap: () async {
- final uri = getBlockExplorerTransactionUrlFor(
- coin: coin,
- txid: _transaction.txid,
- );
-
- if (ref
- .read(prefsChangeNotifierProvider)
- .hideBlockExplorerWarning ==
- false) {
- final shouldContinue = await showExplorerWarning(
- "${uri.scheme}://${uri.host}");
-
- if (!shouldContinue) {
- return;
- }
- }
-
- // ref
- // .read(
- // shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = false;
- try {
- await launchUrl(
- uri,
- mode: LaunchMode.externalApplication,
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Builder(builder: (context) {
+ final feeString = showFeePending
+ ? _transaction.confirmedStatus
+ ? Format.localizedStringAsFixed(
+ value: coin == Coin.monero
+ ? (fee / 10000.toDecimal())
+ .toDecimal()
+ : coin == Coin.wownero
+ ? (fee / 1000.toDecimal())
+ .toDecimal()
+ : fee,
+ locale: ref.watch(
+ localeServiceChangeNotifierProvider
+ .select(
+ (value) => value.locale)),
+ decimalPlaces:
+ Constants.decimalPlaces)
+ : "Pending"
+ : Format.localizedStringAsFixed(
+ value: coin == Coin.monero
+ ? (fee / 10000.toDecimal())
+ .toDecimal()
+ : coin == Coin.wownero
+ ? (fee / 1000.toDecimal())
+ .toDecimal()
+ : fee,
+ locale: ref.watch(
+ localeServiceChangeNotifierProvider
+ .select((value) => value.locale)),
+ decimalPlaces: Constants.decimalPlaces);
+
+ return Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Column(
+ crossAxisAlignment:
+ CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Transaction fee",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 2,
+ ),
+ if (isDesktop)
+ SelectableText(
+ feeString,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(
+ context),
+ ),
+ ],
+ ),
+ if (!isDesktop)
+ SelectableText(
+ feeString,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(context),
+ ),
+ if (isDesktop) IconCopyButton(data: feeString)
+ ],
);
- } catch (_) {
- unawaited(showDialog(
- context: context,
- builder: (_) => StackOkDialog(
- title: "Could not open in block explorer",
- message:
- "Failed to open \"${uri.toString()}\"",
+ }),
+ ),
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
),
- ));
- } finally {
- // Future.delayed(
- // const Duration(seconds: 1),
- // () => ref
- // .read(
- // shouldShowLockscreenOnResumeStateProvider
- // .state)
- // .state = true,
- // );
- }
- },
- ),
- // ),
- // ),
- ],
- ),
- ),
- // if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
- // _transaction.subType == "mint")
- // const SizedBox(
- // height: 12,
- // ),
- // if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
- // _transaction.subType == "mint")
- // RoundedWhiteContainer(
- // child: Column(
- // crossAxisAlignment: CrossAxisAlignment.start,
- // children: [
- // Row(
- // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- // children: [
- // Text(
- // "Mint Transaction ID",
- // style: STextStyles.itemSubtitle(context),
- // ),
- // ],
- // ),
- // const SizedBox(
- // height: 8,
- // ),
- // // Flexible(
- // // child: FittedBox(
- // // fit: BoxFit.scaleDown,
- // // child:
- // SelectableText(
- // _transaction.otherData ?? "Unknown",
- // style: STextStyles.itemSubtitle12(context),
- // ),
- // // ),
- // // ),
- // const SizedBox(
- // height: 8,
- // ),
- // BlueTextButton(
- // text: "Open in block explorer",
- // onTap: () async {
- // final uri = getBlockExplorerTransactionUrlFor(
- // coin: coin,
- // txid: _transaction.otherData ?? "Unknown",
- // );
- // // ref
- // // .read(
- // // shouldShowLockscreenOnResumeStateProvider
- // // .state)
- // // .state = false;
- // try {
- // await launchUrl(
- // uri,
- // mode: LaunchMode.externalApplication,
- // );
- // } catch (_) {
- // unawaited(showDialog(
- // context: context,
- // builder: (_) => StackOkDialog(
- // title: "Could not open in block explorer",
- // message:
- // "Failed to open \"${uri.toString()}\"",
- // ),
- // ));
- // } finally {
- // // Future.delayed(
- // // const Duration(seconds: 1),
- // // () => ref
- // // .read(
- // // shouldShowLockscreenOnResumeStateProvider
- // // .state)
- // // .state = true,
- // // );
- // }
- // },
- // ),
- // ],
- // ),
- // ),
- if (coin == Coin.epicCash)
- const SizedBox(
- height: 12,
- ),
- if (coin == Coin.epicCash)
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "Slate ID",
- style: STextStyles.itemSubtitle(context),
- ),
- // Flexible(
- // child: FittedBox(
- // fit: BoxFit.scaleDown,
- // child:
- SelectableText(
- _transaction.slateId ?? "Unknown",
- style: STextStyles.itemSubtitle12(context),
- ),
- // ),
- // ),
- ],
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Builder(builder: (context) {
+ final height = widget.coin != Coin.epicCash &&
+ _transaction.confirmedStatus
+ ? "${_transaction.height == 0 ? "Unknown" : _transaction.height}"
+ : _transaction.confirmations > 0
+ ? "${_transaction.height}"
+ : "Pending";
+
+ return Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Column(
+ crossAxisAlignment:
+ CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Block height",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 2,
+ ),
+ if (isDesktop)
+ SelectableText(
+ height,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(
+ context),
+ ),
+ ],
+ ),
+ if (!isDesktop)
+ SelectableText(
+ height,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(context),
+ ),
+ if (isDesktop) IconCopyButton(data: height),
+ ],
+ );
+ }),
+ ),
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
+ ),
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment:
+ CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Transaction ID",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ // Flexible(
+ // child: FittedBox(
+ // fit: BoxFit.scaleDown,
+ // child:
+ SelectableText(
+ _transaction.txid,
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(
+ context),
+ ),
+ if (coin != Coin.epicCash)
+ const SizedBox(
+ height: 8,
+ ),
+ if (coin != Coin.epicCash)
+ BlueTextButton(
+ text: "Open in block explorer",
+ onTap: () async {
+ final uri =
+ getBlockExplorerTransactionUrlFor(
+ coin: coin,
+ txid: _transaction.txid,
+ );
+
+ if (ref
+ .read(
+ prefsChangeNotifierProvider)
+ .hideBlockExplorerWarning ==
+ false) {
+ final shouldContinue =
+ await showExplorerWarning(
+ "${uri.scheme}://${uri.host}");
+
+ if (!shouldContinue) {
+ return;
+ }
+ }
+
+ // ref
+ // .read(
+ // shouldShowLockscreenOnResumeStateProvider
+ // .state)
+ // .state = false;
+ try {
+ await launchUrl(
+ uri,
+ mode: LaunchMode
+ .externalApplication,
+ );
+ } catch (_) {
+ unawaited(showDialog(
+ context: context,
+ builder: (_) => StackOkDialog(
+ title:
+ "Could not open in block explorer",
+ message:
+ "Failed to open \"${uri.toString()}\"",
+ ),
+ ));
+ } finally {
+ // Future.delayed(
+ // const Duration(seconds: 1),
+ // () => ref
+ // .read(
+ // shouldShowLockscreenOnResumeStateProvider
+ // .state)
+ // .state = true,
+ // );
+ }
+ },
+ ),
+ // ),
+ // ),
+ ],
+ ),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ width: 12,
+ ),
+ if (isDesktop)
+ IconCopyButton(
+ data: _transaction.txid,
+ ),
+ ],
+ ),
+ ),
+ // if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
+ // _transaction.subType == "mint")
+ // const SizedBox(
+ // height: 12,
+ // ),
+ // if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
+ // _transaction.subType == "mint")
+ // RoundedWhiteContainer(
+ // child: Column(
+ // crossAxisAlignment: CrossAxisAlignment.start,
+ // children: [
+ // Row(
+ // mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ // children: [
+ // Text(
+ // "Mint Transaction ID",
+ // style: STextStyles.itemSubtitle(context),
+ // ),
+ // ],
+ // ),
+ // const SizedBox(
+ // height: 8,
+ // ),
+ // // Flexible(
+ // // child: FittedBox(
+ // // fit: BoxFit.scaleDown,
+ // // child:
+ // SelectableText(
+ // _transaction.otherData ?? "Unknown",
+ // style: STextStyles.itemSubtitle12(context),
+ // ),
+ // // ),
+ // // ),
+ // const SizedBox(
+ // height: 8,
+ // ),
+ // BlueTextButton(
+ // text: "Open in block explorer",
+ // onTap: () async {
+ // final uri = getBlockExplorerTransactionUrlFor(
+ // coin: coin,
+ // txid: _transaction.otherData ?? "Unknown",
+ // );
+ // // ref
+ // // .read(
+ // // shouldShowLockscreenOnResumeStateProvider
+ // // .state)
+ // // .state = false;
+ // try {
+ // await launchUrl(
+ // uri,
+ // mode: LaunchMode.externalApplication,
+ // );
+ // } catch (_) {
+ // unawaited(showDialog(
+ // context: context,
+ // builder: (_) => StackOkDialog(
+ // title: "Could not open in block explorer",
+ // message:
+ // "Failed to open \"${uri.toString()}\"",
+ // ),
+ // ));
+ // } finally {
+ // // Future.delayed(
+ // // const Duration(seconds: 1),
+ // // () => ref
+ // // .read(
+ // // shouldShowLockscreenOnResumeStateProvider
+ // // .state)
+ // // .state = true,
+ // // );
+ // }
+ // },
+ // ),
+ // ],
+ // ),
+ // ),
+ if (coin == Coin.epicCash)
+ isDesktop
+ ? const _Divider()
+ : const SizedBox(
+ height: 12,
+ ),
+ if (coin == Coin.epicCash)
+ RoundedWhiteContainer(
+ padding: isDesktop
+ ? const EdgeInsets.all(16)
+ : const EdgeInsets.all(12),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment:
+ CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Slate ID",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ // Flexible(
+ // child: FittedBox(
+ // fit: BoxFit.scaleDown,
+ // child:
+ SelectableText(
+ _transaction.slateId ?? "Unknown",
+ style: isDesktop
+ ? STextStyles
+ .desktopTextExtraExtraSmall(
+ context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textDark,
+ )
+ : STextStyles.itemSubtitle12(
+ context),
+ ),
+ // ),
+ // ),
+ ],
+ ),
+ if (isDesktop)
+ const SizedBox(
+ width: 12,
+ ),
+ if (isDesktop)
+ IconCopyButton(
+ data: _transaction.slateId ?? "Unknown",
+ ),
+ ],
+ ),
+ ),
+ if (!isDesktop)
+ const SizedBox(
+ height: 12,
+ ),
+ ],
+ ),
),
),
- const SizedBox(
- height: 12,
),
- ],
+ ),
),
- ),
+ ],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
@@ -805,3 +1253,94 @@ class _TransactionDetailsViewState
);
}
}
+
+class _Divider extends StatelessWidget {
+ const _Divider({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 1,
+ color: Theme.of(context).extension()!.background,
+ );
+ }
+}
+
+class IconCopyButton extends StatelessWidget {
+ const IconCopyButton({
+ Key? key,
+ required this.data,
+ }) : super(key: key);
+
+ final String data;
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 26,
+ width: 26,
+ child: RawMaterialButton(
+ fillColor:
+ Theme.of(context).extension()!.buttonBackSecondary,
+ elevation: 0,
+ hoverElevation: 0,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(6),
+ ),
+ onPressed: () async {
+ await Clipboard.setData(ClipboardData(text: data));
+ unawaited(
+ showFloatingFlushBar(
+ type: FlushBarType.info,
+ message: "Copied to clipboard",
+ context: context,
+ ),
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.all(5),
+ child: CopyIcon(
+ width: 16,
+ height: 16,
+ color: Theme.of(context).extension()!.textDark,
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+class IconPencilButton extends StatelessWidget {
+ const IconPencilButton({
+ Key? key,
+ this.onPressed,
+ }) : super(key: key);
+
+ final VoidCallback? onPressed;
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 26,
+ width: 26,
+ child: RawMaterialButton(
+ fillColor:
+ Theme.of(context).extension()!.buttonBackSecondary,
+ elevation: 0,
+ hoverElevation: 0,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(6),
+ ),
+ onPressed: () => onPressed?.call(),
+ child: Padding(
+ padding: const EdgeInsets.all(5),
+ child: PencilIcon(
+ width: 16,
+ height: 16,
+ color: Theme.of(context).extension()!.textDark,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart b/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart
index f9932c6729..abebb71e47 100644
--- a/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart
+++ b/lib/pages/wallet_view/transaction_views/transaction_search_filter_view.dart
@@ -16,6 +16,10 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.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';
@@ -43,6 +47,7 @@ class _TransactionSearchViewState
bool _isActiveReceivedCheckbox = false;
bool _isActiveSentCheckbox = false;
+ bool _isActiveTradeCheckbox = false;
String _fromDateString = "";
String _toDateString = "";
@@ -63,6 +68,12 @@ class _TransactionSearchViewState
_selectedFromDate = filterState.from;
_keywordTextEditingController.text = filterState.keyword;
+ _fromDateString = _selectedFromDate == null
+ ? ""
+ : Format.formatDate(_selectedFromDate!);
+ _toDateString =
+ _selectedToDate == null ? "" : Format.formatDate(_selectedToDate!);
+
// TODO: Fix XMR (modify Format.funcs to take optional Coin parameter)
// final amt = Format.satoshisToAmount(widget.coin == Coin.monero ? )
String amount = "";
@@ -110,8 +121,8 @@ class _TransactionSearchViewState
);
}
- var _selectedFromDate = DateTime(2007);
- var _selectedToDate = DateTime.now();
+ DateTime? _selectedFromDate = DateTime(2007);
+ DateTime? _selectedToDate = DateTime.now();
MaterialRoundedDatePickerStyle _buildDatePickerStyle() {
return MaterialRoundedDatePickerStyle(
@@ -166,104 +177,118 @@ class _TransactionSearchViewState
Widget _buildDateRangePicker() {
const middleSeparatorPadding = 2.0;
const middleSeparatorWidth = 12.0;
- final width = (MediaQuery.of(context).size.width -
- (middleSeparatorWidth +
- (2 * middleSeparatorPadding) +
- (2 * Constants.size.standardPadding))) /
- 2;
+ 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: [
- GestureDetector(
- key: const Key("transactionSearchViewFromDatePickerKey"),
- 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));
- }
-
- 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 = !_selectedFromDate.isBefore(_selectedToDate);
- if (flag) {
- _selectedToDate = DateTime.fromMillisecondsSinceEpoch(
- _selectedFromDate.millisecondsSinceEpoch);
+ Expanded(
+ child: GestureDetector(
+ key: const Key("transactionSearchViewFromDatePickerKey"),
+ 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));
}
- setState(() {
+ 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) {
- _toDateString = Format.formatDate(_selectedToDate);
+ _selectedToDate = DateTime.fromMillisecondsSinceEpoch(
+ _selectedFromDate!.millisecondsSinceEpoch);
}
- _fromDateString = Format.formatDate(_selectedFromDate);
- });
- }
- },
- child: Container(
- width: width,
- decoration: BoxDecoration(
- color: Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
- borderRadius:
- BorderRadius.circular(Constants.size.circularBorderRadius),
- border: Border.all(
+
+ 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,
- width: 1,
+ borderRadius:
+ BorderRadius.circular(Constants.size.circularBorderRadius),
+ border: Border.all(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultBG,
+ width: 1,
+ ),
),
- ),
- child: Padding(
- padding: const EdgeInsets.all(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,
+ 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,
+ ),
+ )
+ ],
+ ),
),
),
),
@@ -277,470 +302,658 @@ class _TransactionSearchViewState
// color: CFColors.smoke,
),
),
- GestureDetector(
- key: const Key("transactionSearchViewToDatePickerKey"),
- 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));
- }
-
- 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 = !_selectedToDate.isAfter(_selectedFromDate);
- if (flag) {
- _selectedFromDate = DateTime.fromMillisecondsSinceEpoch(
- _selectedToDate.millisecondsSinceEpoch);
+ Expanded(
+ child: GestureDetector(
+ key: const Key("transactionSearchViewToDatePickerKey"),
+ 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));
}
- setState(() {
+ 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) {
- _fromDateString = Format.formatDate(_selectedFromDate);
+ _selectedFromDate = DateTime.fromMillisecondsSinceEpoch(
+ _selectedToDate!.millisecondsSinceEpoch);
}
- _toDateString = Format.formatDate(_selectedToDate);
- });
- }
- },
- child: Container(
- width: width,
- decoration: BoxDecoration(
- color: Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
- borderRadius:
- BorderRadius.circular(Constants.size.circularBorderRadius),
- border: Border.all(
+
+ 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,
- width: 1,
+ borderRadius:
+ BorderRadius.circular(Constants.size.circularBorderRadius),
+ border: Border.all(
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultBG,
+ width: 1,
+ ),
),
- ),
- child: Padding(
- padding: const EdgeInsets.all(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,
+ 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) {
- return Scaffold(
- backgroundColor: Theme.of(context).extension()!.background,
- appBar: AppBar(
+ 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 Scaffold(
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();
- }
- },
+ 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(
+ "Transactions filter",
+ style: STextStyles.navBarTitle(context),
+ ),
),
- title: Text(
- "Transactions 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),
+ ),
+ ),
+ );
+ },
+ ),
),
- ),
- body: Padding(
- padding: EdgeInsets.symmetric(
- horizontal: Constants.size.standardPadding,
+ );
+ }
+ }
+
+ Widget _buildContent(BuildContext context) {
+ final isDesktop = Util.isDesktop;
+
+ return Column(
+ children: [
+ if (isDesktop)
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Transaction filter",
+ style: STextStyles.desktopH3(context),
+ textAlign: TextAlign.center,
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ SizedBox(
+ height: isDesktop ? 14 : 10,
),
- child: LayoutBuilder(
- builder: (context, constraints) {
- return SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(minHeight: constraints.maxHeight),
- child: IntrinsicHeight(
- child: Column(
- children: [
- const SizedBox(
- height: 10,
- ),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(
- child: Text(
- "Transactions",
- style: STextStyles.smallMed12(context),
+ if (!isDesktop)
+ Align(
+ alignment: Alignment.centerLeft,
+ child: FittedBox(
+ child: Text(
+ "Transactions",
+ 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(() {
+ _isActiveSentCheckbox = !_isActiveSentCheckbox;
+ });
+ },
+ child: Container(
+ color: Colors.transparent,
+ child: Row(
+ children: [
+ SizedBox(
+ height: 20,
+ width: 20,
+ child: Checkbox(
+ key: const Key(
+ "transactionSearchViewSentCheckboxKey"),
+ materialTapTargetSize:
+ MaterialTapTargetSize.shrinkWrap,
+ value: _isActiveSentCheckbox,
+ onChanged: (newValue) {
+ setState(() {
+ _isActiveSentCheckbox = newValue!;
+ });
+ },
+ ),
),
- ),
- ),
- const SizedBox(
- height: 12,
- ),
- RoundedWhiteContainer(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- GestureDetector(
- onTap: () {
- setState(() {
- _isActiveSentCheckbox =
- !_isActiveSentCheckbox;
- });
- },
- child: Container(
- color: Colors.transparent,
- child: Row(
- children: [
- SizedBox(
- height: 20,
- width: 20,
- child: Checkbox(
- key: const Key(
- "transactionSearchViewSentCheckboxKey"),
- materialTapTargetSize:
- MaterialTapTargetSize
- .shrinkWrap,
- value: _isActiveSentCheckbox,
- onChanged: (newValue) {
- setState(() {
- _isActiveSentCheckbox =
- newValue!;
- });
- },
- ),
- ),
- const SizedBox(
- width: 14,
- ),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(
- child: Text(
- "Sent",
- style: STextStyles.itemSubtitle12(
- context),
- ),
- ),
- )
- ],
- ),
+ const SizedBox(
+ width: 14,
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: FittedBox(
+ child: Column(
+ children: [
+ Text(
+ "Sent",
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.itemSubtitle12(context),
),
- ),
- ],
- ),
- const SizedBox(
- height: 10,
- ),
- Row(
- children: [
- GestureDetector(
- onTap: () {
- setState(() {
- _isActiveReceivedCheckbox =
- !_isActiveReceivedCheckbox;
- });
- },
- child: Container(
- color: Colors.transparent,
- child: Row(
- children: [
- SizedBox(
- height: 20,
- width: 20,
- child: Checkbox(
- key: const Key(
- "transactionSearchViewReceivedCheckboxKey"),
- materialTapTargetSize:
- MaterialTapTargetSize
- .shrinkWrap,
- value: _isActiveReceivedCheckbox,
- onChanged: (newValue) {
- setState(() {
- _isActiveReceivedCheckbox =
- newValue!;
- });
- },
- ),
- ),
- const SizedBox(
- width: 14,
- ),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(
- child: Text(
- "Received",
- style: STextStyles.itemSubtitle12(
- context),
- ),
- ),
- )
- ],
+ if (isDesktop)
+ const SizedBox(
+ height: 4,
),
- ),
- ),
- ],
+ ],
+ ),
),
- ],
- ),
- ),
- const SizedBox(
- height: 24,
- ),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(
- child: Text(
- "Date",
- style: STextStyles.smallMed12(context),
- ),
- ),
- ),
- const SizedBox(
- height: 8,
- ),
- _buildDateRangePicker(),
- const SizedBox(
- height: 24,
- ),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(
- child: Text(
- "Amount",
- style: STextStyles.smallMed12(context),
- ),
- ),
- ),
- const SizedBox(
- height: 8,
+ )
+ ],
),
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- key: const Key("transactionSearchViewAmountFieldKey"),
- controller: _amountTextEditingController,
- focusNode: amountTextFieldFocusNode,
- onChanged: (_) => setState(() {}),
- keyboardType: const TextInputType.numberWithOptions(
- signed: false,
- decimal: true,
- ),
- inputFormatters: [
- // regex to validate a crypto amount with 8 decimal places
- TextInputFormatter.withFunction((oldValue,
- newValue) =>
- RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
- .hasMatch(newValue.text)
- ? newValue
- : oldValue),
- ],
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Enter ${widget.coin.ticker} amount...",
- keywordTextFieldFocusNode,
- context,
- ).copyWith(
- suffixIcon: _amountTextEditingController
- .text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- _amountTextEditingController
- .text = "";
- });
- },
- ),
- ],
- ),
- ),
- )
- : null,
+ ),
+ ),
+ ],
+ ),
+ SizedBox(
+ height: isDesktop ? 4 : 10,
+ ),
+ Row(
+ children: [
+ GestureDetector(
+ onTap: () {
+ setState(() {
+ _isActiveReceivedCheckbox = !_isActiveReceivedCheckbox;
+ });
+ },
+ child: Container(
+ color: Colors.transparent,
+ child: Row(
+ children: [
+ SizedBox(
+ height: 20,
+ width: 20,
+ child: Checkbox(
+ key: const Key(
+ "transactionSearchViewReceivedCheckboxKey"),
+ materialTapTargetSize:
+ MaterialTapTargetSize.shrinkWrap,
+ value: _isActiveReceivedCheckbox,
+ onChanged: (newValue) {
+ setState(() {
+ _isActiveReceivedCheckbox = newValue!;
+ });
+ },
+ ),
),
- ),
- ),
- const SizedBox(
- height: 24,
- ),
- Align(
- alignment: Alignment.centerLeft,
- child: FittedBox(
- child: Text(
- "Keyword",
- style: STextStyles.smallMed12(context),
+ const SizedBox(
+ width: 14,
),
- ),
- ),
- const SizedBox(
- height: 8,
- ),
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- key:
- const Key("transactionSearchViewKeywordFieldKey"),
- controller: _keywordTextEditingController,
- focusNode: keywordTextFieldFocusNode,
- style: STextStyles.field(context),
- onChanged: (_) => setState(() {}),
- decoration: standardInputDecoration(
- "Type keyword...",
- keywordTextFieldFocusNode,
- context,
- ).copyWith(
- 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 = "";
- });
- },
- ),
- ],
- ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: FittedBox(
+ child: Column(
+ children: [
+ Text(
+ "Received",
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.itemSubtitle12(context),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 4,
),
- )
- : null,
- ),
- ),
- ),
- const Spacer(),
- const SizedBox(
- height: 20,
+ ],
+ ),
+ ),
+ )
+ ],
),
- Row(
+ ),
+ ),
+ ],
+ ),
+ SizedBox(
+ height: isDesktop ? 4 : 10,
+ ),
+ Row(
+ children: [
+ GestureDetector(
+ onTap: () {
+ setState(() {
+ _isActiveTradeCheckbox = !_isActiveTradeCheckbox;
+ });
+ },
+ child: Container(
+ color: Colors.transparent,
+ child: Row(
children: [
- Expanded(
- child: SizedBox(
- height: 48,
- child: TextButton(
- onPressed: () async {
- if (FocusScope.of(context).hasFocus) {
- FocusScope.of(context).unfocus();
- await Future.delayed(
- const Duration(milliseconds: 75));
- }
- if (mounted) {
- Navigator.of(context).pop();
- }
- },
- style: Theme.of(context)
- .extension()!
- .getSecondaryEnabledButtonColor(context),
- child: Text(
- "Cancel",
- style: STextStyles.button(context).copyWith(
- color: Theme.of(context)
- .extension()!
- .accentColorDark),
- ),
- ),
+ SizedBox(
+ height: 20,
+ width: 20,
+ child: Checkbox(
+ key: const Key(
+ "transactionSearchViewSentCheckboxKey"),
+ materialTapTargetSize:
+ MaterialTapTargetSize.shrinkWrap,
+ value: _isActiveTradeCheckbox,
+ onChanged: (newValue) {
+ setState(() {
+ _isActiveTradeCheckbox = newValue!;
+ });
+ },
),
),
const SizedBox(
- width: 16,
+ width: 14,
),
- Expanded(
- child: SizedBox(
- height: 48,
- child: TextButton(
- style: Theme.of(context)
- .extension()!
- .getPrimaryEnabledButtonColor(context),
- onPressed: () async {
- _onApplyPressed();
- },
- child: Text(
- "Save",
- style: STextStyles.button(context),
- ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: FittedBox(
+ child: Column(
+ children: [
+ Text(
+ "Trades",
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.itemSubtitle12(context),
+ ),
+ if (isDesktop)
+ const SizedBox(
+ height: 4,
+ ),
+ ],
),
),
- ),
+ )
],
),
- const SizedBox(
- height: 20,
- ),
- ],
+ ),
),
- ),
+ ],
),
- );
- },
+ ],
+ ),
),
- ),
+ 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(
+ "Amount",
+ 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("transactionSearchViewAmountFieldKey"),
+ controller: _amountTextEditingController,
+ focusNode: amountTextFieldFocusNode,
+ onChanged: (_) => setState(() {}),
+ keyboardType: const TextInputType.numberWithOptions(
+ signed: false,
+ decimal: true,
+ ),
+ inputFormatters: [
+ // regex to validate a crypto amount with 8 decimal places
+ TextInputFormatter.withFunction((oldValue, newValue) =>
+ RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
+ .hasMatch(newValue.text)
+ ? newValue
+ : oldValue),
+ ],
+ style: isDesktop
+ ? STextStyles.desktopTextExtraSmall(context).copyWith(
+ color:
+ Theme.of(context).extension()!.textDark,
+ height: 1.8,
+ )
+ : STextStyles.field(context),
+ decoration: standardInputDecoration(
+ "Enter ${widget.coin.ticker} amount...",
+ keywordTextFieldFocusNode,
+ context,
+ desktopMed: isDesktop,
+ ).copyWith(
+ contentPadding: isDesktop
+ ? const EdgeInsets.symmetric(
+ vertical: 10,
+ horizontal: 16,
+ )
+ : null,
+ suffixIcon: _amountTextEditingController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ _amountTextEditingController.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("transactionSearchViewKeywordFieldKey"),
+ 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",
+ desktopMed: isDesktop,
+ 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();
+ }
+ },
+ ),
+ ),
+ // Expanded(
+ // child: SizedBox(
+ // height: 48,
+ // child: TextButton(
+ // onPressed: () async {
+ // if (FocusScope.of(context).hasFocus) {
+ // FocusScope.of(context).unfocus();
+ // await Future.delayed(
+ // const Duration(milliseconds: 75));
+ // }
+ // if (mounted) {
+ // Navigator.of(context).pop();
+ // }
+ // },
+ // style: Theme.of(context)
+ // .extension()!
+ // .getSecondaryEnabledButtonColor(context),
+ // child: Text(
+ // "Cancel",
+ // style: STextStyles.button(context).copyWith(
+ // color: Theme.of(context)
+ // .extension()!
+ // .accentColorDark),
+ // ),
+ // ),
+ // ),
+ // ),
+ const SizedBox(
+ width: 16,
+ ),
+ Expanded(
+ child: PrimaryButton(
+ desktopMed: isDesktop,
+ onPressed: () async {
+ await _onApplyPressed();
+ },
+ label: "Save",
+ ),
+ ),
+ // Expanded(
+ // child: SizedBox(
+ // height: 48,
+ // child: TextButton(
+ // style: Theme.of(context)
+ // .extension()!
+ // .getPrimaryEnabledButtonColor(context),
+ // onPressed: () async {
+ // await _onApplyPressed();
+ // },
+ // child: Text(
+ // "Save",
+ // style: STextStyles.button(context),
+ // ),
+ // ),
+ // ),
+ // ),
+ if (isDesktop)
+ const SizedBox(
+ width: 32,
+ ),
+ ],
+ ),
+ if (!isDesktop)
+ const SizedBox(
+ height: 20,
+ ),
+ ],
);
}
@@ -775,6 +988,7 @@ class _TransactionSearchViewState
final TransactionFilter filter = TransactionFilter(
sent: _isActiveSentCheckbox,
received: _isActiveReceivedCheckbox,
+ trade: _isActiveTradeCheckbox,
from: _selectedFromDate,
to: _selectedToDate,
amount: amount,
diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart
index 924e904f3d..8ce8add170 100644
--- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart
+++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/assets.dart';
@@ -11,6 +12,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
+import 'package:stackwallet/utilities/util.dart';
import 'package:tuple/tuple.dart';
class FavoriteCard extends ConsumerStatefulWidget {
@@ -54,13 +56,20 @@ class _FavoriteCardState extends ConsumerState {
return GestureDetector(
onTap: () {
- Navigator.of(context).pushNamed(
- WalletView.routeName,
- arguments: Tuple2(
- walletId,
- managerProvider,
- ),
- );
+ if (Util.isDesktop) {
+ Navigator.of(context).pushNamed(
+ DesktopWalletView.routeName,
+ arguments: walletId,
+ );
+ } else {
+ Navigator.of(context).pushNamed(
+ WalletView.routeName,
+ arguments: Tuple2(
+ walletId,
+ managerProvider,
+ ),
+ );
+ }
},
child: SizedBox(
width: widget.width,
diff --git a/lib/pages_desktop_specific/home/desktop_settings_view.dart b/lib/pages_desktop_specific/home/desktop_settings_view.dart
index 7aff94b669..719a37a3ac 100644
--- a/lib/pages_desktop_specific/home/desktop_settings_view.dart
+++ b/lib/pages_desktop_specific/home/desktop_settings_view.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart';
-import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore_settings.dart';
+import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/nodes_settings.dart';
diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart
index f86eba750f..97efb2f680 100644
--- a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart
+++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart
@@ -1,47 +1,193 @@
+import 'dart:async';
+
+import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
-import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_summary.dart';
-import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/receive/desktop_receive.dart';
-import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/send/desktop_send.dart';
+import 'package:stackwallet/notifications/show_flush_bar.dart';
+import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
+import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/my_wallet.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/network_info_button.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/recent_desktop_transactions.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_button.dart';
+import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
+import 'package:stackwallet/providers/ui/transaction_filter_provider.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/exchange/change_now/change_now_exchange.dart';
+import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/utilities/assets.dart';
-import 'package:stackwallet/utilities/constants.dart';
+import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
+import 'package:stackwallet/utilities/enums/coin_enum.dart';
+import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
+import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
-import 'package:stackwallet/widgets/custom_buttons/blue_text_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/hover_text_field.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
+import 'package:stackwallet/widgets/stack_dialog.dart';
+import 'package:tuple/tuple.dart';
+/// [eventBus] should only be set during testing
class DesktopWalletView extends ConsumerStatefulWidget {
const DesktopWalletView({
Key? key,
required this.walletId,
+ this.eventBus,
}) : super(key: key);
static const String routeName = "/desktopWalletView";
final String walletId;
+ final EventBus? eventBus;
@override
ConsumerState createState() => _DesktopWalletViewState();
}
class _DesktopWalletViewState extends ConsumerState {
+ late final TextEditingController controller;
late final String walletId;
+ late final EventBus eventBus;
+
+ late final bool _shouldDisableAutoSyncOnLogOut;
+
+ final _cnLoadingService = ExchangeDataLoadingService();
Future onBackPressed() async {
- // TODO log out and close wallet before popping back
- Navigator.of(context).pop();
+ await _logout();
+ if (mounted) {
+ Navigator.of(context).pop();
+ }
+ }
+
+ Future _logout() async {
+ final managerProvider =
+ ref.read(walletsChangeNotifierProvider).getManagerProvider(walletId);
+ if (_shouldDisableAutoSyncOnLogOut) {
+ // disable auto sync if it was enabled only when loading wallet
+ ref.read(managerProvider).shouldAutoSync = false;
+ }
+ ref.read(managerProvider.notifier).isActiveWallet = false;
+ ref.read(transactionFilterProvider.state).state = null;
+ if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled &&
+ ref.read(prefsChangeNotifierProvider).backupFrequencyType ==
+ BackupFrequencyType.afterClosingAWallet) {
+ unawaited(ref.read(autoSWBServiceProvider).doBackup());
+ }
+ }
+
+ void _loadCNData() {
+ // unawaited future
+ if (ref.read(prefsChangeNotifierProvider).externalCalls) {
+ _cnLoadingService.loadAll(ref,
+ coin: ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(walletId)
+ .coin);
+ } else {
+ Logging.instance.log("User does not want to use external calls",
+ level: LogLevel.Info);
+ }
+ }
+
+ void _onExchangePressed(BuildContext context) async {
+ final managerProvider =
+ ref.read(walletsChangeNotifierProvider).getManagerProvider(walletId);
+ unawaited(_cnLoadingService.loadAll(ref));
+
+ final coin = ref.read(managerProvider).coin;
+
+ if (coin == Coin.epicCash) {
+ await showDialog(
+ context: context,
+ builder: (_) => const StackOkDialog(
+ title: "Exchange not available for Epic Cash",
+ ),
+ );
+ } else if (coin.name.endsWith("TestNet")) {
+ await showDialog(
+ context: context,
+ builder: (_) => const StackOkDialog(
+ title: "Exchange not available for test net coins",
+ ),
+ );
+ } else {
+ ref.read(currentExchangeNameStateProvider.state).state =
+ ChangeNowExchange.exchangeName;
+ final walletId = ref.read(managerProvider).walletId;
+ ref.read(prefsChangeNotifierProvider).exchangeRateType =
+ ExchangeRateType.estimated;
+
+ ref.read(exchangeFormStateProvider).exchange = ref.read(exchangeProvider);
+ ref.read(exchangeFormStateProvider).exchangeType =
+ ExchangeRateType.estimated;
+
+ final currencies = ref
+ .read(availableChangeNowCurrenciesProvider)
+ .currencies
+ .where((element) =>
+ element.ticker.toLowerCase() == coin.ticker.toLowerCase());
+
+ if (currencies.isNotEmpty) {
+ ref.read(exchangeFormStateProvider).setCurrencies(
+ currencies.first,
+ ref
+ .read(availableChangeNowCurrenciesProvider)
+ .currencies
+ .firstWhere(
+ (element) =>
+ element.ticker.toLowerCase() !=
+ coin.ticker.toLowerCase(),
+ ),
+ );
+ }
+
+ if (mounted) {
+ unawaited(
+ Navigator.of(context).pushNamed(
+ WalletInitiatedExchangeView.routeName,
+ arguments: Tuple3(
+ walletId,
+ coin,
+ _loadCNData,
+ ),
+ ),
+ );
+ }
+ }
}
@override
void initState() {
+ controller = TextEditingController();
walletId = widget.walletId;
+ final managerProvider =
+ ref.read(walletsChangeNotifierProvider).getManagerProvider(walletId);
+
+ controller.text = ref.read(managerProvider).walletName;
+
+ eventBus =
+ widget.eventBus != null ? widget.eventBus! : GlobalEventBus.instance;
+
+ ref.read(managerProvider).isActiveWallet = true;
+ if (!ref.read(managerProvider).shouldAutoSync) {
+ // enable auto sync if it wasn't enabled when loading wallet
+ ref.read(managerProvider).shouldAutoSync = true;
+ _shouldDisableAutoSyncOnLogOut = true;
+ } else {
+ _shouldDisableAutoSyncOnLogOut = false;
+ }
+
+ ref.read(managerProvider).refresh();
+
super.initState();
}
@@ -56,56 +202,110 @@ class _DesktopWalletViewState extends ConsumerState {
return DesktopScaffold(
appBar: DesktopAppBar(
background: Theme.of(context).extension()!.popupBG,
- leading: 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,
+ leading: Expanded(
+ child: Row(
+ children: [
+ const SizedBox(
+ width: 32,
+ ),
+ AppBarIconButton(
+ size: 32,
color: Theme.of(context)
.extension()!
- .topNavIconPrimary,
+ .textFieldDefaultBG,
+ shadows: const [],
+ icon: SvgPicture.asset(
+ Assets.svg.arrowLeft,
+ width: 18,
+ height: 18,
+ color: Theme.of(context)
+ .extension()!
+ .topNavIconPrimary,
+ ),
+ onPressed: onBackPressed,
),
- onPressed: onBackPressed,
- ),
- const SizedBox(
- width: 15,
- ),
- SvgPicture.asset(
- Assets.svg.iconFor(coin: coin),
- width: 32,
- height: 32,
- ),
- const SizedBox(
- width: 12,
- ),
- Text(
- manager.walletName,
- style: STextStyles.desktopH3(context),
- ),
- ],
- ),
- trailing: Row(
- children: const [
- NetworkInfoButton(),
- SizedBox(
- width: 32,
- ),
- WalletKeysButton(),
- SizedBox(
- width: 32,
- ),
- ],
+ const SizedBox(
+ width: 15,
+ ),
+ SvgPicture.asset(
+ Assets.svg.iconFor(coin: coin),
+ width: 32,
+ height: 32,
+ ),
+ const SizedBox(
+ width: 12,
+ ),
+ ConstrainedBox(
+ constraints: const BoxConstraints(
+ minWidth: 48,
+ ),
+ child: IntrinsicWidth(
+ child: HoverTextField(
+ controller: controller,
+ style: STextStyles.desktopH3(context),
+ readOnly: true,
+ onDone: () async {
+ final currentWalletName =
+ ref.read(managerProvider).walletName;
+ final newName = controller.text;
+ if (newName != currentWalletName) {
+ final success = await ref
+ .read(walletsServiceChangeNotifierProvider)
+ .renameWallet(
+ from: currentWalletName,
+ to: newName,
+ shouldNotifyListeners: true,
+ );
+ if (success) {
+ ref
+ .read(walletsChangeNotifierProvider)
+ .getManager(walletId)
+ .walletName = newName;
+ unawaited(
+ showFloatingFlushBar(
+ type: FlushBarType.success,
+ message: "Wallet renamed",
+ context: context,
+ ),
+ );
+ } else {
+ unawaited(
+ showFloatingFlushBar(
+ type: FlushBarType.warning,
+ message:
+ "Wallet named \"$newName\" already exists",
+ context: context,
+ ),
+ );
+ controller.text = currentWalletName;
+ }
+ }
+ },
+ ),
+ ),
+ ),
+ const Spacer(),
+ Row(
+ children: [
+ NetworkInfoButton(
+ walletId: walletId,
+ eventBus: eventBus,
+ ),
+ const SizedBox(
+ width: 32,
+ ),
+ WalletKeysButton(
+ walletId: walletId,
+ ),
+ const SizedBox(
+ width: 32,
+ ),
+ ],
+ ),
+ ],
+ ),
),
+ useSpacers: false,
isCompactHeight: true,
),
body: Padding(
@@ -132,51 +332,36 @@ class _DesktopWalletViewState extends ConsumerState {
? WalletSyncStatus.syncing
: WalletSyncStatus.synced,
),
- // Column(
- // crossAxisAlignment: CrossAxisAlignment.start,
- // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- // children: [
- // Row(
- // children: [
- // Text(
- // "TODO: balance",
- // style: STextStyles.desktopH3(context),
- // ),
- // const SizedBox(
- // width: 8,
- // ),
- // Container(
- // color: Colors.red,
- // width: 20,
- // height: 20,
- // ),
- // ],
- // ),
- // Text(
- // "todo: fiat balance",
- // style:
- // STextStyles.desktopTextExtraSmall(context).copyWith(
- // color: Theme.of(context)
- // .extension()!
- // .textSubtitle1,
- // ),
- // )
- // ],
- // ),
const Spacer(),
SecondaryButton(
width: 180,
- height: 56,
+ desktopMed: true,
onPressed: () {
- // todo: go to wallet initiated exchange
+ _onExchangePressed(context);
},
label: "Exchange",
icon: Container(
- color: Colors.red,
- width: 20,
- height: 20,
+ width: 24,
+ height: 24,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(24),
+ color: Theme.of(context)
+ .extension()!
+ .buttonBackPrimary
+ .withOpacity(0.2),
+ ),
+ child: Center(
+ child: SvgPicture.asset(
+ Assets.svg.arrowRotate2,
+ width: 14,
+ height: 14,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextSecondary,
+ ),
+ ),
),
- )
+ ),
],
),
),
@@ -196,328 +381,13 @@ class _DesktopWalletViewState extends ConsumerState {
width: 16,
),
Expanded(
- child: RecentDesktopTransactions(),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- );
- }
-}
-
-class MyWallet extends StatefulWidget {
- const MyWallet({
- Key? key,
- required this.walletId,
- }) : super(key: key);
-
- final String walletId;
-
- @override
- State createState() => _MyWalletState();
-}
-
-class _MyWalletState extends State {
- int _selectedIndex = 0;
-
- @override
- Widget build(BuildContext context) {
- return Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "My wallet",
- style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color: Theme.of(context)
- .extension()!
- .textFieldActiveSearchIconLeft,
- ),
- ),
- const SizedBox(
- height: 16,
- ),
- Container(
- decoration: BoxDecoration(
- color: Theme.of(context).extension()!.popupBG,
- borderRadius: BorderRadius.vertical(
- top: Radius.circular(
- Constants.size.circularBorderRadius,
- ),
- ),
- ),
- child: SendReceiveTabMenu(
- onChanged: (index) {
- setState(() {
- _selectedIndex = index;
- });
- },
- ),
- ),
- Container(
- decoration: BoxDecoration(
- color: Theme.of(context).extension()!.popupBG,
- borderRadius: BorderRadius.vertical(
- bottom: Radius.circular(
- Constants.size.circularBorderRadius,
- ),
- ),
- ),
- child: IndexedStack(
- index: _selectedIndex,
- children: [
- Padding(
- key: const Key("desktopSendViewPortKey"),
- padding: const EdgeInsets.all(20),
- child: DesktopSend(
- walletId: widget.walletId,
- ),
- ),
- Padding(
- key: const Key("desktopReceiveViewPortKey"),
- padding: const EdgeInsets.all(20),
- child: DesktopReceive(
- walletId: widget.walletId,
- ),
- ),
- ],
- ),
- ),
- const Spacer(),
- ],
- );
- }
-}
-
-class SendReceiveTabMenu extends StatefulWidget {
- const SendReceiveTabMenu({
- Key? key,
- this.initialIndex = 0,
- this.onChanged,
- }) : super(key: key);
-
- final int initialIndex;
- final void Function(int)? onChanged;
-
- @override
- State createState() => _SendReceiveTabMenuState();
-}
-
-class _SendReceiveTabMenuState extends State {
- late int _selectedIndex;
-
- void _onChanged(int newIndex) {
- if (_selectedIndex != newIndex) {
- setState(() {
- _selectedIndex = newIndex;
- });
- widget.onChanged?.call(_selectedIndex);
- }
- }
-
- @override
- void initState() {
- _selectedIndex = widget.initialIndex;
- super.initState();
- }
-
- @override
- Widget build(BuildContext context) {
- return Row(
- children: [
- Expanded(
- child: GestureDetector(
- onTap: () => _onChanged(0),
- child: Container(
- color: Colors.transparent,
- child: Column(
- children: [
- const SizedBox(
- height: 16,
- ),
- Text(
- "Send",
- style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color: _selectedIndex == 0
- ? Theme.of(context)
- .extension()!
- .accentColorBlue
- : Theme.of(context)
- .extension()!
- .textSubtitle1,
- ),
- ),
- const SizedBox(
- height: 19,
- ),
- Container(
- height: 2,
- decoration: BoxDecoration(
- color: _selectedIndex == 0
- ? Theme.of(context)
- .extension()!
- .accentColorBlue
- : Theme.of(context)
- .extension()!
- .background,
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- Expanded(
- child: GestureDetector(
- onTap: () => _onChanged(1),
- child: Container(
- color: Colors.transparent,
- child: Column(
- children: [
- const SizedBox(
- height: 16,
- ),
- Text(
- "Receive",
- style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color: _selectedIndex == 1
- ? Theme.of(context)
- .extension()!
- .accentColorBlue
- : Theme.of(context)
- .extension()!
- .textSubtitle1,
- ),
- ),
- const SizedBox(
- height: 19,
- ),
- Container(
- height: 2,
- decoration: BoxDecoration(
- color: _selectedIndex == 1
- ? Theme.of(context)
- .extension()!
- .accentColorBlue
- : Theme.of(context)
- .extension()!
- .background,
+ child: RecentDesktopTransactions(
+ walletId: walletId,
),
),
],
),
),
- ),
- ),
- ],
- );
- }
-}
-
-class RecentDesktopTransactions extends StatefulWidget {
- const RecentDesktopTransactions({Key? key}) : super(key: key);
-
- @override
- State createState() =>
- _RecentDesktopTransactionsState();
-}
-
-class _RecentDesktopTransactionsState extends State {
- @override
- Widget build(BuildContext context) {
- return Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Recent transactions",
- style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color: Theme.of(context)
- .extension()!
- .textFieldActiveSearchIconLeft,
- ),
- ),
- BlueTextButton(
- text: "See all",
- onTap: () {
- // todo: show all txns
- },
- ),
- ],
- ),
- const SizedBox(
- height: 16,
- ),
- Expanded(
- child: RoundedWhiteContainer(
- padding: const EdgeInsets.all(0),
- child: Container(),
- ),
- ),
- ],
- );
- }
-}
-
-class NetworkInfoButton extends StatelessWidget {
- const NetworkInfoButton({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return GestureDetector(
- child: Container(
- color: Colors.transparent,
- child: Row(
- children: [
- SvgPicture.asset(
- Assets.svg.network,
- width: 24,
- height: 24,
- color:
- Theme.of(context).extension()!.accentColorGreen,
- ),
- const SizedBox(
- width: 6,
- ),
- Text(
- "Synchronised",
- style: STextStyles.desktopMenuItemSelected(context),
- )
- ],
- ),
- ),
- );
- }
-}
-
-class WalletKeysButton extends StatelessWidget {
- const WalletKeysButton({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return GestureDetector(
- child: Container(
- color: Colors.transparent,
- child: Row(
- children: [
- SvgPicture.asset(
- Assets.svg.key,
- width: 20,
- height: 20,
- color: Theme.of(context)
- .extension()!
- .buttonTextSecondary,
- ),
- const SizedBox(
- width: 6,
- ),
- Text(
- "Wallet keys",
- style: STextStyles.desktopMenuItemSelected(context),
- )
],
),
),
diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart
new file mode 100644
index 0000000000..372c86e2f9
--- /dev/null
+++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart
@@ -0,0 +1,143 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:stackwallet/models/contact.dart';
+import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart';
+import 'package:stackwallet/providers/global/address_book_service_provider.dart';
+import 'package:stackwallet/utilities/enums/coin_enum.dart';
+import 'package:stackwallet/utilities/text_styles.dart';
+
+class AddressBookAddressChooser extends StatefulWidget {
+ const AddressBookAddressChooser({
+ Key? key,
+ this.coin,
+ }) : super(key: key);
+
+ final Coin? coin;
+
+ @override
+ State createState() =>
+ _AddressBookAddressChooserState();
+}
+
+class _AddressBookAddressChooserState extends State