summaryrefslogtreecommitdiff
path: root/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-09-22 02:22:32 -0700
committerFuwn <[email protected]>2025-09-22 02:22:32 -0700
commit1d634eafc0c316e0a88d1e321a9025f1c944686a (patch)
tree673eca151426d0db04ab4cbb3ff10f9ed2a9c8df /Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
parentfeat: Development commit (diff)
downloadsora-testing-1d634eafc0c316e0a88d1e321a9025f1c944686a.tar.xz
sora-testing-1d634eafc0c316e0a88d1e321a9025f1c944686a.zip
feat: Development commit
Diffstat (limited to 'Sora/Views/Settings/Section/SettingsSectionImportExportView.swift')
-rw-r--r--Sora/Views/Settings/Section/SettingsSectionImportExportView.swift131
1 files changed, 131 insertions, 0 deletions
diff --git a/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift b/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
new file mode 100644
index 0000000..b74fe4b
--- /dev/null
+++ b/Sora/Views/Settings/Section/SettingsSectionImportExportView.swift
@@ -0,0 +1,131 @@
+import SwiftUI
+import UniformTypeIdentifiers
+
+struct SettingsSectionImportExportView: View {
+ @EnvironmentObject private var settings: SettingsManager
+ @State private var isFileExporterPresented = false
+ @State private var isFileImporterPresented = false
+ @State private var exportError: Error?
+ @State private var importError: Error?
+ private let dateFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd_HH-mm-ss"
+ return formatter
+ }()
+
+ var body: some View {
+ Group {
+ Button("Import Bookmarks") {
+ isFileImporterPresented = true
+ }
+
+ Button("Export Bookmarks") {
+ exportBookmarksToFile()
+ }
+ }
+ #if os(macOS)
+ .trailingFrame()
+ .fileExporter(
+ isPresented: $isFileExporterPresented,
+ document: try? JSONFileDocument(settings.exportBookmarks()),
+ contentType: .json,
+ defaultFilename: "sora_bookmarks.json"
+ ) { result in
+ switch result {
+ case .success:
+ break
+
+ case .failure(let error):
+ exportError = error
+ }
+ }
+ #endif
+ .fileImporter(
+ isPresented: $isFileImporterPresented,
+ allowedContentTypes: [.json],
+ allowsMultipleSelection: false
+ ) { result in
+ handleImportResult(result)
+ }
+ .alert(
+ "Export Failed",
+ isPresented: Binding(
+ get: { exportError != nil },
+ set: { if !$0 { exportError = nil } }
+ )
+ ) {
+ Button("OK", role: .cancel) { () }
+ } message: {
+ Text(exportError?.localizedDescription ?? "An unknown error occurred while exporting.")
+ }
+ .alert(
+ "Import Failed",
+ isPresented: Binding(
+ get: { importError != nil },
+ set: { if !$0 { importError = nil } }
+ )
+ ) {
+ Button("OK", role: .cancel) { () }
+ } message: {
+ Text(importError?.localizedDescription ?? "An unknown error occurred while importing.")
+ }
+ }
+
+ private func exportBookmarksToFile() {
+ do {
+ let data = try settings.exportBookmarks()
+ let timestamp = dateFormatter.string(from: Date())
+
+ #if os(macOS)
+ _ = data
+ isFileExporterPresented = true
+ #elseif os(iOS)
+ let temporaryURL = FileManager.default.temporaryDirectory
+ .appendingPathComponent("sora_bookmarks_\(timestamp).json")
+
+ try data.write(to: temporaryURL)
+
+ let activityController = UIActivityViewController(
+ activityItems: [temporaryURL],
+ applicationActivities: nil
+ )
+
+ if let windowScene = UIApplication.shared.connectedScenes
+ .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
+ let rootViewController = windowScene.windows.first?.rootViewController
+ {
+ activityController.popoverPresentationController?.sourceView = rootViewController.view
+
+ rootViewController.present(activityController, animated: true)
+ }
+
+ activityController.completionWithItemsHandler = { _, _, _, _ in
+ try? FileManager.default.removeItem(at: temporaryURL)
+ }
+ #endif
+ } catch {
+ exportError = error
+ }
+ }
+
+ private func handleImportResult(_ result: Result<[URL], Error>) {
+ do {
+ guard let selectedFile = try result.get().first else { return }
+ guard selectedFile.startAccessingSecurityScopedResource() else {
+ throw ImportError.accessDenied
+ }
+
+ defer { selectedFile.stopAccessingSecurityScopedResource() }
+
+ let data = try Data(contentsOf: selectedFile)
+
+ try settings.importBookmarks(from: data)
+ } catch {
+ importError = error
+ }
+ }
+
+ private enum ImportError: Error {
+ case accessDenied
+ }
+}