mirror of
https://gitlab.com/Zer0interrupt/recipe-manager.git
synced 2026-11-02 07:28:25 -05:00
135 lines
3.7 KiB
Dart
135 lines
3.7 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'api/api_client.dart';
|
|
import 'models/recipe.dart';
|
|
import 'storage/secure_storage_service.dart';
|
|
|
|
final secureStorageProvider = Provider<SecureStorageService>((ref) {
|
|
return SecureStorageService();
|
|
});
|
|
|
|
final apiClientProvider = Provider<ApiClient>((ref) {
|
|
final storage = ref.watch(secureStorageProvider);
|
|
return ApiClient(storage);
|
|
});
|
|
|
|
// Authentication State Notifier
|
|
class AuthState {
|
|
final bool isAuthenticated;
|
|
final bool isLoading;
|
|
final String? errorMessage;
|
|
final Map<String, dynamic>? user;
|
|
|
|
const AuthState({
|
|
required this.isAuthenticated,
|
|
this.isLoading = false,
|
|
this.errorMessage,
|
|
this.user,
|
|
});
|
|
|
|
factory AuthState.initial() => const AuthState(isAuthenticated: false);
|
|
factory AuthState.loading() => const AuthState(isAuthenticated: false, isLoading: true);
|
|
factory AuthState.authenticated(Map<String, dynamic> user) =>
|
|
AuthState(isAuthenticated: true, user: user);
|
|
factory AuthState.unauthenticated([String? error]) =>
|
|
AuthState(isAuthenticated: false, errorMessage: error);
|
|
}
|
|
|
|
class AuthNotifier extends StateNotifier<AuthState> {
|
|
final ApiClient _api;
|
|
final SecureStorageService _storage;
|
|
|
|
AuthNotifier(this._api, this._storage) : super(AuthState.initial()) {
|
|
checkInitialAuth();
|
|
}
|
|
|
|
Future<void> checkInitialAuth() async {
|
|
final isValid = await _storage.hasValidSession();
|
|
if (isValid) {
|
|
final user = await _storage.getUser();
|
|
state = AuthState.authenticated(user ?? {'username': 'Chef'});
|
|
} else {
|
|
state = AuthState.unauthenticated();
|
|
}
|
|
}
|
|
|
|
Future<bool> login({
|
|
required String serverUrl,
|
|
required String username,
|
|
required String password,
|
|
required String deviceName,
|
|
}) async {
|
|
state = AuthState.loading();
|
|
try {
|
|
final res = await _api.login(
|
|
serverUrl: serverUrl,
|
|
username: username,
|
|
password: password,
|
|
deviceName: deviceName,
|
|
);
|
|
final user = res['user'] as Map<String, dynamic>;
|
|
state = AuthState.authenticated(user);
|
|
return true;
|
|
} catch (e) {
|
|
state = AuthState.unauthenticated(e.toString());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> logout() async {
|
|
await _api.logout();
|
|
state = AuthState.unauthenticated();
|
|
}
|
|
}
|
|
|
|
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
|
final api = ref.watch(apiClientProvider);
|
|
final storage = ref.watch(secureStorageProvider);
|
|
return AuthNotifier(api, storage);
|
|
});
|
|
|
|
// Recipe List State Notifier
|
|
class RecipeListNotifier extends StateNotifier<AsyncValue<List<Recipe>>> {
|
|
final ApiClient _api;
|
|
|
|
RecipeListNotifier(this._api) : super(const AsyncValue.loading()) {
|
|
loadRecipes();
|
|
}
|
|
|
|
Future<void> loadRecipes() async {
|
|
state = const AsyncValue.loading();
|
|
try {
|
|
final recipes = await _api.fetchRecipes();
|
|
state = AsyncValue.data(recipes);
|
|
} catch (e, st) {
|
|
state = AsyncValue.error(e, st);
|
|
}
|
|
}
|
|
|
|
Future<void> addOrUpdate(Recipe recipe) async {
|
|
try {
|
|
final saved = await _api.saveRecipe(recipe);
|
|
final current = state.value ?? [];
|
|
final idx = current.indexWhere((r) => r.id == saved.id);
|
|
if (idx >= 0) {
|
|
current[idx] = saved;
|
|
state = AsyncValue.data([...current]);
|
|
} else {
|
|
state = AsyncValue.data([saved, ...current]);
|
|
}
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<void> remove(String id) async {
|
|
await _api.deleteRecipe(id);
|
|
final current = state.value ?? [];
|
|
state = AsyncValue.data(current.where((r) => r.id != id).toList());
|
|
}
|
|
}
|
|
|
|
final recipeListProvider =
|
|
StateNotifierProvider<RecipeListNotifier, AsyncValue<List<Recipe>>>((ref) {
|
|
final api = ref.watch(apiClientProvider);
|
|
return RecipeListNotifier(api);
|
|
});
|