feat(app): смена сервера API и очистка данных устройства при выходе
Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/auth/switch_server.dart';
|
||||
import '../../core/config.dart';
|
||||
|
||||
/// Asks for a new backend address and applies it. Shared by Настройки and the login screen
|
||||
/// (a wrong address there means you can never reach Настройки).
|
||||
Future<void> showApiBaseUrlDialog(BuildContext context) async {
|
||||
final container = ProviderScope.containerOf(context);
|
||||
final result = await showDialog<_Choice>(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
_ApiBaseUrlDialog(current: container.read(apiBaseUrlProvider)),
|
||||
);
|
||||
if (result == null) return;
|
||||
await switchApiBaseUrl(container, result.url);
|
||||
}
|
||||
|
||||
class _Choice {
|
||||
const _Choice(this.url);
|
||||
|
||||
/// null = back to the default address.
|
||||
final String? url;
|
||||
}
|
||||
|
||||
class _ApiBaseUrlDialog extends StatefulWidget {
|
||||
const _ApiBaseUrlDialog({required this.current});
|
||||
final String current;
|
||||
|
||||
@override
|
||||
State<_ApiBaseUrlDialog> createState() => _ApiBaseUrlDialogState();
|
||||
}
|
||||
|
||||
class _ApiBaseUrlDialogState extends State<_ApiBaseUrlDialog> {
|
||||
late final _controller = TextEditingController(text: widget.current);
|
||||
String? _error;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _save() {
|
||||
final url = ApiBaseUrlController.normalize(_controller.text);
|
||||
if (url == null) {
|
||||
setState(() => _error = 'Нужен адрес вида https://fin.example.com');
|
||||
return;
|
||||
}
|
||||
Navigator.pop(context, _Choice(url));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Адрес API'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: _controller,
|
||||
autofocus: true,
|
||||
keyboardType: TextInputType.url,
|
||||
decoration: InputDecoration(errorText: _error),
|
||||
onSubmitted: (_) => _save(),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Смена адреса завершит сеанс и очистит локальный кэш.',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Отмена'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, const _Choice(null)),
|
||||
child: const Text('По умолчанию'),
|
||||
),
|
||||
FilledButton(onPressed: _save, child: const Text('Сохранить')),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user