Add cross-device grocery list sync via Nextcloud Cookbook API
Store a _groceryState JSON field on each recipe to track which ingredients have been added, completed, or removed. Uses per-item last-writer-wins conflict resolution with ISO 8601 timestamps. Debounced push (2s) avoids excessive API calls; pull reconciles on recipe open and app launch. Includes a settings toggle to enable/disable sync. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,8 +50,9 @@ struct RecipeDetail: Codable {
|
||||
var recipeIngredient: [String]
|
||||
var recipeInstructions: [String]
|
||||
var nutrition: [String:String]
|
||||
|
||||
init(name: String, keywords: String, dateCreated: String, dateModified: String, imageUrl: String, id: String, prepTime: String? = nil, cookTime: String? = nil, totalTime: String? = nil, description: String, url: String, recipeYield: Int, recipeCategory: String, tool: [String], recipeIngredient: [String], recipeInstructions: [String], nutrition: [String:String]) {
|
||||
var groceryState: GroceryState?
|
||||
|
||||
init(name: String, keywords: String, dateCreated: String, dateModified: String, imageUrl: String, id: String, prepTime: String? = nil, cookTime: String? = nil, totalTime: String? = nil, description: String, url: String, recipeYield: Int, recipeCategory: String, tool: [String], recipeIngredient: [String], recipeInstructions: [String], nutrition: [String:String], groceryState: GroceryState? = nil) {
|
||||
self.name = name
|
||||
self.keywords = keywords
|
||||
self.dateCreated = dateCreated
|
||||
@@ -69,8 +70,9 @@ struct RecipeDetail: Codable {
|
||||
self.recipeIngredient = recipeIngredient
|
||||
self.recipeInstructions = recipeInstructions
|
||||
self.nutrition = nutrition
|
||||
self.groceryState = groceryState
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
name = ""
|
||||
keywords = ""
|
||||
@@ -89,11 +91,13 @@ struct RecipeDetail: Codable {
|
||||
recipeIngredient = []
|
||||
recipeInstructions = []
|
||||
nutrition = [:]
|
||||
groceryState = nil
|
||||
}
|
||||
|
||||
|
||||
// Custom decoder to handle value type ambiguity
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case name, keywords, dateCreated, dateModified, image, imageUrl, id, prepTime, cookTime, totalTime, description, url, recipeYield, recipeCategory, tool, recipeIngredient, recipeInstructions, nutrition
|
||||
case groceryState = "_groceryState"
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
@@ -132,6 +136,8 @@ struct RecipeDetail: Codable {
|
||||
} else {
|
||||
nutrition = [:]
|
||||
}
|
||||
|
||||
groceryState = try? container.decode(GroceryState.self, forKey: .groceryState)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@@ -154,6 +160,7 @@ struct RecipeDetail: Codable {
|
||||
try container.encode(recipeIngredient, forKey: .recipeIngredient)
|
||||
try container.encode(recipeInstructions, forKey: .recipeInstructions)
|
||||
try container.encode(nutrition, forKey: .nutrition)
|
||||
try container.encodeIfPresent(groceryState, forKey: .groceryState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user