cw-1584-ironwood-migration-modal (#3481)

* ironwood migration modal

* auto-show when migration begins

* remove restricted import

* new condition

* string fix

* only activate if ironwood is active

* reaction safeguards

* migration*
This commit is contained in:
malik1004x
2026-08-04 14:08:48 +02:00
committed by GitHub
parent dc21341725
commit 0257f5cf76
11 changed files with 260 additions and 8 deletions
+1
View File
@@ -145,4 +145,5 @@ class PreferencesKey {
static const backgroundImage = 'background_image';
static const mwebAdDismissed = "mweb_ad_dismissed";
static const balanceHideCounter = "balance_hide_counter";
static const zcashMigrationModalViewed = "zcash_migration_modal_viewed";
}
+22
View File
@@ -13,10 +13,14 @@ import 'package:cake_wallet/new-ui/widgets/coins_page/mweb_ad.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/top_bar_widget/top_bar.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/unconfirmed_balance_widget.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/wallet_info.dart';
import "package:cake_wallet/new-ui/widgets/coins_page/zcash_migration_modal.dart";
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import "package:cw_core/amount/money.dart";
import "package:cw_core/crypto_currency.dart";
import "package:cw_core/transaction_direction.dart";
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -48,6 +52,24 @@ class _NewHomePageState extends State<NewHomePage> {
_lightningMode = false;
});
});
reaction((_) => widget.dashboardViewModel.isMigratingToIronwood, (val) {
if (val && !widget.dashboardViewModel.settingsStore.zcashMigrationModalViewed) {
if(!context.mounted) {
return;
}
widget.dashboardViewModel.settingsStore.zcashMigrationModalViewed = true;
showMaterialModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (context) => ZcashMigrationModal(
hasContinue: true,
balance: widget.dashboardViewModel.wallet.balance.values.first.unavailable
.toStringWithSymbol(),
),
);
}
});
}
void _setAccountViewModel() {
@@ -1,8 +1,10 @@
import 'package:cake_wallet/generated/i18n.dart';
import "package:cake_wallet/new-ui/widgets/coins_page/zcash_migration_modal.dart";
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import "package:flutter/cupertino.dart";
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
@@ -21,6 +23,7 @@ class UnconfirmedBalanceWidget extends StatelessWidget {
return Observer(builder: (_) {
final show = dashboardViewModel.balanceViewModel
.hasAdditionalBalance(dashboardViewModel.wallet.currency);
final isIronwoodMigration = dashboardViewModel.isMigratingToIronwood;
return AnimatedSize(
duration: const Duration(milliseconds: 300),
@@ -44,13 +47,17 @@ class UnconfirmedBalanceWidget extends StatelessWidget {
color: Colors.transparent,
child: InkWell(
onTap: () {
final modal = isIronwoodMigration
? ZcashMigrationModal(
hasContinue: false, balance: "${balance} ${currency.symbol}")
: UnconfirmedBalanceModal(
balance: balance.toStringWithSymbol(),
currencyIconPath: currency.iconPath ?? "",
);
showMaterialModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => UnconfirmedBalanceModal(
balance: balance.toStringWithSymbol(),
currencyIconPath: currency.iconPath ?? "",
));
builder: (context) => modal);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
@@ -63,8 +70,12 @@ class UnconfirmedBalanceWidget extends StatelessWidget {
SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
child: isIronwoodMigration
? CupertinoActivityIndicator(
color: Theme.of(context).colorScheme.primary,
)
: CircularProgressIndicator(
strokeWidth: 2,
backgroundColor:
Theme.of(context).colorScheme.primary.withAlpha(50),
color: Theme.of(context).colorScheme.primary,
@@ -80,7 +91,7 @@ class UnconfirmedBalanceWidget extends StatelessWidget {
color: Theme.of(context).colorScheme.primary,
),
),
Text(S.of(context).confirming),
Text(isIronwoodMigration ? S.of(context).migrating_to_ironwood : S.of(context).confirming),
],
)
],
@@ -172,3 +183,4 @@ class UnconfirmedBalanceModal extends StatelessWidget {
);
}
}
@@ -0,0 +1,152 @@
import "package:cake_wallet/generated/i18n.dart";
import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
import "package:cake_wallet/src/widgets/cake_image_widget.dart";
import "package:flutter/material.dart";
import "package:url_launcher/url_launcher.dart";
class ZcashMigrationModal extends StatelessWidget {
const ZcashMigrationModal({required this.hasContinue, required this.balance, super.key});
final String balance;
final bool hasContinue;
@override
Widget build(BuildContext context) => SafeArea(
bottom: false,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),),
child: SafeArea(
top: false,
child: Column(
children: [
ModalTopBar(
title: S.of(context).zcash_network_upgrade,
trailingIcon: const Icon(Icons.close),
onTrailingPressed: Navigator.of(context).pop,
trailingSemanticLabel: S.of(context).close,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Expanded(
child: Column(
spacing: 40,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 146,
height: 79,
child: Stack(
children: [
const Positioned(
left: 67,
top: 0,
bottom: 0,
child: CakeImageWidget(
imageUrl: "assets/new-ui/ironwood_migration.svg",
width: 75,
height: 75,
),
),
Positioned(
left: 0,
child: Container(
width: 79,
height: 79,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(99999999),
border: Border.all(
color: Theme.of(context).colorScheme.surface,
width: 4,),),
child: const CakeImageWidget(
imageUrl: "assets/new-ui/crypto_full_icons/zcash.svg",
height: 75,
width: 75,
),
),
),
],
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Theme.of(context).colorScheme.surfaceContainer,),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
Text(
balance,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,),
),
Text(S.of(context).is_currently_being_migrated),
],
),
),),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
children: [
TextSpan(
text: "${S.of(context).zcash_network_upgrade_desc_1}\n\n",
style: Theme.of(context).textTheme.bodyMedium,),
TextSpan(
text: "${S.of(context).zcash_network_upgrade_desc_2}\n\n",
style: Theme.of(context).textTheme.bodyMedium,),
TextSpan(
text: "${S.of(context).zcash_network_upgrade_desc_3}\n\n",
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: const Color(0xFFF9C03C),
),
),
],
),
),
],
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
spacing: 12,
children: [
NewPrimaryButton(
onPressed: _launchDocs,
text: S.of(context).learn_more,
color: Theme.of(context).colorScheme.surfaceContainer,
textColor: Theme.of(context).colorScheme.primary,),
if (hasContinue)
NewPrimaryButton(
onPressed: Navigator.of(context).pop,
text: S.of(context).continue_text,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,),
],
),
),
],
),
),
),
);
void _launchDocs() {
try {
launchUrl(Uri.parse("https://docs.cakewallet.com/cryptos/zcash/ironwood-migration"));
} catch (_) {}
}
}
+9
View File
@@ -151,6 +151,7 @@ abstract class SettingsStoreBase with Store {
required this.mwebNodeUri,
required this.mwebAdDismissed,
required this.balanceHideCounter,
required this.zcashMigrationModalViewed,
required bool initialEnableAutomaticNodeSwitching,
required String initialBackgroundImage,
TransactionPriority? initialBitcoinTransactionPriority,
@@ -766,6 +767,9 @@ abstract class SettingsStoreBase with Store {
(int balanceHideCounter) =>
_sharedPreferences.setInt(PreferencesKey.balanceHideCounter, balanceHideCounter));
reaction((_) => zcashMigrationModalViewed,
(val) => _sharedPreferences.setBool(PreferencesKey.zcashMigrationModalViewed, val));
this.nodes.observe((change) {
if (change.newValue != null && change.key != null) {
_saveCurrentNode(change.newValue!, change.key!);
@@ -1075,6 +1079,9 @@ abstract class SettingsStoreBase with Store {
@observable
bool mwebAdDismissed;
@observable
bool zcashMigrationModalViewed;
final SecureStorage _secureStorage;
final SharedPreferences _sharedPreferences;
@@ -1350,6 +1357,7 @@ abstract class SettingsStoreBase with Store {
final enableAutomaticNodeSwitching =
sharedPreferences.getBool(PreferencesKey.enableAutomaticNodeSwitching) ?? true;
final backgroundImage = sharedPreferences.getString(PreferencesKey.backgroundImage) ?? '';
final zcashMigrationModalViewed = sharedPreferences.getBool(PreferencesKey.zcashMigrationModalViewed) ?? false;
// If no value
if (pinLength == null || pinLength == 0) {
@@ -1762,6 +1770,7 @@ abstract class SettingsStoreBase with Store {
initialBuiltinTor: builtinTor,
mwebAdDismissed: mwebAdDismissed,
balanceHideCounter: balanceHideCounter,
zcashMigrationModalViewed: zcashMigrationModalViewed
);
}
@@ -96,6 +96,7 @@ abstract class DashboardViewModelBase with Store {
isShowFirstYatIntroduction = false,
isShowSecondYatIntroduction = false,
isShowThirdYatIntroduction = false,
isMigratingToIronwood = false,
filterItems = [],
exchangeFilterItems = [],
subname = '',
@@ -250,6 +251,8 @@ abstract class DashboardViewModelBase with Store {
reaction((_) => tradesStore.trades, (_) => tradeMonitor.monitorActiveTrades(wallet.id));
addZcashMigrationReaction();
tradeMonitor.monitorActiveTrades(wallet.id);
}
@@ -374,6 +377,22 @@ abstract class DashboardViewModelBase with Store {
);
}
@observable
ReactionDisposer? zcashMigrationReactionDisposer;
@action
void addZcashMigrationReaction() {
zcashMigrationReactionDisposer?.reaction.dispose();
zcashMigrationReactionDisposer = null;
if (wallet.type == WalletType.zcash) {
zcashMigrationReactionDisposer = reaction((_) => wallet.balance.values.first, (_) async {
isMigratingToIronwood =
wallet.type == WalletType.zcash && await zcash!.hasOrchardMigratableBalance(wallet);
});
}
}
@computed
bool get isSyncHeavy {
if ([
@@ -1270,6 +1289,8 @@ abstract class DashboardViewModelBase with Store {
name = wallet.name;
loadFilterItems();
addZcashMigrationReaction();
if (wallet.type == WalletType.monero) {
subname = monero!.getCurrentAccount(wallet).label;
@@ -1551,6 +1572,9 @@ abstract class DashboardViewModelBase with Store {
}
}
@observable
bool isMigratingToIronwood;
String getTransactionType(TransactionInfo tx) {
if (wallet.type == WalletType.bitcoin) {
if (tx.isReplaced == true) return ' (replaced)';
+5
View File
@@ -213,6 +213,11 @@ class CWZcash extends Zcash {
bool ironwoodActive(WalletAddresses walletAddresses) {
return (walletAddresses as ZcashWalletAddresses).ironwoodActive;
}
@override
Future<bool> hasOrchardMigratableBalance(WalletBase wallet) {
return (wallet as ZcashWallet).hasOrchardMigratableBalance();
}
}
const wordList = [