Download function for all recipes or recipes in a certain category
This commit is contained in:
@@ -9,6 +9,9 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
class DataStore {
|
class DataStore {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
|
||||||
|
|
||||||
private static func fileURL(appending: String) throws -> URL {
|
private static func fileURL(appending: String) throws -> URL {
|
||||||
try FileManager.default.url(
|
try FileManager.default.url(
|
||||||
for: .documentDirectory,
|
for: .documentDirectory,
|
||||||
@@ -53,16 +56,24 @@ class DataStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func recipeDetailExists(recipeId: Int) -> Bool {
|
||||||
|
let filePath = "recipe\(recipeId).data"
|
||||||
|
guard let folderPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first?.path() else { return false }
|
||||||
|
let exists = fileManager.fileExists(atPath: folderPath + filePath)
|
||||||
|
print("Path: ", folderPath + filePath)
|
||||||
|
print("Recipe detail with id \(recipeId)", exists ? "exists" : "does not exist")
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
func clearAll() -> Bool {
|
func clearAll() -> Bool {
|
||||||
print("Attempting to delete all data ...")
|
print("Attempting to delete all data ...")
|
||||||
let fm = FileManager.default
|
guard let folderPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first?.path() else { return false }
|
||||||
guard let folderPath = fm.urls(for: .documentDirectory, in: .userDomainMask).first?.path() else { return false }
|
|
||||||
print("Folder path: ", folderPath)
|
print("Folder path: ", folderPath)
|
||||||
do {
|
do {
|
||||||
let filePaths = try fm.contentsOfDirectory(atPath: folderPath)
|
let filePaths = try fileManager.contentsOfDirectory(atPath: folderPath)
|
||||||
for filePath in filePaths {
|
for filePath in filePaths {
|
||||||
print("File path: ", filePath)
|
print("File path: ", filePath)
|
||||||
try fm.removeItem(atPath: folderPath + filePath)
|
try fileManager.removeItem(atPath: folderPath + filePath)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print("Could not delete documents folder contents: \(error)")
|
print("Could not delete documents folder contents: \(error)")
|
||||||
|
|||||||
@@ -69,6 +69,30 @@ import UIKit
|
|||||||
return RecipeDetail.error()
|
return RecipeDetail.error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downloadAllRecipes() async {
|
||||||
|
for category in categories {
|
||||||
|
await loadRecipeList(categoryName: category.name, needsUpdate: true)
|
||||||
|
guard let recipeList = recipes[category.name] else { continue }
|
||||||
|
for recipe in recipeList {
|
||||||
|
let _ = await loadRecipeDetail(recipeId: recipe.recipe_id, needsUpdate: true)
|
||||||
|
let _ = await loadImage(recipeId: recipe.recipe_id, full: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if recipeDetail is stored locally, either in cache or on disk
|
||||||
|
/// - Parameters
|
||||||
|
/// - recipeId: The id of a recipe.
|
||||||
|
/// - Returns: True if the recipeDetail is stored, otherwise false
|
||||||
|
func recipeDetailExists(recipeId: Int) -> Bool {
|
||||||
|
if recipeDetails[recipeId] != nil {
|
||||||
|
return true
|
||||||
|
} else if (dataStore.recipeDetailExists(recipeId: recipeId)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Try to load the recipe image from cache. If not found, try to load from store or the server.
|
/// Try to load the recipe image from cache. If not found, try to load from store or the server.
|
||||||
/// - Parameters
|
/// - Parameters
|
||||||
@@ -156,7 +180,7 @@ extension MainViewModel {
|
|||||||
let (data, error): (D?, Error?) = await networkController.sendDataRequest(request)
|
let (data, error): (D?, Error?) = await networkController.sendDataRequest(request)
|
||||||
print(error as Any)
|
print(error as Any)
|
||||||
if let data = data {
|
if let data = data {
|
||||||
try await dataStore.save(data: data, toPath: localPath)
|
await dataStore.save(data: data, toPath: localPath)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ struct CategoryCardView: View {
|
|||||||
Text(category.name)
|
Text(category.name)
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
)
|
)
|
||||||
.frame(maxHeight: 30)
|
.frame(maxHeight: 25)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ import SwiftUI
|
|||||||
struct RecipeBookView: View {
|
struct RecipeBookView: View {
|
||||||
@State var categoryName: String
|
@State var categoryName: String
|
||||||
@ObservedObject var viewModel: MainViewModel
|
@ObservedObject var viewModel: MainViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
LazyVStack {
|
LazyVStack {
|
||||||
if let recipes = viewModel.recipes[categoryName] {
|
if let recipes = viewModel.recipes[categoryName] {
|
||||||
ForEach(recipes, id: \.recipe_id) { recipe in
|
ForEach(recipes, id: \.recipe_id) { recipe in
|
||||||
NavigationLink(destination: RecipeDetailView(viewModel: viewModel, recipe: recipe)) {
|
NavigationLink(destination: RecipeDetailView(viewModel: viewModel, recipe: recipe)) {
|
||||||
RecipeCardView(viewModel: viewModel, recipe: recipe)
|
RecipeCardView(viewModel: viewModel, recipe: recipe, isDownloaded: viewModel.recipeDetailExists(recipeId: recipe.recipe_id))
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
@@ -27,6 +28,22 @@ struct RecipeBookView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(categoryName)
|
.navigationTitle(categoryName)
|
||||||
|
.toolbar {
|
||||||
|
Menu {
|
||||||
|
Button {
|
||||||
|
print("Downloading all recipes in category \(categoryName) ...")
|
||||||
|
downloadRecipes()
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("Download recipes")
|
||||||
|
Image(systemName: "icloud.and.arrow.down")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "ellipsis.circle")
|
||||||
|
}
|
||||||
|
}
|
||||||
.task {
|
.task {
|
||||||
await viewModel.loadRecipeList(categoryName: categoryName)
|
await viewModel.loadRecipeList(categoryName: categoryName)
|
||||||
}
|
}
|
||||||
@@ -34,4 +51,18 @@ struct RecipeBookView: View {
|
|||||||
await viewModel.loadRecipeList(categoryName: categoryName, needsUpdate: true)
|
await viewModel.loadRecipeList(categoryName: categoryName, needsUpdate: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downloadRecipes() {
|
||||||
|
if let recipes = viewModel.recipes[categoryName] {
|
||||||
|
let dispatchQueue = DispatchQueue(label: "RecipeDownload", qos: .background)
|
||||||
|
dispatchQueue.async {
|
||||||
|
for recipe in recipes {
|
||||||
|
Task {
|
||||||
|
let _ = await viewModel.loadRecipeDetail(recipeId: recipe.recipe_id)
|
||||||
|
let _ = await viewModel.loadImage(recipeId: recipe.recipe_id, full: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct MainView: View {
|
|||||||
@StateObject var userSettings: UserSettings
|
@StateObject var userSettings: UserSettings
|
||||||
var columns: [GridItem] = [GridItem(.adaptive(minimum: 150), spacing: 0)]
|
var columns: [GridItem] = [GridItem(.adaptive(minimum: 150), spacing: 0)]
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationView {
|
||||||
ScrollView(.vertical, showsIndicators: false) {
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
LazyVGrid(columns: columns, spacing: 0) {
|
LazyVGrid(columns: columns, spacing: 0) {
|
||||||
ForEach(viewModel.categories, id: \.name) { category in
|
ForEach(viewModel.categories, id: \.name) { category in
|
||||||
@@ -30,8 +30,24 @@ struct MainView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("CookBook")
|
.navigationTitle("CookBook")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
Menu {
|
||||||
|
Button {
|
||||||
|
print("Downloading all recipes ...")
|
||||||
|
Task {
|
||||||
|
await viewModel.downloadAllRecipes()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("Download all recipes")
|
||||||
|
Image(systemName: "icloud.and.arrow.down")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "ellipsis.circle")
|
||||||
|
}
|
||||||
NavigationLink( destination: SettingsView(userSettings: userSettings, viewModel: viewModel)) {
|
NavigationLink( destination: SettingsView(userSettings: userSettings, viewModel: viewModel)) {
|
||||||
Image(systemName: "gear")
|
Image(systemName: "gearshape")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ struct RecipeCardView: View {
|
|||||||
@State var viewModel: MainViewModel
|
@State var viewModel: MainViewModel
|
||||||
@State var recipe: Recipe
|
@State var recipe: Recipe
|
||||||
@State var recipeThumb: UIImage?
|
@State var recipeThumb: UIImage?
|
||||||
|
@State var isDownloaded: Bool
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Image(uiImage: recipeThumb ?? UIImage(named: "CookBook")!)
|
Image(uiImage: recipeThumb ?? UIImage(named: "CookBook")!)
|
||||||
@@ -23,6 +25,12 @@ struct RecipeCardView: View {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
VStack {
|
||||||
|
Image(systemName: isDownloaded ? "checkmark.icloud" : "icloud.and.arrow.down")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.padding()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.background(.ultraThickMaterial)
|
.background(.ultraThickMaterial)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
|||||||
Reference in New Issue
Block a user