feat(app): смена сервера API и очистка данных устройства при выходе
Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
This commit is contained in:
@@ -1,11 +1,58 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Backend origin. Override at build time:
|
||||
const _prefsKey = 'api_base_url';
|
||||
|
||||
/// Backend origin when the user has not chosen one. Override at build time:
|
||||
/// `flutter run --dart-define=API_BASE_URL=https://fin.example.com`.
|
||||
/// On web the default is the page's own origin (Caddy serves app and API together).
|
||||
String apiBaseUrl() {
|
||||
String defaultApiBaseUrl() {
|
||||
const fromEnv = String.fromEnvironment('API_BASE_URL');
|
||||
if (fromEnv.isNotEmpty) return fromEnv;
|
||||
if (kIsWeb) return Uri.base.origin;
|
||||
return 'http://127.0.0.1:8000';
|
||||
}
|
||||
|
||||
/// The address saved on this device, read in `main()` before `runApp`. Loading it up front,
|
||||
/// rather than lazily like the theme, matters: the session restore fires a request on
|
||||
/// startup and it must already go to the right server.
|
||||
Future<String?> loadStoredApiBaseUrl() async =>
|
||||
(await SharedPreferences.getInstance()).getString(_prefsKey);
|
||||
|
||||
/// Overridden in `main()` with [loadStoredApiBaseUrl]; null (tests, nothing saved) means default.
|
||||
final initialApiBaseUrlProvider = Provider<String?>((_) => null);
|
||||
|
||||
/// Backend origin every client is built against. Watched by `apiProvider` and
|
||||
/// `authDioProvider`, so changing it rebuilds them. To switch servers use
|
||||
/// `switchApiBaseUrl`: tokens and cached data belong to the old one.
|
||||
final apiBaseUrlProvider = NotifierProvider<ApiBaseUrlController, String>(
|
||||
ApiBaseUrlController.new,
|
||||
);
|
||||
|
||||
class ApiBaseUrlController extends Notifier<String> {
|
||||
@override
|
||||
String build() => ref.watch(initialApiBaseUrlProvider) ?? defaultApiBaseUrl();
|
||||
|
||||
/// [input] as an origin-like URL without a trailing slash, or null if it is not an
|
||||
/// absolute http(s) URL.
|
||||
static String? normalize(String input) {
|
||||
final uri = Uri.tryParse(input.trim());
|
||||
if (uri == null || !uri.hasAuthority || uri.host.isEmpty) return null;
|
||||
if (uri.scheme != 'http' && uri.scheme != 'https') return null;
|
||||
final s = uri.toString();
|
||||
return s.endsWith('/') ? s.substring(0, s.length - 1) : s;
|
||||
}
|
||||
|
||||
/// Persists [url]; null goes back to [defaultApiBaseUrl].
|
||||
Future<void> save(String? url) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (url == null) {
|
||||
await prefs.remove(_prefsKey);
|
||||
state = defaultApiBaseUrl();
|
||||
} else {
|
||||
await prefs.setString(_prefsKey, url);
|
||||
state = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user