@@ -4,7 +4,6 @@ import 'package:equatable/equatable.dart';
44import 'package:flutter_bloc/flutter_bloc.dart' ;
55import 'package:injectable/injectable.dart' ;
66import 'package:on_time_front/core/dio/api_error_message.dart' ;
7- import 'package:on_time_front/core/logging/app_logger.dart' ;
87import 'package:on_time_front/domain/entities/preparation_entity.dart' ;
98import 'package:on_time_front/domain/entities/schedule_entity.dart' ;
109import 'package:on_time_front/domain/use-cases/delete_schedule_use_case.dart' ;
@@ -39,6 +38,8 @@ class MonthlySchedulesBloc
3938 _onPreparationsPrefetchRequested,
4039 );
4140 on < MonthlySchedulesPreparationsStreamChanged > (_onPreparationsStreamChanged);
41+ on < _MonthlySchedulesScheduleStreamChanged > (_onScheduleStreamChanged);
42+ on < _MonthlySchedulesScheduleStreamFailed > (_onScheduleStreamFailed);
4243
4344 _preparationSubscription = _streamPreparationsUseCase ().listen ((
4445 preparations,
@@ -56,101 +57,82 @@ class MonthlySchedulesBloc
5657 final GetPreparationByScheduleIdUseCase _getPreparationByScheduleIdUseCase;
5758 final StreamPreparationsUseCase _streamPreparationsUseCase;
5859
60+ StreamSubscription <List <ScheduleEntity >>? _scheduleSubscription;
5961 StreamSubscription <Map <String , PreparationEntity >>? _preparationSubscription;
62+ int _scheduleRangeRequestId = 0 ;
6063
6164 Future <void > _onSubscriptionRequested (
6265 MonthlySchedulesSubscriptionRequested event,
6366 Emitter <MonthlySchedulesState > emit,
6467 ) async {
65- emit (state.copyWith (status: () => MonthlySchedulesStatus .loading));
66-
67- try {
68- await _loadSchedulesForMonthUseCase (event.date);
69- } catch (_) {
70- emit (state.copyWith (status: () => MonthlySchedulesStatus .error));
71- return ;
72- }
73-
74- await emit.forEach (
75- _getSchedulesByDateUseCase (event.startDate, event.endDate),
76- onData: (schedules) {
77- final groupedSchedules = _groupSchedulesByDate (schedules);
78- final nextState = state.copyWith (
79- status: () => MonthlySchedulesStatus .success,
80- schedules: () => groupedSchedules,
81- startDate: () => event.startDate,
82- endDate: () => event.endDate,
83- );
84- _requestVisibleDatePreparationPrefetch (nextState);
85- return nextState;
86- },
87- onError: (error, stackTrace) =>
88- state.copyWith (status: () => MonthlySchedulesStatus .error),
68+ await _loadAndWatchCalendarMonthRange (
69+ loadDate: event.date,
70+ startDate: event.startDate,
71+ endDate: event.endDate,
72+ emit: emit,
8973 );
9074 }
9175
9276 Future <void > _onMonthAdded (
9377 MonthlySchedulesMonthAdded event,
9478 Emitter <MonthlySchedulesState > emit,
9579 ) async {
96- late DateTime startDate;
97- late DateTime endDate;
98- if (! (state.startDate! .isAfter (event.startDate) ||
99- state.endDate! .isBefore (event.endDate))) {
100- // If the month is already loaded, we don't need to load the schedules again.
101- startDate = state.startDate! ;
102- endDate = state.endDate! ;
103- } else if (event.date.month !=
104- state.startDate! .subtract (Duration (days: 1 )).month &&
105- (event.date.month != state.endDate! .month)) {
106- // If the month is not consecutive, we need to load the schedules for the
107- add (MonthlySchedulesSubscriptionRequested (date: event.date));
80+ final currentStartDate = state.startDate;
81+ final currentEndDate = state.endDate;
82+ if (currentStartDate == null || currentEndDate == null ) {
83+ await _loadAndWatchCalendarMonthRange (
84+ loadDate: event.date,
85+ startDate: event.startDate,
86+ endDate: event.endDate,
87+ emit: emit,
88+ );
10889 return ;
109- } else {
110- // If the month is not consecutive, we need to load the schedules for the
111- // month and update the state with the new schedules.
90+ }
11291
113- startDate = event.startDate. isBefore (state.startDate ! )
114- ? event.startDate
115- : state.startDate ! ;
116- endDate = event.endDate. isAfter (state.endDate ! )
117- ? event.endDate
118- : state.endDate ! ;
92+ final eventIsInsideCurrentRange =
93+ ! currentStartDate. isAfter ( event.startDate) &&
94+ ! currentEndDate. isBefore (event.endDate) ;
95+ if (eventIsInsideCurrentRange) {
96+ return ;
97+ }
11998
120- emit (
121- state.copyWith (
122- status: () => MonthlySchedulesStatus .loading,
123- schedules: () => state.schedules,
124- startDate: () => startDate,
125- endDate: () => endDate,
126- ),
99+ final eventMonth = DateTime (event.date.year, event.date.month, 1 );
100+ final previousAdjacentMonth = DateTime (
101+ currentStartDate.year,
102+ currentStartDate.month - 1 ,
103+ 1 ,
104+ );
105+ final nextAdjacentMonth = DateTime (
106+ currentEndDate.year,
107+ currentEndDate.month,
108+ 1 ,
109+ );
110+ final eventIsAdjacent =
111+ eventMonth == previousAdjacentMonth || eventMonth == nextAdjacentMonth;
112+
113+ if (! eventIsAdjacent) {
114+ await _loadAndWatchCalendarMonthRange (
115+ loadDate: event.date,
116+ startDate: event.startDate,
117+ endDate: event.endDate,
118+ emit: emit,
127119 );
128-
129- try {
130- await _loadSchedulesForMonthUseCase (event.date);
131- } catch (_) {
132- emit (state.copyWith (status: () => MonthlySchedulesStatus .error));
133- return ;
134- }
120+ return ;
135121 }
136122
137- AppLogger .debug ('startDate: $startDate , endDate: $endDate ' );
138- await emit.forEach (
139- _getSchedulesByDateUseCase (startDate, endDate),
140- onData: (schedules) {
141- final groupedSchedules = _groupSchedulesByDate (schedules);
142- final nextState = state.copyWith (
143- status: () => MonthlySchedulesStatus .success,
144- schedules: () => groupedSchedules,
145- startDate: () => startDate,
146- endDate: () => endDate,
147- );
148- _requestVisibleDatePreparationPrefetch (nextState);
149- return nextState;
150- },
151- onError: (error, stackTrace) {
152- return state.copyWith (status: () => MonthlySchedulesStatus .error);
153- },
123+ final startDate = event.startDate.isBefore (currentStartDate)
124+ ? event.startDate
125+ : currentStartDate;
126+ final endDate = event.endDate.isAfter (currentEndDate)
127+ ? event.endDate
128+ : currentEndDate;
129+
130+ await _loadAndWatchCalendarMonthRange (
131+ loadDate: event.date,
132+ startDate: startDate,
133+ endDate: endDate,
134+ emit: emit,
135+ exposeLoadingRange: true ,
154136 );
155137 }
156138
@@ -328,6 +310,97 @@ class MonthlySchedulesBloc
328310 emit (state.copyWith (preparationDurationByScheduleId: () => nextDurations));
329311 }
330312
313+ void _onScheduleStreamChanged (
314+ _MonthlySchedulesScheduleStreamChanged event,
315+ Emitter <MonthlySchedulesState > emit,
316+ ) {
317+ if (event.requestId != _scheduleRangeRequestId) {
318+ return ;
319+ }
320+
321+ final groupedSchedules = _groupSchedulesByDate (event.schedules);
322+ final nextState = state.copyWith (
323+ status: () => MonthlySchedulesStatus .success,
324+ schedules: () => groupedSchedules,
325+ startDate: () => event.startDate,
326+ endDate: () => event.endDate,
327+ );
328+ emit (nextState);
329+ _requestVisibleDatePreparationPrefetch (nextState);
330+ }
331+
332+ void _onScheduleStreamFailed (
333+ _MonthlySchedulesScheduleStreamFailed event,
334+ Emitter <MonthlySchedulesState > emit,
335+ ) {
336+ if (event.requestId != _scheduleRangeRequestId) {
337+ return ;
338+ }
339+
340+ emit (state.copyWith (status: () => MonthlySchedulesStatus .error));
341+ }
342+
343+ Future <void > _loadAndWatchCalendarMonthRange ({
344+ required DateTime loadDate,
345+ required DateTime startDate,
346+ required DateTime endDate,
347+ required Emitter <MonthlySchedulesState > emit,
348+ bool exposeLoadingRange = false ,
349+ }) async {
350+ final requestId = ++ _scheduleRangeRequestId;
351+ await _cancelScheduleSubscription ();
352+
353+ var loadingState = state.copyWith (
354+ status: () => MonthlySchedulesStatus .loading,
355+ );
356+ if (exposeLoadingRange) {
357+ loadingState = loadingState.copyWith (
358+ startDate: () => startDate,
359+ endDate: () => endDate,
360+ );
361+ }
362+ emit (loadingState);
363+
364+ try {
365+ await _loadSchedulesForMonthUseCase (loadDate);
366+ } catch (_) {
367+ if (requestId == _scheduleRangeRequestId) {
368+ emit (state.copyWith (status: () => MonthlySchedulesStatus .error));
369+ }
370+ return ;
371+ }
372+
373+ if (requestId != _scheduleRangeRequestId) {
374+ return ;
375+ }
376+
377+ _scheduleSubscription = _getSchedulesByDateUseCase (startDate, endDate)
378+ .listen (
379+ (schedules) {
380+ if (! isClosed && requestId == _scheduleRangeRequestId) {
381+ add (
382+ _MonthlySchedulesScheduleStreamChanged (
383+ requestId: requestId,
384+ startDate: startDate,
385+ endDate: endDate,
386+ schedules: schedules,
387+ ),
388+ );
389+ }
390+ },
391+ onError: (Object error, StackTrace stackTrace) {
392+ if (! isClosed && requestId == _scheduleRangeRequestId) {
393+ add (_MonthlySchedulesScheduleStreamFailed (requestId: requestId));
394+ }
395+ },
396+ );
397+ }
398+
399+ Future <void > _cancelScheduleSubscription () async {
400+ await _scheduleSubscription? .cancel ();
401+ _scheduleSubscription = null ;
402+ }
403+
331404 void _requestVisibleDatePreparationPrefetch (
332405 MonthlySchedulesState sourceState,
333406 ) {
@@ -393,6 +466,7 @@ class MonthlySchedulesBloc
393466
394467 @override
395468 Future <void > close () async {
469+ await _cancelScheduleSubscription ();
396470 await _preparationSubscription? .cancel ();
397471 return super .close ();
398472 }
0 commit comments