Add Share Extension for importing recipes via URL
Adds a Share Extension so users can share URLs from Safari (or any app) to open the main app with the ImportURLSheet pre-filled. Uses a custom URL scheme (nextcloud-cookbook://) as the bridge between the extension and the main app. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ struct Nextcloud_Cookbook_iOS_ClientApp: App {
|
||||
@AppStorage("language") var language = Locale.current.language.languageCode?.identifier ?? "en"
|
||||
@AppStorage("appearanceMode") var appearanceMode = AppearanceMode.system.rawValue
|
||||
|
||||
@State private var pendingImportURL: String?
|
||||
|
||||
var colorScheme: ColorScheme? {
|
||||
switch appearanceMode {
|
||||
case AppearanceMode.light.rawValue: return .light
|
||||
@@ -29,7 +31,7 @@ struct Nextcloud_Cookbook_iOS_ClientApp: App {
|
||||
if onboarding {
|
||||
OnboardingView()
|
||||
} else {
|
||||
MainView()
|
||||
MainView(pendingImportURL: $pendingImportURL)
|
||||
}
|
||||
}
|
||||
.preferredColorScheme(colorScheme)
|
||||
@@ -39,6 +41,16 @@ struct Nextcloud_Cookbook_iOS_ClientApp: App {
|
||||
.init(identifier: language ==
|
||||
SupportedLanguage.DEVICE.rawValue ? (Locale.current.language.languageCode?.identifier ?? "en") : language)
|
||||
)
|
||||
.onOpenURL { url in
|
||||
guard !onboarding else { return }
|
||||
guard url.scheme == "nextcloud-cookbook",
|
||||
url.host == "import",
|
||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||
let recipeURL = components.queryItems?.first(where: { $0.name == "url" })?.value,
|
||||
!recipeURL.isEmpty
|
||||
else { return }
|
||||
pendingImportURL = recipeURL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ struct MainView: View {
|
||||
|
||||
@State private var selectedTab: Tab = .recipes
|
||||
|
||||
@Binding var pendingImportURL: String?
|
||||
|
||||
enum Tab {
|
||||
case recipes, search, mealPlan, groceryList
|
||||
}
|
||||
@@ -106,5 +108,19 @@ struct MainView: View {
|
||||
}
|
||||
recipeViewModel.presentLoadingIndicator = false
|
||||
}
|
||||
.onChange(of: pendingImportURL) { _, newURL in
|
||||
guard let url = newURL, !url.isEmpty else { return }
|
||||
selectedTab = .recipes
|
||||
recipeViewModel.pendingImportURL = url
|
||||
// Dismiss any currently open import sheet before re-presenting
|
||||
if recipeViewModel.showImportURLSheet {
|
||||
recipeViewModel.showImportURLSheet = false
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
recipeViewModel.showImportURLSheet = true
|
||||
}
|
||||
} else {
|
||||
recipeViewModel.showImportURLSheet = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ struct ImportURLSheet: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var onImport: (RecipeDetail) -> Void
|
||||
var initialURL: String = ""
|
||||
|
||||
@State private var url: String = ""
|
||||
@State private var isLoading: Bool = false
|
||||
@@ -62,6 +63,11 @@ struct ImportURLSheet: View {
|
||||
} message: {
|
||||
Text(alertMessage)
|
||||
}
|
||||
.onAppear {
|
||||
if !initialURL.isEmpty {
|
||||
url = initialURL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -255,10 +255,15 @@ struct RecipeTabView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $viewModel.showImportURLSheet) {
|
||||
ImportURLSheet { recipeDetail in
|
||||
viewModel.navigateToImportedRecipe(recipeDetail: recipeDetail)
|
||||
}
|
||||
.sheet(isPresented: $viewModel.showImportURLSheet, onDismiss: {
|
||||
viewModel.pendingImportURL = nil
|
||||
}) {
|
||||
ImportURLSheet(
|
||||
onImport: { recipeDetail in
|
||||
viewModel.navigateToImportedRecipe(recipeDetail: recipeDetail)
|
||||
},
|
||||
initialURL: viewModel.pendingImportURL ?? ""
|
||||
)
|
||||
.environmentObject(appState)
|
||||
}
|
||||
.sheet(isPresented: $showManualReorderSheet) {
|
||||
@@ -303,6 +308,7 @@ struct RecipeTabView: View {
|
||||
|
||||
@Published var showImportURLSheet: Bool = false
|
||||
@Published var importedRecipeDetail: RecipeDetail? = nil
|
||||
@Published var pendingImportURL: String? = nil
|
||||
|
||||
func navigateToSettings() {
|
||||
sidebarPath.append(SidebarDestination.settings)
|
||||
|
||||
Reference in New Issue
Block a user