157 lines
5.3 KiB
Dart
157 lines
5.3 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:image/image.dart' as img;
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
/// Service untuk mengelola data user (profile, foto, password)
|
|
class UsersService {
|
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
|
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
|
static const String collectionName = 'users';
|
|
|
|
User? get currentUser => _auth.currentUser;
|
|
String get userId => currentUser?.uid ?? '';
|
|
|
|
/// Inisiasi profil saat pertama kali register
|
|
Future<void> createUserDocument(String uid, String name, String email) async {
|
|
try {
|
|
await _firestore.collection(collectionName).doc(uid).set({
|
|
'user_id': uid,
|
|
'display_name': name.trim(),
|
|
'email': email.trim(),
|
|
'created_at': FieldValue.serverTimestamp(),
|
|
'updated_at': FieldValue.serverTimestamp(),
|
|
});
|
|
} catch (e) {
|
|
throw Exception('Gagal inisiasi dokumen user: $e');
|
|
}
|
|
}
|
|
|
|
/// Update nama user di Firebase Auth dan Firestore
|
|
Future<void> updateName(String name) async {
|
|
try {
|
|
await currentUser?.updateDisplayName(name.trim());
|
|
await _firestore.collection(collectionName).doc(userId).set(
|
|
{
|
|
'user_id': userId,
|
|
'display_name': name.trim(),
|
|
'updated_at': FieldValue.serverTimestamp(),
|
|
},
|
|
SetOptions(merge: true),
|
|
);
|
|
await currentUser?.reload();
|
|
} catch (e) {
|
|
throw Exception('Gagal update nama: $e');
|
|
}
|
|
}
|
|
|
|
/// Kompresi gambar ke 800x800px dengan quality 75%
|
|
Future<File> _compressImage(File imageFile) async {
|
|
try {
|
|
final bytes = await imageFile.readAsBytes();
|
|
final image = img.decodeImage(bytes);
|
|
if (image == null) throw Exception('Gagal decode gambar');
|
|
|
|
final resized = img.copyResize(image, width: 800, height: 800, maintainAspect: true);
|
|
final compressed = img.encodeJpg(resized, quality: 75);
|
|
|
|
final dir = await getApplicationDocumentsDirectory();
|
|
final compressedFile = File('${dir.path}/profile_compressed.jpg');
|
|
await compressedFile.writeAsBytes(compressed);
|
|
return compressedFile;
|
|
} catch (e) {
|
|
throw Exception('Gagal kompresi foto: $e');
|
|
}
|
|
}
|
|
|
|
/// Simpan foto profil ke lokal storage dengan kompresi
|
|
Future<String> saveProfilePhotoLocal(File imageFile) async {
|
|
try {
|
|
if (userId.isEmpty) throw Exception('User tidak terautentikasi');
|
|
if (!await imageFile.exists()) throw Exception('File gambar tidak ditemukan');
|
|
|
|
final compressedFile = await _compressImage(imageFile);
|
|
final appDocDir = await getApplicationDocumentsDirectory();
|
|
final photosDir = Directory('${appDocDir.path}/HydroNutrify/photos');
|
|
|
|
if (!await photosDir.exists()) {
|
|
await photosDir.create(recursive: true);
|
|
}
|
|
|
|
final fileName = 'profile_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
final localPath = '${photosDir.path}/$fileName';
|
|
await compressedFile.copy(localPath);
|
|
|
|
await _firestore.collection(collectionName).doc(userId).set({
|
|
'user_id': userId,
|
|
'profile_photo_filename': fileName,
|
|
'profile_photo_path': localPath,
|
|
'updated_at': FieldValue.serverTimestamp(),
|
|
}, SetOptions(merge: true));
|
|
|
|
return localPath;
|
|
} catch (e) {
|
|
throw Exception('Gagal simpan foto lokal: $e');
|
|
}
|
|
}
|
|
|
|
/// Get path foto profil dari lokal storage
|
|
Future<String?> getProfilePhotoPath() async {
|
|
try {
|
|
final profile = await getUserProfile();
|
|
if (profile != null) {
|
|
final filePath = profile['profile_photo_path'] as String?;
|
|
if (filePath != null && await File(filePath).exists()) {
|
|
return filePath;
|
|
}
|
|
}
|
|
return null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Update password dengan re-authentication
|
|
Future<void> reauthenticateAndUpdatePassword(String oldPassword, String newPassword) async {
|
|
try {
|
|
if (currentUser == null) throw Exception('User tidak ditemukan');
|
|
final email = currentUser!.email;
|
|
if (email == null) throw Exception('Email tidak ditemukan');
|
|
|
|
final credential = EmailAuthProvider.credential(email: email, password: oldPassword);
|
|
await currentUser!.reauthenticateWithCredential(credential);
|
|
await currentUser!.updatePassword(newPassword);
|
|
} on FirebaseAuthException catch (e) {
|
|
if (e.code == 'wrong-password') {
|
|
throw Exception('Password lama salah');
|
|
} else if (e.code == 'weak-password') {
|
|
throw Exception('Password baru terlalu lemah');
|
|
}
|
|
throw Exception('Gagal update password: ${e.message}');
|
|
}
|
|
}
|
|
|
|
/// Get data profile user dari Firestore
|
|
Future<Map<String, dynamic>?> getUserProfile() async {
|
|
try {
|
|
if (userId.isEmpty) return null;
|
|
final doc = await _firestore.collection(collectionName).doc(userId).get();
|
|
return doc.data();
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Stream perubahan profile user real-time
|
|
Stream<DocumentSnapshot?> getUserProfileStream() {
|
|
if (userId.isEmpty) return Stream.value(null);
|
|
return _firestore.collection(collectionName).doc(userId).snapshots().handleError((e) {
|
|
debugPrint('[UsersService] Stream error: $e');
|
|
return null;
|
|
});
|
|
}
|
|
}
|
|
|