cyberhybridhub/lib/data/remote/user_profile_remote_store.dart
2026-05-20 10:22:58 -05:00

91 lines
2.6 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../config/api_config.dart';
import '../../models/user_profile.dart';
import '../../services/auth_service.dart';
import 'profile_api_exception.dart';
/// HTTP client for Postgres-backed profile API.
class UserProfileRemoteStore {
UserProfileRemoteStore({http.Client? client}) : _client = client ?? http.Client();
final http.Client _client;
Future<UserProfile?> fetchProfile() async {
final String? token = await AuthService.instance.getIdToken();
if (token == null) {
return null;
}
final http.Response response = await _client.get(
Uri.parse('$apiBaseUrl/v1/me/profile'),
headers: _authHeaders(token),
);
if (response.statusCode == 404) {
return null;
}
if (response.statusCode != 200) {
throw ProfileApiException(response.statusCode, response.body);
}
return _profileFromApi(
jsonDecode(response.body) as Map<String, dynamic>,
);
}
Future<UserProfile> pushProfile(UserProfile profile) async {
final String? token = await AuthService.instance.getIdToken();
if (token == null) {
throw StateError('Not signed in');
}
final http.Response response = await _client.put(
Uri.parse('$apiBaseUrl/v1/me/profile'),
headers: _authHeaders(token),
body: jsonEncode(profile.toApiJson()),
);
if (response.statusCode == 409) {
final Map<String, dynamic> body =
jsonDecode(response.body) as Map<String, dynamic>;
throw ProfileConflictException(
_profileFromApi(body['profile'] as Map<String, dynamic>),
);
}
if (response.statusCode != 200) {
throw ProfileApiException(response.statusCode, response.body);
}
return _profileFromApi(
jsonDecode(response.body) as Map<String, dynamic>,
);
}
Map<String, String> _authHeaders(String token) {
return <String, String>{
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'Accept': 'application/json',
};
}
UserProfile _profileFromApi(Map<String, dynamic> json) {
return UserProfile(
firebaseUid: json['firebaseUid'] as String,
email: json['email'] as String?,
displayName: json['displayName'] as String?,
photoUrl: json['photoUrl'] as String?,
locale: json['locale'] as String? ?? 'en',
timezone: json['timezone'] as String?,
onboardingCompleted: json['onboardingCompleted'] as bool? ?? false,
revision: (json['revision'] as num).toInt(),
updatedAt: DateTime.parse(json['updatedAt'] as String).toUtc(),
lastSyncedAt: DateTime.now().toUtc(),
dirty: false,
);
}
}