summaryrefslogtreecommitdiff
path: root/Sora
diff options
context:
space:
mode:
Diffstat (limited to 'Sora')
-rw-r--r--Sora/Data/JSONFileDocument.swift26
-rw-r--r--Sora/Data/Settings/SettingsManager.swift4
-rw-r--r--Sora/Sora.entitlements2
-rw-r--r--Sora/Views/Settings/Section/SettingsExportView.swift78
-rw-r--r--Sora/Views/Settings/SettingsView.swift4
5 files changed, 113 insertions, 1 deletions
diff --git a/Sora/Data/JSONFileDocument.swift b/Sora/Data/JSONFileDocument.swift
new file mode 100644
index 0000000..bc41616
--- /dev/null
+++ b/Sora/Data/JSONFileDocument.swift
@@ -0,0 +1,26 @@
+import SwiftUI
+import UniformTypeIdentifiers
+
+struct JSONFileDocument: FileDocument {
+ static var readableContentTypes: [UTType] { [.json] }
+
+ var data: Data
+
+ init(_ data: Data) throws {
+ self.data = data
+ }
+
+ init(configuration: ReadConfiguration) throws {
+ guard let data = configuration.file.regularFileContents else {
+ throw CocoaError(.fileReadCorruptFile)
+ }
+
+ self.data = data
+ }
+
+ func fileWrapper(
+ configuration: WriteConfiguration // swiftlint:disable:this unused_parameter
+ ) throws -> FileWrapper {
+ FileWrapper(regularFileWithContents: data)
+ }
+}
diff --git a/Sora/Data/Settings/SettingsManager.swift b/Sora/Data/Settings/SettingsManager.swift
index c9590be..7d38d0f 100644
--- a/Sora/Data/Settings/SettingsManager.swift
+++ b/Sora/Data/Settings/SettingsManager.swift
@@ -125,6 +125,10 @@ class SettingsManager: ObservableObject {
bookmarks.removeAll { $0.id == id }
}
+ func exportBookmarks() throws -> Data {
+ try JSONEncoder().encode(bookmarks)
+ }
+
// MARK: - Search History Management
func removeSearchHistoryEntry(at offsets: IndexSet) {
searchHistory.remove(atOffsets: offsets)
diff --git a/Sora/Sora.entitlements b/Sora/Sora.entitlements
index a2c464c..264aa0a 100644
--- a/Sora/Sora.entitlements
+++ b/Sora/Sora.entitlements
@@ -6,7 +6,7 @@
<true/>
<key>com.apple.security.assets.pictures.read-write</key>
<true/>
- <key>com.apple.security.files.user-selected.read-only</key>
+ <key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
diff --git a/Sora/Views/Settings/Section/SettingsExportView.swift b/Sora/Views/Settings/Section/SettingsExportView.swift
new file mode 100644
index 0000000..2e9723e
--- /dev/null
+++ b/Sora/Views/Settings/Section/SettingsExportView.swift
@@ -0,0 +1,78 @@
+import SwiftUI
+import UniformTypeIdentifiers
+
+struct SettingsExportView: View {
+ @EnvironmentObject private var settings: SettingsManager
+ @State private var isFileExporterPresented = false
+ @State private var exportError: Error?
+
+ var body: some View {
+ Button("Export Bookmarks") {
+ exportBookmarksToFile()
+ }
+ #if os(macOS)
+ .frame(maxWidth: .infinity, alignment: .trailing)
+ .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
+ .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.")
+ }
+ }
+
+ private func exportBookmarksToFile() {
+ do {
+ #if os(macOS)
+ _ = try settings.exportBookmarks()
+ isFileExporterPresented = true
+
+ #elseif os(iOS)
+ let data = try settings.exportBookmarks()
+ let temporaryURL = FileManager.default.temporaryDirectory
+ .appendingPathComponent("sora_bookmarks.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
+ }
+ }
+}
diff --git a/Sora/Views/Settings/SettingsView.swift b/Sora/Views/Settings/SettingsView.swift
index 4f00cbd..82738c8 100644
--- a/Sora/Views/Settings/SettingsView.swift
+++ b/Sora/Views/Settings/SettingsView.swift
@@ -22,6 +22,10 @@ struct SettingsView: View {
SettingsSearchView()
}
+ Section(header: Text("Export")) {
+ SettingsExportView()
+ }
+
Section(header: Text("Settings")) {
Button("Reset to Defaults") {
settings.resetToDefaults()