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';
|
2023-01-04 16:49:13 +00:00
|
|
|
import 'package:stackwallet/pages/buy_view/buy_view.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
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';
|
2023-05-09 21:57:40 +00:00
|
|
|
import 'package:stackwallet/themes/stack_colors.dart';
|
2023-04-24 14:38:17 +00:00
|
|
|
import 'package:stackwallet/themes/theme_providers.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'package:stackwallet/utilities/assets.dart';
|
|
|
|
import 'package:stackwallet/utilities/constants.dart';
|
|
|
|
import 'package:stackwallet/utilities/text_styles.dart';
|
2023-03-03 17:27:20 +00:00
|
|
|
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
|
2022-11-25 19:24:01 +00:00
|
|
|
import 'package:stackwallet/widgets/background.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.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<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-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(),
|
2023-03-10 22:50:06 +00:00
|
|
|
if (Constants.enableExchange) const ExchangeView(),
|
|
|
|
if (Constants.enableExchange) const BuyView(),
|
2022-08-26 08:11:35 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
ref.read(notificationsProvider).startCheckingWatchedNotifications();
|
|
|
|
|
|
|
|
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");
|
|
|
|
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(
|
2023-05-09 23:59:26 +00:00
|
|
|
icon: SvgPicture.file(
|
|
|
|
File(
|
|
|
|
ref.watch(
|
|
|
|
themeProvider.select(
|
|
|
|
(value) => value.assets.stackIcon,
|
|
|
|
),
|
2023-04-25 22:07:28 +00:00
|
|
|
),
|
|
|
|
),
|
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(
|
|
|
|
"My Stack",
|
|
|
|
style: STextStyles.navBarTitle(context),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
actions: [
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
top: 10,
|
|
|
|
bottom: 10,
|
|
|
|
right: 10,
|
|
|
|
),
|
|
|
|
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,
|
2023-05-09 23:59:26 +00:00
|
|
|
icon: ref.watch(notificationsProvider
|
|
|
|
.select((value) => value.hasUnreadNotifications))
|
|
|
|
? 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,
|
|
|
|
color: ref.watch(notificationsProvider.select(
|
|
|
|
(value) => value.hasUnreadNotifications))
|
|
|
|
? null
|
|
|
|
: Theme.of(context)
|
|
|
|
.extension<StackColors>()!
|
|
|
|
.topNavIconPrimary,
|
|
|
|
)
|
|
|
|
: SvgPicture.asset(
|
|
|
|
Assets.svg.bell,
|
|
|
|
width: 20,
|
|
|
|
height: 20,
|
|
|
|
color: ref.watch(notificationsProvider.select(
|
|
|
|
(value) => value.hasUnreadNotifications))
|
|
|
|
? 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
|
|
|
|
2022-11-25 19:24:01 +00:00
|
|
|
List<Future<void>> futures = [];
|
|
|
|
for (int i = 0;
|
|
|
|
i < unreadNotificationIds.length - 1;
|
|
|
|
i++) {
|
|
|
|
futures.add(ref
|
|
|
|
.read(notificationsProvider)
|
|
|
|
.markAsRead(
|
|
|
|
unreadNotificationIds.elementAt(i), false));
|
|
|
|
}
|
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: [
|
|
|
|
if (Constants.enableExchange)
|
|
|
|
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
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|