import 'dart:convert'; /// Represents a shared photo entry in the trip album. /// /// Stores a remote [url], optional [caption], and the uploader identifier for /// basic attribution. Persistence is local/offline via JSON helpers. class AlbumPhoto { /// Unique identifier for the photo entry. final String id; /// Public or signed URL of the photo. final String url; /// Optional caption provided by the user. final String? caption; /// Name or ID of the uploader for display. final String? uploadedBy; /// Creation timestamp. final DateTime createdAt; /// Creates an album photo. const AlbumPhoto({ required this.id, required this.url, required this.createdAt, this.caption, this.uploadedBy, }); /// Convenience builder for a new entry. factory AlbumPhoto.newPhoto({ required String id, required String url, String? caption, String? uploadedBy, }) { return AlbumPhoto( id: id, url: url, caption: caption, uploadedBy: uploadedBy, createdAt: DateTime.now().toUtc(), ); } /// Copy with updates. AlbumPhoto copyWith({ String? id, String? url, String? caption, String? uploadedBy, DateTime? createdAt, }) { return AlbumPhoto( id: id ?? this.id, url: url ?? this.url, caption: caption ?? this.caption, uploadedBy: uploadedBy ?? this.uploadedBy, createdAt: createdAt ?? this.createdAt, ); } /// Serialize to JSON. Map toJson() { return { 'id': id, 'url': url, 'caption': caption, 'uploadedBy': uploadedBy, 'createdAt': createdAt.toIso8601String(), }; } /// Deserialize from JSON. factory AlbumPhoto.fromJson(Map json) { return AlbumPhoto( id: json['id'] as String, url: json['url'] as String, caption: json['caption'] as String?, uploadedBy: json['uploadedBy'] as String?, createdAt: DateTime.parse(json['createdAt'] as String), ); } /// Encode list to string. static String encodeList(List photos) { return json.encode(photos.map((p) => p.toJson()).toList()); } /// Decode list from string. static List decodeList(String raw) { final decoded = json.decode(raw) as List; return decoded .cast>() .map(AlbumPhoto.fromJson) .toList(); } }