blob: 27bd62f30aa6d9661e3a80acc0a3e02b4c60f23b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
import 'package:cookie_jar/cookie_jar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../api/dio_client.dart';
import '../api/dio_player_api_client.dart';
import '../api/player_api_client.dart';
import '../navigation_key.dart';
/// Base URL for the player-server API, resolved at compile time via
/// the PLAYER_BASE_URL environment variable (or the default below).
///
/// Declared as a package-level identifier (no underscore) so it can be
/// shared by [publicApiClientProvider] in [public_api_client_provider.dart].
/// The value is set once at compile time and never changes at runtime,
/// making it safe to share across providers.
///
/// In production this is injected via `--dart-define=PLAYER_BASE_URL=...`.
/// The default points to the Android emulator host loopback address so the
/// app is runnable out-of-the-box without extra configuration.
const kPlayerBaseUrl = String.fromEnvironment(
'PLAYER_BASE_URL',
defaultValue: 'http://10.0.2.2:8080',
);
/// Provides the production [TokenStorage] backed by the OS keychain.
///
/// Riverpod keeps a single instance for the lifetime of [ProviderScope], so
/// there is exactly one [SecureTokenStorage] in the app — consistent with the
/// singleton intent of flutter_secure_storage.
final tokenStorageProvider = Provider<TokenStorage>((ref) {
return SecureTokenStorage();
});
/// Provides a fully configured [PlayerApiClient] wired with bearer-token
/// injection and global 401 → /login redirect.
///
/// Depends on [tokenStorageProvider] and [navigatorKey] (both singletons) so
/// the same [Dio] instance is reused for every call site — avoiding redundant
/// interceptor stacks.
// Single shared DioClient instance: cached as a Riverpod Provider so both the
// API client and the cookie-jar provider observe the same cookie store. The
// API client uses Dio; ExoPlayer/video_player/CachedNetworkImage bypass Dio
// and need the cookie jar to attach the session cookie manually.
final _dioClientProvider = Provider<DioClient>((ref) {
final storage = ref.watch(tokenStorageProvider);
return DioClient(
baseUrl: Uri.parse(kPlayerBaseUrl),
storage: storage,
// Share the navigator key with go_router so 401 redirects go through the
// correct router instance rather than the raw Navigator.
navigatorKey: navigatorKey,
loginRoute: '/login',
);
});
final apiClientProvider = Provider<PlayerApiClient>((ref) {
// Use DioPlayerApiClient — the concrete implementation that maps every
// PlayerApiClient method to a real HTTP call via Dio. The base class now
// acts as the public interface (dependency inversion); callers depend on
// PlayerApiClient, not on this concrete class.
return DioPlayerApiClient(dio: ref.watch(_dioClientProvider).dio);
});
/// Provides the same [CookieJar] backing [apiClientProvider]'s Dio stack so
/// non-Dio HTTP clients (ExoPlayer, video_player, CachedNetworkImage) can
/// authenticate streaming/thumbnail requests using the same session cookie.
final cookieJarProvider = Provider<CookieJar>((ref) {
return ref.watch(_dioClientProvider).cookieJar;
});
|