diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj b/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj index 1886d82..f9f68d5 100644 --- a/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj +++ b/Nextcloud Cookbook iOS Client.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ A7FB0D7A2B25C66600A3469E /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7FB0D792B25C66600A3469E /* OnboardingView.swift */; }; A7FB0D7C2B25C68500A3469E /* TokenLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7FB0D7B2B25C68500A3469E /* TokenLoginView.swift */; }; A7FB0D7E2B25C6A200A3469E /* V2LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7FB0D7D2B25C6A200A3469E /* V2LoginView.swift */; }; + A9CA6CEF2B4C086100F78AB5 /* RecipeExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CA6CEE2B4C086100F78AB5 /* RecipeExporter.swift */; }; + A9CA6CF62B4C63F200F78AB5 /* TPPDF in Frameworks */ = {isa = PBXBuildFile; productRef = A9CA6CF52B4C63F200F78AB5 /* TPPDF */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -110,6 +112,7 @@ A7FB0D792B25C66600A3469E /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; A7FB0D7B2B25C68500A3469E /* TokenLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLoginView.swift; sourceTree = ""; }; A7FB0D7D2B25C6A200A3469E /* V2LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = V2LoginView.swift; sourceTree = ""; }; + A9CA6CEE2B4C086100F78AB5 /* RecipeExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipeExporter.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -118,6 +121,7 @@ buildActionMask = 2147483647; files = ( A74D33BE2AF82AAE00D06555 /* SwiftSoup in Frameworks */, + A9CA6CF62B4C63F200F78AB5 /* TPPDF in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -168,6 +172,7 @@ A70171B72AB2445700064C43 /* ViewModels */, A70171B22AB211F000064C43 /* Network */, A781E75F2AF8228100452F6F /* RecipeImport */, + A9CA6CED2B4C084100F78AB5 /* RecipeExport */, A703226B2ABAF60D00D7C4ED /* Extensions */, A76B8A6E2ADFFA8800096CEC /* SupportedLanguage.swift */, A7AEAE632AD5521400135378 /* Localizable.xcstrings */, @@ -307,6 +312,14 @@ path = Onboarding; sourceTree = ""; }; + A9CA6CED2B4C084100F78AB5 /* RecipeExport */ = { + isa = PBXGroup; + children = ( + A9CA6CEE2B4C086100F78AB5 /* RecipeExporter.swift */, + ); + path = RecipeExport; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -325,6 +338,7 @@ name = "Nextcloud Cookbook iOS Client"; packageProductDependencies = ( A74D33BD2AF82AAE00D06555 /* SwiftSoup */, + A9CA6CF52B4C63F200F78AB5 /* TPPDF */, ); productName = "Nextcloud Cookbook iOS Client"; productReference = A701717E2AA8E71900064C43 /* Nextcloud Cookbook iOS Client.app */; @@ -403,6 +417,7 @@ mainGroup = A70171752AA8E71900064C43; packageReferences = ( A74D33BC2AF82AAE00D06555 /* XCRemoteSwiftPackageReference "SwiftSoup" */, + A9CA6CF42B4C63F200F78AB5 /* XCRemoteSwiftPackageReference "TPPDF" */, ); productRefGroup = A701717F2AA8E71900064C43 /* Products */; projectDirPath = ""; @@ -470,6 +485,7 @@ A7F3F8EA2ACC221C0076C227 /* CategoryPickerView.swift in Sources */, A79AA8E42B02A962007D25F2 /* CookbookApi.swift in Sources */, A70171CD2AB501B100064C43 /* SettingsView.swift in Sources */, + A9CA6CEF2B4C086100F78AB5 /* RecipeExporter.swift in Sources */, A70171C22AB498C600064C43 /* RecipeCardView.swift in Sources */, A70171842AA8E71900064C43 /* MainView.swift in Sources */, A70171CB2AB4CD1700064C43 /* UserSettings.swift in Sources */, @@ -861,6 +877,14 @@ minimumVersion = 2.6.1; }; }; + A9CA6CF42B4C63F200F78AB5 /* XCRemoteSwiftPackageReference "TPPDF" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/techprimate/TPPDF.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.4.1; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -869,6 +893,11 @@ package = A74D33BC2AF82AAE00D06555 /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; + A9CA6CF52B4C63F200F78AB5 /* TPPDF */ = { + isa = XCSwiftPackageProductDependency; + package = A9CA6CF42B4C63F200F78AB5 /* XCRemoteSwiftPackageReference "TPPDF" */; + productName = TPPDF; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = A70171762AA8E71900064C43 /* Project object */; diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6a1c025..d488c1a 100644 --- a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -8,6 +8,15 @@ "revision" : "8b6cf29eead8841a1fa7822481cb3af4ddaadba6", "version" : "2.6.1" } + }, + { + "identity" : "tppdf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/techprimate/TPPDF.git", + "state" : { + "revision" : "1955ebbc090a3fb2149fb53e703595c3146689af", + "version" : "2.4.1" + } } ], "version" : 2 diff --git a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate index de92d38..2c61e7d 100644 Binary files a/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate and b/Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/vincie.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-1024.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-1024.png index f9cb532..9dd3e8d 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-1024.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-1024.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20.png index acfbeb0..bc21218 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@2x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@2x.png index ca9db32..1a8a0f9 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@2x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@2x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@3x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@3x.png index 5d9773a..533a137 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@3x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-20@3x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29.png index da9a19d..b9751e8 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@2x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@2x.png index 34458f9..6ca183a 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@2x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@2x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@3x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@3x.png index 12da8c1..20cd1c1 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@3x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-29@3x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40.png index ca9db32..1a8a0f9 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@2x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@2x.png index d11501d..15f2c01 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@2x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@2x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@3x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@3x.png index 4d221ff..68107eb 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@3x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-40@3x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@2x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@2x.png index 4d221ff..68107eb 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@2x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@2x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@3x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@3x.png index bdb2503..b4fd915 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@3x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-60@3x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76.png index c5a9ef2..ff9e709 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76@2x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76@2x.png index 330b6ef..9bd0504 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76@2x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-76@2x.png differ diff --git a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-83.5@2x.png b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-83.5@2x.png index 4f80f8b..51e3b77 100644 Binary files a/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-83.5@2x.png and b/Nextcloud Cookbook iOS Client/Assets.xcassets/AppIcon.appiconset/Hintergrund-83.5@2x.png differ diff --git a/Nextcloud Cookbook iOS Client/Localizable.xcstrings b/Nextcloud Cookbook iOS Client/Localizable.xcstrings index 03f0cd9..420dbed 100644 --- a/Nextcloud Cookbook iOS Client/Localizable.xcstrings +++ b/Nextcloud Cookbook iOS Client/Localizable.xcstrings @@ -2014,6 +2014,9 @@ } } } + }, + "PDF Document" : { + }, "Please check the entered URL." : { "localizations" : { @@ -2124,6 +2127,9 @@ } } } + }, + "Recipe" : { + }, "Recipes" : { "localizations" : { @@ -2236,6 +2242,7 @@ } }, "Search recipes" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -2366,6 +2373,15 @@ } } } + }, + "Share as PDF" : { + + }, + "Share as text" : { + + }, + "Share recipe" : { + }, "Show help" : { "localizations" : { diff --git a/Nextcloud Cookbook iOS Client/RecipeExport/RecipeExporter.swift b/Nextcloud Cookbook iOS Client/RecipeExport/RecipeExporter.swift new file mode 100644 index 0000000..e4462ff --- /dev/null +++ b/Nextcloud Cookbook iOS Client/RecipeExport/RecipeExporter.swift @@ -0,0 +1,156 @@ +// +// RecipeToPDF.swift +// Nextcloud Cookbook iOS Client +// +// Created by Vincent Meilinger on 08.01.24. +// + +import Foundation +import TPPDF +import SwiftUI + +class RecipeExporter { + + func createPDF(recipe: RecipeDetail, image: UIImage?) -> URL? { + let document = PDFDocument(format: .a4) + + let titleStyle = PDFTextStyle(name: "title", font: UIFont.boldSystemFont(ofSize: 18), color: .black) + let headerStyle = PDFTextStyle(name: "header", font: UIFont.boldSystemFont(ofSize: 16), color: .darkGray) + let textStyle = PDFTextStyle(name: "text", font: UIFont.systemFont(ofSize: 14), color: .black) + + let titleSection = PDFSection(columnWidths: [0.5, 0.5]) + if let image = image, let resizedImage = cropAndResizeImage(image: image, targetHeight: 150) { + let pdfImg = PDFImage( + image: resizedImage, + size: resizedImage.size, + options: [.rounded], + cornerRadius: 5 + ) + titleSection.columns[0].add(image: pdfImg) + } + + // Title + titleSection.columns[1].add(textObject: PDFSimpleText(text: recipe.name, style: titleStyle)) + + // Description + if !recipe.description.isEmpty { + titleSection.columns[1].add(space: 10) + titleSection.columns[1].add(textObject: PDFSimpleText(text: recipe.description, style: textStyle)) + } + + // Time + if let prepTime = recipe.prepTime, let prepTimeString = DurationComponents.ptToText(prepTime) { + let prepString = "Preparation time: \(prepTimeString)" + titleSection.columns[1].add(space: 10) + titleSection.columns[1].add(textObject: PDFSimpleText(text: prepString, style: textStyle)) + } + + if let cookTime = recipe.cookTime, let cookTimeString = DurationComponents.ptToText(cookTime) { + let cookString = "Cooking time: \(cookTimeString)" + titleSection.columns[1].add(space: 10) + titleSection.columns[1].add(textObject: PDFSimpleText(text: cookString, style: textStyle)) + } + + document.add(section: titleSection) + + // Ingredients + var ingr = "" + for ingredient in recipe.recipeIngredient { + ingr.append("• \(ingredient)\n") + } + + let section = PDFSection(columnWidths: [0.5, 0.5]) + section.columns[0].add(textObject: PDFSimpleText(text: ingr, style: textStyle)) + document.add(space: 20) + document.add(section: section) + + // Instructions + var instr = "" + for instruction in recipe.recipeInstructions { + instr += instruction + "\n\n" + } + document.add(space: 10) + document.add(textObject: PDFSimpleText(text: instr, style: textStyle)) + + // Generate PDF + let generator = PDFGenerator(document: document) + + do { + return try generator.generateURL(filename: "\(recipe.name).pdf") + } catch { + return nil + } + } + + func createText(recipe: RecipeDetail) -> String { + var recipeString = "" + recipeString.append("☛ " + recipe.name + "\n") + recipeString.append(recipe.description + "\n\n") + + for ingredient in recipe.recipeIngredient { + recipeString.append("•" + ingredient + "\n") + } + recipeString.append("\n") + var counter = 1 + for instruction in recipe.recipeInstructions { + recipeString.append("\(counter). " + instruction + "\n") + counter += 1 + } + return recipeString + } + + func createJson(recipe: RecipeDetail) -> Data? { + return JSONEncoder.safeEncode(recipe) + } +} + + +private extension RecipeExporter { + func resizeImage(image: UIImage, targetHeight: CGFloat) -> UIImage? { + let size = image.size + + let heightRatio = targetHeight / size.height + let newSize = CGSize(width: size.width * heightRatio, height: targetHeight) + + let renderer = UIGraphicsImageRenderer(size: newSize) + let resizedImage = renderer.image { (context) in + image.draw(in: CGRect(origin: .zero, size: newSize)) + } + + return resizedImage + } + + func cropAndResizeImage(image: UIImage, targetHeight: CGFloat) -> UIImage? { + let originalSize = image.size + let targetAspectRatio = 4.0 / 3.0 + var cropRect: CGRect + + // Calculate the rect to crop to 4:3 + if originalSize.width / originalSize.height > targetAspectRatio { + // Image is wider than 4:3, crop width + let croppedWidth = originalSize.height * targetAspectRatio + let cropX = (originalSize.width - croppedWidth) / 2.0 + cropRect = CGRect(x: cropX, y: 0, width: croppedWidth, height: originalSize.height) + } else { + // Image is narrower than 4:3, crop height + let croppedHeight = originalSize.width / targetAspectRatio + let cropY = (originalSize.height - croppedHeight) / 2.0 + cropRect = CGRect(x: 0, y: cropY, width: originalSize.width, height: croppedHeight) + } + + // Crop the image + guard let croppedCGImage = image.cgImage?.cropping(to: cropRect) else { return nil } + let croppedImage = UIImage(cgImage: croppedCGImage) + + // Resize the cropped image + let resizeRatio = targetHeight / croppedImage.size.height + let resizedSize = CGSize(width: croppedImage.size.width * resizeRatio, height: targetHeight) + + let renderer = UIGraphicsImageRenderer(size: resizedSize) + let resizedImage = renderer.image { (context) in + croppedImage.draw(in: CGRect(origin: .zero, size: resizedSize)) + } + + return resizedImage + } +} diff --git a/Nextcloud Cookbook iOS Client/Views/MainView.swift b/Nextcloud Cookbook iOS Client/Views/MainView.swift index e990b57..17e1794 100644 --- a/Nextcloud Cookbook iOS Client/Views/MainView.swift +++ b/Nextcloud Cookbook iOS Client/Views/MainView.swift @@ -166,6 +166,7 @@ struct MainView: View { Text("Refresh all") Image(systemName: "icloud.and.arrow.down") } + } label: { Image(systemName: "ellipsis.circle") } @@ -235,7 +236,7 @@ struct RecipeSearchView: View { .navigationDestination(for: Recipe.self) { recipe in RecipeDetailView(viewModel: viewModel, recipe: recipe) } - .searchable(text: $searchText, prompt: "Search recipes") + .searchable(text: $searchText, prompt: "Search recipes/keywords") } .navigationTitle("Search recipe") } @@ -247,7 +248,9 @@ struct RecipeSearchView: View { func recipesFiltered() -> [Recipe] { guard searchText != "" else { return allRecipes } return allRecipes.filter { recipe in - recipe.name.lowercased().contains(searchText.lowercased()) + recipe.name.lowercased().contains(searchText.lowercased()) || // check name for occurence of search term + (recipe.keywords != nil && recipe.keywords!.lowercased().contains(searchText.lowercased())) // check keywords for search term } } } + diff --git a/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift b/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift index 5102728..aa3f51e 100644 --- a/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift +++ b/Nextcloud Cookbook iOS Client/Views/RecipeDetailView.swift @@ -19,6 +19,8 @@ struct RecipeDetailView: View { @State private var presentEditView: Bool = false @State private var presentNutritionPopover: Bool = false @State private var presentKeywordPopover: Bool = false + @State private var presentShareSheet: Bool = false + @State private var sharedURL: URL? = nil var body: some View { ScrollView(showsIndicators: false) { @@ -76,6 +78,7 @@ struct RecipeDetailView: View { RecipeKeywordSection(recipeDetail: recipeDetail) MoreInformationSection(recipeDetail: recipeDetail) } + }.padding(.horizontal, 5) } @@ -85,12 +88,25 @@ struct RecipeDetailView: View { .navigationTitle(showTitle ? recipe.name : "") .toolbar { if recipeDetail != nil { - Button { - presentEditView = true - } label: { - HStack { - Text("Edit") + Menu { + Button { + presentEditView = true + } label: { + HStack { + Text("Edit") + Image(systemName: "pencil") + } } + + Button { + print("Sharing recipe ...") + self.presentShareSheet = true + } label: { + Text("Share recipe") + Image(systemName: "square.and.arrow.up") + } + } label: { + Image(systemName: "ellipsis.circle") } } } @@ -107,6 +123,14 @@ struct RecipeDetailView: View { ) } } + .sheet(isPresented: $presentShareSheet) { + if let recipeDetail = recipeDetail { + ShareView(recipeDetail: recipeDetail, + recipeImage: recipeImage, + presentShareSheet: $presentShareSheet) + } + } + .task { recipeDetail = await viewModel.getRecipe( id: recipe.recipe_id, @@ -136,6 +160,50 @@ struct RecipeDetailView: View { } } +fileprivate struct ShareView: View { + @State var recipeDetail: RecipeDetail + @State var recipeImage: UIImage? + @Binding var presentShareSheet: Bool + + @State var exporter = RecipeExporter() + @State var sharedURL: URL? = nil + + var body: some View { + VStack(alignment: .leading) { + if let url = sharedURL { + ShareLink(item: url, subject: Text("PDF Document")) { + Image(systemName: "doc") + Text("Share as PDF") + } + .foregroundStyle(.primary) + .bold() + .padding() + } + + ShareLink(item: exporter.createText(recipe: recipeDetail), subject: Text("Recipe")) { + Image(systemName: "ellipsis.message") + Text("Share as text") + } + .foregroundStyle(.primary) + .bold() + .padding() + + /*ShareLink(item: exporter.createJson(recipe: recipeDetail), subject: Text("Recipe")) { + Image(systemName: "doc.badge.gearshape") + Text("Share as JSON") + } + .foregroundStyle(.primary) + .bold() + .padding() + */ + } + .task { + self.sharedURL = exporter.createPDF(recipe: recipeDetail, image: recipeImage) + } + + } +} + fileprivate struct RecipeDurationSection: View { @State var recipeDetail: RecipeDetail