245 lines
6.9 KiB
Swift
245 lines
6.9 KiB
Swift
//
|
|
// AccountState.swift
|
|
// Nextcloud Cookbook iOS Client
|
|
//
|
|
// Created by Vincent Meilinger on 29.05.24.
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftUI
|
|
|
|
|
|
@Observable
|
|
class CookbookState {
|
|
let id = UUID()
|
|
let account: Account? = nil
|
|
|
|
/// Caches recipe categories.
|
|
var categories: [Category] = []
|
|
|
|
/// Caches RecipeStubs.
|
|
var recipeStubs: [String: [RecipeStub]] = [:]
|
|
|
|
/// Caches Recipes by recipe id.
|
|
var recipes: [String: Recipe] = [:]
|
|
|
|
/// Caches recipe thumbnails by recipe id.
|
|
var thumbnails: [String: UIImage] = [:]
|
|
|
|
/// Caches recipe images by recipe id.
|
|
var images: [String: UIImage] = [:]
|
|
|
|
/// Caches recipe keywords.
|
|
var keywords: [RecipeKeyword] = []
|
|
|
|
/// Read and write interfaces.
|
|
var readLocal: ReadInterface
|
|
var writeLocal: WriteInterface
|
|
var readRemote: [ReadInterface]?
|
|
var writeRemote: [WriteInterface]?
|
|
|
|
var localOnly: Bool = false
|
|
|
|
/// UI state variables
|
|
var selectedCategory: Category? = nil
|
|
var selectedRecipe: RecipeStub? = nil
|
|
var navigationPath: NavigationPath = NavigationPath()
|
|
|
|
/// Grocery List
|
|
var groceryList = GroceryList()
|
|
|
|
init(
|
|
readLocal: ReadInterface,
|
|
writeLocal: WriteInterface,
|
|
readRemote: [ReadInterface] = [],
|
|
writeRemote: [WriteInterface] = []
|
|
) {
|
|
self.readLocal = readLocal
|
|
self.writeLocal = writeLocal
|
|
self.readRemote = readRemote
|
|
self.writeRemote = writeRemote
|
|
}
|
|
}
|
|
|
|
extension CookbookState {
|
|
func removeRecipe(_ id: String) {
|
|
for key in recipeStubs.keys {
|
|
recipeStubs[key]?.removeAll(where: { $0.id == id })
|
|
}
|
|
recipes.removeValue(forKey: id)
|
|
thumbnails.removeValue(forKey: id)
|
|
images.removeValue(forKey: id)
|
|
}
|
|
|
|
func imgToCache(_ image: UIImage, id: String, size: RecipeImage.RecipeImageSize) {
|
|
if size == .THUMB {
|
|
thumbnails[id] = image
|
|
} else {
|
|
images[id] = image
|
|
}
|
|
}
|
|
|
|
func imgFromCache(id: String, size: RecipeImage.RecipeImageSize) -> UIImage? {
|
|
if size == .THUMB {
|
|
return thumbnails[id]
|
|
} else {
|
|
return images[id]
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CookbookState: ReadInterface {
|
|
func getImage(id: String, size: RecipeImage.RecipeImageSize) async -> UIImage? {
|
|
if let image = imgFromCache(id: id, size: size) {
|
|
return image
|
|
}
|
|
|
|
if !localOnly, let readRemote {
|
|
if let image = await readRemote.getImage(id: id, size: size) {
|
|
return image
|
|
}
|
|
}
|
|
|
|
if let image = await readLocal.getImage(id: id, size: size) {
|
|
return image
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getRecipeStubs() async -> [RecipeStub]? {
|
|
if !localOnly, let readRemote {
|
|
if let stubs = await readRemote.getRecipeStubs() {
|
|
return stubs
|
|
}
|
|
}
|
|
|
|
if categories.isEmpty {
|
|
self.categories = await readLocal.getCategories() ?? []
|
|
}
|
|
|
|
for category in self.categories {
|
|
self.recipeStubs[category.name] = await readLocal.getRecipeStubsForCategory(named: category.name)
|
|
}
|
|
|
|
return self.recipeStubs.flatMap({_, val in val})
|
|
}
|
|
|
|
func getRecipe(id: String) async -> Recipe? {
|
|
if let recipe = self.recipes[id] {
|
|
return recipe
|
|
}
|
|
|
|
if !localOnly, let readRemote {
|
|
if let recipe = await readRemote.getRecipe(id: id) {
|
|
return recipe
|
|
}
|
|
}
|
|
|
|
return await readLocal.getRecipe(id: id)
|
|
}
|
|
|
|
func getCategories() async -> [Category]? {
|
|
if !localOnly, let readRemote {
|
|
if let categories = await readRemote.getCategories() {
|
|
self.categories = categories
|
|
return categories
|
|
}
|
|
}
|
|
|
|
if self.categories.isEmpty, let categories = await readLocal.getCategories() {
|
|
self.categories = categories
|
|
return categories
|
|
}
|
|
|
|
return self.categories
|
|
}
|
|
|
|
func getRecipeStubsForCategory(named categoryName: String) async -> [RecipeStub]? {
|
|
if let stubs = self.recipeStubs[categoryName] {
|
|
return stubs
|
|
}
|
|
|
|
if !localOnly, let readRemote {
|
|
if let stubs = await readRemote.getRecipeStubsForCategory(named: categoryName) {
|
|
self.recipeStubs[categoryName] = stubs
|
|
}
|
|
}
|
|
|
|
if let stubs = await readLocal.getRecipeStubsForCategory(named: categoryName) {
|
|
self.recipeStubs[categoryName] = stubs
|
|
return stubs
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getTags() async -> [RecipeKeyword]? {
|
|
if !keywords.isEmpty {
|
|
return keywords
|
|
}
|
|
|
|
if !localOnly, let readRemote {
|
|
if let tags = await readRemote.getTags() {
|
|
self.keywords = tags
|
|
}
|
|
}
|
|
|
|
return await readLocal.getTags()
|
|
}
|
|
|
|
func getRecipesTagged(keyword: String) async -> [RecipeStub]? {
|
|
if !localOnly, let readRemote {
|
|
if let stubs = await readRemote.getRecipesTagged(keyword: keyword) {
|
|
return stubs
|
|
}
|
|
}
|
|
|
|
return await getRecipeStubs()?.filter({ recipe in
|
|
recipe.keywords?.contains(keyword.lowercased()) ?? false
|
|
})
|
|
}
|
|
}
|
|
|
|
extension CookbookState: WriteInterface {
|
|
func postImage(id: String, image: UIImage, size: RecipeImage.RecipeImageSize) async -> ((any UserAlert)?) {
|
|
let _ = await writeLocal.postImage(id: id, image: image, size: size)
|
|
let _ = await writeRemote?.postImage(id: id, image: image, size: size)
|
|
return nil
|
|
}
|
|
|
|
func postRecipe(recipe: Recipe) async -> ((any UserAlert)?) {
|
|
let _ = await writeLocal.postRecipe(recipe: recipe)
|
|
let _ = await writeRemote?.postRecipe(recipe: recipe)
|
|
return nil
|
|
}
|
|
|
|
func updateRecipe(recipe: Recipe) async -> ((any UserAlert)?) {
|
|
let _ = await writeLocal.updateRecipe(recipe: recipe)
|
|
let _ = await writeRemote?.updateRecipe(recipe: recipe)
|
|
return nil
|
|
}
|
|
|
|
func deleteRecipe(id: String) async -> ((any UserAlert)?) {
|
|
let _ = await writeLocal.deleteRecipe(id: id)
|
|
let _ = await writeRemote?.deleteRecipe(id: id)
|
|
return nil
|
|
}
|
|
|
|
func renameCategory(named categoryName: String, newName: String) async -> ((any UserAlert)?) {
|
|
let _ = await writeLocal.renameCategory(named: categoryName, newName: newName)
|
|
let _ = await writeRemote?.renameCategory(named: categoryName, newName: newName)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
extension AccountState: Hashable, Identifiable {
|
|
static func == (lhs: AccountState, rhs: AccountState) -> Bool {
|
|
lhs.id == rhs.id
|
|
}
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(id)
|
|
}
|
|
}
|
|
|