feat: Improve dynamic menu fetching with enhanced error handling and cache fallback

This commit is contained in:
Vaibhav Surve 2025-09-09 10:47:22 +05:30
parent 99bd26942c
commit a02887845b

View File

@ -13,19 +13,29 @@ class DynamicMenuController extends GetxController {
final RxList<MenuItem> menuItems = <MenuItem>[].obs; final RxList<MenuItem> menuItems = <MenuItem>[].obs;
Timer? _autoRefreshTimer; Timer? _autoRefreshTimer;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// Load cached menus immediately (so user doesnt see empty state)
final cachedMenus = LocalStorage.getMenus();
if (cachedMenus.isNotEmpty) {
menuItems.assignAll(cachedMenus);
logSafe("Loaded ${cachedMenus.length} menus from cache at startup");
}
// Fetch from API in background
fetchMenu(); fetchMenu();
/// Auto refresh every 5 minutes (adjust as needed) // Auto refresh every 15 minutes
_autoRefreshTimer = Timer.periodic( _autoRefreshTimer = Timer.periodic(
const Duration(minutes: 15), const Duration(minutes: 15),
(_) => fetchMenu(), (_) => fetchMenu(),
); );
} }
/// Fetch dynamic menu from API with error and local storage support /// Fetch dynamic menu from API with cache fallback
Future<void> fetchMenu() async { Future<void> fetchMenu() async {
isLoading.value = true; isLoading.value = true;
hasError.value = false; hasError.value = false;
@ -34,43 +44,37 @@ class DynamicMenuController extends GetxController {
try { try {
final responseData = await ApiService.getMenuApi(); final responseData = await ApiService.getMenuApi();
if (responseData != null) { if (responseData != null) {
// Directly parse full JSON into MenuResponse // Parse JSON into MenuResponse
final menuResponse = MenuResponse.fromJson(responseData); final menuResponse = MenuResponse.fromJson(responseData);
menuItems.assignAll(menuResponse.data); menuItems.assignAll(menuResponse.data);
// Save menus for offline use // Save for offline use
await LocalStorage.setMenus(menuItems); await LocalStorage.setMenus(menuItems);
logSafe("Menu loaded from API with ${menuItems.length} items"); logSafe("Menu loaded from API with ${menuItems.length} items");
} else { } else {
// If API fails, load from cache _handleApiFailure("Menu API returned null response");
final cachedMenus = LocalStorage.getMenus();
if (cachedMenus.isNotEmpty) {
menuItems.assignAll(cachedMenus);
logSafe("Loaded menus from cache: ${menuItems.length} items");
} else {
hasError.value = true;
errorMessage.value = "Failed to fetch menu";
menuItems.clear();
}
} }
} catch (e) { } catch (e) {
logSafe("Menu fetch exception: $e", level: LogLevel.error); _handleApiFailure("Menu fetch exception: $e");
} finally {
isLoading.value = false;
}
}
void _handleApiFailure(String logMessage) {
logSafe(logMessage, level: LogLevel.error);
// On error, load cached menus
final cachedMenus = LocalStorage.getMenus(); final cachedMenus = LocalStorage.getMenus();
if (cachedMenus.isNotEmpty) { if (cachedMenus.isNotEmpty) {
menuItems.assignAll(cachedMenus); menuItems.assignAll(cachedMenus);
logSafe("Loaded menus from cache after error: ${menuItems.length}"); errorMessage.value = "⚠️ Using offline menus (latest sync failed)";
logSafe("Loaded ${menuItems.length} menus from cache after failure");
} else { } else {
hasError.value = true; hasError.value = true;
errorMessage.value = e.toString(); errorMessage.value = "❌ Unable to load menus. Please try again later.";
menuItems.clear(); menuItems.clear();
} }
} finally {
isLoading.value = false;
}
} }
bool isMenuAllowed(String menuName) { bool isMenuAllowed(String menuName) {
@ -80,7 +84,7 @@ class DynamicMenuController extends GetxController {
@override @override
void onClose() { void onClose() {
_autoRefreshTimer?.cancel(); // clean up timer _autoRefreshTimer?.cancel();
super.onClose(); super.onClose();
} }
} }