CakeWallet/lib/src/screens/dashboard/dashboard_page.dart
OmarHatem c5f5d1dd4d fix edit token page
minor fixes
2025-03-10 11:49:16 +02:00

399 lines
13 KiB
Dart

import 'dart:async';
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/cake_features_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/src/widgets/haven_wallet_removal_popup.dart';
import 'package:cake_wallet/src/widgets/services_updates_widget.dart';
import 'package:cake_wallet/src/widgets/vulnerable_seeds_popup.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/version_comparator.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/navigation_dock.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
class DashboardPage extends StatefulWidget {
DashboardPage({
required this.bottomSheetService,
required this.balancePage,
required this.dashboardViewModel,
required this.addressListViewModel,
});
final BalancePage balancePage;
final BottomSheetService bottomSheetService;
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
@override
State<DashboardPage> createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
@override
void initState() {
super.initState();
bool isMobileLayout =
responsiveLayoutUtil.screenWidth < ResponsiveLayoutUtilBase.kMobileThreshold;
reaction((_) => responsiveLayoutUtil.screenWidth, (screenWidth) {
// Check if it was previously in mobile layout, and now changing to desktop
if (isMobileLayout &&
screenWidth > ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
setState(() {
isMobileLayout = false;
});
}
// Check if it was previously in desktop layout, and now changing to mobile
if (!isMobileLayout &&
screenWidth <= ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
setState(() {
isMobileLayout = true;
});
}
});
}
@override
Widget build(BuildContext context) {
Widget dashboardChild;
final dashboardPageView = RefreshIndicator(
displacement: responsiveLayoutUtil.screenHeight * 0.1,
onRefresh: () async => await widget.dashboardViewModel.refreshDashboard(),
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Container(
height: responsiveLayoutUtil.screenHeight,
child: _DashboardPageView(
balancePage: widget.balancePage,
bottomSheetService: widget.bottomSheetService,
dashboardViewModel: widget.dashboardViewModel,
addressListViewModel: widget.addressListViewModel,
),
),
),
);
if (DeviceInfo.instance.isDesktop) {
if (responsiveLayoutUtil.screenWidth >
ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
dashboardChild = getIt.get<DesktopSidebarWrapper>();
} else {
dashboardChild = dashboardPageView;
}
} else if (responsiveLayoutUtil.shouldRenderMobileUI) {
dashboardChild = dashboardPageView;
} else {
dashboardChild = getIt.get<DesktopSidebarWrapper>();
}
return Scaffold(body: dashboardChild);
}
}
class _DashboardPageView extends BasePage {
_DashboardPageView({
required this.bottomSheetService,
required this.balancePage,
required this.dashboardViewModel,
required this.addressListViewModel,
});
final BalancePage balancePage;
@override
bool get gradientBackground => true;
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
@override
bool get resizeToAvoidBottomInset => false;
@override
Widget get endDrawer =>
MenuWidget(dashboardViewModel, ValueKey('dashboard_page_drawer_menu_widget_key'));
@override
Widget leading(BuildContext context) {
return Observer(
builder: (context) {
return ServicesUpdatesWidget(
key: ValueKey('dashboard_page_services_update_button_key'),
dashboardViewModel.getServicesStatus(),
enabled: dashboardViewModel.isEnabledBulletinAction,
);
},
);
}
@override
Widget middle(BuildContext context) {
return SyncIndicator(
key: ValueKey('dashboard_page_sync_indicator_button_key'),
dashboardViewModel: dashboardViewModel,
onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync),
);
}
@override
Widget trailing(BuildContext context) {
final menuButton = Image.asset(
'assets/images/menu.png',
color: Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
);
return Container(
alignment: Alignment.centerRight,
width: 40,
child: TextButton(
key: ValueKey('dashboard_page_wallet_menu_button_key'),
onPressed: () => onOpenEndDrawer(),
child: Semantics(label: S.of(context).wallet_menu, child: menuButton),
),
);
}
final DashboardViewModel dashboardViewModel;
final BottomSheetService bottomSheetService;
final WalletAddressListViewModel addressListViewModel;
int get initialPage => dashboardViewModel.shouldShowMarketPlaceInDashboard ? 1 : 0;
ObservableList<Widget> pages = ObservableList<Widget>();
bool _isEffectsInstalled = false;
@override
Widget body(BuildContext context) {
final controller = PageController(initialPage: initialPage);
reaction(
(_) => dashboardViewModel.shouldShowMarketPlaceInDashboard,
(bool value) {
if (!dashboardViewModel.shouldShowMarketPlaceInDashboard) {
controller.jumpToPage(0);
}
pages.clear();
_isEffectsInstalled = false;
_setEffects(context);
if (value) {
controller.jumpToPage(1);
} else {
controller.jumpToPage(0);
}
},
);
_setEffects(context);
return SafeArea(
minimum: EdgeInsets.only(bottom: 0),
child: BottomSheetListener(
bottomSheetService: bottomSheetService,
child: Container(
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
//new Expanded(
Observer(
builder: (context) {
return PageView.builder(
key: ValueKey('dashboard_page_view_key'),
controller: controller,
itemCount: pages.length,
itemBuilder: (context, index) => pages[index],
);
},
),
//),
Positioned(
child: Container(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(bottom: 110),
child: Observer(
builder: (context) {
return Semantics(
button: false,
label: 'Page Indicator',
hint: 'Swipe to change page',
excludeSemantics: true,
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context)
.extension<DashboardPageTheme>()!
.indicatorDotTheme
.indicatorColor,
activeDotColor: Theme.of(context)
.extension<DashboardPageTheme>()!
.indicatorDotTheme
.activeIndicatorColor,
),
),
);
},
),
),
),
NavigationDock(dashboardViewModel: dashboardViewModel)
],
),
),
),
);
}
void _setEffects(BuildContext context) async {
if (_isEffectsInstalled || !context.mounted) {
return;
}
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(
Semantics(
label: 'Cake ${S.of(context).features}',
child: CakeFeaturesPage(
dashboardViewModel: dashboardViewModel,
cakeFeaturesViewModel: getIt.get<CakeFeaturesViewModel>(),
),
),
);
}
pages.add(Semantics(label: S.of(context).balance_page, child: balancePage));
pages.add(
Semantics(
label: S.of(context).settings_transactions,
child: TransactionsPage(dashboardViewModel: dashboardViewModel),
),
);
_isEffectsInstalled = true;
_showReleaseNotesPopup(context);
_showVulnerableSeedsPopup(context);
_showHavenPopup(context);
var needToPresentYat = false;
rootKey.currentState?.isInactive.listen(
(inactive) {
if (needToPresentYat) {
Future<void>.delayed(Duration(milliseconds: 500)).then(
(_) {
showPopUp<void>(
context: navigatorKey.currentContext!,
builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji),
);
needToPresentYat = false;
},
);
}
},
);
dashboardViewModel.yatStore.emojiIncommingStream.listen(
(String emoji) {
if (!_isEffectsInstalled || emoji.isEmpty) {
return;
}
needToPresentYat = true;
},
);
}
void _showReleaseNotesPopup(BuildContext context) async {
final sharedPrefs = await SharedPreferences.getInstance();
final currentAppVersion =
VersionComparator.getExtendedVersionNumber(dashboardViewModel.settingsStore.appVersion);
final lastSeenAppVersion = sharedPrefs.getInt(PreferencesKey.lastSeenAppVersion);
final isNewInstall = sharedPrefs.getBool(PreferencesKey.isNewInstall);
if (currentAppVersion != lastSeenAppVersion && !isNewInstall!) {
Future<void>.delayed(
Duration(seconds: 1),
() {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return ReleaseNotesScreen(
title: 'Version ${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 dashboardViewModel.checkAffectedWallets();
if (affectedWalletNames.isNotEmpty) {
Future<void>.delayed(
Duration(seconds: 1),
() {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return VulnerableSeedsPopup(affectedWalletNames);
},
);
},
);
}
}
void _showHavenPopup(BuildContext context) async {
final List<String> havenWalletList = await dashboardViewModel.checkForHavenWallets();
if (havenWalletList.isNotEmpty) {
Future<void>.delayed(
Duration(seconds: 1),
() {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return HavenWalletRemovalPopup(havenWalletList);
},
);
},
);
}
}
}