Files
forkmessager/client-mobile/lib/features/settings/presentation/bloc/settings_bloc.dart
2026-05-14 12:06:34 +03:00

193 lines
6.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}