import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/client.dart'; import '../api/endpoints/library.dart'; import '../auth/auth_provider.dart'; import '../models/album.dart'; import '../models/artist.dart'; import '../models/home_data.dart'; import '../models/track.dart'; /// Shared authenticated dio. This is the ONLY place tokenResolver is wired /// to the secure-storage `session_token` read — every other endpoint /// awaits `dioProvider.future` rather than constructing its own dio. /// /// Riverpod will invalidate any FutureProvider that watches this when /// the server URL changes; auth state changes don't need to invalidate /// the provider because the resolver re-reads the token on every request. final dioProvider = FutureProvider((ref) async { final url = await ref.watch(serverUrlProvider.future); if (url == null || url.isEmpty) { throw StateError('no server URL set'); } final storage = ref.watch(secureStorageProvider); return ApiClient.buildDio( baseUrl: url, tokenResolver: () async => storage.read(key: 'session_token'), on401: () async => ref.read(authControllerProvider.notifier).clearSession(), ); }); final libraryApiProvider = FutureProvider((ref) async { return LibraryApi(await ref.watch(dioProvider.future)); }); final homeProvider = FutureProvider((ref) async { return (await ref.watch(libraryApiProvider.future)).getHome(); }); final artistProvider = FutureProvider.family((ref, id) async { return (await ref.watch(libraryApiProvider.future)).getArtist(id); }); final artistAlbumsProvider = FutureProvider.family, String>((ref, id) async { return (await ref.watch(libraryApiProvider.future)).getArtistAlbums(id); }); final artistTracksProvider = FutureProvider.family, String>((ref, id) async { return (await ref.watch(libraryApiProvider.future)).getArtistTracks(id); }); final albumProvider = FutureProvider.family<({AlbumRef album, List tracks}), String>( (ref, id) async { return (await ref.watch(libraryApiProvider.future)).getAlbum(id); }, );