diff --git a/assets/svg/dots-bg.svg b/assets/svg/dots-bg.svg new file mode 100644 index 000000000..4ef60b348 --- /dev/null +++ b/assets/svg/dots-bg.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/constants.dart b/lib/constants.dart index 5470bfa4e..c458c357b 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -181,6 +181,7 @@ const List CUSTOM_ICON_NAMES = [ 'stake-dark', 'stake-icon-dark', 'stake-icon', + 'dots-bg', 'stake', 'transaction-error', 'transaction-success', diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index a1079ffe4..508b35019 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart' show Color, MaterialColor, WidgetState, WidgetStateColor; class WitnetPallet { - static const black = Color(0xFF1D1D1B); + static const black = Color(0xFF232323); static const white = Color(0xFFFFFFFF); static const opacityWhite = Color(0xAFFFFFFF); static const opacityWhite2 = Color(0xA0FFFFFF); @@ -11,8 +11,9 @@ class WitnetPallet { static const lighterGrey = Color(0xFFEDECEC); static const lightGrey = Color(0xFFBDBDBD); - static const mediumGrey = Color(0xFF656565); + static const mediumGrey = Color(0xFF707070); static const darkGrey = Color(0xFF424242); + static const darkGrey2 = Color(0xFF343434); static const darkerGrey = Color(0xFF292929); static const transparent = Color(0x00FFFFFF); diff --git a/lib/theme/dark_theme.dart b/lib/theme/dark_theme.dart index 11ca44d20..4a3aa0f76 100644 --- a/lib/theme/dark_theme.dart +++ b/lib/theme/dark_theme.dart @@ -106,14 +106,14 @@ TextTheme textTheme = TextTheme( fontWeight: FontWeight.normal), ); InputDecorationTheme inputDecorationTheme = InputDecorationTheme( - fillColor: WitnetPallet.black, + fillColor: WitnetPallet.darkGrey2, filled: true, errorStyle: TextStyle(color: WitnetPallet.brightRed), helperStyle: TextStyle(color: WitnetPallet.white), helperMaxLines: 1, errorMaxLines: 1, - hintStyle: TextStyle(color: WitnetPallet.lighterGrey), - labelStyle: TextStyle(color: WitnetPallet.opacityWhite), + hintStyle: TextStyle(color: WitnetPallet.mediumGrey), + labelStyle: TextStyle(color: WitnetPallet.mediumGrey), hoverColor: const Color.fromARGB(9, 255, 255, 255), focusColor: WitnetPallet.brightCyanOpacity1, isDense: true, @@ -121,14 +121,12 @@ InputDecorationTheme inputDecorationTheme = InputDecorationTheme( contentPadding: EdgeInsets.all(16), enabledBorder: OutlineInputBorder( borderSide: BorderSide( - color: WitnetPallet.white, width: 1.0, style: BorderStyle.solid), + color: WitnetPallet.darkGrey2, width: 1.0, style: BorderStyle.solid), borderRadius: BorderRadius.circular(24), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( - color: WitnetPallet.brightCyanOpacity1, - width: 2.0, - style: BorderStyle.solid), + color: WitnetPallet.brightCyan, width: 2.0, style: BorderStyle.solid), borderRadius: BorderRadius.circular(24), ), errorBorder: OutlineInputBorder( @@ -176,7 +174,7 @@ ElevatedButtonThemeData elevatedButtonTheme = ElevatedButtonThemeData( style: ElevatedButton.styleFrom( padding: const EdgeInsets.all(16), foregroundColor: WitnetPallet.black, - backgroundColor: WitnetPallet.white, + backgroundColor: WitnetPallet.brightCyan, disabledForegroundColor: Color.fromARGB(102, 164, 212, 204).withOpacity(0.38), disabledBackgroundColor: diff --git a/lib/theme/extended_theme.dart b/lib/theme/extended_theme.dart index 57abef8e3..e738cc7a7 100644 --- a/lib/theme/extended_theme.dart +++ b/lib/theme/extended_theme.dart @@ -331,7 +331,7 @@ class ExtendedTheme extends ThemeExtension { dropdownBackgroundColor: WitnetPallet.white, dropdownTextColor: WitnetPallet.darkGrey, navigationActiveButton: WitnetPallet.lightGrey, - navigationPointerActiveButton: WitnetPallet.deepAqua, + navigationPointerActiveButton: WitnetPallet.black, headerActiveTextColor: WitnetPallet.black, headerTextColor: WitnetPallet.black, headerBackgroundColor: WitnetPallet.brightCyan, @@ -345,8 +345,8 @@ class ExtendedTheme extends ThemeExtension { txBorderColor: WitnetPallet.lightGrey, txValueNegativeColor: WitnetPallet.darkRed, txValuePositiveColor: WitnetPallet.darkGreen, - stepBarActiveColor: WitnetPallet.deepAqua, - stepBarActionableColor: WitnetPallet.darkGrey, + stepBarActiveColor: WitnetPallet.black, + stepBarActionableColor: WitnetPallet.mediumGrey, stepBarColor: WitnetPallet.lightGrey, switchActiveBg: WitnetPallet.black, switchActiveFg: WitnetPallet.white, @@ -408,7 +408,7 @@ class ExtendedTheme extends ThemeExtension { selectedTextColor: WitnetPallet.black, dropdownBackgroundColor: WitnetPallet.darkerGrey, dropdownTextColor: WitnetPallet.lighterGrey, - navigationPointerActiveButton: WitnetPallet.deepAqua, + navigationPointerActiveButton: WitnetPallet.lightGrey, navigationActiveButton: WitnetPallet.lightGrey, headerActiveTextColor: WitnetPallet.black, headerTextColor: WitnetPallet.black, @@ -418,8 +418,8 @@ class ExtendedTheme extends ThemeExtension { walletActiveItemBackgroundColor: WitnetPallet.brightCyanOpacity3, walletActiveItemBorderColor: WitnetPallet.brightCyan, walletItemBorderColor: WitnetPallet.opacityWhite2, - inputIconColor: WitnetPallet.opacityWhite2, - txBorderColor: WitnetPallet.opacityWhite2, + inputIconColor: WitnetPallet.white, + txBorderColor: WitnetPallet.darkGrey2, txValueNegativeColor: WitnetPallet.brightRed, txValuePositiveColor: WitnetPallet.brightGreen, stepBarActiveColor: WitnetPallet.brightCyan, diff --git a/lib/theme/light_theme.dart b/lib/theme/light_theme.dart index 856b58e71..cc0643851 100644 --- a/lib/theme/light_theme.dart +++ b/lib/theme/light_theme.dart @@ -44,7 +44,7 @@ TextTheme textTheme = TextTheme( fontFamily: 'Almarai', letterSpacing: 0, height: 1.15, - color: WitnetPallet.deepAqua, + color: WitnetPallet.black, fontSize: 16, fontWeight: FontWeight.normal), titleLarge: TextStyle( @@ -100,7 +100,7 @@ TextTheme textTheme = TextTheme( fontFamily: 'Almarai', letterSpacing: 0, height: 1.15, - color: WitnetPallet.deepAqua, + color: WitnetPallet.black, fontSize: 16, fontWeight: FontWeight.normal), ); @@ -116,7 +116,7 @@ InputDecorationTheme inputDecorationTheme = InputDecorationTheme( helperStyle: TextStyle(color: WitnetPallet.darkerGrey), helperMaxLines: 1, errorMaxLines: 1, - hintStyle: TextStyle(), + hintStyle: TextStyle(color: WitnetPallet.mediumGrey), hoverColor: WitnetPallet.white, focusColor: WitnetPallet.brightCyan, isDense: true, @@ -198,25 +198,25 @@ OutlinedButtonThemeData outlinedButtonTheme = OutlinedButtonThemeData( ), )); IconThemeData iconTheme = IconThemeData( - color: WitnetPallet.deepAqua, + color: WitnetPallet.black, size: 16, ); IconThemeData primaryIconTheme = IconThemeData( - color: WitnetPallet.deepAqua, + color: WitnetPallet.black, size: 24, ); CheckboxThemeData checkboxTheme = CheckboxThemeData( splashRadius: 0, side: WidgetStateBorderSide.resolveWith( - (_) => const BorderSide(width: 2, color: WitnetPallet.deepAqua)), + (_) => const BorderSide(width: 2, color: WitnetPallet.black)), fillColor: WidgetStateProperty.resolveWith((Set states) { if (states.contains(WidgetState.selected)) { - return WitnetPallet.deepAqua; + return WitnetPallet.brightCyan; } return WitnetPallet.transparent; }), - checkColor: WidgetStateProperty.all(WitnetPallet.white), - overlayColor: WidgetStateProperty.all(WitnetPallet.white), + checkColor: WidgetStateProperty.all(WitnetPallet.black), + overlayColor: WidgetStateProperty.all(WitnetPallet.black), ); Color getColorPrimary(Set states) { @@ -255,7 +255,7 @@ PageTransitionsTheme pageTransitionsTheme = PageTransitionsTheme( ); TimePickerThemeData timePickerTheme = TimePickerThemeData( - backgroundColor: const Color.fromARGB(255, 125, 80, 80), + backgroundColor: WitnetPallet.white, cancelButtonStyle: textButtonTheme.style, confirmButtonStyle: textButtonTheme.style, diff --git a/lib/util/storage/database/adapters/transaction_adapter.dart b/lib/util/storage/database/adapters/transaction_adapter.dart index a7e581fc6..b44af4a1c 100644 --- a/lib/util/storage/database/adapters/transaction_adapter.dart +++ b/lib/util/storage/database/adapters/transaction_adapter.dart @@ -144,7 +144,7 @@ class BuildTransaction { } } - dynamic getAddress(layout.TransactionType txType) { + dynamic getOrigin(layout.TransactionType txType) { switch (txType) { case layout.TransactionType.Vtt: return this.vtTransaction?.body.outputs[0].pkh.address; diff --git a/lib/util/storage/database/database_service.dart b/lib/util/storage/database/database_service.dart index 890857b11..7ec58bfec 100644 --- a/lib/util/storage/database/database_service.dart +++ b/lib/util/storage/database/database_service.dart @@ -7,7 +7,6 @@ import 'package:my_wit_wallet/util/storage/database/get_account_vtts_map.dart'; import 'package:my_wit_wallet/util/storage/database/stats.dart'; import 'package:my_wit_wallet/util/storage/database/adapters/transaction_adapter.dart'; import 'package:sembast/sembast_io.dart'; -import 'package:sembast/sembast.dart'; import 'package:witnet/explorer.dart'; import 'package:my_wit_wallet/constants.dart'; import 'package:my_wit_wallet/util/storage/database/account.dart'; diff --git a/lib/util/transactions_list/get_transaction_label.dart b/lib/util/transactions_list/get_transaction_label.dart index 5f5072dad..eb1ee1f27 100644 --- a/lib/util/transactions_list/get_transaction_label.dart +++ b/lib/util/transactions_list/get_transaction_label.dart @@ -1,17 +1,15 @@ import 'package:my_wit_wallet/util/storage/database/account.dart'; -import 'package:flutter/material.dart'; import 'package:my_wit_wallet/util/get_localization.dart'; import 'package:witnet/explorer.dart'; String getTransactionLabel( - List externalAddresses, - List internalAddresses, - List inputs, - Account? singleAddressAccount, - BuildContext? context) { + {required List externalAddresses, + required List internalAddresses, + required List inputs, + required Account? singleAddressAccount}) { String label = ''; - String _from = context != null ? localization.from : "from"; - String _to = context != null ? localization.to : "to"; + String _from = localization.from; + String _to = localization.to; if (inputs.length < 1) { return _from; } diff --git a/lib/util/transactions_list/transaction_details.dart b/lib/util/transactions_list/transaction_details.dart new file mode 100644 index 000000000..111bfa068 --- /dev/null +++ b/lib/util/transactions_list/transaction_details.dart @@ -0,0 +1,241 @@ +import 'package:flutter/material.dart'; +import 'package:my_wit_wallet/constants.dart'; +import 'package:my_wit_wallet/shared/api_database.dart'; +import 'package:my_wit_wallet/shared/locator.dart'; +import 'package:my_wit_wallet/theme/colors.dart'; +import 'package:my_wit_wallet/theme/extended_theme.dart'; +import 'package:my_wit_wallet/util/get_localization.dart'; +import 'package:my_wit_wallet/util/storage/database/account.dart'; +import 'package:my_wit_wallet/util/storage/database/adapters/transaction_adapter.dart'; +import 'package:my_wit_wallet/util/storage/database/wallet.dart'; +import 'package:my_wit_wallet/util/extensions/num_extensions.dart'; +import 'package:my_wit_wallet/util/extensions/string_extensions.dart'; +import 'package:my_wit_wallet/util/transactions_list/get_transaction_label.dart'; +import 'package:my_wit_wallet/util/extensions/int_extensions.dart'; +import 'package:witnet/explorer.dart'; +import 'package:witnet/schema.dart'; + +class TransactionValue { + final String label; + final String prefix; + final String amount; + TransactionValue( + {required this.label, required this.prefix, required this.amount}); +} + +class TransactionUtils { + Wallet currentWallet = + Locator.instance.get().walletStorage.currentWallet; + + final GeneralTransaction vti; + TransactionUtils({required this.vti}); + + List get externalAddresses { + return currentWallet.externalAccounts.values + .map((account) => account.address) + .toList(); + } + + List get internalAddresses { + return currentWallet.internalAccounts.values + .map((account) => account.address) + .toList(); + } + + Account? get singleAddressAccount { + return currentWallet.walletType == WalletType.single + ? currentWallet.masterAccount + : null; + } + + int lockedValue() { + int amountLocked = 0; + if (vti.type == TransactionType.value_transfer) { + vti.vtt!.outputs.forEach((element) { + if (element.timeLock > DateTime.now().millisecondsSinceEpoch ~/ 1000) { + amountLocked += element.value.toInt(); + } + }); + } + return amountLocked; + } + + String? timelock() { + String? timelock = null; + if (vti.type == TransactionType.value_transfer) { + vti.vtt!.outputs.forEach((element) { + if (element.timeLock > DateTime.now().millisecondsSinceEpoch ~/ 1000) { + timelock = element.timeLock.toInt().formatDate(); + } + }); + } + return timelock; + } + + int receiveValue() { + int nanoWitvalue = 0; + if (vti.type == TransactionType.value_transfer) { + vti.vtt!.outputs.forEach((element) { + if ((externalAddresses.contains(element.pkh.address) || + internalAddresses.contains(element.pkh.address))) { + nanoWitvalue += element.value.toInt(); + } else if (singleAddressAccount != null && + singleAddressAccount!.address == element.pkh.address) { + nanoWitvalue += element.value.toInt(); + } + }); + return nanoWitvalue; + } else if (vti.mint != null) { + vti.mint!.outputs.forEach((element) { + if ((externalAddresses.contains(element.pkh.address) || + internalAddresses.contains(element.pkh.address))) { + nanoWitvalue += element.value.toInt(); + } else if (singleAddressAccount != null && + singleAddressAccount!.address == element.pkh.address) { + nanoWitvalue += element.value.toInt(); + } + }); + return nanoWitvalue; + } else if (vti.stake != null) { + // TODO(#542): get value and correct type for for stake transactions + vti.stake!.outputs.forEach((element) { + if ((externalAddresses.contains(element.pkh.address) || + internalAddresses.contains(element.pkh.address))) { + nanoWitvalue += element.value.toInt(); + } else if (singleAddressAccount != null && + singleAddressAccount!.address == element.pkh.address) { + nanoWitvalue += element.value.toInt(); + } + }); + return nanoWitvalue; + } else { + // TODO(#542): get value and correct type for unstake transactions + vti.unstake!.outputs.forEach((element) { + if ((externalAddresses.contains(element.pkh.address) || + internalAddresses.contains(element.pkh.address))) { + nanoWitvalue += element.value.toInt(); + } else if (singleAddressAccount != null && + singleAddressAccount!.address == element.pkh.address) { + nanoWitvalue += element.value.toInt(); + } + }); + return nanoWitvalue; + } + } + + int sendValue() { + if (vti.type != TransactionType.value_transfer || + vti.vtt!.outputs.length <= 0) { + return 0; + } + bool isInternalTx = + externalAddresses.contains(vti.vtt!.outputs[0].pkh.address) || + internalAddresses.contains(vti.vtt!.outputs[0].pkh.address) || + singleAddressAccount?.address == vti.vtt!.outputs[0].pkh.address; + return isInternalTx ? vti.fee : vti.vtt!.outputs[0].value.toInt(); + } + + String getLabel() { + if (vti.type == TransactionType.value_transfer) { + return getTransactionLabel( + externalAddresses: externalAddresses, + internalAddresses: internalAddresses, + inputs: vti.vtt!.inputs, + singleAddressAccount: singleAddressAccount); + } else { + // TODO(#542): set stake and unstake transaction label when feature is supported + return localization.from; + } + } + + String getOrigin() { + if (vti.type == TransactionType.value_transfer) { + return getTransactionAddress( + getLabel(), vti.vtt!.inputs, vti.vtt!.outputs); + } else { + // TODO(#542): set stake and unstake transaction label when feature is supported + return 'Mint'; + } + } + + String getTransactionAddress( + String label, List inputs, List outputs) { + String address = ''; + if (inputs.length < 1) + return 'genesis'; + else if (label == localization.from && inputs.length > 0) { + address = getSenderAddress().cropMiddle(18); + } else if (outputs.length > 0) { + address = getRecipientAddress().cropMiddle(18); + } + return address; + } + + String getSenderAddress() { + return vti.vtt!.inputs[0].address; + } + + String getRecipientAddress() { + return vti.vtt!.outputs[0].pkh.address; + } + + TransactionValue getTransactionValue() { + String _label = getLabel(); + if (_label == localization.from) { + return TransactionValue( + label: _label, + prefix: '+', + amount: + '${receiveValue().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', + ); + } else if (sendValue().standardizeWitUnits().toString() != '0') { + return TransactionValue( + label: _label, + prefix: '-', + amount: + '${sendValue().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', + ); + } else { + return TransactionValue( + label: _label, + prefix: '', + amount: + '${sendValue().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', + ); + } + } + + Widget buildTransactionValue(label, context) { + final theme = Theme.of(context); + final extendedTheme = theme.extension()!; + int _lockedWit = lockedValue(); + if (label == localization.from) { + return Text( + ' + ${receiveValue().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', + style: theme.textTheme.bodyLarge?.copyWith( + color: _lockedWit > 0 + ? WitnetPallet.mediumGrey + : extendedTheme.txValuePositiveColor), + overflow: TextOverflow.ellipsis, + ); + } else if (sendValue().standardizeWitUnits().toString() != '0') { + return Text( + ' - ${sendValue().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', + style: theme.textTheme.bodyLarge?.copyWith( + color: _lockedWit > 0 + ? WitnetPallet.mediumGrey + : extendedTheme.txValueNegativeColor), + overflow: TextOverflow.ellipsis, + ); + } else { + return Text( + '${sendValue().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', + style: theme.textTheme.bodyLarge!.copyWith( + color: _lockedWit > 0 + ? WitnetPallet.mediumGrey + : theme.textTheme.bodyLarge!.color), + overflow: TextOverflow.ellipsis, + ); + } + } +} diff --git a/lib/widgets/PaddedButton.dart b/lib/widgets/PaddedButton.dart index c19a8eb81..d5aba3445 100644 --- a/lib/widgets/PaddedButton.dart +++ b/lib/widgets/PaddedButton.dart @@ -20,12 +20,11 @@ enum ButtonType { enum IconPosition { left, right } Widget buildCircularProgress(context, ThemeData theme) { - final extendedTheme = theme.extension()!; return SizedBox( height: 20, width: 20, child: CircularProgressIndicator( - color: extendedTheme.spinnerColor, + color: theme.colorScheme.surface, strokeWidth: 2, value: null, semanticsLabel: 'Circular progress indicator', diff --git a/lib/widgets/clickable_box.dart b/lib/widgets/clickable_box.dart index 88492e352..e15119cc7 100644 --- a/lib/widgets/clickable_box.dart +++ b/lib/widgets/clickable_box.dart @@ -88,7 +88,7 @@ class ClickableBox extends StatelessWidget { padding: EdgeInsets.only(top: 14, bottom: 14, left: 16, right: 16), decoration: BoxDecoration( color: localTheme(extendedTheme)[ClickableBoxTheme.BgColor], - borderRadius: BorderRadius.all(Radius.circular(4)), + borderRadius: BorderRadius.all(Radius.circular(24)), border: Border.all( color: localTheme(extendedTheme)[ClickableBoxTheme.BorderColor]!, width: 1, diff --git a/lib/widgets/container_background.dart b/lib/widgets/container_background.dart new file mode 100644 index 000000000..2b4f9ecd7 --- /dev/null +++ b/lib/widgets/container_background.dart @@ -0,0 +1,24 @@ +import 'package:my_wit_wallet/theme/extended_theme.dart'; +import 'package:flutter/material.dart'; + +typedef void VoidCallback(); + +class ContainerBackground extends StatelessWidget { + ContainerBackground({required this.content, this.padding, this.marginTop}); + final Widget content; + final double? padding; + final double? marginTop; + + Widget build(BuildContext context) { + final theme = Theme.of(context); + final extendedTheme = theme.extension()!; + ; + return Container( + margin: EdgeInsets.only(top: marginTop ?? 8), + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24), + color: extendedTheme.backgroundBox), + child: Padding(padding: EdgeInsets.all(padding ?? 16), child: content)); + } +} diff --git a/lib/widgets/copy_button.dart b/lib/widgets/copy_button.dart new file mode 100644 index 000000000..7a2d00155 --- /dev/null +++ b/lib/widgets/copy_button.dart @@ -0,0 +1,62 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:my_wit_wallet/theme/extended_theme.dart'; +import 'package:my_wit_wallet/util/get_localization.dart'; +import 'package:my_wit_wallet/widgets/PaddedButton.dart'; +import 'package:my_wit_wallet/widgets/snack_bars.dart'; + +typedef void VoidCallback(); + +class CopyButton extends StatefulWidget { + final String copyContent; + CopyButton({required this.copyContent}); + + @override + CopyButtonState createState() => CopyButtonState(); +} + +class CopyButtonState extends State { + bool isAddressCopied = false; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final extendedTheme = theme.extension()!; + + return PaddedButton( + padding: EdgeInsets.zero, + label: localization.copyAddressToClipboard, + text: localization.copyAddressToClipboard, + type: ButtonType.iconButton, + color: extendedTheme.headerTextColor, + iconSize: 12, + onPressed: () async { + if (!isAddressCopied) { + await Clipboard.setData(ClipboardData(text: widget.copyContent)); + if (await Clipboard.hasStrings()) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + buildCopiedSnackbar(theme, localization.addressCopied)); + setState(() { + isAddressCopied = true; + }); + if (this.mounted) { + Timer(Duration(milliseconds: 500), () { + setState(() { + isAddressCopied = false; + }); + }); + } + } + } + }, + icon: Icon( + color: extendedTheme.headerTextColor, + isAddressCopied ? FontAwesomeIcons.check : FontAwesomeIcons.copy, + size: 12, + )); + } +} diff --git a/lib/widgets/info_element.dart b/lib/widgets/info_element.dart index a422ba045..867a5e506 100644 --- a/lib/widgets/info_element.dart +++ b/lib/widgets/info_element.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:my_wit_wallet/widgets/copy_button.dart'; import 'package:my_wit_wallet/widgets/link.dart'; class InfoElement extends StatelessWidget { @@ -8,6 +9,7 @@ class InfoElement extends StatelessWidget { final bool plainText; final Color? color; final bool isLastItem; + final String? copyText; const InfoElement({ required this.label, @@ -16,8 +18,28 @@ class InfoElement extends StatelessWidget { this.isLastItem = false, this.url, this.color, + this.copyText, }); + Widget buildContentWithCopyIcon(BuildContext context, ThemeData theme) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + buildContent(theme), + Flexible(child: CopyButton(copyContent: copyText ?? '')), + ]); + } + + Widget buildContent(ThemeData theme) { + return url != null + ? CustomLink(text: text, url: url ?? '', color: color) + : Text(text, + style: (color != null + ? theme.textTheme.bodyLarge?.copyWith(color: color) + : theme.textTheme.bodyLarge)); + } + Widget build(BuildContext context) { final theme = Theme.of(context); return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -28,12 +50,9 @@ class InfoElement extends StatelessWidget { : theme.textTheme.displaySmall, ), SizedBox(height: 8), - url != null - ? CustomLink(text: text, url: url ?? '') - : Text(text, - style: (color != null - ? theme.textTheme.bodyLarge?.copyWith(color: color) - : theme.textTheme.bodyLarge)), + copyText != null + ? buildContentWithCopyIcon(context, theme) + : buildContent(theme), SizedBox(height: isLastItem ? 0 : 16), ]); } diff --git a/lib/widgets/input_amount.dart b/lib/widgets/input_amount.dart index b3295e52f..b76d0b20e 100644 --- a/lib/widgets/input_amount.dart +++ b/lib/widgets/input_amount.dart @@ -48,47 +48,50 @@ typedef PointerDownCallback = void Function(PointerDownEvent); class _InputAmountState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); - return Container( - child: TextFormField( - decoration: InputDecoration( - hintText: widget.hint ?? localization.inputAmountHint, - errorText: widget.errorText, - contentPadding: - EdgeInsets.only(top: 8, bottom: 8, left: 16, right: 16), - prefixIcon: - widget.prefixIcon != null ? Icon(widget.prefixIcon) : null, - suffixText: WIT_UNIT[WitUnit.Wit], - suffixIconConstraints: BoxConstraints(minHeight: 44), - suffixIcon: widget.onSuffixTap != null + return Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [ + TextFormField( + decoration: InputDecoration( + hintText: widget.hint ?? localization.inputAmountHint, + errorText: widget.errorText, + prefixIcon: + widget.prefixIcon != null ? Icon(widget.prefixIcon) : null, + suffixText: WIT_UNIT[WitUnit.Wit], + suffixIconConstraints: BoxConstraints(minHeight: 44), + ), + minLines: 1, + keyboardType: Platform.isIOS + ? TextInputType.numberWithOptions(signed: true, decimal: true) + : widget.keyboardType, + inputFormatters: [WitValueFormatter()], + style: theme.textTheme.bodyLarge, + autocorrect: false, + focusNode: widget.focusNode, + controller: widget.textEditingController, + onChanged: widget.onChanged ?? (String? value) {}, + onEditingComplete: widget.onEditingComplete ?? () {}, + onFieldSubmitted: widget.onFieldSubmitted ?? (String? value) {}, + onTapOutside: widget.onTapOutside ?? (PointerDownEvent? event) {}, + onTap: widget.onTap ?? () {}, + validator: widget.validator, + ), + widget.onSuffixTap != null ? SizedBox(height: 8) : Container(), + widget.onSuffixTap != null ? Padding( padding: EdgeInsets.only(left: 8), child: Semantics( - label: 'Max', + label: 'Max amount', child: PaddedButton( padding: EdgeInsets.zero, + boldText: true, text: 'Max', sizeCover: false, onPressed: widget.onSuffixTap ?? () {}, - type: ButtonType.sufix), + type: ButtonType.text), )) - : null, - ), - minLines: 1, - keyboardType: Platform.isIOS - ? TextInputType.numberWithOptions(signed: true, decimal: true) - : widget.keyboardType, - inputFormatters: [WitValueFormatter()], - style: theme.textTheme.bodyLarge, - autocorrect: false, - focusNode: widget.focusNode, - controller: widget.textEditingController, - onChanged: widget.onChanged ?? (String? value) {}, - onEditingComplete: widget.onEditingComplete ?? () {}, - onFieldSubmitted: widget.onFieldSubmitted ?? (String? value) {}, - onTapOutside: widget.onTapOutside ?? (PointerDownEvent? event) {}, - onTap: widget.onTap ?? () {}, - validator: widget.validator, - ), - ); + : Container(), + ]); } } diff --git a/lib/widgets/layouts/headerLayout.dart b/lib/widgets/layouts/headerLayout.dart index f4ff4a4e5..8308c750e 100644 --- a/lib/widgets/layouts/headerLayout.dart +++ b/lib/widgets/layouts/headerLayout.dart @@ -77,37 +77,45 @@ class HeaderLayout extends StatelessWidget { : HEADER_HEIGHT, width: MediaQuery.of(context).size.width, color: extendedTheme.headerBackgroundColor, - child: Column(children: [ - Container( - padding: navigationBarPadding(), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: navigationActions.length > 1 - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.start, - children: navigationActions, - )), - Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: 50, - maxWidth: dashboardActions != null - ? MediaQuery.of(context).size.width * 0.9 - : MediaQuery.of(context).size.width * 0.3), - child: Column( - children: [ - dashboardActions != null - ? dashboardActions! - : witnetEyeIcon(theme, - height: witnetLogoHeight) - ], - ), + child: Stack(children: [ + Positioned( + top: -100, + right: -60, + child: + svgThemeImage(theme, name: 'dots-bg', height: 270)), + Column(children: [ + Container( + padding: navigationBarPadding(), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: navigationActions.length > 1 + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.start, + children: navigationActions, )), - ])), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: 50, + maxWidth: dashboardActions != null + ? MediaQuery.of(context).size.width * 0.9 + : MediaQuery.of(context).size.width * + 0.3), + child: Column( + children: [ + dashboardActions != null + ? dashboardActions! + : witnetEyeIcon(theme, + height: witnetLogoHeight) + ], + ), + )), + ])), + ]) ]), ))))); } diff --git a/lib/widgets/layouts/layout.dart b/lib/widgets/layouts/layout.dart index 5a78fe344..15c7019d9 100644 --- a/lib/widgets/layouts/layout.dart +++ b/lib/widgets/layouts/layout.dart @@ -290,30 +290,20 @@ class LayoutState extends State with TickerProviderStateMixin { Widget dashboardBottomBar() { final theme = Theme.of(context); + final extendedTheme = theme.extension()!; return Container( decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - theme.colorScheme.surface.withOpacity(0), - theme.colorScheme.surface.withOpacity(0.7), - theme.colorScheme.surface.withOpacity(0.8), - theme.colorScheme.surface.withOpacity(0.9), - theme.colorScheme.surface.withOpacity(0.95), - theme.colorScheme.surface.withOpacity(0.97), - theme.colorScheme.surface.withOpacity(0.98), - theme.colorScheme.surface.withOpacity(0.99), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - stops: [0, 0.03, 0.05, 0.1, 0.15, 0.2, 0.95, 1], - )), + color: theme.colorScheme.surface, + border: Border( + top: BorderSide( + width: 0.3, color: extendedTheme.txBorderColor!))), child: Padding( padding: EdgeInsets.only(top: 16), child: BottomAppBar( padding: const EdgeInsets.symmetric(horizontal: 10), height: 60, - surfaceTintColor: theme.colorScheme.surface.withOpacity(0.0), - color: theme.colorScheme.surface.withOpacity(0.0), + surfaceTintColor: theme.colorScheme.surface, + color: theme.colorScheme.surface, child: widget.bottomNavigation))); } diff --git a/lib/widgets/link.dart b/lib/widgets/link.dart index 846dafc09..96986ee4b 100644 --- a/lib/widgets/link.dart +++ b/lib/widgets/link.dart @@ -5,11 +5,10 @@ import 'package:my_wit_wallet/theme/extended_theme.dart'; class CustomLink extends StatelessWidget { final String text; final String url; + final Color? color; - const CustomLink({ - required this.text, - required this.url, - }); + const CustomLink( + {required this.text, required this.url, required this.color}); _launchUrl(String searchItem) async { try { @@ -29,6 +28,9 @@ class CustomLink extends StatelessWidget { padding: EdgeInsets.only(right: 8), child: Text(text, style: extendedTheme.monoSmallText!.copyWith( + color: color != null + ? color + : extendedTheme.monoSmallText!.color, fontWeight: FontWeight.w400, decoration: TextDecoration.underline))), onTap: () => {_launchUrl(url)}, diff --git a/lib/widgets/select_wallet_box.dart b/lib/widgets/select_wallet_box.dart index 698c552f6..a8270f9ed 100644 --- a/lib/widgets/select_wallet_box.dart +++ b/lib/widgets/select_wallet_box.dart @@ -60,9 +60,9 @@ class SelectWalletBox extends StatelessWidget { decoration: BoxDecoration( color: WitnetPallet.black, border: Border.all(color: WitnetPallet.transparent), - borderRadius: BorderRadius.all(Radius.circular(8))), + borderRadius: BorderRadius.all(Radius.circular(24))), child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8)), + borderRadius: BorderRadius.all(Radius.circular(24)), child: Container( decoration: BoxDecoration( color: WitnetPallet.black, diff --git a/lib/widgets/snack_bars.dart b/lib/widgets/snack_bars.dart index 2471c5b2c..283c81c9c 100644 --- a/lib/widgets/snack_bars.dart +++ b/lib/widgets/snack_bars.dart @@ -12,6 +12,8 @@ SnackBar buildCopiedSnackbar(ThemeData theme, String text) { final extendedTheme = theme.extension()!; return SnackBar( width: 150, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(24))), clipBehavior: Clip.none, content: Text(text, textAlign: TextAlign.center, diff --git a/lib/widgets/suffix_icon_button.dart b/lib/widgets/suffix_icon_button.dart index 75fcf4a8d..fa2a80fb8 100644 --- a/lib/widgets/suffix_icon_button.dart +++ b/lib/widgets/suffix_icon_button.dart @@ -34,6 +34,6 @@ class SuffixIcon extends StatelessWidget { onPressed: onPressed, color: isFocus ? theme.textSelectionTheme.cursorColor - : theme.inputDecorationTheme.enabledBorder?.borderSide.color); + : theme.textTheme.bodyMedium!.color); } } diff --git a/lib/widgets/toggle_switch.dart b/lib/widgets/toggle_switch.dart index ab7798aeb..95cf8778d 100644 --- a/lib/widgets/toggle_switch.dart +++ b/lib/widgets/toggle_switch.dart @@ -88,7 +88,7 @@ class ToggleSwitch extends StatefulWidget { this.inactiveBgColor, this.inactiveFgColor, this.onToggle, - this.cornerRadius = 8.0, + this.cornerRadius = 24.0, this.initialLabelIndex = 0, this.minWidth = 72.0, this.minHeight = 40.0, diff --git a/lib/widgets/transaction_details.dart b/lib/widgets/transaction_details.dart index 35f8b1337..046dd6736 100644 --- a/lib/widgets/transaction_details.dart +++ b/lib/widgets/transaction_details.dart @@ -1,19 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:my_wit_wallet/util/storage/database/account.dart'; import 'package:my_wit_wallet/util/storage/database/adapters/transaction_adapter.dart'; import 'package:my_wit_wallet/util/storage/database/wallet.dart'; -import 'package:my_wit_wallet/util/transactions_list/get_transaction_label.dart'; +import 'package:my_wit_wallet/util/transactions_list/transaction_details.dart'; import 'package:my_wit_wallet/widgets/closable_view.dart'; +import 'package:my_wit_wallet/widgets/container_background.dart'; import 'package:my_wit_wallet/widgets/speedup_btn.dart'; import 'package:witnet/explorer.dart'; -import 'package:witnet/schema.dart'; import 'package:my_wit_wallet/util/get_localization.dart'; import 'package:my_wit_wallet/constants.dart'; -import 'package:my_wit_wallet/theme/colors.dart'; import 'package:my_wit_wallet/theme/extended_theme.dart'; import 'package:my_wit_wallet/util/extensions/int_extensions.dart'; import 'package:my_wit_wallet/util/extensions/num_extensions.dart'; +import 'package:my_wit_wallet/util/extensions/string_extensions.dart'; import 'package:my_wit_wallet/widgets/info_element.dart'; typedef void VoidCallback(); @@ -75,92 +74,6 @@ class TransactionDetails extends StatelessWidget { } } - Widget _buildOutput( - ThemeData theme, ValueTransferOutput output, bool isLastOutput) { - final extendedTheme = theme.extension()!; - Widget timelock = SizedBox(height: 0); - if (output.timeLock != 0) { - timelock = Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Icon( - DateTime.now().millisecondsSinceEpoch ~/ 1000 < - output.timeLock.toInt() - ? FontAwesomeIcons.lock - : FontAwesomeIcons.unlock, - size: 10), - SizedBox( - width: 8, - ), - Text( - output.timeLock.toInt().formatDate(), - style: theme.textTheme.bodySmall, - ), - ]); - } - // - return Container( - padding: EdgeInsets.only(top: 16, bottom: 16), - decoration: BoxDecoration( - color: WitnetPallet.transparent, - border: Border( - bottom: BorderSide( - color: !isLastOutput - ? extendedTheme.txBorderColor! - : WitnetPallet.transparent, - width: 1, - )), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text(output.pkh.address.toString(), - style: extendedTheme.monoSmallText), - SizedBox(height: 8), - Text( - '${output.value.toInt().standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', - style: theme.textTheme.labelMedium), - SizedBox(height: 8), - output.timeLock.toInt() > 0 ? timelock : Container() - ], - ), - ), - ], - )); - } - - Widget _buildInput(ThemeData theme, InputUtxo input, bool isLastInput) { - final extendedTheme = theme.extension()!; - return Container( - padding: EdgeInsets.only(top: 8, bottom: 8), - decoration: BoxDecoration( - color: WitnetPallet.transparent, - border: Border( - bottom: BorderSide( - color: !isLastInput - ? extendedTheme.txBorderColor! - : WitnetPallet.transparent, - width: 1, - )), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(input.address.toString(), style: extendedTheme.monoSmallText), - SizedBox(height: 8), - Text( - '${input.value.standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', - style: theme.textTheme.labelMedium), - ], - )); - } - bool _isPendingTransaction(TxStatusLabel status) { return status == TxStatusLabel.pending; } @@ -171,87 +84,90 @@ class TransactionDetails extends StatelessWidget { transaction: transaction); } + Color? getStatusColor(TxStatusLabel status, ThemeData theme) { + final extendedTheme = theme.extension()!; + switch (status) { + case TxStatusLabel.pending: + return extendedTheme.warningColor; + case TxStatusLabel.confirmed: + return extendedTheme.txValuePositiveColor; + case TxStatusLabel.mined: + return extendedTheme.txValuePositiveColor; + case TxStatusLabel.reverted: + return extendedTheme.txValueNegativeColor; + case TxStatusLabel.unknown: + return theme.textTheme.labelMedium?.color; + } + } + Widget build(BuildContext context) { final theme = Theme.of(context); - String label = ''; - if (transaction.type == TransactionType.value_transfer) { - label = getTransactionLabel(externalAddresses, internalAddresses, - transaction.vtt!.inputs, singleAddressAccount, context); - } - List outputs = - transaction.type == TransactionType.value_transfer - ? transaction.vtt!.outputs - : transaction.mint!.outputs; + TransactionUtils transactionUtils = TransactionUtils(vti: transaction); + String label = transactionUtils.getLabel(); + String? timelock = transactionUtils.timelock(); return ClosableView(closeSetting: goToList, children: [ Text( localization.transactionDetails, style: theme.textTheme.titleLarge, ), SizedBox(height: 24), - InfoElement( - label: localization.status, - text: transactionStatus(transaction.status), - color: theme.textTheme.labelMedium?.color), - InfoElement( - label: localization.transactionId, - text: transaction.txnHash, - url: 'https://witnet.network/search/${transaction.txnHash}', - ), - InfoElement( - label: localization.epoch, - text: _isPendingTransaction(transaction.status) - ? '_' - : transaction.epoch.toString()), - InfoElement( - label: localization.type, text: transactionType(transaction.type)), - InfoElement( - label: transaction.type == TransactionType.value_transfer - ? localization.feesPayed - : localization.feesCollected, - text: - '${transaction.fee.standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}'), - InfoElement( - label: localization.timestamp, - text: _isPendingTransaction(transaction.status) - ? '_' - : transaction.txnTime.formatDate()), - transaction.type == TransactionType.value_transfer - ? Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - localization.inputs, - style: theme.textTheme.displaySmall, - ), - SizedBox(height: 8), - ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - physics: NeverScrollableScrollPhysics(), - itemCount: transaction.vtt!.inputs.length, - itemBuilder: (context, index) { - return _buildInput(theme, transaction.vtt!.inputs[index], - index + 1 == transaction.vtt!.inputs.length); - }, - ), - SizedBox(height: 16), - ]) - : Container(), - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - localization.outputs, - style: theme.textTheme.displaySmall, - ), - SizedBox(height: 8), - ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - physics: NeverScrollableScrollPhysics(), - itemCount: outputs.length, - itemBuilder: (context, index) { - return _buildOutput( - theme, outputs[index], index + 1 == outputs.length); - }, - ), - ]), + ContainerBackground( + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + InfoElement( + label: localization.status, + text: transactionStatus(transaction.status), + url: 'https://witnet.network/search/${transaction.txnHash}', + color: getStatusColor(transaction.status, theme)), + InfoElement( + label: localization.type, + text: transactionType(transaction.type)), + InfoElement( + label: localization.from.toTitleCase(), + text: transactionUtils.getSenderAddress()), + InfoElement( + label: localization.to, + text: transactionUtils.getRecipientAddress()), + InfoElement( + label: localization.amount, + text: transactionUtils.getTransactionValue().amount, + ), + timelock != null ? InfoElement( + label: localization.timelock, + text: transactionUtils.timelock()!, + ): Container(), + InfoElement( + label: transaction.type == TransactionType.value_transfer + ? localization.feesPayed + : localization.feesCollected, + text: + '${transaction.fee.standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}'), + ], + )), + ContainerBackground( + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + InfoElement( + label: localization.transactionId, + text: transaction.txnHash.cropMiddle(30), + copyText: transaction.txnHash, + url: 'https://witnet.network/search/${transaction.txnHash}', + ), + InfoElement( + label: localization.timestamp, + text: _isPendingTransaction(transaction.status) + ? '_' + : transaction.txnTime.formatDate()), + InfoElement( + label: localization.epoch, + text: _isPendingTransaction(transaction.status) + ? '_' + : transaction.epoch.toString()), + ])), SizedBox(height: 8), transaction.status == TxStatusLabel.pending && label == localization.to ? buildSpeedUpBtn() diff --git a/lib/widgets/transaction_item.dart b/lib/widgets/transaction_item.dart index 5ce15e438..65e8c5abf 100644 --- a/lib/widgets/transaction_item.dart +++ b/lib/widgets/transaction_item.dart @@ -1,19 +1,13 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:my_wit_wallet/constants.dart'; import 'package:my_wit_wallet/util/get_localization.dart'; -import 'package:my_wit_wallet/shared/api_database.dart'; -import 'package:my_wit_wallet/shared/locator.dart'; import 'package:my_wit_wallet/util/extensions/string_extensions.dart'; -import 'package:my_wit_wallet/util/extensions/num_extensions.dart'; import 'package:flutter/material.dart'; -import 'package:my_wit_wallet/util/storage/database/account.dart'; import 'package:my_wit_wallet/util/storage/database/adapters/transaction_adapter.dart'; -import 'package:my_wit_wallet/util/storage/database/wallet.dart'; -import 'package:my_wit_wallet/util/transactions_list/get_transaction_address.dart'; -import 'package:my_wit_wallet/util/transactions_list/get_transaction_label.dart'; +import 'package:my_wit_wallet/util/transactions_list/transaction_details.dart'; import 'package:my_wit_wallet/theme/colors.dart'; import 'package:my_wit_wallet/theme/extended_theme.dart'; import 'package:my_wit_wallet/util/extensions/int_extensions.dart'; +import 'package:my_wit_wallet/widgets/container_background.dart'; import 'package:witnet/explorer.dart'; typedef void GeneralTransactionCallback(GeneralTransaction? value); @@ -32,30 +26,13 @@ class TransactionsItem extends StatefulWidget { class TransactionsItemState extends State { GeneralTransaction? transactionDetails; final ScrollController _scroller = ScrollController(); - Wallet currentWallet = - Locator.instance.get().walletStorage.currentWallet; dynamic nextAction; - List get externalAddresses { - return currentWallet.externalAccounts.values - .map((account) => account.address) - .toList(); - } - - List get internalAddresses { - return currentWallet.internalAccounts.values - .map((account) => account.address) - .toList(); - } - - Account? get singleAddressAccount { - return currentWallet.walletType == WalletType.single - ? currentWallet.masterAccount - : null; - } + TransactionUtils? transactionUtils; @override void initState() { super.initState(); + transactionUtils = TransactionUtils(vti: widget.transaction); } @override @@ -64,115 +41,6 @@ class TransactionsItemState extends State { super.dispose(); } - int lockedValue(GeneralTransaction gt) { - int amountLocked = 0; - if (gt.type == TransactionType.value_transfer) { - gt.vtt!.outputs.forEach((element) { - if (element.timeLock > DateTime.now().millisecondsSinceEpoch ~/ 1000) { - amountLocked += element.value.toInt(); - } - }); - } - return amountLocked; - } - - int receiveValue(GeneralTransaction vti) { - int nanoWitvalue = 0; - if (vti.type == TransactionType.value_transfer) { - vti.vtt!.outputs.forEach((element) { - if ((externalAddresses.contains(element.pkh.address) || - internalAddresses.contains(element.pkh.address))) { - nanoWitvalue += element.value.toInt(); - } else if (singleAddressAccount != null && - singleAddressAccount!.address == element.pkh.address) { - nanoWitvalue += element.value.toInt(); - } - }); - return nanoWitvalue; - } else if (vti.mint != null) { - vti.mint!.outputs.forEach((element) { - if ((externalAddresses.contains(element.pkh.address) || - internalAddresses.contains(element.pkh.address))) { - nanoWitvalue += element.value.toInt(); - } else if (singleAddressAccount != null && - singleAddressAccount!.address == element.pkh.address) { - nanoWitvalue += element.value.toInt(); - } - }); - return nanoWitvalue; - } else if (vti.stake != null) { - // TODO(#542): get value and correct type for for stake transactions - vti.stake!.outputs.forEach((element) { - if ((externalAddresses.contains(element.pkh.address) || - internalAddresses.contains(element.pkh.address))) { - nanoWitvalue += element.value.toInt(); - } else if (singleAddressAccount != null && - singleAddressAccount!.address == element.pkh.address) { - nanoWitvalue += element.value.toInt(); - } - }); - return nanoWitvalue; - } else { - // TODO(#542): get value and correct type for unstake transactions - vti.unstake!.outputs.forEach((element) { - if ((externalAddresses.contains(element.pkh.address) || - internalAddresses.contains(element.pkh.address))) { - nanoWitvalue += element.value.toInt(); - } else if (singleAddressAccount != null && - singleAddressAccount!.address == element.pkh.address) { - nanoWitvalue += element.value.toInt(); - } - }); - return nanoWitvalue; - } - } - - int sendValue(GeneralTransaction vti) { - if (vti.type != TransactionType.value_transfer || - vti.vtt!.outputs.length <= 0) { - return 0; - } - bool isInternalTx = - externalAddresses.contains(vti.vtt!.outputs[0].pkh.address) || - internalAddresses.contains(vti.vtt!.outputs[0].pkh.address) || - singleAddressAccount?.address == vti.vtt!.outputs[0].pkh.address; - return isInternalTx ? vti.fee : vti.vtt!.outputs[0].value.toInt(); - } - - Widget buildTransactionValue(label, transaction) { - final theme = Theme.of(context); - final extendedTheme = theme.extension()!; - int _lockedWit = lockedValue(transaction); - if (label == localization.from) { - return Text( - ' + ${receiveValue(transaction).standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', - style: theme.textTheme.bodyLarge?.copyWith( - color: _lockedWit > 0 - ? WitnetPallet.mediumGrey - : extendedTheme.txValuePositiveColor), - overflow: TextOverflow.ellipsis, - ); - } else if (sendValue(transaction).standardizeWitUnits().toString() != '0') { - return Text( - ' - ${sendValue(transaction).standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', - style: theme.textTheme.bodyLarge?.copyWith( - color: _lockedWit > 0 - ? WitnetPallet.mediumGrey - : extendedTheme.txValueNegativeColor), - overflow: TextOverflow.ellipsis, - ); - } else { - return Text( - '${sendValue(transaction).standardizeWitUnits().formatWithCommaSeparator()} ${WIT_UNIT[WitUnit.Wit]}', - style: theme.textTheme.bodyLarge!.copyWith( - color: _lockedWit > 0 - ? WitnetPallet.mediumGrey - : theme.textTheme.bodyLarge!.color), - overflow: TextOverflow.ellipsis, - ); - } - } - Widget buildTransactionStatus(ThemeData theme) { TxStatusLabel transactionStatus = widget.transaction.status; String localizedtxnStatus = localization.txnStatus(transactionStatus.name); @@ -204,25 +72,39 @@ class TransactionsItemState extends State { ); } - @override - Widget build(BuildContext context) { + Color? getAmountColor(String prefix) { final theme = Theme.of(context); final extendedTheme = theme.extension()!; - String label; - String address; - TransactionType type = widget.transaction.type; - - if (type == TransactionType.value_transfer) { - label = getTransactionLabel(externalAddresses, internalAddresses, - widget.transaction.vtt!.inputs, singleAddressAccount, context); - address = getTransactionAddress(label, widget.transaction.vtt!.inputs, - widget.transaction.vtt!.outputs); + int _lockedValue = transactionUtils!.lockedValue(); + if (_lockedValue > 0) { + return WitnetPallet.mediumGrey; + } else if (prefix == '-') { + return extendedTheme.txValueNegativeColor; + } else if (prefix == '+') { + return extendedTheme.txValuePositiveColor; } else { - // TODO(#542): set stake and unstake transaction label when feature is supported - label = localization.from; - address = 'Mint'; + return theme.textTheme.bodyLarge!.color; } - int _lockedWit = lockedValue(widget.transaction); + } + + Widget buildTransactionValue(label) { + final theme = Theme.of(context); + TransactionValue transactionValue = transactionUtils!.getTransactionValue(); + + return Text(' ${transactionValue.prefix} ${transactionValue.amount}', + style: theme.textTheme.bodyLarge?.copyWith( + color: getAmountColor(transactionValue.prefix), + overflow: TextOverflow.ellipsis, + )); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final extendedTheme = theme.extension()!; + String label = transactionUtils!.getLabel(); + String address = transactionUtils!.getOrigin(); + int _lockedWit = transactionUtils!.lockedValue(); return Semantics( button: true, enabled: true, @@ -231,69 +113,54 @@ class TransactionsItemState extends State { cursor: SystemMouseCursors.click, child: GestureDetector( child: Padding( - padding: EdgeInsets.only(left: 8, right: 8), + padding: EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildTransactionStatus(theme), - Container( - margin: EdgeInsets.only(bottom: 8), - decoration: BoxDecoration( - color: WitnetPallet.transparent, - border: Border( - bottom: BorderSide( - color: extendedTheme.txBorderColor!, - width: 0.5, - )), - ), - child: Padding( - padding: EdgeInsets.only(top: 16, bottom: 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Row( - children: [ - _lockedWit > 0 - ? Icon(FontAwesomeIcons.lock, - size: 11) - : Container(), - _lockedWit > 0 - ? SizedBox(width: 10) - : Container(), - buildTransactionValue( - label, widget.transaction), - ], - ), - ])), - SizedBox( - width: 8, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - label.capitalize(), - style: theme.textTheme.bodyMedium, - ), - SizedBox(height: 4), - Text( - address, - overflow: TextOverflow.fade, - style: extendedTheme.monoSmallText!.copyWith( - color: theme.textTheme.bodyMedium!.color), + ContainerBackground( + content: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + _lockedWit > 0 + ? Icon(FontAwesomeIcons.lock, size: 11) + : Container(), + _lockedWit > 0 + ? SizedBox(width: 10) + : Container(), + buildTransactionValue(label), + ], ), - ], - )), - ], - ), + ])), + SizedBox( + width: 8, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + label.capitalize(), + style: theme.textTheme.bodyMedium, + ), + SizedBox(height: 4), + Text( + address, + overflow: TextOverflow.fade, + style: extendedTheme.monoSmallText!.copyWith( + color: theme.textTheme.bodyMedium!.color), + ), + ], + )), + ], ), - ) + ), ], )), onTap: () { diff --git a/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/01_recipient_step.dart b/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/01_recipient_step.dart index fcddf4d6a..c92b829f9 100644 --- a/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/01_recipient_step.dart +++ b/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/01_recipient_step.dart @@ -245,7 +245,7 @@ class RecipientStepState extends State if (vttBloc.state.transaction.hasOutput(widget.transactionType)) { String? savedAddress = vttBloc.state.transaction.get(widget.transactionType) != null - ? vttBloc.state.transaction.getAddress(widget.transactionType) + ? vttBloc.state.transaction.getOrigin(widget.transactionType) : null; String? savedAmount = vttBloc.state.transaction.get(widget.transactionType) != null diff --git a/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/02_select_miner_fee.dart b/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/02_select_miner_fee.dart index f618ff7f6..a40b21306 100644 --- a/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/02_select_miner_fee.dart +++ b/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/02_select_miner_fee.dart @@ -304,7 +304,7 @@ class SelectMinerFeeStepState extends State activeFgColor: extendedTheme?.switchActiveFg, inactiveFgColor: extendedTheme?.switchInactiveFg, activeBgColor: [extendedTheme!.switchActiveBg!], - cornerRadius: 4, + cornerRadius: 24, borderWidth: 1.0, borderColor: [extendedTheme.switchBorderColor!], totalSwitches: 2, diff --git a/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/03_review_step.dart b/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/03_review_step.dart index d47369263..30c01db63 100644 --- a/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/03_review_step.dart +++ b/lib/widgets/witnet/transactions/value_transfer/create_dialog_box/vtt_builder/03_review_step.dart @@ -160,8 +160,7 @@ class ReviewStepState extends State builder: (context, state) { bool hasTimelock = state.transaction.hasTimelock(state.transactionType); - String address = - state.transaction.getAddress(state.transactionType); + String address = state.transaction.getOrigin(state.transactionType); return Padding( padding: EdgeInsets.only(left: 8, right: 8), child: Column( diff --git a/test/util/transactions_list/get_transaction_label_test.dart b/test/util/transactions_list/get_transaction_label_test.dart index ddd82a4b5..3a18f2312 100644 --- a/test/util/transactions_list/get_transaction_label_test.dart +++ b/test/util/transactions_list/get_transaction_label_test.dart @@ -1,9 +1,11 @@ +import 'package:flutter/material.dart'; import 'package:my_wit_wallet/util/storage/database/account.dart'; import 'package:my_wit_wallet/util/transactions_list/get_transaction_label.dart'; import 'package:test/test.dart'; import 'package:witnet/explorer.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); List externalAddresses = [ 'wit10v6vv58udnxtw92ckkkfw2csg74x5aqewan4ac', 'wit136ksszlmep3np2chvd9pp5vwhhnk5d30fljw08', @@ -27,9 +29,16 @@ void main() { ]; String transactionFromLabel = getTransactionLabel( - externalAddresses, internalAddresses, inputs, null, null); - String transactionToLabel = - getTransactionLabel([], [], inputs, singleAddressAccount, null); + externalAddresses: externalAddresses, + internalAddresses: internalAddresses, + inputs: inputs, + singleAddressAccount: null); + + String transactionToLabel = getTransactionLabel( + externalAddresses: [], + internalAddresses: [], + inputs: inputs, + singleAddressAccount: singleAddressAccount); group( 'getTransactionAddress', @@ -37,7 +46,7 @@ void main() { test( 'with to label', () => { - expect(transactionToLabel, 'to'), + expect(transactionToLabel, 'To'), }), test( 'with from label',