payfrit-app/lib/services/preload_cache.dart
John Mizerek c4792189dd App Store Version 2: Beacon scanning, preload caching, business selector
Features:
- Beacon scanner service for detecting nearby beacons
- Beacon cache for offline-first beacon resolution
- Preload cache for instant menu display
- Business selector screen for multi-location support
- Rescan button widget for quick beacon refresh
- Sign-in dialog for guest checkout flow
- Task type model for server tasks

Improvements:
- Enhanced menu browsing with category filtering
- Improved cart view with better modifier display
- Order history with detailed order tracking
- Chat screen improvements
- Better error handling in API service

Fixes:
- CashApp payment return crash fix
- Modifier nesting issues resolved
- Auto-expand modifier groups

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 19:51:54 -08:00

114 lines
3.8 KiB
Dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'api.dart';
import '../models/task_type.dart';
/// Centralized preload cache for app startup optimization
class PreloadCache {
static const _keyTaskTypes = 'preload_task_types';
static const _keyTaskTypesTimestamp = 'preload_task_types_ts';
static const _cacheDuration = Duration(hours: 12);
// In-memory cache for current session
static Map<int, List<TaskType>> _taskTypesCache = {};
/// Preload all cacheable data during splash
/// Note: Task types require a business ID, so they're loaded on-demand per business
static Future<void> preloadAll() async {
debugPrint('[PreloadCache] Preload cache initialized');
// Future: Add any global preloads here
}
/// Get or fetch task types for a business
static Future<List<TaskType>> getTaskTypes(int businessId) async {
// Check in-memory cache first
if (_taskTypesCache.containsKey(businessId)) {
return _taskTypesCache[businessId]!;
}
// Check disk cache
final cached = await _loadTaskTypesFromDisk(businessId);
if (cached != null) {
_taskTypesCache[businessId] = cached;
// Refresh in background
_refreshTaskTypesInBackground(businessId);
return cached;
}
// Fetch from server
final types = await Api.getTaskTypes(businessId: businessId);
_taskTypesCache[businessId] = types;
await _saveTaskTypesToDisk(businessId, types);
return types;
}
/// Preload task types for a specific business (call when entering a business)
static Future<void> preloadTaskTypes(int businessId) async {
if (_taskTypesCache.containsKey(businessId)) return;
try {
final types = await Api.getTaskTypes(businessId: businessId);
_taskTypesCache[businessId] = types;
await _saveTaskTypesToDisk(businessId, types);
debugPrint('[PreloadCache] Preloaded ${types.length} task types for business $businessId');
} catch (e) {
debugPrint('[PreloadCache] Failed to preload task types: $e');
}
}
static void _refreshTaskTypesInBackground(int businessId) {
Api.getTaskTypes(businessId: businessId).then((types) {
_taskTypesCache[businessId] = types;
_saveTaskTypesToDisk(businessId, types);
}).catchError((e) {
debugPrint('[PreloadCache] Task types refresh failed: $e');
});
}
static Future<List<TaskType>?> _loadTaskTypesFromDisk(int businessId) async {
final prefs = await SharedPreferences.getInstance();
final key = '${_keyTaskTypes}_$businessId';
final tsKey = '${_keyTaskTypesTimestamp}_$businessId';
final ts = prefs.getInt(tsKey);
final data = prefs.getString(key);
if (ts == null || data == null) return null;
if (DateTime.now().difference(DateTime.fromMillisecondsSinceEpoch(ts)) > _cacheDuration) {
return null;
}
try {
final list = jsonDecode(data) as List;
return list.map((j) => TaskType.fromJson(j as Map<String, dynamic>)).toList();
} catch (e) {
return null;
}
}
static Future<void> _saveTaskTypesToDisk(int businessId, List<TaskType> types) async {
final prefs = await SharedPreferences.getInstance();
final key = '${_keyTaskTypes}_$businessId';
final tsKey = '${_keyTaskTypesTimestamp}_$businessId';
final data = types.map((t) => {
'tasktypeid': t.taskTypeId,
'tasktypename': t.taskTypeName,
'tasktypeicon': t.taskTypeIcon,
}).toList();
await prefs.setString(key, jsonEncode(data));
await prefs.setInt(tsKey, DateTime.now().millisecondsSinceEpoch);
}
/// Clear all caches
static Future<void> clearAll() async {
_taskTypesCache.clear();
final prefs = await SharedPreferences.getInstance();
final keys = prefs.getKeys().where((k) => k.startsWith('preload_'));
for (final key in keys) {
await prefs.remove(key);
}
}
}