feat: Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -0,0 +1,337 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow/core/application/common/date_time_extension.dart';
|
||||
import 'package:krow/core/application/di/injectable.dart';
|
||||
import 'package:krow/core/data/enums/state_status.dart';
|
||||
import 'package:krow/features/profile/schedule/domain/entities/day_shedule.dart';
|
||||
import 'package:krow/features/profile/schedule/domain/entities/schedule_slot.dart';
|
||||
import 'package:krow/features/profile/schedule/domain/staff_schedule_repository.dart';
|
||||
|
||||
part 'schedule_event.dart';
|
||||
|
||||
part 'schedule_state.dart';
|
||||
|
||||
class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
||||
ScheduleBloc() : super(const ScheduleState()) {
|
||||
on<ScheduleInitEvent>(_onBlocInit);
|
||||
on<ScheduleEventChangeMode>(_onChangeMode);
|
||||
on<ScheduleEventSelectDates>(_onSelectDates);
|
||||
on<ScheduleEventAddSlot>(_onAddSlot);
|
||||
on<ScheduleEventRemoveSlot>(_onRemoveSlot);
|
||||
on<ScheduleEventEditSlot>(_onEditSlot);
|
||||
on<ScheduleEventSave>(_onConfirmChanges);
|
||||
on<ScheduleEventCancel>(_onCancelChanges);
|
||||
on<ScheduleEventDeleteSchedule>(_onDeleteSchedule);
|
||||
}
|
||||
|
||||
final StaffScheduleRepository staffRepository =
|
||||
getIt<StaffScheduleRepository>();
|
||||
|
||||
Future<void> _onBlocInit(
|
||||
ScheduleInitEvent event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(status: StateStatus.loading));
|
||||
|
||||
try {
|
||||
await for (final scheduleData in staffRepository.getStaffSchedule()) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
schedules: scheduleData,
|
||||
status: StateStatus.idle,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (except) {
|
||||
log(except.toString());
|
||||
}
|
||||
|
||||
if (state.status == StateStatus.loading) {
|
||||
emit(state.copyWith(status: StateStatus.idle));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSelectDates(
|
||||
ScheduleEventSelectDates event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) async {
|
||||
if (event.dates.length == 1) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
scheduleErrorText: '',
|
||||
selectedDates: [event.dates.first],
|
||||
prevSelectedDates: [],
|
||||
isInEditMode: false,
|
||||
),
|
||||
);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedSchedules: await state.findSchedulesForSelection(),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var startDate = event.dates.first;
|
||||
final endDate = event.dates.last;
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
scheduleErrorText: '',
|
||||
selectedDates: [
|
||||
for (var i = 0; i < endDate.difference(startDate).inDays; i++)
|
||||
startDate.add(Duration(days: i)),
|
||||
endDate,
|
||||
],
|
||||
prevSelectedDates: [],
|
||||
isInEditMode: false,
|
||||
),
|
||||
);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedSchedules: await state.findSchedulesForSelection(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onChangeMode(
|
||||
ScheduleEventChangeMode event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) {
|
||||
if (event.isInScheduleAddMode != null && state.selectedDates.length == 1) {
|
||||
emit(state.copyWith(
|
||||
scheduleErrorText: '',
|
||||
isScheduleAddMode: event.isInScheduleAddMode,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.editedDate != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedDates: [event.editedDate!],
|
||||
prevSelectedDates: List.from(state.selectedDates),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
isInEditMode: event.isInEditScheduleMode,
|
||||
scheduleErrorText: '',
|
||||
isScheduleAddMode: false,
|
||||
tempSchedules: state.selectedDates.length == 1
|
||||
? [
|
||||
state.getScheduleForDate(
|
||||
date: state.selectedDates.first,
|
||||
isWeekly: event.isWeekly,
|
||||
),
|
||||
]
|
||||
: [
|
||||
for (int i = 0; i < state.selectedDates.length; i++)
|
||||
DaySchedule(
|
||||
date: state.selectedDates[i],
|
||||
slots: const [],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onAddSlot(
|
||||
ScheduleEventAddSlot event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
tempSchedules: state.tempSchedules.map(
|
||||
(daySchedule) {
|
||||
final startTime = daySchedule.getLatestSlot();
|
||||
|
||||
return daySchedule.copyWith(
|
||||
slots: [
|
||||
...daySchedule.slots,
|
||||
ScheduleSlot.minFromStartTime(
|
||||
start: startTime,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onRemoveSlot(
|
||||
ScheduleEventRemoveSlot event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
scheduleErrorText: '',
|
||||
tempSchedules: state.tempSchedules.map(
|
||||
(daySchedule) {
|
||||
return daySchedule.removeSlotAtIndex(event.slotIndex);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onEditSlot(
|
||||
ScheduleEventEditSlot event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
scheduleErrorText: '',
|
||||
tempSchedules: state.tempSchedules.map(
|
||||
(daySchedule) {
|
||||
final daySlots = List<ScheduleSlot>.from(daySchedule.slots);
|
||||
daySlots[event.slotIndex] = daySlots[event.slotIndex].editTime(
|
||||
startTime: event.start?.copyWith(
|
||||
day: daySchedule.date.day,
|
||||
month: daySchedule.date.month,
|
||||
),
|
||||
endTime: event.end?.copyWith(
|
||||
day: daySchedule.date.day,
|
||||
month: daySchedule.date.month,
|
||||
),
|
||||
);
|
||||
|
||||
return daySchedule.copyWith(
|
||||
slots: daySlots,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, DaySchedule>> _handleTemporaryScheduleSave() async {
|
||||
final previousSchedules = state.getDailySchedulesForTemporary();
|
||||
if (state.tempSchedules.length == 1) {
|
||||
return previousSchedules.isNotEmpty
|
||||
? staffRepository.updateStaffSchedule(
|
||||
schedules: state.tempSchedules,
|
||||
)
|
||||
: staffRepository.createStaffSchedule(
|
||||
schedules: state.tempSchedules,
|
||||
);
|
||||
}
|
||||
|
||||
if (previousSchedules.isNotEmpty) {
|
||||
await staffRepository.deleteStaffSchedule(schedules: previousSchedules);
|
||||
}
|
||||
|
||||
return staffRepository.createStaffSchedule(
|
||||
schedules: state.tempSchedules,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onConfirmChanges(
|
||||
ScheduleEventSave event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: StateStatus.loading,
|
||||
selectedDates:
|
||||
state.prevSelectedDates.isNotEmpty ? state.prevSelectedDates : null,
|
||||
prevSelectedDates: [],
|
||||
),
|
||||
);
|
||||
|
||||
final isTempScheduleValid = await state.isValid;
|
||||
if (!isTempScheduleValid) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: StateStatus.error,
|
||||
scheduleErrorText: 'overlap_error'.tr(),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, DaySchedule>? schedules;
|
||||
try {
|
||||
schedules = await _handleTemporaryScheduleSave();
|
||||
} catch (except) {
|
||||
log(except.toString());
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: StateStatus.idle,
|
||||
isInEditMode: false,
|
||||
schedules: schedules,
|
||||
tempSchedules: [],
|
||||
),
|
||||
);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedSchedules: await state.findSchedulesForSelection(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onCancelChanges(
|
||||
ScheduleEventCancel event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isInEditMode: false,
|
||||
scheduleErrorText: '',
|
||||
tempSchedules: [],
|
||||
selectedDates:
|
||||
state.prevSelectedDates.isNotEmpty ? state.prevSelectedDates : null,
|
||||
prevSelectedDates: [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onDeleteSchedule(
|
||||
ScheduleEventDeleteSchedule event,
|
||||
Emitter<ScheduleState> emit,
|
||||
) async {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isInEditMode: false,
|
||||
status: StateStatus.loading,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, DaySchedule>? schedules;
|
||||
try {
|
||||
final result = staffRepository.deleteStaffSchedule(
|
||||
schedules: [event.schedule],
|
||||
);
|
||||
schedules = Map<String, DaySchedule>.from(state.schedules);
|
||||
|
||||
await result;
|
||||
schedules.remove(event.schedule.getIdKey());
|
||||
} catch (except) {
|
||||
log(except.toString());
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: StateStatus.idle,
|
||||
schedules: schedules,
|
||||
),
|
||||
);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedSchedules: await state.findSchedulesForSelection(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
part of 'schedule_bloc.dart';
|
||||
|
||||
@immutable
|
||||
sealed class ScheduleEvent {
|
||||
const ScheduleEvent();
|
||||
}
|
||||
|
||||
class ScheduleInitEvent extends ScheduleEvent {
|
||||
const ScheduleInitEvent();
|
||||
}
|
||||
|
||||
class ScheduleEventChangeMode extends ScheduleEvent {
|
||||
const ScheduleEventChangeMode({
|
||||
this.isInEditScheduleMode,
|
||||
this.isInScheduleAddMode,
|
||||
this.editedDate,
|
||||
this.isWeekly = false,
|
||||
});
|
||||
|
||||
final bool? isInEditScheduleMode;
|
||||
final bool? isInScheduleAddMode;
|
||||
final DateTime? editedDate;
|
||||
final bool isWeekly;
|
||||
}
|
||||
|
||||
class ScheduleEventSelectDates extends ScheduleEvent {
|
||||
const ScheduleEventSelectDates({required this.dates});
|
||||
|
||||
final List<DateTime> dates;
|
||||
}
|
||||
|
||||
class ScheduleEventAddSlot extends ScheduleEvent {
|
||||
const ScheduleEventAddSlot();
|
||||
}
|
||||
|
||||
class ScheduleEventRemoveSlot extends ScheduleEvent {
|
||||
const ScheduleEventRemoveSlot({
|
||||
required this.slot,
|
||||
required this.slotIndex,
|
||||
});
|
||||
|
||||
final ScheduleSlot slot;
|
||||
final int slotIndex;
|
||||
}
|
||||
|
||||
class ScheduleEventDeleteSchedule extends ScheduleEvent {
|
||||
const ScheduleEventDeleteSchedule({
|
||||
required this.schedule,
|
||||
});
|
||||
|
||||
final DaySchedule schedule;
|
||||
}
|
||||
|
||||
class ScheduleEventEditSlot extends ScheduleEvent {
|
||||
const ScheduleEventEditSlot({
|
||||
required this.slot,
|
||||
required this.slotIndex,
|
||||
this.start,
|
||||
this.end,
|
||||
});
|
||||
|
||||
final ScheduleSlot slot;
|
||||
final int slotIndex;
|
||||
final DateTime? start;
|
||||
final DateTime? end;
|
||||
}
|
||||
|
||||
class ScheduleEventSave extends ScheduleEvent {
|
||||
const ScheduleEventSave();
|
||||
}
|
||||
|
||||
class ScheduleEventCancel extends ScheduleEvent {
|
||||
const ScheduleEventCancel();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
part of 'schedule_bloc.dart';
|
||||
|
||||
@immutable
|
||||
class ScheduleState {
|
||||
const ScheduleState({
|
||||
this.schedules = const {},
|
||||
this.tempSchedules = const [],
|
||||
this.selectedDates = const [],
|
||||
this.prevSelectedDates = const [],
|
||||
this.selectedSchedules = const [],
|
||||
this.status = StateStatus.idle,
|
||||
this.isInEditMode = false,
|
||||
this.isScheduleAddMode = false,
|
||||
this.scheduleErrorText = '',
|
||||
});
|
||||
|
||||
final Map<String, DaySchedule> schedules;
|
||||
final List<DaySchedule> tempSchedules;
|
||||
final List<DateTime> selectedDates;
|
||||
final List<DateTime> prevSelectedDates;
|
||||
final List<DaySchedule> selectedSchedules;
|
||||
final StateStatus status;
|
||||
final bool isInEditMode;
|
||||
final bool isScheduleAddMode;
|
||||
final String scheduleErrorText;
|
||||
|
||||
Future<bool> get isValid async {
|
||||
if (tempSchedules.isEmpty) return false;
|
||||
bool isValid = true;
|
||||
|
||||
for (int i = 0; i < tempSchedules.length; i++) {
|
||||
isValid = tempSchedules[i].isValid;
|
||||
|
||||
if (!isValid) return isValid;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
ScheduleState copyWith({
|
||||
Map<String, DaySchedule>? schedules,
|
||||
List<DaySchedule>? tempSchedules,
|
||||
List<DateTime>? selectedDates,
|
||||
List<DateTime>? prevSelectedDates,
|
||||
List<DaySchedule>? selectedSchedules,
|
||||
StateStatus? status,
|
||||
bool? isInEditMode,
|
||||
bool? isScheduleAddMode,
|
||||
String? scheduleErrorText,
|
||||
}) {
|
||||
return ScheduleState(
|
||||
schedules: schedules ?? this.schedules,
|
||||
tempSchedules: tempSchedules ?? this.tempSchedules,
|
||||
selectedDates: selectedDates ?? this.selectedDates,
|
||||
prevSelectedDates: prevSelectedDates ?? this.prevSelectedDates,
|
||||
selectedSchedules: selectedSchedules ?? this.selectedSchedules,
|
||||
status: status ?? this.status,
|
||||
isInEditMode: isInEditMode ?? this.isInEditMode,
|
||||
isScheduleAddMode: isScheduleAddMode ?? this.isScheduleAddMode,
|
||||
scheduleErrorText: scheduleErrorText ?? this.scheduleErrorText,
|
||||
);
|
||||
}
|
||||
|
||||
DaySchedule? findScheduleForDate(DateTime date) {
|
||||
return schedules[date.getDayDateId()] ?? schedules[date.getWeekdayId()];
|
||||
}
|
||||
|
||||
bool containsScheduleForDate(DateTime date) {
|
||||
return schedules.containsKey(date.getDayDateId()) ||
|
||||
schedules.containsKey(date.getWeekdayId());
|
||||
}
|
||||
|
||||
Future<List<DaySchedule>> findSchedulesForSelection() async {
|
||||
return [
|
||||
for (final date in selectedDates)
|
||||
if (schedules.containsKey(date.getDayDateId()))
|
||||
schedules[date.getDayDateId()]!
|
||||
else if (schedules.containsKey(date.getWeekdayId()))
|
||||
schedules[date.getWeekdayId()]!.copyWith(
|
||||
date: date,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<DaySchedule> getDailySchedulesForTemporary() {
|
||||
return [
|
||||
for (final day in tempSchedules)
|
||||
if (schedules.containsKey(day.getIdKey())) schedules[day.getIdKey()]!
|
||||
];
|
||||
}
|
||||
|
||||
bool isRangeContainsSchedules(List<DateTime> dates) {
|
||||
return dates.any(
|
||||
(date) => schedules.containsKey(date.getDayDateId()),
|
||||
);
|
||||
}
|
||||
|
||||
DaySchedule getScheduleForDate({
|
||||
required DateTime date,
|
||||
required bool isWeekly,
|
||||
}) {
|
||||
final daySchedule =
|
||||
schedules[isWeekly ? date.getWeekdayId() : date.getDayDateId()];
|
||||
|
||||
return daySchedule ??
|
||||
DaySchedule(
|
||||
date: date,
|
||||
slots: const [],
|
||||
isWeekly: isWeekly,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, DaySchedule> mergeSchedules() {
|
||||
final schedules = Map<String, DaySchedule>.from(this.schedules);
|
||||
|
||||
for (final schedule in tempSchedules) {
|
||||
schedules[schedule.getIdKey()] = schedule;
|
||||
}
|
||||
|
||||
return schedules;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user