@@ -7,6 +7,7 @@ import 'package:on_time_front/core/constants/external_links.dart';
77import 'package:on_time_front/core/di/di_setup.dart' ;
88import 'package:on_time_front/core/services/alarm_scheduler_service.dart' ;
99import 'package:on_time_front/core/services/fallback_alarm_notification_service.dart' ;
10+ import 'package:on_time_front/core/services/notification_service.dart' ;
1011import 'package:on_time_front/domain/entities/alarm_entities.dart' ;
1112import 'package:on_time_front/domain/entities/preparation_entity.dart' ;
1213import 'package:on_time_front/domain/repositories/alarm_registry_repository.dart' ;
@@ -17,23 +18,16 @@ import 'package:on_time_front/l10n/app_localizations.dart';
1718import 'package:on_time_front/presentation/app/bloc/auth/auth_bloc.dart' ;
1819import 'package:on_time_front/presentation/my_page/my_page_modal/delete_user_modal.dart' ;
1920import 'package:on_time_front/presentation/my_page/my_page_modal/logout_modal.dart' ;
20- import 'package:on_time_front/presentation/notification_allow/screens/notification_allow_screen.dart' ;
2121import 'package:on_time_front/presentation/shared/components/modal_wide_button.dart' ;
2222import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart' ;
2323
2424typedef PrivacyPolicyLauncher = Future <bool > Function (Uri uri);
2525
2626class MyPageScreen extends StatelessWidget {
27- const MyPageScreen ({
28- super .key,
29- PrivacyPolicyLauncher ? openPrivacyPolicy,
30- NotificationPermissionGateway notificationPermissionGateway =
31- const NotificationServicePermissionGateway (),
32- }) : _openPrivacyPolicy = openPrivacyPolicy,
33- _notificationPermissionGateway = notificationPermissionGateway;
27+ const MyPageScreen ({super .key, PrivacyPolicyLauncher ? openPrivacyPolicy})
28+ : _openPrivacyPolicy = openPrivacyPolicy;
3429
3530 final PrivacyPolicyLauncher ? _openPrivacyPolicy;
36- final NotificationPermissionGateway _notificationPermissionGateway;
3731
3832 @override
3933 Widget build (BuildContext context) {
@@ -98,8 +92,11 @@ class MyPageScreen extends StatelessWidget {
9892 if (updatedPreparation != null ) {}
9993 },
10094 ),
101- _NotificationStatusView (
102- permissionGateway: _notificationPermissionGateway,
95+ _SettingTile (
96+ title: AppLocalizations .of (context)! .allowAppNotifications,
97+ onTap: () async {
98+ await _handleNotificationPermission (context);
99+ },
103100 ),
104101 _SettingTile (
105102 title: AppLocalizations .of (context)! .privacyPolicy,
@@ -305,118 +302,7 @@ class _AlarmStatusViewState extends State<_AlarmStatusView> {
305302 ),
306303 ],
307304 ),
308- Switch (
309- key: const Key ('alarm_permission_switch' ),
310- value: _alarmsEnabled,
311- onChanged: _isUpdating ? null : _toggle,
312- ),
313- ],
314- );
315- }
316- }
317-
318- class _NotificationStatusView extends StatefulWidget {
319- const _NotificationStatusView ({required this .permissionGateway});
320-
321- final NotificationPermissionGateway permissionGateway;
322-
323- @override
324- State <_NotificationStatusView > createState () =>
325- _NotificationStatusViewState ();
326- }
327-
328- class _NotificationStatusViewState extends State <_NotificationStatusView >
329- with WidgetsBindingObserver {
330- bool _isLoading = true ;
331- bool _isUpdating = false ;
332- bool _notificationsEnabled = false ;
333-
334- @override
335- void initState () {
336- super .initState ();
337- WidgetsBinding .instance.addObserver (this );
338- _load ();
339- }
340-
341- @override
342- void didChangeAppLifecycleState (AppLifecycleState state) {
343- if (state != AppLifecycleState .resumed) return ;
344- _load ();
345- }
346-
347- @override
348- void dispose () {
349- WidgetsBinding .instance.removeObserver (this );
350- super .dispose ();
351- }
352-
353- Future <void > _load () async {
354- setState (() {
355- _isLoading = true ;
356- });
357- final status = await widget.permissionGateway.checkNotificationPermission ();
358- if (! mounted) return ;
359- setState (() {
360- _notificationsEnabled = _isNotificationAllowed (status);
361- _isLoading = false ;
362- });
363- }
364-
365- Future <void > _toggle (bool value) async {
366- setState (() {
367- _isUpdating = true ;
368- _notificationsEnabled = value;
369- });
370- try {
371- if (value) {
372- final status = await widget.permissionGateway.requestPermission ();
373- if (_isNotificationAllowed (status)) {
374- await widget.permissionGateway.initializeNotifications ();
375- }
376- } else {
377- final shouldOpenSettings = await _showNotificationSettingsDialog (
378- context,
379- );
380- if (shouldOpenSettings == true ) {
381- await widget.permissionGateway.openNotificationSettings ();
382- }
383- }
384- await _load ();
385- } finally {
386- if (mounted) {
387- setState (() {
388- _isUpdating = false ;
389- });
390- }
391- }
392- }
393-
394- @override
395- Widget build (BuildContext context) {
396- final textTheme = Theme .of (context).textTheme;
397- final colorScheme = Theme .of (context).colorScheme;
398- return Row (
399- mainAxisAlignment: MainAxisAlignment .spaceBetween,
400- children: [
401- Column (
402- crossAxisAlignment: CrossAxisAlignment .start,
403- children: [
404- Text (
405- AppLocalizations .of (context)! .allowAppNotifications,
406- style: textTheme.bodyLarge,
407- ),
408- const SizedBox (height: 4 ),
409- Text (
410- _isLoading ? '확인 중' : (_notificationsEnabled ? '켜짐' : '꺼짐' ),
411- style: textTheme.bodySmall? .copyWith (color: colorScheme.outline),
412- ),
413- ],
414- ),
415- Switch (
416- key: const Key ('notification_permission_switch' ),
417- value: _notificationsEnabled,
418- onChanged: _isUpdating ? null : _toggle,
419- ),
305+ Switch (value: _alarmsEnabled, onChanged: _isUpdating ? null : _toggle),
420306 ],
421307 );
422308 }
@@ -427,11 +313,6 @@ bool _needsExactAlarmRecovery(AlarmPermissionState permission) {
427313 permission == AlarmPermissionState .notDetermined;
428314}
429315
430- bool _isNotificationAllowed (AuthorizationStatus status) {
431- return status == AuthorizationStatus .authorized ||
432- status == AuthorizationStatus .provisional;
433- }
434-
435316class _MyAccountView extends StatelessWidget {
436317 const _MyAccountView ();
437318
@@ -537,6 +418,87 @@ class _SettingTile extends StatelessWidget {
537418 }
538419}
539420
421+ Future <void > _handleNotificationPermission (BuildContext context) async {
422+ final notificationService = NotificationService .instance;
423+ final currentStatus = await notificationService.checkNotificationPermission ();
424+
425+ if (! context.mounted) return ;
426+
427+ if (currentStatus == AuthorizationStatus .authorized) {
428+ await _showAlreadyEnabledDialog (context);
429+ } else if (currentStatus == AuthorizationStatus .denied) {
430+ final shouldRequest = await _showPermissionRationaleDialog (context);
431+ if (shouldRequest == true && context.mounted) {
432+ final newStatus = await notificationService.requestPermission ();
433+
434+ if (! context.mounted) return ;
435+
436+ if (newStatus == AuthorizationStatus .authorized) {
437+ await notificationService.initialize ();
438+ if (! context.mounted) return ;
439+ await _showPermissionGrantedDialog (context);
440+ } else if (newStatus == AuthorizationStatus .denied) {
441+ await _showGoToSettingsDialog (context);
442+ }
443+ }
444+ } else if (currentStatus == AuthorizationStatus .notDetermined) {
445+ final shouldRequest = await _showPermissionRationaleDialog (context);
446+ if (shouldRequest == true && context.mounted) {
447+ final newStatus = await notificationService.requestPermission ();
448+
449+ if (! context.mounted) return ;
450+
451+ if (newStatus == AuthorizationStatus .authorized) {
452+ await notificationService.initialize ();
453+ if (! context.mounted) return ;
454+ await _showPermissionGrantedDialog (context);
455+ } else if (newStatus == AuthorizationStatus .denied) {
456+ await _showGoToSettingsDialog (context);
457+ }
458+ }
459+ } else {
460+ await _showGoToSettingsDialog (context);
461+ }
462+ }
463+
464+ Future <void > _showAlreadyEnabledDialog (BuildContext context) async {
465+ final l10n = AppLocalizations .of (context)! ;
466+
467+ await showTwoActionDialog (
468+ context,
469+ config: TwoActionDialogConfig (
470+ title: l10n.notificationAlreadyEnabled,
471+ description: l10n.notificationAlreadyEnabledDescription,
472+ primaryAction: DialogActionConfig (
473+ label: l10n.ok,
474+ variant: ModalWideButtonVariant .primary,
475+ ),
476+ ),
477+ );
478+ }
479+
480+ Future <bool ?> _showPermissionRationaleDialog (BuildContext context) async {
481+ final l10n = AppLocalizations .of (context)! ;
482+
483+ final result = await showTwoActionDialog (
484+ context,
485+ config: TwoActionDialogConfig (
486+ title: l10n.notificationPermissionRequired,
487+ description: l10n.notificationPermissionRequiredDescription,
488+ secondaryAction: DialogActionConfig (
489+ label: l10n.cancel,
490+ variant: ModalWideButtonVariant .neutral,
491+ ),
492+ primaryAction: DialogActionConfig (
493+ label: l10n.allow,
494+ variant: ModalWideButtonVariant .primary,
495+ ),
496+ ),
497+ );
498+
499+ return result == DialogActionResult .primary;
500+ }
501+
540502Future <DialogActionResult ?> _showExactAlarmPermissionDialog (
541503 BuildContext context,
542504) async {
@@ -559,14 +521,30 @@ Future<DialogActionResult?> _showExactAlarmPermissionDialog(
559521 );
560522}
561523
562- Future <bool ?> _showNotificationSettingsDialog (BuildContext context) async {
524+ Future <void > _showPermissionGrantedDialog (BuildContext context) async {
525+ final l10n = AppLocalizations .of (context)! ;
526+
527+ await showTwoActionDialog (
528+ context,
529+ config: TwoActionDialogConfig (
530+ title: l10n.notificationPermissionGranted,
531+ description: l10n.notificationPermissionGrantedDescription,
532+ primaryAction: DialogActionConfig (
533+ label: l10n.ok,
534+ variant: ModalWideButtonVariant .primary,
535+ ),
536+ ),
537+ );
538+ }
539+
540+ Future <void > _showGoToSettingsDialog (BuildContext context) async {
563541 final l10n = AppLocalizations .of (context)! ;
564542
565543 final result = await showTwoActionDialog (
566544 context,
567545 config: TwoActionDialogConfig (
568- title: l10n.manageAppNotifications ,
569- description: l10n.manageAppNotificationsDescription ,
546+ title: l10n.openNotificationSettings ,
547+ description: l10n.openNotificationSettingsDescription ,
570548 secondaryAction: DialogActionConfig (
571549 label: l10n.cancel,
572550 variant: ModalWideButtonVariant .neutral,
@@ -578,5 +556,7 @@ Future<bool?> _showNotificationSettingsDialog(BuildContext context) async {
578556 ),
579557 );
580558
581- return result == DialogActionResult .primary;
559+ if (result == DialogActionResult .primary) {
560+ await NotificationService .instance.openNotificationSettings ();
561+ }
582562}
0 commit comments