Merge pull request from cypherstack/ui-testing

UI testing
This commit is contained in:
julian-CStack 2022-09-07 11:41:29 -06:00 committed by GitHub
commit ff00e74141
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 2607 additions and 750 deletions

View file

@ -0,0 +1,13 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#E0E3E3"/>
<g clip-path="url(#clip0_5035_57298)">
<path d="M10.0169 11.6619C10.1425 12.2062 9.54581 12.6328 9.0721 12.3345L8.43612 11.9367L7.10397 14.0678C6.7551 14.6258 7.15642 15.3497 7.81454 15.3497H8.65256C9.11423 15.3497 9.48954 15.7245 9.48954 16.1862C9.48954 16.6479 9.11423 17.025 8.65256 17.025H7.81716C5.84484 17.025 4.64251 14.8569 5.68493 13.183L7.01525 11.0521L6.3777 10.6516C5.90373 10.3533 6.02464 9.63093 6.56954 9.5053L8.96244 8.95177C9.18725 8.90335 9.41495 9.03944 9.46729 9.26714L10.0169 11.6619ZM12.71 7.36601L13.7896 9.09702L13.1549 9.49169C12.6796 9.7877 12.7998 10.5108 13.3454 10.6372L15.7375 11.1908C15.9632 11.243 16.1884 11.1021 16.2403 10.8764L16.7909 8.48355C16.9163 7.93892 16.3211 7.51389 15.8469 7.80937L15.2093 8.20718L14.1311 6.48088C13.1476 4.90691 10.8544 4.90586 9.87034 6.47927L9.67667 6.78866C9.43327 7.17732 9.55366 7.6963 9.941 7.94023C10.3315 8.18517 10.851 8.0681 11.0954 7.67746L11.2902 7.36628C11.6239 6.83263 12.3907 6.85226 12.71 7.36601ZM18.316 13.1851L17.8713 12.4716C17.627 12.0796 17.1108 11.9597 16.7187 12.2044C16.3277 12.4483 16.2079 12.9673 16.4523 13.3581L16.897 14.0686C17.2464 14.6266 16.845 15.3511 16.1867 15.3511H13.6744L13.675 14.5989C13.675 14.0393 12.9985 13.759 12.6028 14.1547L10.8647 15.8933C10.7019 16.0562 10.7019 16.3228 10.8648 16.4856L12.6031 18.2218C12.9989 18.617 13.6749 18.3366 13.6749 17.7774L13.6743 17.0268H16.1831C18.1564 17.0271 19.3603 14.8575 18.316 13.1851Z" fill="#494949"/>
</g>
<circle cx="20.5" cy="20.5" r="3.5" fill="#C00205"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.1465 21.2072C18.9512 21.4025 18.9512 21.7191 19.1465 21.9143C19.3418 22.1096 19.6583 22.1096 19.8536 21.9143L20.5293 21.2386L21.2054 21.9146C21.4007 22.1099 21.7172 22.1099 21.9125 21.9146C22.1078 21.7194 22.1078 21.4028 21.9125 21.2075L21.2365 20.5315L21.9749 19.793C22.1702 19.5977 22.1702 19.2812 21.9749 19.0859C21.7797 18.8906 21.4631 18.8906 21.2678 19.0859L20.5293 19.8244L19.7912 19.0862C19.5959 18.8909 19.2793 18.8909 19.0841 19.0862C18.8888 19.2815 18.8888 19.598 19.0841 19.7933L19.8222 20.5315L19.1465 21.2072Z" fill="white"/>
<defs>
<clipPath id="clip0_5035_57298">
<rect width="13.4" height="13.4" fill="white" transform="translate(5.30078 5.2998)"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 2.6 KiB

View file

@ -0,0 +1,13 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.0154 16.7681C23.6489 15.3066 24 13.6943 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C13.6943 24 15.3066 23.6489 16.7681 23.0154C16.2832 22.2973 16 21.4317 16 20.5C16 18.0147 18.0147 16 20.5 16C21.4317 16 22.2973 16.2832 23.0154 16.7681Z" fill="#E0E3E3"/>
<circle cx="20.5" cy="20.5" r="3.5" fill="#F4C517"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5 18.5C20.7761 18.5 21 18.7239 21 19V20H21.5C21.7761 20 22 20.2239 22 20.5C22 20.7761 21.7761 21 21.5 21H20.5C20.2239 21 20 20.7761 20 20.5V19C20 18.7239 20.2239 18.5 20.5 18.5Z" fill="white"/>
<g clip-path="url(#clip0_5035_57297)">
<path d="M10.0169 11.6619C10.1425 12.2062 9.54581 12.6328 9.0721 12.3345L8.43612 11.9367L7.10397 14.0678C6.7551 14.6258 7.15642 15.3497 7.81454 15.3497H8.65256C9.11423 15.3497 9.48954 15.7245 9.48954 16.1862C9.48954 16.6479 9.11423 17.025 8.65256 17.025H7.81716C5.84484 17.025 4.64251 14.8569 5.68493 13.183L7.01525 11.0521L6.3777 10.6516C5.90373 10.3533 6.02464 9.63093 6.56954 9.5053L8.96244 8.95177C9.18725 8.90335 9.41495 9.03944 9.46729 9.26714L10.0169 11.6619ZM12.71 7.36601L13.7896 9.09702L13.1549 9.49169C12.6796 9.7877 12.7998 10.5108 13.3454 10.6372L15.7375 11.1908C15.9632 11.243 16.1884 11.1021 16.2403 10.8764L16.7909 8.48355C16.9163 7.93892 16.3211 7.51389 15.8469 7.80937L15.2093 8.20718L14.1311 6.48088C13.1476 4.90691 10.8544 4.90586 9.87034 6.47927L9.67667 6.78866C9.43327 7.17732 9.55366 7.6963 9.941 7.94023C10.3315 8.18517 10.851 8.0681 11.0954 7.67746L11.2902 7.36628C11.6239 6.83263 12.3907 6.85226 12.71 7.36601ZM18.316 13.1851L17.8713 12.4716C17.627 12.0796 17.1108 11.9597 16.7187 12.2044C16.3277 12.4483 16.2079 12.9673 16.4523 13.3581L16.897 14.0686C17.2464 14.6266 16.845 15.3511 16.1867 15.3511H13.6744L13.675 14.5989C13.675 14.0393 12.9985 13.759 12.6028 14.1547L10.8647 15.8933C10.7019 16.0562 10.7019 16.3228 10.8648 16.4856L12.6031 18.2218C12.9989 18.617 13.6749 18.3366 13.6749 17.7774L13.6743 17.0268H16.1831C18.1564 17.0271 19.3603 14.8575 18.316 13.1851Z" fill="#494949"/>
</g>
<defs>
<clipPath id="clip0_5035_57297">
<rect width="13.4" height="13.4" fill="white" transform="translate(5.30078 5.2998)"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 2.3 KiB

View file

@ -0,0 +1,16 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5035_57299)">
<circle cx="12" cy="12" r="12" fill="#E0E3E3"/>
<g clip-path="url(#clip1_5035_57299)">
<path d="M9.92728 11.6469C10.0585 12.2157 9.4351 12.6614 8.94018 12.3497L8.27572 11.934L6.88393 14.1606C6.51943 14.7436 6.93872 15.4999 7.62631 15.4999H8.50185C8.9842 15.4999 9.37631 15.8915 9.37631 16.3738C9.37631 16.8562 8.9842 17.2502 8.50185 17.2502H7.62904C5.56842 17.2502 4.31225 14.9851 5.40135 13.2361L6.79123 11.0098L6.12514 10.5915C5.62994 10.2797 5.75627 9.52505 6.32557 9.3938L8.8256 8.81548C9.06049 8.7649 9.29838 8.90709 9.35307 9.14498L9.92728 11.6469ZM12.741 7.15873L13.8689 8.96724L13.2058 9.37959C12.7092 9.68884 12.8347 10.4444 13.4048 10.5764L15.904 11.1547C16.1398 11.2093 16.3752 11.0621 16.4293 10.8263L17.0046 8.32631C17.1356 7.75728 16.5138 7.31322 16.0183 7.62193L15.3522 8.03755L14.2257 6.23396C13.1981 4.58951 10.8023 4.58841 9.77416 6.23227L9.57182 6.55552C9.31752 6.96158 9.4433 7.50381 9.84799 7.75865C10.256 8.01456 10.7987 7.89225 11.0541 7.48412L11.2576 7.159C11.6062 6.60146 12.4074 6.62197 12.741 7.15873ZM18.598 13.2383L18.1334 12.4929C17.8781 12.0833 17.3388 11.9581 16.9292 12.2138C16.5207 12.4686 16.3954 13.0108 16.6508 13.4191L17.1154 14.1615C17.4804 14.7444 17.0612 15.5013 16.3733 15.5013H13.7486L13.7492 14.7154C13.7492 14.1308 13.0424 13.838 12.6289 14.2514L10.813 16.0679C10.6429 16.238 10.6429 16.5166 10.8132 16.6867L12.6293 18.5006C13.0428 18.9135 13.7491 18.6206 13.7491 18.0363L13.7484 17.2521H16.3696C18.4312 17.2524 19.689 14.9856 18.598 13.2383Z" fill="#494949"/>
</g>
</g>
<defs>
<clipPath id="clip0_5035_57299">
<rect width="24" height="24" fill="white"/>
</clipPath>
<clipPath id="clip1_5035_57299">
<rect width="14" height="14" fill="white" transform="translate(5 5)"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 1.8 KiB

View file

@ -243,6 +243,64 @@ class _WalletInitiatedExchangeViewState
ref.read(estimatedRateExchangeFormProvider).clearAmounts(true);
// ref.read(fixedRateExchangeFormProvider);
});
_sendFocusNode.addListener(() async {
if (!_sendFocusNode.hasFocus) {
final newFromAmount = Decimal.tryParse(_sendController.text);
if (newFromAmount != null) {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(newFromAmount, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(newFromAmount, true);
}
} else {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
}
_receiveController.text = "";
}
}
});
_receiveFocusNode.addListener(() async {
if (!_receiveFocusNode.hasFocus) {
final newToAmount = Decimal.tryParse(_receiveController.text);
if (newToAmount != null) {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
// await ref
// .read(estimatedRateExchangeFormProvider)
// .setToAmountAndCalculateFromAmount(newToAmount, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount(newToAmount, true);
}
} else {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
// await ref
// .read(estimatedRateExchangeFormProvider)
// .setToAmountAndCalculateFromAmount(Decimal.zero, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount(Decimal.zero, true);
}
_sendController.text = "";
}
}
});
super.initState();
}
@ -379,12 +437,12 @@ class _WalletInitiatedExchangeViewState
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(
newFromAmount, true);
newFromAmount, false);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(
newFromAmount, true);
newFromAmount, false);
}
} else {
if (ref
@ -394,12 +452,12 @@ class _WalletInitiatedExchangeViewState
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(
Decimal.zero, true);
Decimal.zero, false);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(
Decimal.zero, true);
Decimal.zero, false);
}
_receiveController.text = "";
}
@ -735,12 +793,12 @@ class _WalletInitiatedExchangeViewState
// await ref
// .read(estimatedRateExchangeFormProvider)
// .setToAmountAndCalculateFromAmount(
// newToAmount, true);
// newToAmount, false);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount(
newToAmount, true);
newToAmount, false);
}
} else {
if (ref
@ -750,12 +808,12 @@ class _WalletInitiatedExchangeViewState
// await ref
// .read(estimatedRateExchangeFormProvider)
// .setToAmountAndCalculateFromAmount(
// Decimal.zero, true);
// Decimal.zero, false);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount(
Decimal.zero, true);
Decimal.zero, false);
}
_sendController.text = "";
}

View file

@ -9,6 +9,7 @@ import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
@ -19,6 +20,8 @@ import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import '../../providers/wallet/public_private_balance_state_provider.dart';
class ConfirmTransactionView extends ConsumerStatefulWidget {
const ConfirmTransactionView({
Key? key,
@ -56,10 +59,20 @@ class _ConfirmTransactionViewState
final note = transactionInfo["note"] as String? ?? "";
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
ref.read(walletsChangeNotifierProvider).getManager(walletId);
try {
final txid = await manager.confirmSend(txData: transactionInfo);
String txid;
final coin = manager.coin;
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
txid = await (manager.wallet as FiroWallet)
.confirmSendPublic(txData: transactionInfo);
} else {
txid = await manager.confirmSend(txData: transactionInfo);
}
unawaited(manager.refresh());
// save note
@ -79,7 +92,7 @@ class _ConfirmTransactionViewState
showFloatingFlushBar(
type: FlushBarType.warning,
message:
"Connection failed. Please check the address and try again.",
"Connection failed. Please check the address and try again.",
context: context,
),
);
@ -100,10 +113,10 @@ class _ConfirmTransactionViewState
message: e.toString(),
rightButton: TextButton(
style: Theme.of(context).textButtonTheme.style?.copyWith(
backgroundColor: MaterialStateProperty.all<Color>(
CFColors.buttonGray,
),
),
backgroundColor: MaterialStateProperty.all<Color>(
CFColors.buttonGray,
),
),
child: Text(
"Ok",
style: STextStyles.button.copyWith(
@ -212,9 +225,9 @@ class _ConfirmTransactionViewState
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
managerProvider
.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12,
textAlign: TextAlign.right,
),
@ -240,9 +253,9 @@ class _ConfirmTransactionViewState
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
managerProvider
.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12,
textAlign: TextAlign.right,
),
@ -292,9 +305,9 @@ class _ConfirmTransactionViewState
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider
.select((value) => value.coin),
).ticker}",
managerProvider
.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12,
textAlign: TextAlign.right,
),
@ -306,18 +319,18 @@ class _ConfirmTransactionViewState
),
TextButton(
style:
Theme.of(context).textButtonTheme.style?.copyWith(
backgroundColor:
MaterialStateProperty.all<Color>(
CFColors.stackAccent,
),
),
Theme.of(context).textButtonTheme.style?.copyWith(
backgroundColor:
MaterialStateProperty.all<Color>(
CFColors.stackAccent,
),
),
onPressed: () async {
final unlocked = await Navigator.push(
context,
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
RouteGenerator.useMaterialPageRoute,
builder: (_) => const LockscreenView(
showBackButton: true,
popOnSuccess: true,
@ -325,9 +338,9 @@ class _ConfirmTransactionViewState
routeOnSuccess: "",
biometricsCancelButtonString: "CANCEL",
biometricsLocalizedReason:
"Authenticate to send transaction",
"Authenticate to send transaction",
biometricsAuthenticationTitle:
"Confirm Transaction",
"Confirm Transaction",
),
settings: const RouteSettings(
name: "/confirmsendlockscreen"),

View file

@ -13,7 +13,9 @@ import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selectio
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -37,6 +39,8 @@ import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'sub_widgets/firo_balance_selection_sheet.dart';
class SendView extends ConsumerStatefulWidget {
const SendView({
Key? key,
@ -82,6 +86,9 @@ class _SendViewState extends ConsumerState<SendView> {
Decimal? _cachedAmountToSend;
String? _address;
String? _privateBalanceString;
String? _publicBalanceString;
bool _addressToggleFlag = false;
bool _cryptoAmountChangeLock = false;
@ -160,13 +167,26 @@ class _SendViewState extends ConsumerState<SendView> {
late Future<String> _calculateFeesFuture;
Map<int, String> cachedFees = {};
Map<int, String> cachedFiroPrivateFees = {};
Map<int, String> cachedFiroPublicFees = {};
Future<String> calculateFees(int amount) async {
if (amount <= 0) {
return "0";
}
if (cachedFees[amount] != null) {
if (coin == Coin.firo || coin == Coin.firoTestNet) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
if (cachedFiroPrivateFees[amount] != null) {
return cachedFiroPrivateFees[amount]!;
}
} else {
if (cachedFiroPublicFees[amount] != null) {
return cachedFiroPublicFees[amount]!;
}
}
} else if (cachedFees[amount] != null) {
return cachedFees[amount]!;
}
@ -188,12 +208,53 @@ class _SendViewState extends ConsumerState<SendView> {
break;
}
final fee = await manager.estimateFeeFor(amount, feeRate);
int fee;
cachedFees[amount] =
Format.satoshisToAmount(fee).toStringAsFixed(Constants.decimalPlaces);
if (coin == Coin.firo || coin == Coin.firoTestNet) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
fee = await manager.estimateFeeFor(amount, feeRate);
return cachedFees[amount]!;
cachedFiroPrivateFees[amount] = Format.satoshisToAmount(fee)
.toStringAsFixed(Constants.decimalPlaces);
return cachedFiroPrivateFees[amount]!;
} else {
fee = await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
cachedFiroPublicFees[amount] = Format.satoshisToAmount(fee)
.toStringAsFixed(Constants.decimalPlaces);
return cachedFiroPublicFees[amount]!;
}
} else {
fee = await manager.estimateFeeFor(amount, feeRate);
cachedFees[amount] =
Format.satoshisToAmount(fee).toStringAsFixed(Constants.decimalPlaces);
return cachedFees[amount]!;
}
}
Future<String?> _firoBalanceFuture(
ChangeNotifierProvider<Manager> provider, String locale) async {
final wallet = ref.read(provider).wallet as FiroWallet?;
if (wallet != null) {
Decimal? balance;
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
balance = await wallet.availablePrivateBalance();
} else {
balance = await wallet.availablePublicBalance();
}
return Format.localizedStringAsFixed(
value: balance, locale: locale, decimalPlaces: 8);
}
return null;
}
@override
@ -282,6 +343,22 @@ class _SendViewState extends ConsumerState<SendView> {
.select((value) => value.getManagerProvider(walletId)));
final String locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
if (coin == Coin.firo || coin == Coin.firoTestNet) {
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
if (_amountToSend == null) {
setState(() {
_calculateFeesFuture = calculateFees(0);
});
} else {
setState(() {
_calculateFeesFuture =
calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
});
}
});
}
return Scaffold(
backgroundColor: CFColors.almostWhite,
appBar: AppBar(
@ -334,21 +411,59 @@ class _SendViewState extends ConsumerState<SendView> {
children: [
SvgPicture.asset(
Assets.svg.iconFor(coin: coin),
width: 18,
height: 18,
width: 22,
height: 22,
),
const SizedBox(
width: 6,
),
Text(
ref.watch(provider
.select((value) => value.walletName)),
style: STextStyles.titleBold12,
),
if (coin != Coin.firo &&
coin != Coin.firoTestNet)
Text(
ref.watch(provider
.select((value) => value.walletName)),
style: STextStyles.titleBold12,
),
if (coin == Coin.firo ||
coin == Coin.firoTestNet)
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
ref.watch(provider.select(
(value) => value.walletName)),
style: STextStyles.titleBold12
.copyWith(fontSize: 14),
),
// const SizedBox(
// height: 2,
// ),
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
style: STextStyles.label
.copyWith(fontSize: 10),
),
],
),
const Spacer(),
FutureBuilder(
future: ref.watch(provider.select(
(value) => value.availableBalance)),
future: (coin != Coin.firo &&
coin != Coin.firoTestNet)
? ref.watch(provider.select(
(value) => value.availableBalance))
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? (ref.watch(provider).wallet
as FiroWallet)
.availablePrivateBalance()
: (ref.watch(provider).wallet
as FiroWallet)
.availablePublicBalance(),
builder:
(_, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
@ -423,6 +538,9 @@ class _SendViewState extends ConsumerState<SendView> {
fontSize: 10,
),
),
const SizedBox(
height: 2,
),
AnimatedText(
stringsToLoopThrough: const [
"Loading balance ",
@ -730,6 +848,141 @@ class _SendViewState extends ConsumerState<SendView> {
}
},
),
if (coin == Coin.firo)
const SizedBox(
height: 12,
),
if (coin == Coin.firo)
Text(
"Send from",
style: STextStyles.smallMed12,
textAlign: TextAlign.left,
),
if (coin == Coin.firo)
const SizedBox(
height: 8,
),
if (coin == Coin.firo)
Stack(
children: [
const TextField(
readOnly: true,
textInputAction: TextInputAction.none,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
),
child: RawMaterialButton(
splashColor: CFColors.splashLight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => FiroBalanceSelectionSheet(
walletId: walletId,
),
);
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
style: STextStyles.itemSubtitle12,
),
const SizedBox(
width: 10,
),
FutureBuilder(
future: _firoBalanceFuture(
provider, locale),
builder: (context,
AsyncSnapshot<String?>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
_privateBalanceString =
snapshot.data!;
} else {
_publicBalanceString =
snapshot.data!;
}
}
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private" &&
_privateBalanceString !=
null) {
return Text(
"$_privateBalanceString ${coin.ticker}",
style:
STextStyles.itemSubtitle,
);
} else if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Public" &&
_publicBalanceString !=
null) {
return Text(
"$_publicBalanceString ${coin.ticker}",
style:
STextStyles.itemSubtitle,
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance...",
],
style:
STextStyles.itemSubtitle,
);
}
},
),
],
),
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: CFColors.gray3,
),
],
),
),
)
],
),
const SizedBox(
height: 12,
),
@ -744,10 +997,34 @@ class _SendViewState extends ConsumerState<SendView> {
BlueTextButton(
text: "Send all ${coin.ticker}",
onTap: () async {
cryptoAmountController.text = (await ref
.read(provider)
.availableBalance)
.toStringAsFixed(Constants.decimalPlaces);
if (coin == Coin.firo ||
coin == Coin.firoTestNet) {
final firoWallet =
ref.read(provider).wallet as FiroWallet;
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
cryptoAmountController.text =
(await firoWallet
.availablePrivateBalance())
.toStringAsFixed(
Constants.decimalPlaces);
} else {
cryptoAmountController.text =
(await firoWallet
.availablePublicBalance())
.toStringAsFixed(
Constants.decimalPlaces);
}
} else {
cryptoAmountController.text = (await ref
.read(provider)
.availableBalance)
.toStringAsFixed(Constants.decimalPlaces);
}
},
),
],
@ -973,74 +1250,126 @@ class _SendViewState extends ConsumerState<SendView> {
Constants.size.circularBorderRadius,
),
),
onPressed: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
onPressed: (coin == Coin.firo ||
coin == Coin.firoTestNet) &&
ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? null
: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) =>
TransactionFeeSelectionSheet(
walletId: walletId,
amount: Decimal.tryParse(
cryptoAmountController
.text) ??
Decimal.zero,
),
);
},
child: ((coin == Coin.firo ||
coin == Coin.firoTestNet) &&
ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private")
? Row(
children: [
FutureBuilder(
future: _calculateFeesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"~${snapshot.data! as String} ${coin.ticker}",
style:
STextStyles.itemSubtitle,
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Calculating",
"Calculating.",
"Calculating..",
"Calculating...",
],
style:
STextStyles.itemSubtitle,
);
}
},
),
],
)
: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
ref
.watch(
feeRateTypeStateProvider
.state)
.state
.prettyName,
style:
STextStyles.itemSubtitle12,
),
const SizedBox(
width: 10,
),
FutureBuilder(
future: _calculateFeesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState
.done &&
snapshot.hasData) {
return Text(
"~${snapshot.data! as String} ${coin.ticker}",
style: STextStyles
.itemSubtitle,
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Calculating",
"Calculating.",
"Calculating..",
"Calculating...",
],
style: STextStyles
.itemSubtitle,
);
}
},
),
],
),
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: CFColors.gray3,
),
],
),
),
builder: (_) =>
TransactionFeeSelectionSheet(
walletId: walletId,
amount: Decimal.tryParse(
cryptoAmountController.text) ??
Decimal.zero,
),
);
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
ref
.watch(feeRateTypeStateProvider
.state)
.state
.prettyName,
style: STextStyles.itemSubtitle12,
),
const SizedBox(
width: 10,
),
FutureBuilder(
future: _calculateFeesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"~${snapshot.data! as String} ${coin.ticker}",
style: STextStyles.itemSubtitle,
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Calculating",
"Calculating.",
"Calculating..",
"Calculating...",
],
style: STextStyles.itemSubtitle,
);
}
},
),
],
),
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: CFColors.gray3,
),
],
),
),
)
],
@ -1106,9 +1435,32 @@ class _SendViewState extends ConsumerState<SendView> {
final amount = Format.decimalAmountToSatoshis(
_amountToSend!);
final availableBalance =
Format.decimalAmountToSatoshis(
await manager.availableBalance);
int availableBalance;
if ((coin == Coin.firo ||
coin == Coin.firoTestNet)) {
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
availableBalance =
Format.decimalAmountToSatoshis(
await (manager.wallet
as FiroWallet)
.availablePrivateBalance());
} else {
availableBalance =
Format.decimalAmountToSatoshis(
await (manager.wallet
as FiroWallet)
.availablePublicBalance());
}
} else {
availableBalance =
Format.decimalAmountToSatoshis(
await manager.availableBalance);
}
// confirm send all
if (amount == availableBalance) {
@ -1192,14 +1544,36 @@ class _SendViewState extends ConsumerState<SendView> {
},
));
final txData = await manager.prepareSend(
address: _address!,
satoshiAmount: amount,
args: {
"feeRate":
ref.read(feeRateTypeStateProvider)
},
);
Map<String, dynamic> txData;
if ((coin == Coin.firo ||
coin == Coin.firoTestNet) &&
ref
.read(
publicPrivateBalanceStateProvider
.state)
.state !=
"Private") {
txData =
await (manager.wallet as FiroWallet)
.prepareSendPublic(
address: _address!,
satoshiAmount: amount,
args: {
"feeRate":
ref.read(feeRateTypeStateProvider)
},
);
} else {
txData = await manager.prepareSend(
address: _address!,
satoshiAmount: amount,
args: {
"feeRate":
ref.read(feeRateTypeStateProvider)
},
);
}
if (!wasCancelled && mounted) {
// pop building dialog

View file

@ -0,0 +1,287 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/animated_text.dart';
class FiroBalanceSelectionSheet extends ConsumerStatefulWidget {
const FiroBalanceSelectionSheet({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<FiroBalanceSelectionSheet> createState() =>
_FiroBalanceSelectionSheetState();
}
class _FiroBalanceSelectionSheetState
extends ConsumerState<FiroBalanceSelectionSheet> {
late final String walletId;
final stringsToLoopThrough = [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance...",
];
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
final firoWallet = manager.wallet as FiroWallet;
return Container(
decoration: const BoxDecoration(
color: CFColors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 24,
right: 24,
top: 10,
bottom: 0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: CFColors.fieldGray,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
width: 60,
height: 4,
),
),
const SizedBox(
height: 36,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Select balance",
style: STextStyles.pageTitleH2,
textAlign: TextAlign.left,
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Private") {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
}
Navigator.of(context).pop();
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: CFColors.link2,
value: "Private",
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
.state,
onChanged: (x) {
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Private";
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
Text(
"Private balance",
style: STextStyles.titleBold12.copyWith(
color: const Color(0xFF44464E),
),
textAlign: TextAlign.left,
),
const SizedBox(
width: 2,
),
FutureBuilder(
future: firoWallet.availablePrivateBalance(),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle,
textAlign: TextAlign.left,
);
} else {
return AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle,
);
}
},
)
],
),
// ],
// ),
)
],
),
),
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Public") {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
}
Navigator.of(context).pop();
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: CFColors.link2,
value: "Public",
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
.state,
onChanged: (x) {
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Public";
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
Text(
"Public balance",
style: STextStyles.titleBold12.copyWith(
color: const Color(0xFF44464E),
),
textAlign: TextAlign.left,
),
const SizedBox(
width: 2,
),
FutureBuilder(
future: firoWallet.availablePublicBalance(),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle,
textAlign: TextAlign.left,
);
} else {
return AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle,
);
}
},
)
// ],
// ),
],
),
),
],
),
),
),
const SizedBox(
height: 16,
),
const SizedBox(
height: 24,
),
],
),
],
),
),
);
}
}

View file

@ -4,6 +4,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -58,35 +60,63 @@ class _TransactionFeeSelectionSheetState
required int amount,
required FeeRateType feeRateType,
required int feeRate,
required Coin coin,
}) async {
switch (feeRateType) {
case FeeRateType.fast:
if (ref.read(feeSheetSessionCacheProvider).fast[amount] == null) {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.estimateFeeFor(amount, feeRate));
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate));
} else {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate));
}
}
return ref.read(feeSheetSessionCacheProvider).fast[amount]!;
case FeeRateType.average:
if (ref.read(feeSheetSessionCacheProvider).average[amount] == null) {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.estimateFeeFor(amount, feeRate));
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate));
} else {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate));
}
}
return ref.read(feeSheetSessionCacheProvider).average[amount]!;
case FeeRateType.slow:
if (ref.read(feeSheetSessionCacheProvider).slow[amount] == null) {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.estimateFeeFor(amount, feeRate));
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate));
} else {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate));
}
}
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
}
@ -249,6 +279,7 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
feeRateType: FeeRateType.fast,
feeRate: feeObject!.fast,
amount: Format
@ -372,6 +403,7 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
feeRateType: FeeRateType.fast,
feeRate: feeObject!.fast,
amount: Format
@ -496,6 +528,7 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
feeRateType: FeeRateType.slow,
feeRate: feeObject!.slow,
amount: Format

View file

@ -1,15 +1,14 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lottie/lottie.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/debug_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class HiddenSettings extends StatelessWidget {
const HiddenSettings({Key? key}) : super(key: key);
@ -55,11 +54,11 @@ class HiddenSettings extends StatelessWidget {
.read(notificationsProvider)
.delete(notifs[0], true);
showFloatingFlushBar(
unawaited(showFloatingFlushBar(
type: FlushBarType.success,
message: "Notification history deleted",
context: context,
);
));
},
child: RoundedWhiteContainer(
child: Text(
@ -109,11 +108,11 @@ class HiddenSettings extends StatelessWidget {
.read(debugServiceProvider)
.deleteAllMessages();
showFloatingFlushBar(
unawaited(showFloatingFlushBar(
type: FlushBarType.success,
message: "Debug Logs deleted",
context: context,
);
));
},
child: RoundedWhiteContainer(
child: Text(
@ -125,34 +124,34 @@ class HiddenSettings extends StatelessWidget {
),
);
}),
const SizedBox(
height: 12,
),
GestureDetector(
onTap: () async {
showDialog<void>(
context: context,
builder: (_) {
return StackDialogBase(
child: SizedBox(
width: 200,
child: Lottie.asset(
Assets.lottie.test2,
),
),
);
},
);
},
child: RoundedWhiteContainer(
child: Text(
"Lottie test",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
),
),
),
// const SizedBox(
// height: 12,
// ),
// GestureDetector(
// onTap: () async {
// showDialog<void>(
// context: context,
// builder: (_) {
// return StackDialogBase(
// child: SizedBox(
// width: 200,
// child: Lottie.asset(
// Assets.lottie.test2,
// ),
// ),
// );
// },
// );
// },
// child: RoundedWhiteContainer(
// child: Text(
// "Lottie test",
// style: STextStyles.button.copyWith(
// color: CFColors.stackAccent,
// ),
// ),
// ),
// ),
],
),
),

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
@ -110,7 +112,7 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
.read(walletsChangeNotifierProvider)
.getManagerProvider(widget.walletId);
if (!ref.read(managerProvider).isRefreshing) {
ref.read(managerProvider).refresh();
unawaited(ref.read(managerProvider).refresh());
}
},
child: ListView.builder(

View file

@ -10,6 +10,16 @@ class TxIcon extends StatelessWidget {
static const Size size = Size(32, 32);
String _getAssetName(bool isCancelled, bool isReceived, bool isPending) {
if (!isReceived && transaction.subType == "mint") {
if (isCancelled) {
return Assets.svg.anonymizeFailed;
}
if (isPending) {
return Assets.svg.anonymizePending;
}
return Assets.svg.anonymize;
}
if (isReceived) {
if (isCancelled) {
return Assets.svg.receiveCancelled;

View file

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -18,6 +20,9 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final maxHeight = MediaQuery.of(context).size.height * 0.60;
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin));
return Container(
decoration: const BoxDecoration(
color: CFColors.white,
@ -105,25 +110,44 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Available balance",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 2,
),
// TODO need text from design
Text(
"Current spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
if (coin != Coin.firo && coin != Coin.firoTestNet)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Available balance",
style: STextStyles.titleBold12,
),
),
],
),
const SizedBox(
height: 2,
),
Text(
"Current spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
if (coin == Coin.firo || coin == Coin.firoTestNet)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Private balance",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 2,
),
Text(
"Current private spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
],
),
),
@ -172,25 +196,44 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Full balance",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 2,
),
// TODO need text from design
Text(
"Total wallet balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
if (coin != Coin.firo && coin != Coin.firoTestNet)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Full balance",
style: STextStyles.titleBold12,
),
),
],
),
const SizedBox(
height: 2,
),
Text(
"Total wallet balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
if (coin == Coin.firo || coin == Coin.firoTestNet)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Public balance",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 2,
),
Text(
"Current public spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
],
),
),

View file

@ -6,6 +6,7 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -67,15 +68,25 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
Expanded(
child: Consumer(
builder: (_, ref, __) {
final totalBalanceFuture = ref
.watch(managerProvider.select((value) => value.totalBalance));
final availableBalanceFuture = ref.watch(
managerProvider.select((value) => value.availableBalance));
final Coin coin =
ref.watch(managerProvider.select((value) => value.coin));
Future<Decimal>? totalBalanceFuture;
Future<Decimal>? availableBalanceFuture;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final firoWallet =
ref.watch(managerProvider.select((value) => value.wallet))
as FiroWallet;
totalBalanceFuture = firoWallet.availablePublicBalance();
availableBalanceFuture = firoWallet.availablePrivateBalance();
} else {
totalBalanceFuture = ref.watch(
managerProvider.select((value) => value.totalBalance));
availableBalanceFuture = ref.watch(
managerProvider.select((value) => value.availableBalance));
}
final locale = ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale));
@ -114,12 +125,20 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
onTap: showSheet,
child: Row(
children: [
Text(
"${_showAvailable ? "Available" : "Full"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
if (coin == Coin.firo || coin == Coin.firoTestNet)
Text(
"${_showAvailable ? "Private" : "Public"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
),
if (coin != Coin.firo && coin != Coin.firoTestNet)
Text(
"${_showAvailable ? "Available" : "Full"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(
width: 4,
),
@ -166,12 +185,20 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
onTap: showSheet,
child: Row(
children: [
Text(
"${_showAvailable ? "Available" : "Full"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
if (coin == Coin.firo || coin == Coin.firoTestNet)
Text(
"${_showAvailable ? "Private" : "Public"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
),
if (coin != Coin.firo && coin != Coin.firoTestNet)
Text(
"${_showAvailable ? "Available" : "Full"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(
width: 4,
),

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -69,11 +71,11 @@ class _TransactionDetailsViewState
fee = Format.satoshisToAmount(_transaction.fees);
amountPrefix = _transaction.txType.toLowerCase() == "sent" ? "- " : "+ ";
if (coin == Coin.firo || coin == Coin.firoTestNet) {
showFeePending = true;
} else {
showFeePending = false;
}
// if (coin == Coin.firo || coin == Coin.firoTestNet) {
// showFeePending = true;
// } else {
// showFeePending = false;
// }
super.initState();
}
@ -84,9 +86,10 @@ class _TransactionDetailsViewState
String whatIsIt(String type) {
if (type == "Received") {
if (_transaction.isMinting) {
return "Minting";
} else if (_transaction.confirmedStatus) {
// if (_transaction.isMinting) {
// return "Minting";
// } else
if (_transaction.confirmedStatus) {
return "Received";
} else {
return "Receiving";
@ -480,14 +483,14 @@ class _TransactionDetailsViewState
mode: LaunchMode.externalApplication,
);
} catch (_) {
showDialog<void>(
unawaited(showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Could not open in block explorer",
message:
"Failed to open \"${uri.toString()}\"",
),
);
));
} finally {
// Future<void>.delayed(
// const Duration(seconds: 1),
@ -505,83 +508,83 @@ class _TransactionDetailsViewState
],
),
),
if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
_transaction.subType == "mint")
const SizedBox(
height: 12,
),
if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
_transaction.subType == "mint")
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Mint Transaction ID",
style: STextStyles.itemSubtitle,
),
],
),
const SizedBox(
height: 8,
),
// Flexible(
// child: FittedBox(
// fit: BoxFit.scaleDown,
// child:
SelectableText(
_transaction.otherData ?? "Unknown",
style: STextStyles.itemSubtitle12,
),
// ),
// ),
const SizedBox(
height: 8,
),
BlueTextButton(
text: "Open in block explorer",
onTap: () async {
final uri = getBlockExplorerTransactionUrlFor(
coin: coin,
txid: _transaction.otherData ?? "Unknown",
);
// ref
// .read(
// shouldShowLockscreenOnResumeStateProvider
// .state)
// .state = false;
try {
await launchUrl(
uri,
mode: LaunchMode.externalApplication,
);
} catch (_) {
showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Could not open in block explorer",
message:
"Failed to open \"${uri.toString()}\"",
),
);
} finally {
// Future<void>.delayed(
// const Duration(seconds: 1),
// () => ref
// .read(
// shouldShowLockscreenOnResumeStateProvider
// .state)
// .state = true,
// );
}
},
),
],
),
),
// if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
// _transaction.subType == "mint")
// const SizedBox(
// height: 12,
// ),
// if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
// _transaction.subType == "mint")
// RoundedWhiteContainer(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// "Mint Transaction ID",
// style: STextStyles.itemSubtitle,
// ),
// ],
// ),
// const SizedBox(
// height: 8,
// ),
// // Flexible(
// // child: FittedBox(
// // fit: BoxFit.scaleDown,
// // child:
// SelectableText(
// _transaction.otherData ?? "Unknown",
// style: STextStyles.itemSubtitle12,
// ),
// // ),
// // ),
// const SizedBox(
// height: 8,
// ),
// BlueTextButton(
// text: "Open in block explorer",
// onTap: () async {
// final uri = getBlockExplorerTransactionUrlFor(
// coin: coin,
// txid: _transaction.otherData ?? "Unknown",
// );
// // ref
// // .read(
// // shouldShowLockscreenOnResumeStateProvider
// // .state)
// // .state = false;
// try {
// await launchUrl(
// uri,
// mode: LaunchMode.externalApplication,
// );
// } catch (_) {
// unawaited(showDialog<void>(
// context: context,
// builder: (_) => StackOkDialog(
// title: "Could not open in block explorer",
// message:
// "Failed to open \"${uri.toString()}\"",
// ),
// ));
// } finally {
// // Future<void>.delayed(
// // const Duration(seconds: 1),
// // () => ref
// // .read(
// // shouldShowLockscreenOnResumeStateProvider
// // .state)
// // .state = true,
// // );
// }
// },
// ),
// ],
// ),
// ),
if (coin == Coin.epicCash)
const SizedBox(
height: 12,
@ -637,20 +640,20 @@ class _TransactionDetailsViewState
if (manager.wallet is EpicCashWallet) {
final String? id = _transaction.slateId;
if (id == null) {
showFloatingFlushBar(
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Could not find Epic transaction ID",
context: context,
);
));
return;
}
showDialog<dynamic>(
unawaited(showDialog<dynamic>(
barrierDismissible: false,
context: context,
builder: (_) =>
const CancellingTransactionProgressDialog(),
);
));
final result = await (manager.wallet as EpicCashWallet)
.cancelPendingTransactionAndPost(id);
@ -681,11 +684,11 @@ class _TransactionDetailsViewState
}
}
} else {
showFloatingFlushBar(
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "ERROR: Wallet type is not Epic Cash",
context: context,
);
));
return;
}
},

View file

@ -1,9 +1,11 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
@ -22,6 +24,7 @@ import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
@ -31,12 +34,18 @@ import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:tuple/tuple.dart';
import '../../providers/wallet/public_private_balance_state_provider.dart';
import '../../providers/wallet/wallet_balance_toggle_state_provider.dart';
import '../../utilities/enums/wallet_balance_toggle_state.dart';
/// [eventBus] should only be set during testing
class WalletView extends ConsumerStatefulWidget {
const WalletView({
@ -155,7 +164,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
const timeout = Duration(milliseconds: 1500);
if (_cachedTime == null || now.difference(_cachedTime!) > timeout) {
_cachedTime = now;
showDialog<dynamic>(
unawaited(showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => WillPopScope(
@ -173,7 +182,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
onTimeout: () => Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
),
);
));
}
return false;
}
@ -222,14 +231,14 @@ class _WalletViewState extends ConsumerState<WalletView> {
final coin = ref.read(managerProvider).coin;
if (coin == Coin.epicCash) {
showDialog<void>(
await showDialog<void>(
context: context,
builder: (_) => const StackOkDialog(
title: "ChangeNOW not available for Epic Cash",
),
);
} else if (coin.name.endsWith("TestNet")) {
showDialog<void>(
await showDialog<void>(
context: context,
builder: (_) => const StackOkDialog(
title: "ChangeNOW not available for test net coins",
@ -247,10 +256,10 @@ class _WalletViewState extends ConsumerState<WalletView> {
element.ticker.toLowerCase() == coin.ticker.toLowerCase());
if (currencies.isNotEmpty) {
ref
unawaited(ref
.read(estimatedRateExchangeFormProvider)
.updateFrom(currencies.first, false);
ref.read(estimatedRateExchangeFormProvider).updateTo(
.updateFrom(currencies.first, false));
unawaited(ref.read(estimatedRateExchangeFormProvider).updateTo(
ref
.read(availableChangeNowCurrenciesStateProvider.state)
.state
@ -258,16 +267,82 @@ class _WalletViewState extends ConsumerState<WalletView> {
(element) =>
element.ticker.toLowerCase() != coin.ticker.toLowerCase(),
),
false);
false));
}
Navigator.of(context).pushNamed(
unawaited(Navigator.of(context).pushNamed(
WalletInitiatedExchangeView.routeName,
arguments: Tuple2(
walletId,
coin,
),
);
));
}
}
Future<void> attemptAnonymize() async {
bool shouldPop = false;
unawaited(
showDialog(
context: context,
builder: (context) => WillPopScope(
child: const CustomLoadingOverlay(
message: "Anonymizing balance",
eventBus: null,
),
onWillPop: () async => shouldPop,
),
),
);
final firoWallet = ref.read(managerProvider).wallet as FiroWallet;
final publicBalance = await firoWallet.availablePublicBalance();
if (publicBalance <= Decimal.zero) {
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "No funds available to anonymize!",
context: context,
),
);
}
return;
}
try {
await firoWallet.anonymizeAllPublicFunds();
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "Anonymize transaction submitted",
context: context,
),
);
}
} catch (e) {
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
);
await showDialog<dynamic>(
context: context,
builder: (_) => StackOkDialog(
title: "Anonymize all failed",
message: "Reason: $e",
),
);
}
}
}
@ -275,6 +350,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final coin = ref.watch(managerProvider.select((value) => value.coin));
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
@ -283,9 +360,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
title: Row(
children: [
SvgPicture.asset(
Assets.svg.iconFor(
coin: ref
.watch(managerProvider.select((value) => value.coin))),
Assets.svg.iconFor(coin: coin),
// color: CFColors.stackAccent,
width: 24,
height: 24,
@ -440,6 +515,69 @@ class _WalletViewState extends ConsumerState<WalletView> {
),
),
),
if (coin == Coin.firo)
const SizedBox(
height: 10,
),
if (coin == Coin.firo)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: TextButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (context) => StackDialog(
title: "Attention!",
message:
"You're about to anonymize all of your public funds.",
leftButton: TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Cancel",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
),
),
rightButton: TextButton(
onPressed: () async {
Navigator.of(context).pop();
unawaited(attemptAnonymize());
},
style: Theme.of(context)
.textButtonTheme
.style
?.copyWith(
backgroundColor:
MaterialStateProperty.all<Color>(
CFColors.stackAccent,
),
),
child: Text(
"Continue",
style: STextStyles.button,
),
),
),
);
},
child: Text(
"Anonymize funds",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
),
),
),
],
),
),
const SizedBox(
height: 20,
),
@ -550,6 +688,25 @@ class _WalletViewState extends ConsumerState<WalletView> {
ref.read(managerProvider).walletId;
final coin =
ref.read(managerProvider).coin;
switch (ref
.read(walletBalanceToggleStateProvider
.state)
.state) {
case WalletBalanceToggleState.full:
ref
.read(
publicPrivateBalanceStateProvider
.state)
.state = "Public";
break;
case WalletBalanceToggleState.available:
ref
.read(
publicPrivateBalanceStateProvider
.state)
.state = "Private";
break;
}
Navigator.of(context).pushNamed(
SendView.routeName,
arguments: Tuple2(

View file

@ -0,0 +1,4 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
final publicPrivateBalanceStateProvider =
StateProvider<String>((_) => "Private");

View file

@ -43,7 +43,7 @@ import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 2;
const int DUST_LIMIT = 546;
const int DUST_LIMIT = 294;
const String GENESIS_HASH_MAINNET =
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
@ -2715,11 +2715,11 @@ class BitcoinWallet extends CoinServiceAPI {
final txModel = TransactionData.fromMap(transactionsMap);
DB.instance.put<dynamic>(
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'storedTxnDataHeight',
value: latestTxnBlockHeight);
DB.instance.put<dynamic>(
await DB.instance.put<dynamic>(
boxName: walletId, key: 'latest_tx_model', value: txModel);
return txModel;
@ -2897,7 +2897,7 @@ class BitcoinWallet extends CoinServiceAPI {
int changeOutputSize =
satoshisBeingUsed - satoshiAmountToSend - feeForTwoOutputs;
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > 546 satoshis, we perform the mechanics required to properly generate and use a new
// the second output's size > DUST_LIMIT satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
@ -2972,7 +2972,7 @@ class BitcoinWallet extends CoinServiceAPI {
return transactionObject;
} else {
// Something went wrong here. It either overshot or undershot the estimated fee amount or the changeOutputSize
// is smaller than or equal to 546. Revert to single output transaction.
// is smaller than or equal to DUST_LIMIT. Revert to single output transaction.
Logging.instance.log('1 output in tx', level: LogLevel.Info);
Logging.instance
.log('Input size: $satoshisBeingUsed', level: LogLevel.Info);
@ -2999,7 +2999,7 @@ class BitcoinWallet extends CoinServiceAPI {
return transactionObject;
}
} else {
// No additional outputs needed since adding one would mean that it'd be smaller than 546 sats
// No additional outputs needed since adding one would mean that it'd be smaller than DUST_LIMIT sats
// which makes it uneconomical to add to the transaction. Here, we pass data directly to instruct
// the wallet to begin crafting the transaction that the user requested.
Logging.instance.log('1 output in tx', level: LogLevel.Info);

File diff suppressed because it is too large Load diff

View file

@ -85,6 +85,9 @@ class _SVG {
String get questionMessage => "assets/svg/message-question.svg";
String get envelope => "assets/svg/envelope.svg";
String get share => "assets/svg/share-2.svg";
String get anonymize => "assets/svg/tx-icon-anonymize.svg";
String get anonymizePending => "assets/svg/tx-icon-anonymize-pending.svg";
String get anonymizeFailed => "assets/svg/tx-icon-anonymize-failed.svg";
String get receive => "assets/svg/tx-icon-receive.svg";
String get receivePending => "assets/svg/tx-icon-receive-pending.svg";

View file

@ -107,18 +107,36 @@ class DbVersionMigrator {
}
}
// finally update version
// update version
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 1);
return;
// not needed yet
// return migrate(1);
// try to continue migrating
return await migrate(1);
// case 1:
// return migrate(2);
// await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
// final walletsService = WalletsService();
// final walletInfoList = await walletsService.walletNames;
// for (final walletInfo in walletInfoList.values) {
// if (walletInfo.coin == Coin.firo) {
// await Hive.openBox<dynamic>(walletInfo.walletId);
// await DB.instance.delete<dynamic>(
// key: "latest_tx_model", boxName: walletInfo.walletId);
// await DB.instance.delete<dynamic>(
// key: "latest_lelantus_tx_model", boxName: walletInfo.walletId);
// }
// }
//
// // update version
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 2);
//
// // try to continue migrating
// return await migrate(2);
default:
// finally return
return;
}
}

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
@ -35,10 +37,30 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
if (coin == Coin.epicCash && _transaction.slateId == null) {
return "Restored Funds";
}
if (_transaction.subType == "mint") {
// if (type == "Received") {
if (_transaction.confirmedStatus) {
return "Anonymized";
} else {
return "Anonymizing";
}
// } else if (type == "Sent") {
// if (_transaction.confirmedStatus) {
// return "Sent MINT";
// } else {
// return "Sending MINT";
// }
// } else {
// return type;
// }
}
if (type == "Received") {
if (_transaction.isMinting) {
return "Minting";
} else if (_transaction.confirmedStatus) {
// if (_transaction.isMinting) {
// return "Minting";
// } else
if (_transaction.confirmedStatus) {
return "Received";
} else {
return "Receiving";
@ -95,23 +117,23 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
),
onPressed: () async {
if (coin == Coin.epicCash && _transaction.slateId == null) {
showFloatingFlushBar(
unawaited(showFloatingFlushBar(
context: context,
message:
"Restored Epic funds from your Seed have no Data.\nUse Stack Backup to keep your transaction history.",
type: FlushBarType.warning,
duration: const Duration(seconds: 5),
);
));
return;
}
Navigator.of(context).pushNamed(
unawaited(Navigator.of(context).pushNamed(
TransactionDetailsView.routeName,
arguments: Tuple3(
_transaction,
coin,
walletId,
),
);
));
},
child: Padding(
padding: const EdgeInsets.all(8),

View file

@ -256,6 +256,9 @@ flutter:
- assets/svg/message-question.svg
- assets/svg/envelope.svg
- assets/svg/share-2.svg
- assets/svg/tx-icon-anonymize.svg
- assets/svg/tx-icon-anonymize-pending.svg
- assets/svg/tx-icon-anonymize-failed.svg
# coin icons
- assets/svg/coin_icons/Bitcoin.svg
- assets/svg/coin_icons/Dogecoin.svg

View file

@ -30,7 +30,7 @@ void main() {
expect(MINIMUM_CONFIRMATIONS, 2);
});
test("bitcoin dust limit", () async {
expect(DUST_LIMIT, 546);
expect(DUST_LIMIT, 294);
});
test("bitcoin mainnet genesis block hash", () async {
expect(GENESIS_HASH_MAINNET,

View file

@ -26,7 +26,6 @@ import 'firo_wallet_test.mocks.dart';
import 'firo_wallet_test_parameters.dart';
import 'sample_data/get_anonymity_set_sample_data.dart';
import 'sample_data/get_used_serials_sample_data.dart';
import 'sample_data/get_utxos_sample_data.dart';
import 'sample_data/gethistory_samples.dart';
import 'sample_data/transaction_data_samples.dart';
@ -3398,224 +3397,224 @@ void main() {
await firo.exit();
});
test("autoMint", () async {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel('uk.spiralarm.flutter/devicelocale')
.setMockMethodCallHandler((methodCall) async => 'en_US');
final client = MockElectrumX();
final cachedClient = MockCachedElectrumX();
final secureStore = FakeSecureStorage();
final priceAPI = MockPriceAPI();
// mock electrumx client calls
when(client.getServerFeatures()).thenAnswer((_) async => {
"hosts": {},
"pruning": null,
"server_version": "Unit tests",
"protocol_min": "1.4",
"protocol_max": "1.4.2",
"genesis_hash": GENESIS_HASH_MAINNET,
"hash_function": "sha256",
"services": []
});
when(client.getBlockHeadTip()).thenAnswer(
(_) async => {"height": 465873, "hex": "this value not used here"});
when(client.broadcastTransaction(rawTx: anyNamed("rawTx")))
.thenAnswer((realInvocation) async {
final rawTx =
realInvocation.namedArguments[const Symbol("rawTx")] as String;
final rawTxData = Format.stringToUint8List(rawTx);
final hash = sha256
.convert(sha256.convert(rawTxData.toList(growable: false)).bytes);
final reversedBytes =
Uint8List.fromList(hash.bytes.reversed.toList(growable: false));
final txid = Format.uint8listToString(reversedBytes);
return txid;
});
when(client.estimateFee(blocks: 1))
.thenAnswer((_) async => Decimal.parse("0.00001000"));
when(client.estimateFee(blocks: 5))
.thenAnswer((_) async => Decimal.parse("0.00001000"));
when(client.estimateFee(blocks: 20))
.thenAnswer((_) async => Decimal.parse("0.00001000"));
when(cachedClient.getAnonymitySet(
groupId: "1", blockhash: "", coin: Coin.firo))
.thenAnswer((_) async => GetAnonymitySetSampleData.data);
when(cachedClient.getUsedCoinSerials(startNumber: 0, coin: Coin.firo))
.thenAnswer(
(_) async => GetUsedSerialsSampleData.serials['serials'] as List);
when(client.getLatestCoinId()).thenAnswer((_) async => 1);
// when(client.getCoinsForRecovery(setId: 1))
// .thenAnswer((_) async => getCoinsForRecoveryResponse);
when(client.getUsedCoinSerials(startNumber: 0))
.thenAnswer((_) async => GetUsedSerialsSampleData.serials);
// mock price calls
when(priceAPI.getPricesAnd24hChange(baseCurrency: "USD")).thenAnswer(
(_) async => {Coin.firo: Tuple2(Decimal.fromInt(10), 1.0)});
// mock transaction calls
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash0,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData0);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash1,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData1);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash2,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData2);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash3,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData3);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash4,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData4);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash5,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData5);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash6,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData6);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash7,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData7);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash8,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData8);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash9,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData9);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash10,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData10);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash11,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData11);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash12,
coin: Coin.firo,
)).thenAnswer((_) async => SampleGetTransactionData.txData12);
final firo = FiroWallet(
walletName: testWalletName,
walletId: "${testWalletId}autoMint",
coin: Coin.firo,
client: client,
cachedClient: cachedClient,
secureStore: secureStore,
priceAPI: priceAPI,
tracker: MockTransactionNotificationTracker(),
);
// pre grab derivations in order to set up mock calls needed later on
await firo.fillAddresses(TEST_MNEMONIC);
final wallet = await Hive.openBox<dynamic>("${testWalletId}autoMint");
await wallet.put(
'receivingAddresses', RefreshTestParams.receivingAddresses);
await wallet.put('changeAddresses', RefreshTestParams.changeAddresses);
final rcv = await secureStore.read(
key: "${testWalletId}autoMint_receiveDerivations");
final chg = await secureStore.read(
key: "${testWalletId}autoMint_changeDerivations");
final receiveDerivations =
Map<String, dynamic>.from(jsonDecode(rcv as String) as Map);
final changeDerivations =
Map<String, dynamic>.from(jsonDecode(chg as String) as Map);
for (int i = 0; i < receiveDerivations.length; i++) {
final receiveHash = AddressUtils.convertToScriptHash(
receiveDerivations["$i"]!["address"] as String, firoNetwork);
final changeHash = AddressUtils.convertToScriptHash(
changeDerivations["$i"]!["address"] as String, firoNetwork);
List<Map<String, dynamic>> data;
switch (receiveHash) {
case SampleGetHistoryData.scripthash0:
data = SampleGetHistoryData.data0;
break;
case SampleGetHistoryData.scripthash1:
data = SampleGetHistoryData.data1;
break;
case SampleGetHistoryData.scripthash2:
data = SampleGetHistoryData.data2;
break;
case SampleGetHistoryData.scripthash3:
data = SampleGetHistoryData.data3;
break;
default:
data = [];
}
when(client.getHistory(scripthash: receiveHash))
.thenAnswer((_) async => data);
switch (changeHash) {
case SampleGetHistoryData.scripthash0:
data = SampleGetHistoryData.data0;
break;
case SampleGetHistoryData.scripthash1:
data = SampleGetHistoryData.data1;
break;
case SampleGetHistoryData.scripthash2:
data = SampleGetHistoryData.data2;
break;
case SampleGetHistoryData.scripthash3:
data = SampleGetHistoryData.data3;
break;
default:
data = [];
}
when(client.getHistory(scripthash: changeHash))
.thenAnswer((_) async => data);
}
when(client.getUTXOs(scripthash: GetUtxoSampleData.scriptHash0))
.thenAnswer((_) async => GetUtxoSampleData.utxos0);
when(client.getUTXOs(scripthash: GetUtxoSampleData.scriptHash1))
.thenAnswer((_) async => GetUtxoSampleData.utxos1);
await firo.recoverFromMnemonic(
mnemonic: TEST_MNEMONIC,
maxUnusedAddressGap: 20,
height: 0,
maxNumberOfIndexesToCheck: 1000);
firo.timer = Timer(const Duration(minutes: 3), () {});
await firo.refresh();
bool flag = false;
try {
await firo.autoMint();
} catch (_) {
flag = true;
}
expect(flag, false);
await firo.exit();
}, timeout: const Timeout(Duration(minutes: 3)));
// test("autoMint", () async {
// TestWidgetsFlutterBinding.ensureInitialized();
// const MethodChannel('uk.spiralarm.flutter/devicelocale')
// .setMockMethodCallHandler((methodCall) async => 'en_US');
//
// final client = MockElectrumX();
// final cachedClient = MockCachedElectrumX();
// final secureStore = FakeSecureStorage();
// final priceAPI = MockPriceAPI();
//
// // mock electrumx client calls
// when(client.getServerFeatures()).thenAnswer((_) async => {
// "hosts": {},
// "pruning": null,
// "server_version": "Unit tests",
// "protocol_min": "1.4",
// "protocol_max": "1.4.2",
// "genesis_hash": GENESIS_HASH_MAINNET,
// "hash_function": "sha256",
// "services": []
// });
//
// when(client.getBlockHeadTip()).thenAnswer(
// (_) async => {"height": 465873, "hex": "this value not used here"});
//
// when(client.broadcastTransaction(rawTx: anyNamed("rawTx")))
// .thenAnswer((realInvocation) async {
// final rawTx =
// realInvocation.namedArguments[const Symbol("rawTx")] as String;
// final rawTxData = Format.stringToUint8List(rawTx);
//
// final hash = sha256
// .convert(sha256.convert(rawTxData.toList(growable: false)).bytes);
//
// final reversedBytes =
// Uint8List.fromList(hash.bytes.reversed.toList(growable: false));
//
// final txid = Format.uint8listToString(reversedBytes);
//
// return txid;
// });
//
// when(client.estimateFee(blocks: 1))
// .thenAnswer((_) async => Decimal.parse("0.00001000"));
// when(client.estimateFee(blocks: 5))
// .thenAnswer((_) async => Decimal.parse("0.00001000"));
// when(client.estimateFee(blocks: 20))
// .thenAnswer((_) async => Decimal.parse("0.00001000"));
//
// when(cachedClient.getAnonymitySet(
// groupId: "1", blockhash: "", coin: Coin.firo))
// .thenAnswer((_) async => GetAnonymitySetSampleData.data);
// when(cachedClient.getUsedCoinSerials(startNumber: 0, coin: Coin.firo))
// .thenAnswer(
// (_) async => GetUsedSerialsSampleData.serials['serials'] as List);
//
// when(client.getLatestCoinId()).thenAnswer((_) async => 1);
// // when(client.getCoinsForRecovery(setId: 1))
// // .thenAnswer((_) async => getCoinsForRecoveryResponse);
// when(client.getUsedCoinSerials(startNumber: 0))
// .thenAnswer((_) async => GetUsedSerialsSampleData.serials);
//
// // mock price calls
// when(priceAPI.getPricesAnd24hChange(baseCurrency: "USD")).thenAnswer(
// (_) async => {Coin.firo: Tuple2(Decimal.fromInt(10), 1.0)});
//
// // mock transaction calls
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash0,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData0);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash1,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData1);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash2,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData2);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash3,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData3);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash4,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData4);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash5,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData5);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash6,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData6);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash7,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData7);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash8,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData8);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash9,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData9);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash10,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData10);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash11,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData11);
// when(cachedClient.getTransaction(
// txHash: SampleGetTransactionData.txHash12,
// coin: Coin.firo,
// )).thenAnswer((_) async => SampleGetTransactionData.txData12);
//
// final firo = FiroWallet(
// walletName: testWalletName,
// walletId: "${testWalletId}autoMint",
// coin: Coin.firo,
// client: client,
// cachedClient: cachedClient,
// secureStore: secureStore,
// priceAPI: priceAPI,
// tracker: MockTransactionNotificationTracker(),
// );
//
// // pre grab derivations in order to set up mock calls needed later on
// await firo.fillAddresses(TEST_MNEMONIC);
// final wallet = await Hive.openBox<dynamic>("${testWalletId}autoMint");
// await wallet.put(
// 'receivingAddresses', RefreshTestParams.receivingAddresses);
// await wallet.put('changeAddresses', RefreshTestParams.changeAddresses);
//
// final rcv = await secureStore.read(
// key: "${testWalletId}autoMint_receiveDerivations");
// final chg = await secureStore.read(
// key: "${testWalletId}autoMint_changeDerivations");
// final receiveDerivations =
// Map<String, dynamic>.from(jsonDecode(rcv as String) as Map);
// final changeDerivations =
// Map<String, dynamic>.from(jsonDecode(chg as String) as Map);
//
// for (int i = 0; i < receiveDerivations.length; i++) {
// final receiveHash = AddressUtils.convertToScriptHash(
// receiveDerivations["$i"]!["address"] as String, firoNetwork);
// final changeHash = AddressUtils.convertToScriptHash(
// changeDerivations["$i"]!["address"] as String, firoNetwork);
// List<Map<String, dynamic>> data;
// switch (receiveHash) {
// case SampleGetHistoryData.scripthash0:
// data = SampleGetHistoryData.data0;
// break;
// case SampleGetHistoryData.scripthash1:
// data = SampleGetHistoryData.data1;
// break;
// case SampleGetHistoryData.scripthash2:
// data = SampleGetHistoryData.data2;
// break;
// case SampleGetHistoryData.scripthash3:
// data = SampleGetHistoryData.data3;
// break;
// default:
// data = [];
// }
// when(client.getHistory(scripthash: receiveHash))
// .thenAnswer((_) async => data);
//
// switch (changeHash) {
// case SampleGetHistoryData.scripthash0:
// data = SampleGetHistoryData.data0;
// break;
// case SampleGetHistoryData.scripthash1:
// data = SampleGetHistoryData.data1;
// break;
// case SampleGetHistoryData.scripthash2:
// data = SampleGetHistoryData.data2;
// break;
// case SampleGetHistoryData.scripthash3:
// data = SampleGetHistoryData.data3;
// break;
// default:
// data = [];
// }
//
// when(client.getHistory(scripthash: changeHash))
// .thenAnswer((_) async => data);
// }
//
// when(client.getUTXOs(scripthash: GetUtxoSampleData.scriptHash0))
// .thenAnswer((_) async => GetUtxoSampleData.utxos0);
// when(client.getUTXOs(scripthash: GetUtxoSampleData.scriptHash1))
// .thenAnswer((_) async => GetUtxoSampleData.utxos1);
//
// await firo.recoverFromMnemonic(
// mnemonic: TEST_MNEMONIC,
// maxUnusedAddressGap: 20,
// height: 0,
// maxNumberOfIndexesToCheck: 1000);
//
// firo.timer = Timer(const Duration(minutes: 3), () {});
//
// await firo.refresh();
//
// bool flag = false;
// try {
// await firo.autoMint();
// } catch (_) {
// flag = true;
// }
// expect(flag, false);
//
// await firo.exit();
// }, timeout: const Timeout(Duration(minutes: 3)));
test("exit", () async {
final firo = FiroWallet(

View file

@ -386,11 +386,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i8.Future<void>);
@override
_i8.Future<dynamic> GetCoinsToJoinSplit(int? required) =>
_i8.Future<dynamic> getCoinsToJoinSplit(int? required) =>
(super.noSuchMethod(Invocation.method(#GetCoinsToJoinSplit, [required]),
returnValue: Future<dynamic>.value()) as _i8.Future<dynamic>);
@override
_i8.Future<int> EstimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod(
_i8.Future<int> estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod(
Invocation.method(#EstimateJoinSplitFee, [spendAmount]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override