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>
114 lines
3.8 KiB
Dart
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);
|
|
}
|
|
}
|
|
}
|