mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-29 06:38:45 +01:00
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
349 lines
16 KiB
Dart
349 lines
16 KiB
Dart
import "dart:async";
|
|
|
|
import "package:hash_wallet/bitcoin/bitcoin.dart";
|
|
import "package:hash_wallet/core/auth_service.dart";
|
|
import "package:hash_wallet/di.dart";
|
|
import "package:hash_wallet/generated/i18n.dart";
|
|
import "package:hash_wallet/main.dart";
|
|
import "package:hash_wallet/new-ui/modal_navigator.dart";
|
|
import "package:hash_wallet/new-ui/pages/card_customizer.dart";
|
|
import "package:hash_wallet/new-ui/pages/account_education_page.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/accounts_promo.dart";
|
|
import "package:hash_wallet/src/widgets/alert_with_one_action.dart";
|
|
import "package:cw_core/sync_status.dart";
|
|
import "package:hash_wallet/new-ui/pages/seed/seed_backup_reminder_page.dart";
|
|
import "package:hash_wallet/new-ui/pages/send_page.dart";
|
|
import "package:hash_wallet/new-ui/pages/settings_page.dart";
|
|
import "package:hash_wallet/new-ui/pages/wallet_accounts_page.dart";
|
|
import "package:hash_wallet/new-ui/widgets/buy_sell/buy_sell_selector_modal.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/action_row/coin_action_row.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/assets_history/assets_history_section.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/cards/balance_card.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/cards/cards_view.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/mweb_ad.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/seed_backup_reminder_card.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/top_bar_widget/top_bar.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/unconfirmed_balance_widget.dart";
|
|
import "package:hash_wallet/new-ui/widgets/coins_page/zcash_migration_modal.dart";
|
|
import "package:hash_wallet/routes.dart";
|
|
import "package:hash_wallet/utils/payment_request.dart";
|
|
import "package:hash_wallet/view_model/dashboard/dashboard_view_model.dart";
|
|
import "package:hash_wallet/view_model/dashboard/nft_view_model.dart";
|
|
import "package:cw_core/unspent_coin_type.dart";
|
|
import "package:cw_core/wallet_type.dart";
|
|
import "package:flutter/cupertino.dart";
|
|
import "package:flutter/material.dart";
|
|
import "package:flutter_mobx/flutter_mobx.dart";
|
|
import "package:mobx/mobx.dart";
|
|
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
|
|
|
|
class NewHomePage extends StatefulWidget {
|
|
const NewHomePage({required this.dashboardViewModel, required this.nftViewModel, super.key});
|
|
|
|
final DashboardViewModel dashboardViewModel;
|
|
final NFTViewModel nftViewModel;
|
|
|
|
@override
|
|
State<NewHomePage> createState() => _NewHomePageState();
|
|
}
|
|
|
|
class _NewHomePageState extends State<NewHomePage> with RouteAware {
|
|
late final ReactionDisposer _walletReaction;
|
|
late final ReactionDisposer _migrationReaction;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_walletReaction = reaction((_) => widget.dashboardViewModel.wallet, (_) {
|
|
setState(() {});
|
|
});
|
|
|
|
_migrationReaction = 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(),
|
|
),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_walletReaction();
|
|
_migrationReaction();
|
|
routeObserver.unsubscribe(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didPopNext() => widget.dashboardViewModel.loadSeedBackupReminder();
|
|
|
|
@override
|
|
Widget build(BuildContext context) => Container(
|
|
height: MediaQuery.of(context).size.height,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
Theme.of(context).colorScheme.surface,
|
|
Theme.of(context).colorScheme.surfaceDim,
|
|
],
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
),
|
|
),
|
|
child: Stack(
|
|
children: [
|
|
CustomScrollView(
|
|
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
|
slivers: [
|
|
SliverPadding(
|
|
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
|
sliver: CupertinoSliverRefreshControl(
|
|
refreshTriggerPullDistance: 160,
|
|
refreshIndicatorExtent: 90,
|
|
onRefresh: () {
|
|
unawaited(widget.nftViewModel.getNFTAssetByWallet());
|
|
|
|
return widget.dashboardViewModel.refreshDashboard();
|
|
},
|
|
),
|
|
),
|
|
SliverToBoxAdapter(
|
|
child: Observer(
|
|
builder: (_) {
|
|
final _lightningMode = widget.dashboardViewModel.lightningMode;
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
spacing: 24.0,
|
|
children: [
|
|
TopBar(
|
|
key: ValueKey(widget.dashboardViewModel.wallet.id),
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
onLightningSwitchPress: () {
|
|
widget.dashboardViewModel.toggleLightningMode();
|
|
},
|
|
onSettingsButtonPress: () {
|
|
CupertinoScaffold.showCupertinoModalBottomSheet(
|
|
context: context,
|
|
barrierColor: Colors.black.withAlpha(85),
|
|
builder: (context) => FractionallySizedBox(
|
|
child: Material(
|
|
child: NewSettingsPage(
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
authService: getIt.get<AuthService>(),
|
|
),
|
|
),
|
|
),
|
|
).then((_) async {
|
|
widget.dashboardViewModel.accountListViewModel?.reload();
|
|
await Future<void>.delayed(Duration.zero);
|
|
await widget.dashboardViewModel.loadCardDesigns();
|
|
if (mounted) setState(() {});
|
|
});
|
|
},
|
|
),
|
|
Column(
|
|
children: [
|
|
AnimatedSwitcher(
|
|
duration: const Duration(milliseconds: 200),
|
|
switchInCurve: Curves.easeOut,
|
|
switchOutCurve: Curves.easeOut,
|
|
layoutBuilder: (currentChild, previousChildren) => Stack(
|
|
// the front (selected) card sits at the bottom of the stack box
|
|
alignment: Alignment.bottomCenter,
|
|
children: <Widget>[
|
|
...previousChildren,
|
|
if (currentChild != null) currentChild,
|
|
],
|
|
),
|
|
child: KeyedSubtree(
|
|
key: ValueKey(_lightningMode),
|
|
child: CardsView(
|
|
key: ValueKey(
|
|
"${widget.dashboardViewModel.wallet.name}_${_lightningMode}_${widget.dashboardViewModel.accountListViewModel?.accounts.length ?? 0}_${widget.dashboardViewModel.cardDesigns.length}"),
|
|
onCustomizeTapped: openCardCustomizer,
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
accountListViewModel:
|
|
widget.dashboardViewModel.accountListViewModel,
|
|
onCompactModeBackgroundCardsTapped: openAccountCustomizer,
|
|
lightningMode: _lightningMode),
|
|
),
|
|
),
|
|
Observer(
|
|
builder: (_) => AnimatedSize(
|
|
duration: const Duration(milliseconds: 150),
|
|
curve: Curves.easeInOutCubic,
|
|
child: (widget.dashboardViewModel
|
|
.shouldShowBalanceHiddenMessage)
|
|
? Column(
|
|
children: [
|
|
const SizedBox(
|
|
height: 12,
|
|
width: double.infinity,
|
|
),
|
|
Text(
|
|
S.of(context).long_press_show_balance,
|
|
style: TextStyle(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.onSurfaceVariant),
|
|
)
|
|
],
|
|
)
|
|
: const SizedBox(width: double.infinity),
|
|
)),
|
|
UnconfirmedBalanceWidget(
|
|
dashboardViewModel: widget.dashboardViewModel),
|
|
SeedBackupReminderCard(
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
onTap: openSeedBackupReminder,
|
|
),
|
|
],
|
|
),
|
|
if (AccountsPromo.supportsWallet(widget.dashboardViewModel.wallet.type) &&
|
|
widget.dashboardViewModel.accountListViewModel != null &&
|
|
!_lightningMode)
|
|
AccountsPromo(
|
|
settingsStore: widget.dashboardViewModel.settingsStore,
|
|
walletName: walletTypeToString(widget.dashboardViewModel.wallet.type),
|
|
onTap: _openAccountsFromPromo,
|
|
),
|
|
Column(
|
|
children: [
|
|
CoinActionRow(
|
|
lightningMode: _lightningMode,
|
|
showSwap: widget.dashboardViewModel.isEnabledSwapAction,
|
|
walletType: widget.dashboardViewModel.wallet.type,
|
|
),
|
|
MwebAd(
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
),
|
|
],
|
|
)
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
Observer(
|
|
builder: (_) => AssetsHistorySection(
|
|
nftViewModel: widget.nftViewModel,
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(
|
|
child: SizedBox(height: 80.0),
|
|
)
|
|
]),
|
|
Container(
|
|
height: (MediaQuery.of(context).padding.top),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.bottomCenter,
|
|
end: Alignment.topCenter,
|
|
colors: <Color>[
|
|
Theme.of(context).colorScheme.surface.withAlpha(5),
|
|
Theme.of(context).colorScheme.surface.withAlpha(25),
|
|
Theme.of(context).colorScheme.surface.withAlpha(50),
|
|
Theme.of(context).colorScheme.surface.withAlpha(100),
|
|
Theme.of(context).colorScheme.surface.withAlpha(150),
|
|
Theme.of(context).colorScheme.surface.withAlpha(175),
|
|
Theme.of(context).colorScheme.surface.withAlpha(200),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
Future<void> openCardCustomizer() async {
|
|
if (!_checkReadyToManage()) return;
|
|
await CardCustomizer.show(
|
|
context: context,
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
account: !widget.dashboardViewModel.lightningMode &&
|
|
widget.dashboardViewModel.isMultiAccountsEnabled
|
|
? widget.dashboardViewModel.accountListViewModel?.selectedAccount
|
|
: null,
|
|
lightningMode: widget.dashboardViewModel.lightningMode,
|
|
);
|
|
await widget.dashboardViewModel.accountListViewModel?.reload();
|
|
await widget.dashboardViewModel.loadCardDesigns();
|
|
}
|
|
|
|
void openSeedBackupReminder() {
|
|
Navigator.of(context).push(
|
|
CupertinoPageRoute<void>(
|
|
builder: (_) => SeedBackupReminderPage(
|
|
dashboardViewModel: widget.dashboardViewModel,
|
|
authService: getIt.get<AuthService>(),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> openAccountCustomizer() async {
|
|
if (widget.dashboardViewModel.accountListViewModel == null || !_checkReadyToManage()) return;
|
|
|
|
final page = getIt.get<WalletAccountsPage>(param1: widget.dashboardViewModel);
|
|
|
|
await CupertinoScaffold.showCupertinoModalBottomSheet(
|
|
barrierColor: Colors.black.withAlpha(60),
|
|
context: context,
|
|
builder: (context) => ModalNavigator(
|
|
parentContext: context,
|
|
heightMode: ModalHeightModes.fullScreen,
|
|
rootPage: Material(
|
|
child: page,
|
|
),
|
|
),
|
|
);
|
|
await widget.dashboardViewModel.loadCardDesigns();
|
|
}
|
|
|
|
Future<void> _openAccountsFromPromo() async {
|
|
if (!AccountsPromo.supportsWallet(widget.dashboardViewModel.wallet.type) ||
|
|
widget.dashboardViewModel.accountListViewModel == null || !_checkReadyToManage()) return;
|
|
|
|
await AccountEducationPage(
|
|
settingsStore: widget.dashboardViewModel.settingsStore,
|
|
).show(context);
|
|
if (mounted) await openAccountCustomizer();
|
|
}
|
|
|
|
bool _checkReadyToManage() {
|
|
if (widget.dashboardViewModel.status is! SyncedSyncStatus) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertWithOneAction(
|
|
alertTitle: S.of(context).wallet_is_syncing,
|
|
alertContent: S.of(context).cannot_manage_accounts_during_sync,
|
|
buttonText: S.of(context).ok,
|
|
buttonAction: Navigator.of(context).pop,
|
|
),
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|