cyberhybridhub/lib/models/user_profile.dart
2026-05-20 10:22:58 -05:00

124 lines
3.5 KiB
Dart

/// App-owned user profile synced between local Drift storage and Postgres.
class UserProfile {
const UserProfile({
required this.firebaseUid,
this.email,
this.displayName,
this.photoUrl,
this.locale = 'en',
this.timezone,
this.onboardingCompleted = false,
this.revision = 1,
required this.updatedAt,
this.lastSyncedAt,
this.dirty = false,
});
final String firebaseUid;
final String? email;
final String? displayName;
final String? photoUrl;
final String locale;
final String? timezone;
final bool onboardingCompleted;
final int revision;
final DateTime updatedAt;
final DateTime? lastSyncedAt;
final bool dirty;
UserProfile copyWith({
String? email,
String? displayName,
String? photoUrl,
String? locale,
String? timezone,
bool? onboardingCompleted,
int? revision,
DateTime? updatedAt,
DateTime? lastSyncedAt,
bool? dirty,
bool clearLastSyncedAt = false,
}) {
return UserProfile(
firebaseUid: firebaseUid,
email: email ?? this.email,
displayName: displayName ?? this.displayName,
photoUrl: photoUrl ?? this.photoUrl,
locale: locale ?? this.locale,
timezone: timezone ?? this.timezone,
onboardingCompleted: onboardingCompleted ?? this.onboardingCompleted,
revision: revision ?? this.revision,
updatedAt: updatedAt ?? this.updatedAt,
lastSyncedAt: clearLastSyncedAt
? null
: (lastSyncedAt ?? this.lastSyncedAt),
dirty: dirty ?? this.dirty,
);
}
factory UserProfile.fromAuth({
required String firebaseUid,
String? email,
String? displayName,
String? photoUrl,
}) {
return UserProfile(
firebaseUid: firebaseUid,
email: email,
displayName: displayName,
photoUrl: photoUrl,
updatedAt: DateTime.now().toUtc(),
dirty: true,
);
}
factory UserProfile.fromJson(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() ?? 1,
updatedAt: DateTime.parse(json['updatedAt'] as String).toUtc(),
lastSyncedAt: json['lastSyncedAt'] == null
? null
: DateTime.parse(json['lastSyncedAt'] as String).toUtc(),
dirty: json['dirty'] as bool? ?? false,
);
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
'firebaseUid': firebaseUid,
'email': email,
'displayName': displayName,
'photoUrl': photoUrl,
'locale': locale,
'timezone': timezone,
'onboardingCompleted': onboardingCompleted,
'revision': revision,
'updatedAt': updatedAt.toUtc().toIso8601String(),
if (lastSyncedAt != null)
'lastSyncedAt': lastSyncedAt!.toUtc().toIso8601String(),
'dirty': dirty,
};
}
/// Payload sent to the API (server assigns authoritative revision on conflict).
Map<String, dynamic> toApiJson() {
return <String, dynamic>{
'email': email,
'displayName': displayName,
'photoUrl': photoUrl,
'locale': locale,
'timezone': timezone,
'onboardingCompleted': onboardingCompleted,
'revision': revision,
'updatedAt': updatedAt.toUtc().toIso8601String(),
};
}
}