Refactor calendar event loading: replace parameters with LoadEventsParam class and implement memory caching for improved performance

This commit is contained in:
mohammad
2025-07-17 11:20:32 +03:00
parent c6729f476f
commit c9b8fbb0c2
8 changed files with 100 additions and 47 deletions

View File

@ -1,4 +1,5 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/api_exception.dart';
@ -13,17 +14,17 @@ class RemoteCalendarService implements CalendarSystemService {
@override @override
Future<CalendarEventsResponse> getCalendarEvents({ Future<CalendarEventsResponse> getCalendarEvents({
required String spaceId, required LoadEventsParam params,
required String month,
required String year,
}) async { }) async {
final month = params.startDate.month.toString().padLeft(2, '0');
final year = params.startDate.year.toString();
try { try {
return await _httpService.get<CalendarEventsResponse>( return await _httpService.get<CalendarEventsResponse>(
path: ApiEndpoints.getBookings path: ApiEndpoints.getBookings
.replaceAll('{mm}', month) .replaceAll('{mm}', month)
.replaceAll('{yyyy}', year) .replaceAll('{yyyy}', year)
.replaceAll('{space}', spaceId), .replaceAll('{space}', params.id),
expectedResponseModel: (json) { expectedResponseModel: (json) {
return CalendarEventsResponse.fromJson(json as Map<String, dynamic>); return CalendarEventsResponse.fromJson(json as Map<String, dynamic>);
}, },

View File

@ -0,0 +1,28 @@
import 'package:equatable/equatable.dart';
class LoadEventsParam extends Equatable {
final DateTime startDate;
final DateTime endDate;
final String id;
const LoadEventsParam({
required this.startDate,
required this.endDate,
required this.id,
});
@override
List<Object?> get props => [startDate, endDate, id];
LoadEventsParam copyWith({
DateTime? startDate,
DateTime? endDate,
String? id,
}) {
return LoadEventsParam(
startDate: startDate ?? this.startDate,
endDate: endDate ?? this.endDate,
id: id ?? this.id,
);
}
}

View File

@ -1,9 +1,8 @@
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
abstract class CalendarSystemService { abstract class CalendarSystemService {
Future<CalendarEventsResponse> getCalendarEvents({ Future<CalendarEventsResponse> getCalendarEvents({
required String spaceId, required LoadEventsParam params,
required String month,
required String year,
}); });
} }

View File

@ -2,8 +2,10 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:calendar_view/calendar_view.dart'; import 'package:calendar_view/calendar_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart';
part 'events_event.dart'; part 'events_event.dart';
part 'events_state.dart'; part 'events_state.dart';
@ -11,12 +13,14 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
final EventController eventController = EventController(); final EventController eventController = EventController();
final CalendarSystemService calendarService; final CalendarSystemService calendarService;
final Map<String, List<CalendarEventData>> _eventsCache = {}; final Map<String, List<CalendarEventData>> _eventsCache = {};
final MemoryBookableSpaceService memoryService;
String? _lastSpaceId;
int? _lastMonth;
int? _lastYear;
CalendarEventsBloc({required this.calendarService}) : super(EventsInitial()) {
CalendarEventsBloc({
required this.calendarService,
required this.memoryService,
}) : super(EventsInitial()) {
on<LoadEvents>(_onLoadEvents); on<LoadEvents>(_onLoadEvents);
on<AddEvent>(_onAddEvent); on<AddEvent>(_onAddEvent);
on<DisposeResources>(_onDisposeResources); on<DisposeResources>(_onDisposeResources);
@ -26,45 +30,32 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
LoadEvents event, LoadEvents event,
Emitter<CalendarEventState> emit, Emitter<CalendarEventState> emit,
) async { ) async {
final month = event.weekEnd.month; final param = event.param;
final year = event.weekEnd.year; final month = param.endDate.month;
final cacheKey = final year = param.endDate.year;
'${event.spaceId}-$year-${month.toString().padLeft(2, '0')}'; final spaceId = param.id;
final cachedEvents = memoryService.getEvents(spaceId, year, month);
if (_eventsCache.containsKey(cacheKey)) { if (cachedEvents != null) {
final cachedEvents = _eventsCache[cacheKey]!;
eventController.addAll(cachedEvents); eventController.addAll(cachedEvents);
emit(EventsLoaded( emit(EventsLoaded(
events: cachedEvents, events: cachedEvents,
spaceId: event.spaceId, spaceId: spaceId,
month: month, month: month,
year: year, year: year,
)); ));
return; return;
} }
if (_lastSpaceId == event.spaceId &&
_lastMonth == month &&
_lastYear == year) {
return;
}
emit(EventsLoading()); emit(EventsLoading());
try { try {
final response = await calendarService.getCalendarEvents( final response = await calendarService.getCalendarEvents(params: param);
month: month.toString().padLeft(2, '0'),
year: year.toString(),
spaceId: event.spaceId,
);
final events = response.data.map(_toCalendarEventData).toList(); final events = response.data.map(_toCalendarEventData).toList();
_eventsCache[cacheKey] = events; memoryService.setEvents(spaceId, year, month, events);
eventController.addAll(events); eventController.addAll(events);
_lastSpaceId = event.spaceId;
_lastMonth = month;
_lastYear = year;
emit(EventsLoaded( emit(EventsLoaded(
events: events, events: events,
spaceId: event.spaceId, spaceId: spaceId,
month: month, month: month,
year: year, year: year,
)); ));

View File

@ -6,17 +6,11 @@ abstract class CalendarEventsEvent {
} }
class LoadEvents extends CalendarEventsEvent { class LoadEvents extends CalendarEventsEvent {
final String spaceId; final LoadEventsParam param;
final DateTime weekStart; const LoadEvents(this.param);
final DateTime weekEnd;
const LoadEvents({
required this.spaceId,
required this.weekStart,
required this.weekEnd,
});
} }
class AddEvent extends CalendarEventsEvent { class AddEvent extends CalendarEventsEvent {
final CalendarEventData event; final CalendarEventData event;
const AddEvent(this.event); const AddEvent(this.event);

View File

@ -0,0 +1,35 @@
import 'package:calendar_view/calendar_view.dart';
class MemoryBookableSpaceService {
final Map<String, List<CalendarEventData>> _eventsCache = {};
List<CalendarEventData>? getEvents(String spaceId, int year, int month) {
final key = _generateKey(spaceId, year, month);
return _eventsCache[key];
}
void setEvents(
String spaceId,
int year,
int month,
List<CalendarEventData> events,
) {
final key = _generateKey(spaceId, year, month);
_eventsCache[key] = events;
}
void addEvent(String spaceId, int year, int month, CalendarEventData event) {
final key = _generateKey(spaceId, year, month);
final events = _eventsCache[key] ?? [];
events.add(event);
_eventsCache[key] = events;
}
void clear() {
_eventsCache.clear();
}
String _generateKey(String spaceId, int year, int month) {
return '$spaceId-$year-${month.toString().padLeft(2, '0')}';
}
}

View File

@ -2,11 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:calendar_view/calendar_view.dart'; import 'package:calendar_view/calendar_view.dart';
import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart'; import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_bloc.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_bloc.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_event.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_event.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
@ -46,9 +48,11 @@ class _BookingPageState extends State<BookingPage> {
if (selectedRoom != null) { if (selectedRoom != null) {
context.read<CalendarEventsBloc>().add( context.read<CalendarEventsBloc>().add(
LoadEvents( LoadEvents(
spaceId: selectedRoom.uuid, LoadEventsParam(
weekStart: dateState.weekStart, startDate: dateState.weekStart,
weekEnd: dateState.weekStart.add(const Duration(days: 6)), endDate: dateState.weekStart.add(const Duration(days: 6)),
id: selectedRoom.uuid,
),
), ),
); );
} }
@ -62,6 +66,7 @@ class _BookingPageState extends State<BookingPage> {
BlocProvider(create: (_) => DateSelectionBloc()), BlocProvider(create: (_) => DateSelectionBloc()),
BlocProvider( BlocProvider(
create: (_) => CalendarEventsBloc( create: (_) => CalendarEventsBloc(
memoryService: MemoryBookableSpaceService(),
calendarService: RemoteCalendarService( calendarService: RemoteCalendarService(
HTTPService(), HTTPService(),
), ),

View File

@ -24,7 +24,7 @@ class WeeklyCalendarPage extends StatelessWidget {
}); });
static const double timeLineWidth = 65; static const double timeLineWidth = 65;
static const int totalDays = 7; static const int totalDays = 7;
static const double dayColumnWidth = 220; // or any width you want static const double dayColumnWidth = 220;
final double calendarContentWidth = final double calendarContentWidth =
timeLineWidth + (totalDays * dayColumnWidth); timeLineWidth + (totalDays * dayColumnWidth);