193 lines
6.3 KiB
Dart
193 lines
6.3 KiB
Dart
import 'package:dio/dio.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:get_it/get_it.dart';
|
|
import '../../../../core/errors/errors.dart';
|
|
import '../../domain/repositories/settings_repository.dart';
|
|
import 'settings_event.dart';
|
|
import 'settings_state.dart';
|
|
|
|
class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
|
|
final SettingsRepository _repository;
|
|
|
|
SettingsBloc(this._repository) : super(const SettingsState.initial()) {
|
|
on<SettingsStarted>(_onStarted);
|
|
on<SettingsConfigRequested>(_onConfigRequested);
|
|
on<SettingsApiUrlChanged>(_onApiUrlChanged);
|
|
on<SettingsApiUrlSaved>(_onApiUrlSaved);
|
|
on<SettingsLanguageChanged>(_onLanguageChanged);
|
|
on<SettingsRefreshConfig>(_onRefreshConfig);
|
|
}
|
|
|
|
Future<void> _onStarted(SettingsStarted event, emit) async {
|
|
emit(const SettingsState.loading());
|
|
|
|
final apiUrl = await _repository.getApiUrl() ?? '';
|
|
final languageCode = await _repository.getLanguageCode() ?? 'ru';
|
|
|
|
if (apiUrl.isEmpty) {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: '',
|
|
serverConfig: null,
|
|
languageCode: languageCode,
|
|
));
|
|
return;
|
|
}
|
|
|
|
final configResult = await _repository.getServerConfig();
|
|
|
|
if (configResult.isSuccess) {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: apiUrl,
|
|
serverConfig: configResult.data,
|
|
languageCode: languageCode,
|
|
connectionStatus: ConnectionStatus.connected,
|
|
));
|
|
} else {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: apiUrl,
|
|
serverConfig: null,
|
|
languageCode: languageCode,
|
|
connectionStatus: ConnectionStatus.error,
|
|
error: _errorMessage(configResult.error!),
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onConfigRequested(SettingsConfigRequested event, emit) async {
|
|
if (state is! SettingsLoaded) return;
|
|
final currentState = state as SettingsLoaded;
|
|
|
|
if (currentState.apiUrl.isEmpty) return;
|
|
|
|
emit(currentState.copyWith(connectionStatus: ConnectionStatus.checking));
|
|
final configResult = await _repository.refreshServerConfig();
|
|
|
|
if (configResult.isSuccess) {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: currentState.apiUrl,
|
|
serverConfig: configResult.data,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.connected,
|
|
));
|
|
} else {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: currentState.apiUrl,
|
|
serverConfig: null,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.error,
|
|
error: _errorMessage(configResult.error!),
|
|
));
|
|
}
|
|
}
|
|
|
|
void _onApiUrlChanged(SettingsApiUrlChanged event, emit) {
|
|
if (state is! SettingsLoaded) return;
|
|
final currentState = state as SettingsLoaded;
|
|
emit(SettingsState.loaded(
|
|
apiUrl: event.url,
|
|
serverConfig: currentState.serverConfig,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.unknown,
|
|
));
|
|
}
|
|
|
|
Future<void> _onApiUrlSaved(SettingsApiUrlSaved event, emit) async {
|
|
if (state is! SettingsLoaded) return;
|
|
final currentState = state as SettingsLoaded;
|
|
|
|
// Нормализуем URL: убираем дублирование /api и другие проблемные суффиксы
|
|
final normalizedUrl = _normalizeApiUrl(event.url);
|
|
|
|
await _repository.saveApiUrl(normalizedUrl);
|
|
|
|
// Обновляем baseUrl у Dio, чтобы последующие запросы шли на новый адрес
|
|
final dio = GetIt.instance<Dio>();
|
|
dio.options.baseUrl = normalizedUrl;
|
|
|
|
if (event.url.isEmpty) {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: '',
|
|
serverConfig: null,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.unknown,
|
|
));
|
|
return;
|
|
}
|
|
|
|
// Проверяем подключение к новому адресу
|
|
emit(SettingsState.loaded(
|
|
apiUrl: event.url,
|
|
serverConfig: currentState.serverConfig,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.checking,
|
|
));
|
|
|
|
final configResult = await _repository.refreshServerConfig();
|
|
|
|
if (configResult.isSuccess) {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: event.url,
|
|
serverConfig: configResult.data,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.connected,
|
|
));
|
|
} else {
|
|
emit(SettingsState.loaded(
|
|
apiUrl: event.url,
|
|
serverConfig: null,
|
|
languageCode: currentState.languageCode,
|
|
connectionStatus: ConnectionStatus.error,
|
|
error: _errorMessage(configResult.error!),
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onLanguageChanged(SettingsLanguageChanged event, emit) async {
|
|
if (state is! SettingsLoaded) return;
|
|
final currentState = state as SettingsLoaded;
|
|
|
|
await _repository.saveLanguageCode(event.code);
|
|
|
|
emit(SettingsState.loaded(
|
|
apiUrl: currentState.apiUrl,
|
|
serverConfig: currentState.serverConfig,
|
|
languageCode: event.code,
|
|
connectionStatus: currentState.connectionStatus,
|
|
));
|
|
}
|
|
|
|
Future<void> _onRefreshConfig(SettingsRefreshConfig event, emit) async {
|
|
add(const SettingsConfigRequested());
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
}
|
|
|
|
String _errorMessage(AppError error) => error.when(
|
|
unknown: (msg) => msg?.contains('parsing') == true ? 'parsingError' : 'unknownError',
|
|
network: (msg) => 'networkError',
|
|
unauthorized: (msg) => 'unauthorized',
|
|
forbidden: (msg) => 'forbidden',
|
|
notFound: (msg) => 'notFound',
|
|
server: (code, msg) => 'serverError',
|
|
database: (msg) => 'databaseError',
|
|
validation: (message, errors) => 'validationError',
|
|
parsing: (msg) => 'parsingError',
|
|
);
|
|
|
|
String _normalizeApiUrl(String url) {
|
|
if (url.isEmpty) return url;
|
|
|
|
// Убираем все слэши в конце
|
|
String normalized = url.replaceAll(RegExp(r'/+$'), '');
|
|
|
|
// Если URL заканчивается на /api, убираем этот суффикс
|
|
// (так как мы всегда добавляем /api к путям при запросе)
|
|
if (normalized.endsWith('/api')) {
|
|
normalized = normalized.substring(0, normalized.length - 4);
|
|
normalized = normalized.replaceAll(RegExp(r'/+$'), '');
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
}
|
|
|