This commit is contained in:
2026-08-08 10:57:54 +08:00
parent d0d48c1ffc
commit 604af909b0
184 changed files with 12521 additions and 12722 deletions
@@ -0,0 +1,80 @@
import 'dart:io';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import '../../../core/constants/app_defaults.dart';
import '../../../core/providers/app_providers.dart';
import '../../../data/local/models/models.dart';
final profileProvider =
AsyncNotifierProvider<ProfileNotifier, UserProfile>(ProfileNotifier.new);
class ProfileNotifier extends AsyncNotifier<UserProfile> {
@override
Future<UserProfile> build() async {
final isar = await ref.watch(isarProvider.future);
final profile = await isar.userProfiles.get(AppDefaults.singletonId);
if (profile != null) return profile;
// Bootstrap should have created one; create defensively.
final fresh = UserProfile.createDefault();
await isar.writeTxn(() => isar.userProfiles.put(fresh));
return fresh;
}
Future<void> updateNickname(String nickname) async {
final trimmed = nickname.trim();
if (trimmed.isEmpty) return;
final current = state.value;
if (current == null) return;
final updated = current.copyWith(nickname: trimmed);
final isar = await ref.read(isarProvider.future);
await isar.writeTxn(() => isar.userProfiles.put(updated));
state = AsyncData(updated);
}
/// Picks an image from gallery or camera, copies it into the app sandbox
/// and stores the local file path on the profile.
Future<bool> updateAvatar({required bool fromCamera}) async {
final current = state.value;
if (current == null) return false;
final picker = ImagePicker();
final xfile = await picker.pickImage(
source: fromCamera ? ImageSource.camera : ImageSource.gallery,
maxWidth: 512,
maxHeight: 512,
imageQuality: 85,
);
if (xfile == null) return false;
final docs = await getApplicationDocumentsDirectory();
final destPath = p.join(docs.path, AppDefaults.avatarFileName);
final sourceFile = File(xfile.path);
await sourceFile.copy(destPath);
final updated = current.copyWith(
avatarLocalPath: destPath,
avatarSource: AvatarSource.localFile,
);
final isar = await ref.read(isarProvider.future);
await isar.writeTxn(() => isar.userProfiles.put(updated));
state = AsyncData(updated);
return true;
}
Future<void> resetAvatar() async {
final current = state.value;
if (current == null) return;
final updated = current.copyWith(
avatarAssetPath: AppDefaults.defaultAvatarAssetPath,
avatarLocalPath: null,
avatarSource: AvatarSource.asset,
);
final isar = await ref.read(isarProvider.future);
await isar.writeTxn(() => isar.userProfiles.put(updated));
state = AsyncData(updated);
}
}