Skip to content

Commit 79dc6bb

Browse files
committed
Add confirm home button on alarm
1 parent 2da856d commit 79dc6bb

2 files changed

Lines changed: 207 additions & 0 deletions

File tree

lib/presentation/alarm/screens/alarm_screen.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:on_time_front/presentation/app/bloc/schedule/schedule_bloc.dart'
1010
import 'package:on_time_front/presentation/alarm/components/alarm_screen_bottom_section.dart';
1111
import 'package:on_time_front/presentation/alarm/components/alarm_screen_top_section.dart';
1212
import 'package:on_time_front/presentation/alarm/components/preparation_completion_dialog.dart';
13+
import 'package:on_time_front/presentation/shared/components/modal_wide_button.dart';
14+
import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart';
1315
import 'package:on_time_front/presentation/shared/utils/time_format.dart';
1416

1517
class AlarmScreen extends StatefulWidget {
@@ -144,6 +146,30 @@ class _AlarmScreenState extends State<AlarmScreen> {
144146
);
145147
}
146148

149+
Future<void> _showLeaveConfirmationDialog(BuildContext context) async {
150+
final l10n = AppLocalizations.of(context)!;
151+
final result = await showTwoActionDialog(
152+
context,
153+
config: TwoActionDialogConfig(
154+
title: l10n.confirmLeave,
155+
description: l10n.confirmLeaveDescription,
156+
barrierDismissible: false,
157+
secondaryAction: DialogActionConfig(
158+
label: l10n.leave,
159+
variant: ModalWideButtonVariant.neutral,
160+
),
161+
primaryAction: DialogActionConfig(
162+
label: l10n.stay,
163+
variant: ModalWideButtonVariant.primary,
164+
),
165+
),
166+
);
167+
168+
if (result == DialogActionResult.secondary && context.mounted) {
169+
context.go('/home');
170+
}
171+
}
172+
147173
Widget _buildAlarmScreen({
148174
required ScheduleWithPreparationEntity schedule,
149175
}) {
@@ -178,6 +204,21 @@ class _AlarmScreenState extends State<AlarmScreen> {
178204
),
179205
],
180206
),
207+
Positioned(
208+
top: 0,
209+
right: 0,
210+
child: SafeArea(
211+
minimum: EdgeInsets.zero,
212+
child: Padding(
213+
padding: const EdgeInsets.only(right: 4),
214+
child: IconButton(
215+
key: const Key('alarm_close_button'),
216+
icon: const Icon(Icons.close, color: Colors.white),
217+
onPressed: () => _showLeaveConfirmationDialog(context),
218+
),
219+
),
220+
),
221+
),
181222
],
182223
),
183224
);

test/presentation/alarm/screens/preparation_flow_widget_test.dart

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,172 @@ void main() {
527527
expect(find.text('HOME_ROUTE'), findsOneWidget);
528528
}, timeout: const Timeout(Duration(seconds: 15)));
529529

530+
testWidgets('active alarm screen renders top-corner close button',
531+
(tester) async {
532+
await setLargeTestViewport(tester);
533+
now = DateTime.now();
534+
535+
final schedule = buildSchedule(
536+
id: 's-active-close',
537+
scheduleTime: now.add(const Duration(minutes: 35)),
538+
steps: const [
539+
PreparationStepWithTimeEntity(
540+
id: 'p1',
541+
preparationName: 'Prep',
542+
preparationTime: Duration(minutes: 10),
543+
nextPreparationId: null,
544+
),
545+
],
546+
);
547+
548+
final router = GoRouter(
549+
initialLocation: '/alarmScreen',
550+
routes: [
551+
GoRoute(path: '/home', builder: (_, __) => const Text('HOME')),
552+
GoRoute(
553+
path: '/alarmScreen',
554+
builder: (_, __) => const AlarmScreen(),
555+
),
556+
],
557+
);
558+
559+
final earlyBundle = createEarlyStartUseCaseBundle();
560+
final alarmBloc = ScheduleBloc.test(
561+
StubGetNearestUpcomingScheduleUseCase(() => Stream.value(schedule)),
562+
navigationService,
563+
NoopSaveTimedPreparationUseCase(),
564+
StubGetTimedPreparationSnapshotUseCase({}),
565+
NoopClearTimedPreparationUseCase(),
566+
finishUseCase,
567+
markEarlyStartSessionUseCase: earlyBundle.markUseCase,
568+
getEarlyStartSessionUseCase: earlyBundle.getUseCase,
569+
clearEarlyStartSessionUseCase: earlyBundle.clearUseCase,
570+
nowProvider: () => now,
571+
);
572+
addTearDown(alarmBloc.close);
573+
574+
await pumpWithRouter(tester, bloc: alarmBloc, router: router);
575+
await pumpUntilFound(tester, find.byKey(const Key('alarm_close_button')));
576+
577+
expect(find.byKey(const Key('alarm_close_button')), findsOneWidget);
578+
alarmBloc.add(const ScheduleFinished(0));
579+
await tester.pump();
580+
}, timeout: const Timeout(Duration(seconds: 15)));
581+
582+
testWidgets(
583+
'active alarm close button shows confirm dialog and stay keeps alarm',
584+
(tester) async {
585+
await setLargeTestViewport(tester);
586+
now = DateTime.now();
587+
588+
final schedule = buildSchedule(
589+
id: 's-active-stay',
590+
scheduleTime: now.add(const Duration(minutes: 35)),
591+
steps: const [
592+
PreparationStepWithTimeEntity(
593+
id: 'p1',
594+
preparationName: 'Prep',
595+
preparationTime: Duration(minutes: 10),
596+
nextPreparationId: null,
597+
),
598+
],
599+
);
600+
601+
final router = GoRouter(
602+
initialLocation: '/alarmScreen',
603+
routes: [
604+
GoRoute(path: '/home', builder: (_, __) => const Text('HOME')),
605+
GoRoute(
606+
path: '/alarmScreen',
607+
builder: (_, __) => const AlarmScreen(),
608+
),
609+
],
610+
);
611+
612+
final earlyBundle = createEarlyStartUseCaseBundle();
613+
final alarmBloc = ScheduleBloc.test(
614+
StubGetNearestUpcomingScheduleUseCase(() => Stream.value(schedule)),
615+
navigationService,
616+
NoopSaveTimedPreparationUseCase(),
617+
StubGetTimedPreparationSnapshotUseCase({}),
618+
NoopClearTimedPreparationUseCase(),
619+
finishUseCase,
620+
markEarlyStartSessionUseCase: earlyBundle.markUseCase,
621+
getEarlyStartSessionUseCase: earlyBundle.getUseCase,
622+
clearEarlyStartSessionUseCase: earlyBundle.clearUseCase,
623+
nowProvider: () => now,
624+
);
625+
addTearDown(alarmBloc.close);
626+
627+
await pumpWithRouter(tester, bloc: alarmBloc, router: router);
628+
await tapAndPump(tester, find.byKey(const Key('alarm_close_button')));
629+
await pumpUntilFound(tester, find.byType(TwoActionDialog));
630+
631+
await tapAndPump(tester, find.text("I'll stay"));
632+
await tester.pump(const Duration(milliseconds: 150));
633+
634+
expect(find.text('HOME'), findsNothing);
635+
expect(find.byType(AlarmScreen), findsOneWidget);
636+
alarmBloc.add(const ScheduleFinished(0));
637+
await tester.pump();
638+
}, timeout: const Timeout(Duration(seconds: 15)));
639+
640+
testWidgets('active alarm close button leave action navigates home',
641+
(tester) async {
642+
await setLargeTestViewport(tester);
643+
now = DateTime.now();
644+
645+
final schedule = buildSchedule(
646+
id: 's-active-leave',
647+
scheduleTime: now.add(const Duration(minutes: 35)),
648+
steps: const [
649+
PreparationStepWithTimeEntity(
650+
id: 'p1',
651+
preparationName: 'Prep',
652+
preparationTime: Duration(minutes: 10),
653+
nextPreparationId: null,
654+
),
655+
],
656+
);
657+
658+
final router = GoRouter(
659+
initialLocation: '/alarmScreen',
660+
routes: [
661+
GoRoute(path: '/home', builder: (_, __) => const Text('HOME')),
662+
GoRoute(
663+
path: '/alarmScreen',
664+
builder: (_, __) => const AlarmScreen(),
665+
),
666+
],
667+
);
668+
669+
final earlyBundle = createEarlyStartUseCaseBundle();
670+
final alarmBloc = ScheduleBloc.test(
671+
StubGetNearestUpcomingScheduleUseCase(() => Stream.value(schedule)),
672+
navigationService,
673+
NoopSaveTimedPreparationUseCase(),
674+
StubGetTimedPreparationSnapshotUseCase({}),
675+
NoopClearTimedPreparationUseCase(),
676+
finishUseCase,
677+
markEarlyStartSessionUseCase: earlyBundle.markUseCase,
678+
getEarlyStartSessionUseCase: earlyBundle.getUseCase,
679+
clearEarlyStartSessionUseCase: earlyBundle.clearUseCase,
680+
nowProvider: () => now,
681+
);
682+
addTearDown(alarmBloc.close);
683+
684+
await pumpWithRouter(tester, bloc: alarmBloc, router: router);
685+
await tapAndPump(tester, find.byKey(const Key('alarm_close_button')));
686+
await pumpUntilFound(tester, find.byType(TwoActionDialog));
687+
688+
await tapAndPump(tester, find.text("I'm leaving"));
689+
await pumpUntilRouteText(tester, 'HOME');
690+
691+
expect(find.text('HOME'), findsOneWidget);
692+
alarmBloc.add(const ScheduleFinished(0));
693+
await tester.pump();
694+
}, timeout: const Timeout(Duration(seconds: 15)));
695+
530696
testWidgets(
531697
'manual finish before leave time sends lateness 0 and navigates',
532698
(tester) async {

0 commit comments

Comments
 (0)