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'; import 'benchmarks_card.dart' show benchmarkRowsProvider; /// The reporting unit every portfolio screen is scoped to: `all`, `account:` or /// `portfolio:`. Held in one place so switching it on Позиции also switches Аллокация /// and the instrument card — three screens showing different scopes would be a trap. final scopeProvider = StateProvider((ref) => 'all'); /// A small cross-screen selector (Портфель, Доходы, Налоги, the goal dialog), not a screen's /// own primary read — left un-cached per `docs/ai/offline-cache.md`. The underlying `GET` /// still gets served from the shared cache transparently on a network failure; this provider /// just does not surface `fetchedAt` for a banner. final scopesProvider = FutureProvider.autoDispose>((ref) async { final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsScopes(); return r.data ?? const []; }); /// See `docs/ai/offline-cache.md`. final portfolioSummaryProvider = FutureProvider.autoDispose>((ref) async { final scope = ref.watch(scopeProvider); final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsSummary(scope: scope); return r.cached; }); final holdingsProvider = FutureProvider.autoDispose>>((ref) async { final scope = ref.watch(scopeProvider); final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsHoldings(scope: scope); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); final portfolioReturnsProvider = FutureProvider.autoDispose>>((ref) async { final scope = ref.watch(scopeProvider); final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsReturns(scope: scope); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); final allocationProvider = FutureProvider.autoDispose>>((ref) async { final scope = ref.watch(scopeProvider); final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsAllocation(scope: scope); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); /// Daily portfolio value for the last year, for the chart on Позиции. final valueSeriesProvider = FutureProvider.autoDispose>>((ref) async { final scope = ref.watch(scopeProvider); final now = DateTime.now(); final r = await ref.watch(apiProvider).getAnalyticsApi().analyticsValueSeries( scope: scope, from: now.subtract(const Duration(days: 365)), to: now, ); return Cached(r.data ?? const [], fetchedAt: r.extra['fetchedAt'] as DateTime?); }); final instrumentProvider = FutureProvider.autoDispose.family, int>((ref, instrumentId) async { final scope = ref.watch(scopeProvider); final r = await ref .watch(apiProvider) .getInstrumentsApi() .instrumentsGet(instrumentId: instrumentId, scope: scope); return r.cached; }); /// Every portfolio provider, refreshed together after a metrics rebuild or a pull-to-refresh. void invalidatePortfolioProviders(WidgetRef ref) { ref.invalidate(portfolioSummaryProvider); ref.invalidate(holdingsProvider); ref.invalidate(portfolioReturnsProvider); ref.invalidate(allocationProvider); ref.invalidate(valueSeriesProvider); ref.invalidate(instrumentProvider); // the benchmark block lives on Позиции and is scoped the same way ref.invalidate(benchmarkRowsProvider); }