-
Notifications
You must be signed in to change notification settings - Fork 0
Feature maintainance mode #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
9f76151
feat(service): implement AppStatusService for lifecycle-based status …
fulleni 9cf9420
feat(app): implement automated app status checks
fulleni 36ed682
feat(status): add maintenance page for app downtime
fulleni 4ed56ef
fix(status): update localization import and usage
fulleni c2a3240
feat(l10n): add translations for maintenance page
fulleni 743545e
feat(l10n): add Arabic translations for maintenance page
fulleni 44bd634
feat(l10n): add translations for force update page
fulleni 55356db
feat(l10n): add Arabic translations for update required page
fulleni 5de474a
refactor(status): remove unused import
fulleni 12358a5
feat(ui): add update required page
fulleni 29224a5
feat(status): add global maintenance and update required checks
fulleni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart'; | ||
|
||
/// {@template app_status_service} | ||
/// A service dedicated to monitoring the application's lifecycle and | ||
/// proactively triggering status checks. | ||
/// | ||
/// This service ensures that the application can react to server-side | ||
/// status changes (like maintenance mode or forced updates) in real-time, | ||
/// both when the app is resumed from the background and during extended | ||
/// foreground sessions. | ||
/// | ||
/// It works by: | ||
/// 1. Implementing [WidgetsBindingObserver] to listen for app lifecycle events. | ||
/// 2. Triggering a remote configuration fetch via the [AppBloc] whenever the | ||
/// app is resumed (`AppLifecycleState.resumed`). | ||
/// 3. Using a periodic [Timer] to trigger fetches at a regular interval, | ||
/// catching status changes even if the app remains in the foreground. | ||
/// {@endtemplate} | ||
class AppStatusService with WidgetsBindingObserver { | ||
/// {@macro app_status_service} | ||
/// | ||
/// Requires a [BuildContext] to access the [AppBloc] and a [Duration] | ||
/// for the periodic check interval. | ||
AppStatusService({ | ||
required BuildContext context, | ||
required Duration checkInterval, | ||
}) : _context = context, | ||
_checkInterval = checkInterval { | ||
// Immediately register this service as a lifecycle observer. | ||
WidgetsBinding.instance.addObserver(this); | ||
// Start the periodic checks. | ||
_startPeriodicChecks(); | ||
} | ||
|
||
/// The build context used to look up the AppBloc. | ||
final BuildContext _context; | ||
|
||
/// The interval at which to perform periodic status checks. | ||
final Duration _checkInterval; | ||
|
||
/// The timer responsible for periodic checks. | ||
Timer? _timer; | ||
|
||
/// Starts the periodic timer to trigger config fetches. | ||
/// | ||
/// This ensures that even if the app stays in the foreground, it will | ||
/// eventually learn about new server-side status changes. | ||
void _startPeriodicChecks() { | ||
// Cancel any existing timer to prevent duplicates. | ||
_timer?.cancel(); | ||
// Create a new periodic timer. | ||
_timer = Timer.periodic(_checkInterval, (_) { | ||
print( | ||
'[AppStatusService] Periodic check triggered. Requesting AppConfig fetch.', | ||
); | ||
fulleni marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Add the event to the AppBloc to fetch the latest config. | ||
_context.read<AppBloc>().add(const AppConfigFetchRequested()); | ||
}); | ||
} | ||
|
||
/// Overridden from [WidgetsBindingObserver]. | ||
/// | ||
/// This method is called whenever the application's lifecycle state changes. | ||
@override | ||
void didChangeAppLifecycleState(AppLifecycleState state) { | ||
// We are only interested in the 'resumed' state. | ||
if (state == AppLifecycleState.resumed) { | ||
print( | ||
'[AppStatusService] App resumed. Requesting AppConfig fetch.', | ||
); | ||
// When the app comes to the foreground, immediately trigger a check. | ||
// This is crucial for catching maintenance mode that was enabled | ||
// while the app was in the background. | ||
_context.read<AppBloc>().add(const AppConfigFetchRequested()); | ||
} | ||
} | ||
|
||
/// Cleans up resources used by the service. | ||
/// | ||
/// This must be called when the service is no longer needed (e.g., when | ||
/// the main app widget is disposed) to prevent memory leaks from the | ||
/// timer and the observer registration. | ||
void dispose() { | ||
print('[AppStatusService] Disposing service.'); | ||
// Stop the periodic timer. | ||
_timer?.cancel(); | ||
// Remove this object from the list of lifecycle observers. | ||
WidgetsBinding.instance.removeObserver(this); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/app_localizations.dart'; | ||
import 'package:ui_kit/ui_kit.dart'; | ||
|
||
/// A page displayed to the user when the application is in maintenance mode. | ||
/// | ||
/// This is a simple, static page that informs the user that the app is | ||
/// temporarily unavailable and asks them to check back later. It's designed | ||
/// to be displayed globally, blocking access to all other app features. | ||
class MaintenancePage extends StatelessWidget { | ||
/// {@macro maintenance_page} | ||
const MaintenancePage({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final l10n = AppLocalizations.of(context); | ||
|
||
// The Scaffold provides the basic Material Design visual layout structure. | ||
return Scaffold( | ||
body: Padding( | ||
// Use consistent padding from the UI kit. | ||
padding: const EdgeInsets.all(AppSpacing.lg), | ||
// The InitialStateWidget from the UI kit is reused here to provide a | ||
// consistent look and feel for full-screen informational states. | ||
child: InitialStateWidget( | ||
icon: Icons.build_circle_outlined, | ||
headline: l10n.maintenanceHeadline, | ||
subheadline: l10n.maintenanceSubheadline, | ||
), | ||
), | ||
); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.