Backend: Rewrote EventListAPI to query per-type with DB-level LIMIT instead of loading all 734 events into memory. Added slim serializer (32KB vs 154KB). Added DB indexes on event_type_id and pincode. Frontend: Category chips now filter locally from _allEvents (instant, no API call). Top Events and category sections always show all types regardless of selected category. Added TTL caching for event types (30min) and events (5min). Reduced API timeout from 30s to 10s. Added memCacheHeight to all CachedNetworkImage widgets. Batched setState calls from 5 to 2 during startup. Cached _eventDates getter. Switched baseUrl to em.eventifyplus.com (Django via Nginx+SSL). Added initialEvent param to LearnMoreScreen for instant detail views. Resolved relative media URLs for category icons. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
108 lines
3.9 KiB
Dart
108 lines
3.9 KiB
Dart
// lib/features/events/services/events_service.dart
|
|
import '../../../core/api/api_client.dart';
|
|
import '../../../core/api/api_endpoints.dart';
|
|
import '../models/event_models.dart';
|
|
|
|
class EventsService {
|
|
final ApiClient _api = ApiClient();
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// In-memory caches with TTL
|
|
// ---------------------------------------------------------------------------
|
|
static List<EventTypeModel>? _cachedTypes;
|
|
static DateTime? _typesCacheTime;
|
|
static const _typesCacheTTL = Duration(minutes: 30);
|
|
|
|
static List<EventModel>? _cachedAllEvents;
|
|
static DateTime? _eventsCacheTime;
|
|
static const _eventsCacheTTL = Duration(minutes: 5);
|
|
|
|
/// Get event types (POST to /events/type-list/)
|
|
/// Cached for 30 minutes since event types rarely change.
|
|
Future<List<EventTypeModel>> getEventTypes() async {
|
|
if (_cachedTypes != null &&
|
|
_typesCacheTime != null &&
|
|
DateTime.now().difference(_typesCacheTime!) < _typesCacheTTL) {
|
|
return _cachedTypes!;
|
|
}
|
|
|
|
final res = await _api.post(ApiEndpoints.eventTypes, requiresAuth: false);
|
|
final list = <EventTypeModel>[];
|
|
final data = res['event_types'] ?? res;
|
|
if (data is List) {
|
|
for (final e in data) {
|
|
if (e is Map<String, dynamic>) list.add(EventTypeModel.fromJson(e));
|
|
}
|
|
}
|
|
|
|
_cachedTypes = list;
|
|
_typesCacheTime = DateTime.now();
|
|
return list;
|
|
}
|
|
|
|
/// Get events filtered by pincode with pagination.
|
|
/// [page] starts at 1. [pageSize] defaults to 50.
|
|
/// Returns a list of events for the requested page.
|
|
Future<List<EventModel>> getEventsByPincode(String pincode, {int page = 1, int pageSize = 50, int perType = 5}) async {
|
|
// Use cache for 'all' pincode queries (first page only for initial load)
|
|
if (pincode == 'all' &&
|
|
page == 1 &&
|
|
_cachedAllEvents != null &&
|
|
_eventsCacheTime != null &&
|
|
DateTime.now().difference(_eventsCacheTime!) < _eventsCacheTTL) {
|
|
return _cachedAllEvents!;
|
|
}
|
|
|
|
final Map<String, dynamic> body = {'pincode': pincode, 'page': page, 'page_size': pageSize};
|
|
// Diverse mode: fetch a few events per type so all categories are represented
|
|
if (perType > 0 && page == 1) body['per_type'] = perType;
|
|
|
|
final res = await _api.post(
|
|
ApiEndpoints.eventsByPincode,
|
|
body: body,
|
|
requiresAuth: false,
|
|
);
|
|
final list = <EventModel>[];
|
|
final events = res['events'] ?? res['data'] ?? [];
|
|
if (events is List) {
|
|
for (final e in events) {
|
|
if (e is Map<String, dynamic>) list.add(EventModel.fromJson(Map<String, dynamic>.from(e)));
|
|
}
|
|
}
|
|
|
|
if (pincode == 'all' && page == 1) {
|
|
_cachedAllEvents = list;
|
|
_eventsCacheTime = DateTime.now();
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/// Event details
|
|
Future<EventModel> getEventDetails(int eventId) async {
|
|
final res = await _api.post(ApiEndpoints.eventDetails, body: {'event_id': eventId}, requiresAuth: true);
|
|
return EventModel.fromJson(Map<String, dynamic>.from(res));
|
|
}
|
|
|
|
/// Events by month and year for calendar (POST to /events/events-by-month-year/)
|
|
Future<Map<String, dynamic>> getEventsByMonthYear(String month, int year) async {
|
|
final res = await _api.post(ApiEndpoints.eventsByMonth, body: {'month': month, 'year': year}, requiresAuth: false);
|
|
return res;
|
|
}
|
|
|
|
/// Convenience: get events for a specific date (YYYY-MM-DD).
|
|
/// Uses the cached events list when available to avoid redundant API calls.
|
|
Future<List<EventModel>> getEventsForDate(String date) async {
|
|
final all = await getEventsByPincode('all');
|
|
return all.where((e) {
|
|
try {
|
|
return e.startDate == date ||
|
|
e.endDate == date ||
|
|
(DateTime.parse(e.startDate).isBefore(DateTime.parse(date)) &&
|
|
DateTime.parse(e.endDate).isAfter(DateTime.parse(date)));
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}).toList();
|
|
}
|
|
}
|