Skip to content

Commit c3a21d1

Browse files
authored
Merge pull request #358 from DevKor-github/feat/investigate-schedule-data-flow
Refresh calendar schedules on edit and refresh events
2 parents 1f513d2 + 46797f5 commit c3a21d1

13 files changed

Lines changed: 412 additions & 60 deletions

File tree

lib/data/repositories/schedule_repository_impl.dart

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,14 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
2525

2626
@override
2727
Stream<Set<ScheduleEntity>> get scheduleStream =>
28-
_scheduleStreamController.asBroadcastStream();
28+
_scheduleStreamController.stream;
2929

3030
@override
3131
Future<void> createSchedule(ScheduleEntity schedule) async {
3232
try {
3333
await scheduleRemoteDataSource.createSchedule(schedule);
3434
//await scheduleLocalDataSource.createSchedule(schedule);
35-
_scheduleStreamController.add(
36-
Set.from(_scheduleStreamController.value)..add(schedule),
37-
);
35+
_emitUpsertedSchedule(schedule);
3836
} catch (e) {
3937
rethrow;
4038
}
@@ -47,7 +45,8 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
4745
await _clearTimedPreparationSafe(schedule.id);
4846
//await scheduleLocalDataSource.deleteSchedule(schedule);
4947
_scheduleStreamController.add(
50-
Set.from(_scheduleStreamController.value)..remove(schedule),
48+
Set.from(_scheduleStreamController.value)
49+
..removeWhere((existing) => existing.id == schedule.id),
5150
);
5251
} catch (e) {
5352
rethrow;
@@ -61,9 +60,7 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
6160
if (_isEnded(schedule.doneStatus)) {
6261
await _clearTimedPreparationSafe(schedule.id);
6362
}
64-
_scheduleStreamController.add(
65-
Set.from(_scheduleStreamController.value)..add(schedule),
66-
);
63+
_emitUpsertedSchedule(schedule);
6764
return schedule;
6865
} catch (e) {
6966
rethrow;
@@ -85,14 +82,11 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
8582
await _clearTimedPreparationSafe(schedule.id);
8683
}
8784
}
88-
final mergedSchedules = Set<ScheduleEntity>.from(
89-
_scheduleStreamController.value,
85+
_replaceSchedulesInRange(
86+
startDate: startDate,
87+
endDate: endDate,
88+
schedules: schedules,
9089
);
91-
for (final schedule in schedules) {
92-
mergedSchedules.removeWhere((existing) => existing.id == schedule.id);
93-
mergedSchedules.add(schedule);
94-
}
95-
_scheduleStreamController.add(mergedSchedules);
9690
return schedules;
9791
} catch (e) {
9892
rethrow;
@@ -104,11 +98,13 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
10498
try {
10599
await scheduleRemoteDataSource.updateSchedule(schedule);
106100
await _clearTimedPreparationSafe(schedule.id);
107-
_scheduleStreamController.add(
108-
Set.from(_scheduleStreamController.value)
109-
..remove(schedule)
110-
..add(schedule),
101+
final refreshedSchedule = await scheduleRemoteDataSource.getScheduleById(
102+
schedule.id,
111103
);
104+
if (_isEnded(refreshedSchedule.doneStatus)) {
105+
await _clearTimedPreparationSafe(refreshedSchedule.id);
106+
}
107+
_emitUpsertedSchedule(refreshedSchedule);
112108
//await scheduleLocalDataSource.updateSchedule(schedule);
113109
} catch (e) {
114110
rethrow;
@@ -126,11 +122,7 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
126122
final schedule = _scheduleStreamController.value.firstWhere(
127123
(schedule) => schedule.id == scheduleId,
128124
);
129-
_scheduleStreamController.add(
130-
Set.from(_scheduleStreamController.value)
131-
..remove(schedule)
132-
..add(schedule.copyWith(doneStatus: lateStatus)),
133-
);
125+
_emitUpsertedSchedule(schedule.copyWith(doneStatus: lateStatus));
134126
} catch (e) {
135127
rethrow;
136128
}
@@ -149,4 +141,40 @@ class ScheduleRepositoryImpl implements ScheduleRepository {
149141
// Best-effort cleanup: cache invalidation must not fail schedule operations.
150142
}
151143
}
144+
145+
void _emitUpsertedSchedule(ScheduleEntity schedule) {
146+
final nextSchedules =
147+
Set<ScheduleEntity>.from(_scheduleStreamController.value)
148+
..removeWhere((existing) => existing.id == schedule.id)
149+
..add(schedule);
150+
_scheduleStreamController.add(nextSchedules);
151+
}
152+
153+
void _emitUpsertedSchedules(Iterable<ScheduleEntity> schedules) {
154+
final nextSchedules =
155+
Set<ScheduleEntity>.from(_scheduleStreamController.value);
156+
for (final schedule in schedules) {
157+
nextSchedules.removeWhere((existing) => existing.id == schedule.id);
158+
nextSchedules.add(schedule);
159+
}
160+
_scheduleStreamController.add(nextSchedules);
161+
}
162+
163+
void _replaceSchedulesInRange({
164+
required DateTime startDate,
165+
required DateTime? endDate,
166+
required Iterable<ScheduleEntity> schedules,
167+
}) {
168+
final nextSchedules =
169+
Set<ScheduleEntity>.from(_scheduleStreamController.value)
170+
..removeWhere(
171+
(existing) =>
172+
!existing.scheduleTime.isBefore(startDate) &&
173+
(endDate == null || existing.scheduleTime.isBefore(endDate)),
174+
);
175+
for (final schedule in schedules) {
176+
nextSchedules.add(schedule);
177+
}
178+
_scheduleStreamController.add(nextSchedules);
179+
}
152180
}

lib/domain/entities/place_entity.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import 'package:equatable/equatable.dart';
2+
13
import '/core/database/database.dart';
24

3-
class PlaceEntity {
5+
class PlaceEntity extends Equatable {
46
final String id;
57
final String placeName;
68

@@ -22,4 +24,7 @@ class PlaceEntity {
2224
placeName: placeName,
2325
);
2426
}
27+
28+
@override
29+
List<Object?> get props => [id, placeName];
2530
}

lib/domain/entities/schedule_entity.dart

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@ class ScheduleEntity extends Equatable {
9595
return 'ScheduleEntity(id: $id, place: $place, scheduleName: $scheduleName, scheduleTime: $scheduleTime, moveTime: $moveTime, isChanged: $isChanged, isStarted: $isStarted, scheduleSpareTime: $scheduleSpareTime, scheduleNote: $scheduleNote, latenessTime: $latenessTime)';
9696
}
9797

98-
@override
99-
bool operator ==(Object other) {
100-
if (identical(this, other)) return true;
101-
102-
return other is ScheduleEntity && other.id == id;
103-
}
104-
105-
@override
106-
int get hashCode {
107-
return id.hashCode;
108-
}
109-
11098
@override
11199
List<Object?> get props => [
112100
id,

lib/presentation/calendar/bloc/monthly_schedules_bloc.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class MonthlySchedulesBloc
2929
) : super(MonthlySchedulesState()) {
3030
on<MonthlySchedulesSubscriptionRequested>(_onSubscriptionRequested);
3131
on<MonthlySchedulesMonthAdded>(_onMonthAdded);
32+
on<MonthlySchedulesRefreshRequested>(_onRefreshRequested);
3233
on<MonthlySchedulesScheduleDeleted>(_onScheduleDeleted);
3334
on<MonthlySchedulesVisibleDateChanged>(_onVisibleDateChanged);
3435
on<MonthlySchedulesPreparationsPrefetchRequested>(
@@ -165,6 +166,19 @@ class MonthlySchedulesBloc
165166
}
166167
}
167168

169+
Future<void> _onRefreshRequested(
170+
MonthlySchedulesRefreshRequested event,
171+
Emitter<MonthlySchedulesState> emit,
172+
) async {
173+
try {
174+
await _loadSchedulesForMonthUseCase(event.date);
175+
} catch (_) {
176+
emit(state.copyWith(
177+
status: () => MonthlySchedulesStatus.error,
178+
));
179+
}
180+
}
181+
168182
void _onVisibleDateChanged(
169183
MonthlySchedulesVisibleDateChanged event,
170184
Emitter<MonthlySchedulesState> emit,

lib/presentation/calendar/bloc/monthly_schedules_event.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ final class MonthlySchedulesMonthAdded extends MonthlySchedulesEvent {
3232
List<Object> get props => [date];
3333
}
3434

35+
final class MonthlySchedulesRefreshRequested extends MonthlySchedulesEvent {
36+
final DateTime date;
37+
38+
const MonthlySchedulesRefreshRequested({required this.date});
39+
40+
@override
41+
List<Object> get props => [date.year, date.month];
42+
}
43+
3544
final class MonthlySchedulesScheduleDeleted extends MonthlySchedulesEvent {
3645
final ScheduleEntity schedule;
3746

lib/presentation/calendar/component/schedule_detail.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,20 @@ class _ScheduleDetailState extends State<ScheduleDetail> {
8181
bottom: 4.0,
8282
);
8383

84+
String get _scheduleRenderKey => [
85+
widget.schedule.id,
86+
widget.schedule.scheduleName,
87+
widget.schedule.place.placeName,
88+
widget.schedule.scheduleTime.millisecondsSinceEpoch,
89+
widget.schedule.moveTime.inMinutes,
90+
widget.schedule.scheduleSpareTime?.inMinutes ?? -1,
91+
widget.schedule.doneStatus.name,
92+
].join('|');
93+
8494
@override
8595
Widget build(BuildContext context) {
8696
return SwipeActionCell(
87-
key: ValueKey<String>(widget.schedule.id),
97+
key: ValueKey<String>(_scheduleRenderKey),
8898
backgroundColor: Colors.transparent,
8999
trailingActions: _buildSwipeActions(context),
90100
child: _buildScheduleContent(context),

lib/presentation/calendar/screens/calendar_screen.dart

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,46 @@ class _CalendarScreenState extends State<CalendarScreen> {
7171
});
7272
}
7373

74+
Future<void> _refreshSchedulesIfSaved(
75+
BuildContext context, bool? saved) async {
76+
if (saved != true || !mounted) {
77+
return;
78+
}
79+
80+
context.read<MonthlySchedulesBloc>().add(
81+
MonthlySchedulesRefreshRequested(date: _selectedDate),
82+
);
83+
}
84+
85+
Future<void> _openCreateScheduleSheet(BuildContext context) async {
86+
final saved = await showModalBottomSheet<bool>(
87+
context: context,
88+
isScrollControlled: true,
89+
backgroundColor: Colors.transparent,
90+
builder: (context) => ScheduleCreateScreen(
91+
initialDate: _selectedDate,
92+
),
93+
);
94+
95+
await _refreshSchedulesIfSaved(context, saved);
96+
}
97+
98+
Future<void> _openEditScheduleSheet(
99+
BuildContext context, {
100+
required String scheduleId,
101+
}) async {
102+
final saved = await showModalBottomSheet<bool>(
103+
context: context,
104+
isScrollControlled: true,
105+
backgroundColor: Colors.transparent,
106+
builder: (context) => ScheduleEditScreen(
107+
scheduleId: scheduleId,
108+
),
109+
);
110+
111+
await _refreshSchedulesIfSaved(context, saved);
112+
}
113+
74114
@override
75115
Widget build(BuildContext context) {
76116
final theme = Theme.of(context);
@@ -280,17 +320,8 @@ class _CalendarScreenState extends State<CalendarScreen> {
280320
),
281321
if (!isPastSelectedDay)
282322
ElevatedButton(
283-
onPressed: () {
284-
showModalBottomSheet(
285-
context: context,
286-
isScrollControlled: true,
287-
backgroundColor: Colors.transparent,
288-
builder: (context) =>
289-
ScheduleCreateScreen(
290-
initialDate: _selectedDate,
291-
),
292-
);
293-
},
323+
onPressed: () =>
324+
_openCreateScheduleSheet(context),
294325
style: ElevatedButton.styleFrom(
295326
backgroundColor:
296327
theme.colorScheme.surface,
@@ -332,13 +363,9 @@ class _CalendarScreenState extends State<CalendarScreen> {
332363
state.preparationDurationByScheduleId[
333364
schedule.id],
334365
onEdit: () {
335-
showModalBottomSheet(
336-
context: context,
337-
isScrollControlled: true,
338-
backgroundColor: Colors.transparent,
339-
builder: (context) => ScheduleEditScreen(
340-
scheduleId: schedule.id,
341-
),
366+
_openEditScheduleSheet(
367+
context,
368+
scheduleId: schedule.id,
342369
);
343370
},
344371
onDeleted: () {

0 commit comments

Comments
 (0)