import 'package:dio/dio.dart'; import 'package:fintracker_api/fintracker_api.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/api/api_client.dart'; import '../../core/cache/cached.dart'; /// Every provider below returns `Cached`, not `T`: `HomePage` reads /// `.data` for the tiles and `.fetchedAt` (via `oldestFetch`) for the one /// offline banner at the top. See `docs/ai/offline-cache.md`. final netWorthBreakdownProvider = FutureProvider.autoDispose>((ref) async { final r = await ref.watch(apiProvider).getNetworthApi().networthBreakdown(); return r.cached; }); /// Daily net worth for the last 365 days. final netWorthSeriesProvider = FutureProvider.autoDispose>>((ref) async { final now = DateTime.now(); final r = await ref.watch(apiProvider).getNetworthApi().networthSeries( from: now.subtract(const Duration(days: 365)), to: now, ); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); /// The current (partial) month's cashflow, or null before any data exists. final cashflowThisMonthProvider = FutureProvider.autoDispose>((ref) async { final r = await ref.watch(apiProvider).getCashflowApi().cashflowMonthly(months: 1); final rows = r.data ?? const []; return Cached(rows.isEmpty ? null : rows.last, fetchedAt: r.extra['fetchedAt'] as DateTime?); }); /// The last 12 months, for the income-vs-expense bar chart. final cashflowLast12Provider = FutureProvider.autoDispose>>((ref) async { final r = await ref.watch(apiProvider).getCashflowApi().cashflowMonthly(months: 12); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); final runwayProvider = FutureProvider.autoDispose>((ref) async { final r = await ref.watch(apiProvider).getCashflowApi().cashflowRunway(); return r.cached; }); /// When the metric tables were last rebuilt; null before the first refresh. final metricsStatusProvider = FutureProvider.autoDispose>((ref) async { try { final r = await ref.watch(apiProvider).getMetricsApi().metricsStatus(); return r.cached; } on DioException catch (e) { if (e.response?.statusCode == 404) return const Cached(null); rethrow; } }); final dataQualityProvider = FutureProvider.autoDispose>>((ref) async { final r = await ref.watch(apiProvider).getMetricsApi().metricsDataQuality(); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); /// Every dashboard provider, refreshed together after a manual /// `POST /metrics/refresh` or a pull-to-refresh. void invalidateHomeProviders(WidgetRef ref) { ref.invalidate(netWorthBreakdownProvider); ref.invalidate(netWorthSeriesProvider); ref.invalidate(cashflowThisMonthProvider); ref.invalidate(cashflowLast12Provider); ref.invalidate(runwayProvider); ref.invalidate(metricsStatusProvider); ref.invalidate(dataQualityProvider); ref.invalidate(portfolioSummaryHomeProvider); } /// The investment side of the dashboard: one scope-wide summary, `all` by default. /// Null when the ledger is empty, which is the normal state before a broker sync. final portfolioSummaryHomeProvider = FutureProvider.autoDispose>((ref) async { try { final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsSummary(); return r.cached; } on DioException catch (e) { if (e.response?.statusCode == 404) return const Cached(null); rethrow; } });