@@ -17,13 +17,14 @@ import 'package:tsdm_client/features/local_notice/stream.dart';
1717import 'package:tsdm_client/features/notification/bloc/notification_state_auto_sync_cubit.dart' ;
1818import 'package:tsdm_client/features/notification/models/models.dart' ;
1919import 'package:tsdm_client/features/root/bloc/root_location_cubit.dart' ;
20+ import 'package:tsdm_client/features/root/models/models.dart' ;
21+ import 'package:tsdm_client/features/root/stream/root_location_stream.dart' ;
2022import 'package:tsdm_client/features/settings/bloc/settings_bloc.dart' ;
2123import 'package:tsdm_client/features/settings/repositories/settings_repository.dart' ;
2224import 'package:tsdm_client/i18n/strings.g.dart' ;
2325import 'package:tsdm_client/instance.dart' ;
2426import 'package:tsdm_client/routes/screen_paths.dart' ;
2527import 'package:tsdm_client/shared/providers/storage_provider/storage_provider.dart' ;
26- import 'package:tsdm_client/shared/repositories/forum_home_repository/forum_home_repository.dart' ;
2728import 'package:tsdm_client/utils/logger.dart' ;
2829import 'package:tsdm_client/utils/platform.dart' ;
2930import 'package:tsdm_client/utils/show_dialog.dart' ;
@@ -34,12 +35,7 @@ const _drawerWidth = 250.0;
3435/// Page of the homepage of the app.
3536class HomePage extends StatefulWidget {
3637 /// Constructor.
37- const HomePage ({
38- required ForumHomeRepository forumHomeRepository,
39- required this .showNavigationBar,
40- required this .child,
41- super .key,
42- }) : _forumHomeRepository = forumHomeRepository;
38+ const HomePage ({required this .showNavigationBar, required this .child, super .key});
4339
4440 /// Control to show the app level navigation bar or not.
4541 ///
@@ -49,8 +45,6 @@ class HomePage extends StatefulWidget {
4945 /// Child widget, or call it the body widget.
5046 final Widget child;
5147
52- final ForumHomeRepository _forumHomeRepository;
53-
5448 @override
5549 State <HomePage > createState () => _HomePageState ();
5650}
@@ -244,39 +238,68 @@ class _HomePageState extends State<HomePage> with LoggerMixin {
244238 );
245239 }
246240
247- return RepositoryProvider .value (
248- value: widget._forumHomeRepository,
241+ // The global listener handles app-wide leave page events.
242+ // Every time user intended to leave a certain page, `lastRequestLeavePageTime` is updated and this
243+ // listener process check for leave event, decide the page can be popped or not.
244+ //
245+ // The logic here is to implements double-press before exit app feature. All popping page events are
246+ // intercepted by the `BackButtonListener` below, it only triggers the update of `lastRequestLeavePageTime`,
247+ // which notifies this listener.
248+ //
249+ // Every time the pop is allowed, update page locations in `RootLocationCubit` by adding
250+ // `RootLocationEventLeave` to stream.
251+ return BlocListener <RootLocationCubit , RootLocationState >(
252+ listenWhen: (prev, curr) => prev.lastRequestLeavePageTime != curr.lastRequestLeavePageTime,
253+ listener: (context, state) async {
254+ // Check if fine to pop the current page.
255+ final location = state.locations.lastOrNull;
256+ if (location != ScreenPaths .homepage &&
257+ location != ScreenPaths .topic &&
258+ location != ScreenPaths .settings.path &&
259+ location != null ) {
260+ // Popping current page will not close the app, allow to pop.
261+ rootLocationStream.add (RootLocationEventLeave (location));
262+ return context.pop ();
263+ }
264+
265+ // Check for double press exit feature.
266+ // From here, if we intend to pop the page, in fact we shall exit the app.
267+
268+ final doublePressExit = getIt.get <SettingsRepository >().currentSettings.doublePressExit;
269+ if (! doublePressExit) {
270+ // Double-press before exit feature is disabled, exit app.
271+ await exitApp ();
272+ return ;
273+ }
274+
275+ // From here, double-press before exit feature is enabled.
276+
277+ final tr = context.t.home;
278+ final currentTime = DateTime .now ();
279+ if (lastPopTime == null ||
280+ currentTime.difference (lastPopTime! ).inMilliseconds > exitConfirmDuration.inMilliseconds) {
281+ lastPopTime = currentTime;
282+ ScaffoldMessenger .of (context).hideCurrentSnackBar ();
283+ showSnackBar (context: context, message: tr.confirmExit);
284+ // Require the second pop event.
285+ return ;
286+ }
287+
288+ // A second pop event is here, exit app.
289+ await exitApp ();
290+ // Unreachable
291+ return ;
292+ },
249293 child: BackButtonListener (
250294 onBackButtonPressed: () async {
295+ // App wide popping events interceptor, handles all popping events and notify the listener above.
296+ rootLocationStream.add (const RootLocationEventLeavingLast ());
251297 if (! context.mounted) {
298+ // Well, leave it here.
252299 await exitApp ();
253300 return true ;
254301 }
255- final location = context.read <RootLocationCubit >().current;
256- if (location != ScreenPaths .homepage &&
257- location != ScreenPaths .topic &&
258- location != ScreenPaths .settings.path) {
259- // Do NOT handle pop events on other pages.
260- return false ;
261- }
262- final doublePressExit = getIt.get <SettingsRepository >().currentSettings.doublePressExit;
263- if (! doublePressExit) {
264- // Do NOT handle pop events on double press check is disabled.
265- await exitApp ();
266- return true ;
267- }
268- final tr = context.t.home;
269- final currentTime = DateTime .now ();
270- if (lastPopTime == null ||
271- currentTime.difference (lastPopTime! ).inMilliseconds > exitConfirmDuration.inMilliseconds) {
272- lastPopTime = currentTime;
273- ScaffoldMessenger .of (context).hideCurrentSnackBar ();
274- showSnackBar (context: context, message: tr.confirmExit);
275- return true ;
276- }
277- await exitApp ();
278- // Unreachable
279- return false ;
302+ return true ;
280303 },
281304 child: child,
282305 ),
0 commit comments