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',