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); }