2023-05-26 21:21:16 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'dart:async';
|
2023-05-09 23:59:26 +00:00
|
|
|
import 'dart:io';
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'package:flutter_svg/svg.dart';
|
2024-05-23 21:38:37 +00:00
|
|
|
|
2024-05-23 00:37:06 +00:00
|
|
|
import '../../app_config.dart';
|
|
|
|
import '../../providers/global/notifications_provider.dart';
|
2024-07-04 16:13:11 +00:00
|
|
|
import '../../providers/global/prefs_provider.dart';
|
2024-05-23 00:37:06 +00:00
|
|
|
import '../../providers/ui/home_view_index_provider.dart';
|
|
|
|
import '../../providers/ui/unread_notifications_provider.dart';
|
|
|
|
import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart';
|
|
|
|
import '../../themes/stack_colors.dart';
|
|
|
|
import '../../themes/theme_providers.dart';
|
|
|
|
import '../../utilities/assets.dart';
|
|
|
|
import '../../utilities/constants.dart';
|
|
|
|
import '../../utilities/text_styles.dart';
|
|
|
|
import '../../widgets/animated_widgets/rotate_icon.dart';
|
2024-05-23 21:38:37 +00:00
|
|
|
import '../../widgets/app_icon.dart';
|
2024-05-23 00:37:06 +00:00
|
|
|
import '../../widgets/background.dart';
|
|
|
|
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
|
|
|
import '../../widgets/small_tor_icon.dart';
|
|
|
|
import '../../widgets/stack_dialog.dart';
|
2024-05-23 21:38:37 +00:00
|
|
|
import '../buy_view/buy_view.dart';
|
|
|
|
import '../exchange_view/exchange_view.dart';
|
|
|
|
import '../notification_views/notifications_view.dart';
|
|
|
|
import '../settings_views/global_settings_view/global_settings_view.dart';
|
|
|
|
import '../settings_views/global_settings_view/hidden_settings.dart';
|
|
|
|
import '../wallets_view/wallets_view.dart';
|
|
|
|
import 'sub_widgets/home_view_button_bar.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
class HomeView extends ConsumerStatefulWidget {
|
2024-05-23 21:38:37 +00:00
|
|
|
const HomeView({super.key});
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
static const routeName = "/home";
|
|
|
|
|
|
|
|
@override
|
|
|
|
ConsumerState<HomeView> createState() => _HomeViewState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _HomeViewState extends ConsumerState<HomeView> {
|
|
|
|
final GlobalKey<ScaffoldState> _key = GlobalKey<ScaffoldState>();
|
|
|
|
|
|
|
|
late final PageController _pageController;
|
2023-03-03 17:27:20 +00:00
|
|
|
late final RotateIconController _rotateIconController;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
late final List<Widget> _children;
|
|
|
|
|
|
|
|
DateTime? _cachedTime;
|
|
|
|
|
|
|
|
bool _exitEnabled = false;
|
|
|
|
|
2023-09-07 20:44:53 +00:00
|
|
|
late TorConnectionStatus _currentSyncStatus;
|
2023-09-05 19:11:08 +00:00
|
|
|
|
2023-01-14 17:22:48 +00:00
|
|
|
// final _buyDataLoadingService = BuyDataLoadingService();
|
2022-09-09 14:03:36 +00:00
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
Future<bool> _onWillPop() async {
|
2022-09-09 14:11:59 +00:00
|
|
|
// go to home view when tapping back on the main exchange view
|
2023-02-01 00:14:06 +00:00
|
|
|
if (ref.read(homeViewPageIndexStateProvider.state).state != 0) {
|
2022-09-09 14:11:59 +00:00
|
|
|
ref.read(homeViewPageIndexStateProvider.state).state = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
if (_exitEnabled) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
final now = DateTime.now();
|
|
|
|
const timeout = Duration(milliseconds: 1500);
|
|
|
|
if (_cachedTime == null || now.difference(_cachedTime!) > timeout) {
|
|
|
|
_cachedTime = now;
|
|
|
|
await showDialog<dynamic>(
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-01-14 17:22:48 +00:00
|
|
|
// 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);
|
|
|
|
// }
|
|
|
|
// }
|
2023-01-12 00:13:34 +00:00
|
|
|
|
2023-01-14 18:17:55 +00:00
|
|
|
bool _lock = false;
|
|
|
|
|
|
|
|
Future<void> _animateToPage(int index) async {
|
|
|
|
await _pageController.animateToPage(
|
|
|
|
index,
|
|
|
|
duration: const Duration(milliseconds: 300),
|
|
|
|
curve: Curves.decelerate,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
_pageController = PageController();
|
2023-03-03 17:27:20 +00:00
|
|
|
_rotateIconController = RotateIconController();
|
2022-08-26 08:11:35 +00:00
|
|
|
_children = [
|
|
|
|
const WalletsView(),
|
2024-06-06 22:01:08 +00:00
|
|
|
if (AppConfig.hasFeature(AppFeature.swap) && Constants.enableExchange)
|
|
|
|
const ExchangeView(),
|
|
|
|
if (AppConfig.hasFeature(AppFeature.buy) && Constants.enableExchange)
|
|
|
|
const BuyView(),
|
2022-08-26 08:11:35 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
ref.read(notificationsProvider).startCheckingWatchedNotifications();
|
|
|
|
|
2024-01-04 17:28:38 +00:00
|
|
|
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
|
|
|
// showOneTimeTorHasBeenAddedDialogIfRequired(context);
|
|
|
|
// });
|
2023-09-05 19:11:08 +00:00
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
dispose() {
|
|
|
|
_pageController.dispose();
|
2023-03-03 17:27:20 +00:00
|
|
|
_rotateIconController.forward = null;
|
|
|
|
_rotateIconController.reverse = null;
|
|
|
|
_rotateIconController.reset = null;
|
2022-08-26 08:11:35 +00:00
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
DateTime _hiddenTime = DateTime.now();
|
|
|
|
int _hiddenCount = 0;
|
|
|
|
|
|
|
|
void _hiddenOptions() {
|
2023-03-03 17:27:20 +00:00
|
|
|
_rotateIconController.reset?.call();
|
|
|
|
_rotateIconController.forward?.call();
|
2022-08-26 08:11:35 +00:00
|
|
|
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");
|
2024-07-04 16:13:11 +00:00
|
|
|
|
|
|
|
// dirty hack
|
|
|
|
ref.listen(
|
|
|
|
prefsChangeNotifierProvider.select((value) => value.enableExchange),
|
|
|
|
(prev, next) {
|
|
|
|
if (next == false &&
|
|
|
|
mounted &&
|
|
|
|
ref.read(homeViewPageIndexStateProvider) != 0) {
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback(
|
|
|
|
(_) => ref.read(homeViewPageIndexStateProvider.state).state = 0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
return WillPopScope(
|
|
|
|
onWillPop: _onWillPop,
|
2022-11-25 19:24:01 +00:00
|
|
|
child: Background(
|
|
|
|
child: Scaffold(
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
key: _key,
|
|
|
|
appBar: AppBar(
|
|
|
|
automaticallyImplyLeading: false,
|
|
|
|
backgroundColor:
|
|
|
|
Theme.of(context).extension<StackColors>()!.backgroundAppBar,
|
|
|
|
title: Row(
|
|
|
|
children: [
|
|
|
|
GestureDetector(
|
|
|
|
onTap: _hiddenOptions,
|
2023-03-03 17:27:20 +00:00
|
|
|
child: RotateIcon(
|
2024-05-23 21:38:37 +00:00
|
|
|
icon: const AppIcon(
|
2023-03-03 17:27:20 +00:00
|
|
|
width: 24,
|
|
|
|
height: 24,
|
|
|
|
),
|
|
|
|
curve: Curves.easeInOutCubic,
|
|
|
|
rotationPercent: 1.0,
|
|
|
|
controller: _rotateIconController,
|
2022-08-26 08:11:35 +00:00
|
|
|
),
|
2022-11-25 19:24:01 +00:00
|
|
|
),
|
|
|
|
const SizedBox(
|
|
|
|
width: 16,
|
|
|
|
),
|
|
|
|
Text(
|
2024-05-13 19:44:19 +00:00
|
|
|
"My ${AppConfig.prefix}",
|
2022-11-25 19:24:01 +00:00
|
|
|
style: STextStyles.navBarTitle(context),
|
2024-05-27 23:56:22 +00:00
|
|
|
),
|
2022-11-25 19:24:01 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
actions: [
|
2023-09-13 16:11:14 +00:00
|
|
|
const Padding(
|
|
|
|
padding: EdgeInsets.only(
|
2022-11-25 19:24:01 +00:00
|
|
|
top: 10,
|
|
|
|
bottom: 10,
|
|
|
|
right: 10,
|
|
|
|
),
|
2023-09-05 19:11:08 +00:00
|
|
|
child: AspectRatio(
|
|
|
|
aspectRatio: 1,
|
2023-09-13 16:11:14 +00:00
|
|
|
child: SmallTorIcon(),
|
2023-09-05 19:11:08 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
top: 10,
|
|
|
|
bottom: 10,
|
|
|
|
right: 10,
|
|
|
|
),
|
2022-11-25 19:24:01 +00:00
|
|
|
child: AspectRatio(
|
|
|
|
aspectRatio: 1,
|
|
|
|
child: AppBarIconButton(
|
2023-05-09 21:57:40 +00:00
|
|
|
semanticsLabel:
|
|
|
|
"Notifications Button. Takes To Notifications Page.",
|
2022-11-25 19:24:01 +00:00
|
|
|
key: const Key("walletsViewAlertsButton"),
|
|
|
|
size: 36,
|
|
|
|
shadows: const [],
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.backgroundAppBar,
|
2024-05-27 23:56:22 +00:00
|
|
|
icon: ref.watch(
|
|
|
|
notificationsProvider
|
|
|
|
.select((value) => value.hasUnreadNotifications),
|
|
|
|
)
|
2023-05-09 23:59:26 +00:00
|
|
|
? SvgPicture.file(
|
|
|
|
File(
|
|
|
|
ref.watch(
|
|
|
|
themeProvider.select(
|
|
|
|
(value) => value.assets.bellNew,
|
|
|
|
),
|
2023-04-24 14:38:17 +00:00
|
|
|
),
|
2023-05-09 23:59:26 +00:00
|
|
|
),
|
|
|
|
width: 20,
|
|
|
|
height: 20,
|
2024-05-27 23:56:22 +00:00
|
|
|
color: ref.watch(
|
|
|
|
notificationsProvider.select(
|
|
|
|
(value) => value.hasUnreadNotifications,
|
|
|
|
),
|
|
|
|
)
|
2023-05-09 23:59:26 +00:00
|
|
|
? null
|
|
|
|
: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.topNavIconPrimary,
|
|
|
|
)
|
|
|
|
: SvgPicture.asset(
|
|
|
|
Assets.svg.bell,
|
|
|
|
width: 20,
|
|
|
|
height: 20,
|
2024-05-27 23:56:22 +00:00
|
|
|
color: ref.watch(
|
|
|
|
notificationsProvider.select(
|
|
|
|
(value) => value.hasUnreadNotifications,
|
|
|
|
),
|
|
|
|
)
|
2023-05-09 23:59:26 +00:00
|
|
|
? null
|
|
|
|
: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.topNavIconPrimary,
|
|
|
|
),
|
2022-11-25 19:24:01 +00:00
|
|
|
onPressed: () {
|
|
|
|
// reset unread state
|
|
|
|
ref.refresh(unreadNotificationsStateProvider);
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2022-11-25 19:24:01 +00:00
|
|
|
Navigator.of(context)
|
|
|
|
.pushNamed(NotificationsView.routeName)
|
|
|
|
.then((_) {
|
|
|
|
final Set<int> unreadNotificationIds = ref
|
|
|
|
.read(unreadNotificationsStateProvider.state)
|
|
|
|
.state;
|
|
|
|
if (unreadNotificationIds.isEmpty) return;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2024-05-27 23:56:22 +00:00
|
|
|
final List<Future<void>> futures = [];
|
2022-11-25 19:24:01 +00:00
|
|
|
for (int i = 0;
|
|
|
|
i < unreadNotificationIds.length - 1;
|
|
|
|
i++) {
|
2024-05-27 23:56:22 +00:00
|
|
|
futures.add(
|
|
|
|
ref.read(notificationsProvider).markAsRead(
|
|
|
|
unreadNotificationIds.elementAt(i),
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
);
|
2022-11-25 19:24:01 +00:00
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2022-11-25 19:24:01 +00:00
|
|
|
// wait for multiple to update if any
|
|
|
|
Future.wait(futures).then((_) {
|
|
|
|
// only notify listeners once
|
|
|
|
ref
|
|
|
|
.read(notificationsProvider)
|
|
|
|
.markAsRead(unreadNotificationIds.last, true);
|
|
|
|
});
|
2022-08-26 08:11:35 +00:00
|
|
|
});
|
2022-11-25 19:24:01 +00:00
|
|
|
},
|
|
|
|
),
|
2022-08-26 08:11:35 +00:00
|
|
|
),
|
|
|
|
),
|
2022-11-25 19:24:01 +00:00
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
top: 10,
|
|
|
|
bottom: 10,
|
|
|
|
right: 10,
|
|
|
|
),
|
|
|
|
child: AspectRatio(
|
|
|
|
aspectRatio: 1,
|
|
|
|
child: AppBarIconButton(
|
2023-04-28 18:21:30 +00:00
|
|
|
semanticsLabel: "Settings Button. Takes To Settings Page.",
|
2022-11-25 19:24:01 +00:00
|
|
|
key: const Key("walletsViewSettingsButton"),
|
|
|
|
size: 36,
|
|
|
|
shadows: const [],
|
2022-09-22 23:48:50 +00:00
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
2022-11-25 19:24:01 +00:00
|
|
|
.backgroundAppBar,
|
|
|
|
icon: SvgPicture.asset(
|
|
|
|
Assets.svg.gear,
|
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.topNavIconPrimary,
|
|
|
|
width: 20,
|
|
|
|
height: 20,
|
|
|
|
),
|
|
|
|
onPressed: () {
|
2022-12-13 00:17:02 +00:00
|
|
|
//todo: check if print needed
|
|
|
|
// debugPrint("main view settings tapped");
|
2022-11-25 19:24:01 +00:00
|
|
|
Navigator.of(context)
|
|
|
|
.pushNamed(GlobalSettingsView.routeName);
|
|
|
|
},
|
2022-08-26 08:11:35 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2022-11-25 19:24:01 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
body: Column(
|
2022-08-26 08:11:35 +00:00
|
|
|
children: [
|
2024-07-04 16:13:11 +00:00
|
|
|
if (_children.length > 1 &&
|
|
|
|
ref.watch(prefsChangeNotifierProvider).enableExchange)
|
2022-08-26 08:11:35 +00:00
|
|
|
Container(
|
2022-09-21 00:46:07 +00:00
|
|
|
decoration: BoxDecoration(
|
2022-11-25 19:24:01 +00:00
|
|
|
color: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.backgroundAppBar,
|
2023-01-24 18:42:41 +00:00
|
|
|
boxShadow: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.homeViewButtonBarBoxShadow !=
|
|
|
|
null
|
|
|
|
? [
|
|
|
|
Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.homeViewButtonBarBoxShadow!,
|
|
|
|
]
|
|
|
|
: null,
|
2022-08-26 08:11:35 +00:00
|
|
|
),
|
|
|
|
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) {
|
2023-01-14 18:17:55 +00:00
|
|
|
if (next is int && next >= 0 && next <= 2) {
|
2023-02-05 20:32:39 +00:00
|
|
|
// if (next == 1) {
|
|
|
|
// _exchangeDataLoadingService.loadAll(ref);
|
|
|
|
// }
|
2023-01-14 17:22:48 +00:00
|
|
|
// if (next == 2) {
|
|
|
|
// _buyDataLoadingService.loadAll(ref);
|
|
|
|
// }
|
2023-01-14 18:17:55 +00:00
|
|
|
|
|
|
|
_lock = true;
|
|
|
|
_animateToPage(next).then((value) => _lock = false);
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return PageView(
|
|
|
|
controller: _pageController,
|
|
|
|
children: _children,
|
|
|
|
onPageChanged: (pageIndex) {
|
2023-01-14 18:17:55 +00:00
|
|
|
if (!_lock) {
|
|
|
|
ref.read(homeViewPageIndexStateProvider.state).state =
|
|
|
|
pageIndex;
|
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|