227 lines
7.0 KiB
Dart
227 lines
7.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../../../../core/errors/error_handler.dart';
|
|
import '../../../../core/network/dio_client.dart';
|
|
import '../../data/datasources/settings_datasource.dart';
|
|
import '../../data/datasources/settings_remote_datasource.dart';
|
|
import '../../data/repositories/settings_repository_impl.dart';
|
|
import '../../domain/entities/app_settings.dart';
|
|
import '../../domain/repositories/settings_repository.dart';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Dependency providers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final _sharedPreferencesProvider = FutureProvider<SharedPreferences>((ref) {
|
|
return SharedPreferences.getInstance();
|
|
});
|
|
|
|
final settingsDatasourceProvider = Provider<SettingsDatasource?>((ref) {
|
|
final prefsAsync = ref.watch(_sharedPreferencesProvider);
|
|
return prefsAsync.whenOrNull(
|
|
data: (prefs) => SettingsDatasource(prefs),
|
|
);
|
|
});
|
|
|
|
final settingsRepositoryProvider = Provider<SettingsRepository?>((ref) {
|
|
final datasource = ref.watch(settingsDatasourceProvider);
|
|
if (datasource == null) return null;
|
|
return SettingsRepositoryImpl(datasource);
|
|
});
|
|
|
|
final settingsRemoteDatasourceProvider =
|
|
Provider<SettingsRemoteDatasource>((ref) {
|
|
final dio = ref.watch(dioClientProvider);
|
|
return SettingsRemoteDatasource(dio);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Account profile state
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class AccountProfile {
|
|
final String displayName;
|
|
final String email;
|
|
final bool isLoading;
|
|
final String? error;
|
|
|
|
const AccountProfile({
|
|
this.displayName = '',
|
|
this.email = '',
|
|
this.isLoading = false,
|
|
this.error,
|
|
});
|
|
|
|
AccountProfile copyWith({
|
|
String? displayName,
|
|
String? email,
|
|
bool? isLoading,
|
|
String? error,
|
|
}) {
|
|
return AccountProfile(
|
|
displayName: displayName ?? this.displayName,
|
|
email: email ?? this.email,
|
|
isLoading: isLoading ?? this.isLoading,
|
|
error: error,
|
|
);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Settings state notifier
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class SettingsNotifier extends StateNotifier<AppSettings> {
|
|
final SettingsRepository? _repository;
|
|
|
|
SettingsNotifier(this._repository) : super(const AppSettings()) {
|
|
_loadSettings();
|
|
}
|
|
|
|
Future<void> _loadSettings() async {
|
|
if (_repository == null) return;
|
|
try {
|
|
final settings = await _repository.loadSettings();
|
|
state = settings;
|
|
} catch (_) {
|
|
// Use defaults on error
|
|
}
|
|
}
|
|
|
|
Future<void> setThemeMode(ThemeMode mode) async {
|
|
state = state.copyWith(themeMode: mode);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setNotificationsEnabled(bool enabled) async {
|
|
state = state.copyWith(notificationsEnabled: enabled);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setSoundEnabled(bool enabled) async {
|
|
state = state.copyWith(soundEnabled: enabled);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setHapticFeedback(bool enabled) async {
|
|
state = state.copyWith(hapticFeedback: enabled);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setTenant(String? id, String? name) async {
|
|
state = state.copyWith(selectedTenantId: id, selectedTenantName: name);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setLanguage(String language) async {
|
|
state = state.copyWith(language: language);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setBiometricEnabled(bool enabled) async {
|
|
state = state.copyWith(biometricEnabled: enabled);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setTtsVoice(String voice) async {
|
|
state = state.copyWith(ttsVoice: voice);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setTtsStyle(String style) async {
|
|
state = state.copyWith(ttsStyle: style);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> setEngineType(String type) async {
|
|
state = state.copyWith(engineType: type);
|
|
await _repository?.saveSettings(state);
|
|
}
|
|
|
|
Future<void> resetToDefaults() async {
|
|
await _repository?.resetSettings();
|
|
state = const AppSettings();
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Account profile notifier
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class AccountProfileNotifier extends StateNotifier<AccountProfile> {
|
|
final SettingsRemoteDatasource _remote;
|
|
|
|
AccountProfileNotifier(this._remote) : super(const AccountProfile());
|
|
|
|
Future<void> loadProfile() async {
|
|
state = state.copyWith(isLoading: true, error: null);
|
|
try {
|
|
final data = await _remote.getAccount();
|
|
state = state.copyWith(
|
|
displayName: data['displayName'] as String? ?? '',
|
|
email: data['email'] as String? ?? '',
|
|
isLoading: false,
|
|
);
|
|
} catch (e) {
|
|
state = state.copyWith(isLoading: false, error: ErrorHandler.friendlyMessage(e));
|
|
}
|
|
}
|
|
|
|
Future<bool> updateDisplayName(String name) async {
|
|
state = state.copyWith(isLoading: true, error: null);
|
|
try {
|
|
final data = await _remote.updateAccount(name);
|
|
state = state.copyWith(
|
|
displayName: data['displayName'] as String? ?? name,
|
|
isLoading: false,
|
|
);
|
|
return true;
|
|
} catch (e) {
|
|
state = state.copyWith(isLoading: false, error: ErrorHandler.friendlyMessage(e));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<({bool success, String? message})> changePassword({
|
|
required String currentPassword,
|
|
required String newPassword,
|
|
}) async {
|
|
try {
|
|
final data = await _remote.changePassword(
|
|
currentPassword: currentPassword,
|
|
newPassword: newPassword,
|
|
);
|
|
final success = data['success'] as bool? ?? false;
|
|
final message = data['message'] as String?;
|
|
return (success: success, message: message);
|
|
} catch (e) {
|
|
return (success: false, message: ErrorHandler.friendlyMessage(e));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main providers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final settingsProvider =
|
|
StateNotifierProvider<SettingsNotifier, AppSettings>((ref) {
|
|
final repository = ref.watch(settingsRepositoryProvider);
|
|
return SettingsNotifier(repository);
|
|
});
|
|
|
|
final accountProfileProvider =
|
|
StateNotifierProvider<AccountProfileNotifier, AccountProfile>((ref) {
|
|
final remote = ref.watch(settingsRemoteDatasourceProvider);
|
|
return AccountProfileNotifier(remote);
|
|
});
|
|
|
|
final themeModeProvider = Provider<ThemeMode>((ref) {
|
|
return ref.watch(settingsProvider).themeMode;
|
|
});
|
|
|
|
final notificationsEnabledProvider = Provider<bool>((ref) {
|
|
return ref.watch(settingsProvider).notificationsEnabled;
|
|
});
|