Files
hash-wallet/lib/new-ui/new_dashboard.dart
T
jwinterm dd449f2594 Merge upstream Cake Wallet v6.5.0
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.
2026-10-24 23:49:29 -04:00

201 lines
7.5 KiB
Dart

import 'package:hash_wallet/utils/feature_flag.dart';
import 'dart:io';
import 'package:hash_wallet/di.dart';
import 'package:hash_wallet/entities/preferences_key.dart';
import "package:hash_wallet/new-ui/page_open_listener.dart";
import "package:hash_wallet/new-ui/page_open_listener.dart";
import 'package:hash_wallet/new-ui/pages/charts_page.dart';
import 'package:hash_wallet/new-ui/pages/home_page.dart';
import 'package:hash_wallet/new-ui/widgets/changelog_modal.dart';
import "package:hash_wallet/new-ui/widgets/wallet_removal_popup.dart";
import 'package:hash_wallet/src/screens/contact/contact_list_page.dart';
import 'package:hash_wallet/src/screens/dashboard/pages/cake_features_page.dart';
import 'package:hash_wallet/src/screens/dashboard/widgets/new_main_navbar_widget.dart';
import 'package:hash_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
import 'package:hash_wallet/src/screens/wallet_connect/widgets/bottom_sheet/bottom_sheet_listener_widget.dart';
import 'package:hash_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:hash_wallet/src/widgets/vulnerable_seeds_popup.dart';
import 'package:hash_wallet/utils/show_pop_up.dart';
import 'package:hash_wallet/utils/version_comparator.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../view_model/dashboard/dashboard_view_model.dart';
class NewDashboard extends StatefulWidget {
NewDashboard({
super.key,
required this.dashboardViewModel,
required this.bottomSheetService,
});
final DashboardViewModel dashboardViewModel;
final BottomSheetService bottomSheetService;
final List<Widget> dashboardPageWidgets = [
getIt.get<NewHomePage>(),
getIt.get<WalletListPage>(),
getIt.get<ContactListPage>(),
getIt.get<CakeFeaturesPage>(),
if (FeatureFlag.isChartsEnabled) getIt.get<ChartsPage>(),
];
@override
State<NewDashboard> createState() => _NewDashboardState();
}
class _NewDashboardState extends State<NewDashboard> {
int _selectedPage = 0;
@override
void initState() {
super.initState();
reaction((_) => widget.dashboardViewModel.appStore.wallet, (_) {
setState(() {
_selectedPage = 0;
});
_showWalletRemovalPopup(context);
});
// Hash Bags: changelog modal disabled. The Cake-supplied changelog
// text (assets/new-ui/changelog/text) describes Cake's release history
// including v6.1.0 entries that aren't ours, and the 'View more info'
// button linked to blog.cakewallet.com. Re-enable once we have our
// own changelog content + versioning under our control.
// Future.delayed(Duration(milliseconds: 300)).then((_) => _showChangelog(context));
_showVulnerableSeedsPopup(context);
_showWalletRemovalPopup(context);
}
@override
Widget build(BuildContext context) {
return BottomSheetListener(
bottomSheetService: widget.bottomSheetService,
child: CupertinoScaffold(
body: Material(
key: ValueKey("new_dashboard_page_key"),
child: Stack(
alignment: Alignment.bottomCenter,
children: [
// widget.dashboardPageWidgets[_selectedPage],
IndexedStack(
index: _selectedPage,
children: widget.dashboardPageWidgets,
),
IgnorePointer(
child: Container(
height: 150,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Theme.of(context).colorScheme.surfaceDim.withAlpha(5),
Theme.of(context).colorScheme.surfaceDim.withAlpha(50),
Theme.of(context).colorScheme.surfaceDim.withAlpha(100),
Theme.of(context).colorScheme.surfaceDim.withAlpha(150),
Theme.of(context).colorScheme.surfaceDim.withAlpha(200),
Theme.of(context).colorScheme.surfaceDim.withAlpha(200),
],
),
),
),
),
// Invisible pointer-absorbing strip behind the nav bar: it must not
// be reachable by screen-reader traversal either.
ExcludeSemantics(
child: SafeArea(
bottom: !(Platform.isIOS),
child: SizedBox(
width: double.infinity,
height: NewMainNavBar.barHeight + NewMainNavBar.barBottomPadding,
child: AbsorbPointer(
absorbing: true,
child: Container(color: Colors.transparent),
),
),
),
),
NewMainNavBar(
dashboardViewModel: widget.dashboardViewModel,
selectedIndex: _selectedPage,
onItemTap: (index) {
setState(() {
_selectedPage = index;
});
if (widget.dashboardPageWidgets[_selectedPage] case PageOpenListener page) {
page.onPageOpen();
}
},
)
],
),
),
),
);
}
void _showChangelog(BuildContext context) async {
final sharedPrefs = await SharedPreferences.getInstance();
final currentAppVersion = VersionComparator.getExtendedVersionNumber(
widget.dashboardViewModel.settingsStore.appVersion);
final lastSeenAppVersion = sharedPrefs.getInt(PreferencesKey.lastSeenAppVersion);
final isNewInstall = sharedPrefs.getBool(PreferencesKey.isNewInstall) ?? true;
if (currentAppVersion != lastSeenAppVersion && !isNewInstall) {
Future<void>.delayed(
Duration(seconds: 1),
() {
if (context.mounted) {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return ChangelogModal(
version: widget.dashboardViewModel.settingsStore.appVersion,
);
});
}
},
);
sharedPrefs.setInt(PreferencesKey.lastSeenAppVersion, currentAppVersion);
} else if (isNewInstall) {
sharedPrefs.setInt(PreferencesKey.lastSeenAppVersion, currentAppVersion);
}
}
void _showVulnerableSeedsPopup(BuildContext context) async {
final List<String> affectedWalletNames = await widget.dashboardViewModel.checkAffectedWallets();
if (affectedWalletNames.isNotEmpty) {
Future<void>.delayed(
Duration(seconds: 1),
() {
if (context.mounted) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return VulnerableSeedsPopup(affectedWalletNames);
},
);
}
},
);
}
}
Future<void> _showWalletRemovalPopup(BuildContext context) async {
if(await widget.dashboardViewModel.shouldShowRemovalPopup()) {
await Future.delayed(const Duration(seconds: 1));
final toBeRemoved = await widget.dashboardViewModel.walletsToBeRemoved;
if(context.mounted) {
await showPopUp<void>(context: context, builder: (context)=>WalletRemovalPopup(affectedWallets: toBeRemoved,));
}
}
}
}