/* * This file is part of Stack Wallet. * * Copyright (c) 2023 Cypher Stack * All Rights Reserved. * The code is distributed under GPLv3 license, see LICENSE file for details. * Generated by Cypher Stack on 2023-05-26 * */ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages/buy_view/buy_view.dart'; import 'package:stackwallet/pages/exchange_view/exchange_view.dart'; import 'package:stackwallet/pages/home_view/sub_widgets/home_view_button_bar.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/global_settings_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart'; import 'package:stackwallet/pages/wallets_view/wallets_view.dart'; import 'package:stackwallet/providers/global/notifications_provider.dart'; import 'package:stackwallet/providers/ui/home_view_index_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/theme_providers.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart'; import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/onetime_popups/tor_has_been_add_dialog.dart'; import 'package:stackwallet/widgets/small_tor_icon.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; class HomeView extends ConsumerStatefulWidget { const HomeView({Key? key}) : super(key: key); static const routeName = "/home"; @override ConsumerState createState() => _HomeViewState(); } class _HomeViewState extends ConsumerState { final GlobalKey _key = GlobalKey(); late final PageController _pageController; late final RotateIconController _rotateIconController; late final List _children; DateTime? _cachedTime; bool _exitEnabled = false; late TorConnectionStatus _currentSyncStatus; // final _buyDataLoadingService = BuyDataLoadingService(); Future _onWillPop() async { // go to home view when tapping back on the main exchange view if (ref.read(homeViewPageIndexStateProvider.state).state != 0) { ref.read(homeViewPageIndexStateProvider.state).state = 0; return false; } if (_exitEnabled) { return true; } final now = DateTime.now(); const timeout = Duration(milliseconds: 1500); if (_cachedTime == null || now.difference(_cachedTime!) > timeout) { _cachedTime = now; await showDialog( context: context, barrierDismissible: false, builder: (_) => WillPopScope( onWillPop: () async { _exitEnabled = true; return true; }, child: const StackDialog(title: "Tap back again to exit"), ), ).timeout( timeout, onTimeout: () { _exitEnabled = false; Navigator.of(context).pop(); }, ); } return _exitEnabled; } // void _loadSimplexData() { // // unawaited future // if (ref.read(prefsChangeNotifierProvider).externalCalls) { // _buyDataLoadingService.loadAll(ref); // } else { // Logging.instance.log("User does not want to use external calls", // level: LogLevel.Info); // } // } bool _lock = false; Future _animateToPage(int index) async { await _pageController.animateToPage( index, duration: const Duration(milliseconds: 300), curve: Curves.decelerate, ); } @override void initState() { _pageController = PageController(); _rotateIconController = RotateIconController(); _children = [ const WalletsView(), if (Constants.enableExchange) const ExchangeView(), if (Constants.enableExchange) const BuyView(), ]; ref.read(notificationsProvider).startCheckingWatchedNotifications(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { showOneTimeTorHasBeenAddedDialogIfRequired(context); }); super.initState(); } @override dispose() { _pageController.dispose(); _rotateIconController.forward = null; _rotateIconController.reverse = null; _rotateIconController.reset = null; super.dispose(); } DateTime _hiddenTime = DateTime.now(); int _hiddenCount = 0; void _hiddenOptions() { _rotateIconController.reset?.call(); _rotateIconController.forward?.call(); if (_hiddenCount == 5) { Navigator.of(context).pushNamed(HiddenSettings.routeName); } final now = DateTime.now(); const timeout = Duration(seconds: 1); if (now.difference(_hiddenTime) < timeout) { _hiddenCount++; } else { _hiddenCount = 0; } _hiddenTime = now; } @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); return WillPopScope( onWillPop: _onWillPop, child: Background( child: Scaffold( backgroundColor: Colors.transparent, key: _key, appBar: AppBar( automaticallyImplyLeading: false, backgroundColor: Theme.of(context).extension()!.backgroundAppBar, title: Row( children: [ GestureDetector( onTap: _hiddenOptions, child: RotateIcon( icon: SvgPicture.file( File( ref.watch( themeProvider.select( (value) => value.assets.stackIcon, ), ), ), width: 24, height: 24, ), curve: Curves.easeInOutCubic, rotationPercent: 1.0, controller: _rotateIconController, ), ), const SizedBox( width: 16, ), Text( "My Stack", style: STextStyles.navBarTitle(context), ) ], ), actions: [ const Padding( padding: EdgeInsets.only( top: 10, bottom: 10, right: 10, ), child: AspectRatio( aspectRatio: 1, child: SmallTorIcon(), ), ), Padding( padding: const EdgeInsets.only( top: 10, bottom: 10, right: 10, ), child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( semanticsLabel: "Notifications Button. Takes To Notifications Page.", key: const Key("walletsViewAlertsButton"), size: 36, shadows: const [], color: Theme.of(context) .extension()! .backgroundAppBar, icon: ref.watch(notificationsProvider .select((value) => value.hasUnreadNotifications)) ? SvgPicture.file( File( ref.watch( themeProvider.select( (value) => value.assets.bellNew, ), ), ), width: 20, height: 20, color: ref.watch(notificationsProvider.select( (value) => value.hasUnreadNotifications)) ? null : Theme.of(context) .extension()! .topNavIconPrimary, ) : SvgPicture.asset( Assets.svg.bell, width: 20, height: 20, color: ref.watch(notificationsProvider.select( (value) => value.hasUnreadNotifications)) ? null : Theme.of(context) .extension()! .topNavIconPrimary, ), onPressed: () { // reset unread state ref.refresh(unreadNotificationsStateProvider); Navigator.of(context) .pushNamed(NotificationsView.routeName) .then((_) { final Set unreadNotificationIds = ref .read(unreadNotificationsStateProvider.state) .state; if (unreadNotificationIds.isEmpty) return; List> futures = []; for (int i = 0; i < unreadNotificationIds.length - 1; i++) { futures.add(ref .read(notificationsProvider) .markAsRead( unreadNotificationIds.elementAt(i), false)); } // wait for multiple to update if any Future.wait(futures).then((_) { // only notify listeners once ref .read(notificationsProvider) .markAsRead(unreadNotificationIds.last, true); }); }); }, ), ), ), Padding( padding: const EdgeInsets.only( top: 10, bottom: 10, right: 10, ), child: AspectRatio( aspectRatio: 1, child: AppBarIconButton( semanticsLabel: "Settings Button. Takes To Settings Page.", key: const Key("walletsViewSettingsButton"), size: 36, shadows: const [], color: Theme.of(context) .extension()! .backgroundAppBar, icon: SvgPicture.asset( Assets.svg.gear, color: Theme.of(context) .extension()! .topNavIconPrimary, width: 20, height: 20, ), onPressed: () { //todo: check if print needed // debugPrint("main view settings tapped"); Navigator.of(context) .pushNamed(GlobalSettingsView.routeName); }, ), ), ), ], ), body: Column( children: [ if (Constants.enableExchange) Container( decoration: BoxDecoration( color: Theme.of(context) .extension()! .backgroundAppBar, boxShadow: Theme.of(context) .extension()! .homeViewButtonBarBoxShadow != null ? [ Theme.of(context) .extension()! .homeViewButtonBarBoxShadow!, ] : null, ), child: const Padding( padding: EdgeInsets.only( left: 16, bottom: 12, right: 16, top: 0, ), child: HomeViewButtonBar(), ), ), Expanded( child: Consumer( builder: (_, _ref, __) { _ref.listen(homeViewPageIndexStateProvider, (previous, next) { if (next is int && next >= 0 && next <= 2) { // if (next == 1) { // _exchangeDataLoadingService.loadAll(ref); // } // if (next == 2) { // _buyDataLoadingService.loadAll(ref); // } _lock = true; _animateToPage(next).then((value) => _lock = false); } }); return PageView( controller: _pageController, children: _children, onPageChanged: (pageIndex) { if (!_lock) { ref.read(homeViewPageIndexStateProvider.state).state = pageIndex; } }, ); }, ), ), ], ), ), ), ); } }