You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.9 KiB
167 lines
4.9 KiB
import 'dart:convert';
|
|
|
|
/// Represents a recipe with metadata.
|
|
class RecipeModel {
|
|
/// Unique identifier for the recipe.
|
|
final String id;
|
|
|
|
/// Recipe title.
|
|
final String title;
|
|
|
|
/// Recipe description.
|
|
final String? description;
|
|
|
|
/// List of tags.
|
|
final List<String> tags;
|
|
|
|
/// Rating (0-5).
|
|
final int rating;
|
|
|
|
/// Whether the recipe is marked as favourite.
|
|
final bool isFavourite;
|
|
|
|
/// List of image URLs (from Immich).
|
|
final List<String> imageUrls;
|
|
|
|
/// Timestamp when recipe was created (milliseconds since epoch).
|
|
final int createdAt;
|
|
|
|
/// Timestamp when recipe was last updated (milliseconds since epoch).
|
|
final int updatedAt;
|
|
|
|
/// Whether the recipe has been deleted.
|
|
final bool isDeleted;
|
|
|
|
/// Nostr event ID if synced to Nostr.
|
|
final String? nostrEventId;
|
|
|
|
/// Creates a [RecipeModel] instance.
|
|
RecipeModel({
|
|
required this.id,
|
|
required this.title,
|
|
this.description,
|
|
List<String>? tags,
|
|
this.rating = 0,
|
|
this.isFavourite = false,
|
|
List<String>? imageUrls,
|
|
int? createdAt,
|
|
int? updatedAt,
|
|
this.isDeleted = false,
|
|
this.nostrEventId,
|
|
}) : tags = tags ?? [],
|
|
imageUrls = imageUrls ?? [],
|
|
createdAt = createdAt ?? DateTime.now().millisecondsSinceEpoch,
|
|
updatedAt = updatedAt ?? DateTime.now().millisecondsSinceEpoch;
|
|
|
|
/// Creates a [RecipeModel] from a database row (Map).
|
|
factory RecipeModel.fromMap(Map<String, dynamic> map) {
|
|
return RecipeModel(
|
|
id: map['id'] as String,
|
|
title: map['title'] as String,
|
|
description: map['description'] as String?,
|
|
tags: map['tags'] != null
|
|
? (jsonDecode(map['tags'] as String) as List<dynamic>)
|
|
.map((e) => e.toString())
|
|
.toList()
|
|
: [],
|
|
rating: map['rating'] as int? ?? 0,
|
|
isFavourite: (map['is_favourite'] as int? ?? 0) == 1,
|
|
imageUrls: map['image_urls'] != null
|
|
? (jsonDecode(map['image_urls'] as String) as List<dynamic>)
|
|
.map((e) => e.toString())
|
|
.toList()
|
|
: [],
|
|
createdAt: map['created_at'] as int,
|
|
updatedAt: map['updated_at'] as int,
|
|
isDeleted: (map['is_deleted'] as int? ?? 0) == 1,
|
|
nostrEventId: map['nostr_event_id'] as String?,
|
|
);
|
|
}
|
|
|
|
/// Converts the [RecipeModel] to a Map for database storage.
|
|
Map<String, dynamic> toMap() {
|
|
return {
|
|
'id': id,
|
|
'title': title,
|
|
'description': description,
|
|
'tags': jsonEncode(tags),
|
|
'rating': rating,
|
|
'is_favourite': isFavourite ? 1 : 0,
|
|
'image_urls': jsonEncode(imageUrls),
|
|
'created_at': createdAt,
|
|
'updated_at': updatedAt,
|
|
'is_deleted': isDeleted ? 1 : 0,
|
|
'nostr_event_id': nostrEventId,
|
|
};
|
|
}
|
|
|
|
/// Creates a [RecipeModel] from JSON (for Nostr content).
|
|
factory RecipeModel.fromJson(Map<String, dynamic> json) {
|
|
return RecipeModel(
|
|
id: json['id'] as String,
|
|
title: json['title'] as String,
|
|
description: json['description'] as String?,
|
|
tags: (json['tags'] as List<dynamic>?)?.map((e) => e.toString()).toList() ?? [],
|
|
rating: json['rating'] as int? ?? 0,
|
|
isFavourite: json['isFavourite'] as bool? ?? false,
|
|
imageUrls: (json['imageUrls'] as List<dynamic>?)?.map((e) => e.toString()).toList() ?? [],
|
|
createdAt: json['createdAt'] as int? ?? DateTime.now().millisecondsSinceEpoch,
|
|
updatedAt: json['updatedAt'] as int? ?? DateTime.now().millisecondsSinceEpoch,
|
|
isDeleted: json['isDeleted'] as bool? ?? false,
|
|
nostrEventId: json['nostrEventId'] as String?,
|
|
);
|
|
}
|
|
|
|
/// Converts the [RecipeModel] to JSON (for Nostr content).
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'id': id,
|
|
'title': title,
|
|
if (description != null) 'description': description,
|
|
'tags': tags,
|
|
'rating': rating,
|
|
'isFavourite': isFavourite,
|
|
'imageUrls': imageUrls,
|
|
'createdAt': createdAt,
|
|
'updatedAt': updatedAt,
|
|
'isDeleted': isDeleted,
|
|
if (nostrEventId != null) 'nostrEventId': nostrEventId,
|
|
};
|
|
}
|
|
|
|
/// Creates a copy of this [RecipeModel] with updated fields.
|
|
RecipeModel copyWith({
|
|
String? id,
|
|
String? title,
|
|
String? description,
|
|
List<String>? tags,
|
|
int? rating,
|
|
bool? isFavourite,
|
|
List<String>? imageUrls,
|
|
int? createdAt,
|
|
int? updatedAt,
|
|
bool? isDeleted,
|
|
String? nostrEventId,
|
|
}) {
|
|
return RecipeModel(
|
|
id: id ?? this.id,
|
|
title: title ?? this.title,
|
|
description: description ?? this.description,
|
|
tags: tags ?? this.tags,
|
|
rating: rating ?? this.rating,
|
|
isFavourite: isFavourite ?? this.isFavourite,
|
|
imageUrls: imageUrls ?? this.imageUrls,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
isDeleted: isDeleted ?? this.isDeleted,
|
|
nostrEventId: nostrEventId ?? this.nostrEventId,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'RecipeModel(id: $id, title: $title, tags: ${tags.length}, rating: $rating, favourite: $isFavourite)';
|
|
}
|
|
}
|
|
|