Skip to content

Commit 2ad033d

Browse files
committed
export feature
1 parent dd66219 commit 2ad033d

6 files changed

Lines changed: 215 additions & 34 deletions

File tree

iOS/ScriptWidget.xcodeproj/project.pbxproj

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
5AC769492CD370C60022A138 /* ExportImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC769482CD370BC0022A138 /* ExportImportView.swift */; };
10+
5AC386D22CDA09800027B976 /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC386D12CDA09760027B976 /* ImportView.swift */; };
11+
5AC386D42CDA0CB10027B976 /* ImportExportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC386D32CDA0CA90027B976 /* ImportExportManager.swift */; };
12+
5AC769492CD370C60022A138 /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC769482CD370BC0022A138 /* ExportView.swift */; };
1113
D132336528E08C15002C26A2 /* ScriptLiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D132336428E08C15002C26A2 /* ScriptLiveActivityManager.swift */; };
1214
D132336728E09630002C26A2 /* ScriptDynamicIslandCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D132336628E09630002C26A2 /* ScriptDynamicIslandCreator.swift */; };
1315
D13832AE25A1EB6B00DC9C20 /* CreateGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D13832AD25A1EB6B00DC9C20 /* CreateGuideView.swift */; };
@@ -333,7 +335,9 @@
333335
/* End PBXCopyFilesBuildPhase section */
334336

335337
/* Begin PBXFileReference section */
336-
5AC769482CD370BC0022A138 /* ExportImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportImportView.swift; sourceTree = "<group>"; };
338+
5AC386D12CDA09760027B976 /* ImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportView.swift; sourceTree = "<group>"; };
339+
5AC386D32CDA0CA90027B976 /* ImportExportManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportExportManager.swift; sourceTree = "<group>"; };
340+
5AC769482CD370BC0022A138 /* ExportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportView.swift; sourceTree = "<group>"; };
337341
D132336428E08C15002C26A2 /* ScriptLiveActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptLiveActivityManager.swift; sourceTree = "<group>"; };
338342
D132336628E09630002C26A2 /* ScriptDynamicIslandCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptDynamicIslandCreator.swift; sourceTree = "<group>"; };
339343
D13832AD25A1EB6B00DC9C20 /* CreateGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGuideView.swift; sourceTree = "<group>"; };
@@ -963,7 +967,8 @@
963967
F240AC7F27B2D6AB00D249EA /* Settings */ = {
964968
isa = PBXGroup;
965969
children = (
966-
5AC769482CD370BC0022A138 /* ExportImportView.swift */,
970+
5AC769482CD370BC0022A138 /* ExportView.swift */,
971+
5AC386D12CDA09760027B976 /* ImportView.swift */,
967972
F2C5C5E327AF5B5400797C5B /* AppIconsView.swift */,
968973
F2C5C5DC27AF5A2F00797C5B /* BuyMeCoffeeView.swift */,
969974
F2BC995725CDABE200285C7B /* SettingsView.swift */,
@@ -1202,6 +1207,7 @@
12021207
F296EA1425E88D1100E87A4A /* Manager */ = {
12031208
isa = PBXGroup;
12041209
children = (
1210+
5AC386D32CDA0CA90027B976 /* ImportExportManager.swift */,
12051211
F2C5C5E127AF5B2000797C5B /* StoreManager.swift */,
12061212
F23A40BA262492FF0035CBA7 /* DeepLinkManager.swift */,
12071213
);
@@ -1674,6 +1680,7 @@
16741680
F29119642793035E00B860B0 /* ScriptWidgetAttributeTextAlignmentModifier.swift in Sources */,
16751681
F291194C2793035E00B860B0 /* ScriptWidgetRuntimeElement.swift in Sources */,
16761682
D1BD329728DF0D9600F217ED /* ScriptCodePreviewDataObject.swift in Sources */,
1683+
5AC386D42CDA0CB10027B976 /* ImportExportManager.swift in Sources */,
16771684
D1F9883228CCE7BF00C56B06 /* ScriptCodePreviewConsoleView.swift in Sources */,
16781685
F291196E2793035E00B860B0 /* ScriptWidgetElementTagImage.swift in Sources */,
16791686
D1403DED2552FEB40076F87C /* TOCropScrollView.m in Sources */,
@@ -1686,7 +1693,7 @@
16861693
F240AC7827B2CE1C00D249EA /* TabBarAccessor.swift in Sources */,
16871694
F29119622793035E00B860B0 /* ScriptWidgetAttributeFontModifier.swift in Sources */,
16881695
D149EF67255994CA00A24CAD /* TOCropWidgetSizeHelper.m in Sources */,
1689-
5AC769492CD370C60022A138 /* ExportImportView.swift in Sources */,
1696+
5AC769492CD370C60022A138 /* ExportView.swift in Sources */,
16901697
D1CBAED228D5B13400DE4E65 /* ScriptLiveActivityAttributes.swift in Sources */,
16911698
F29119562793035E00B860B0 /* ScriptWidgetAttributeRotation3DEffectModifier.swift in Sources */,
16921699
D1403DEE2552FEB40076F87C /* TOCropView.m in Sources */,
@@ -1699,6 +1706,7 @@
16991706
F291197E2793035E00B860B0 /* ScriptWidgetRuntimePromise.swift in Sources */,
17001707
D1403E0D2552FF860076F87C /* CropViewController.swift in Sources */,
17011708
F27B247325D83F6D00F84330 /* ScreenData.swift in Sources */,
1709+
5AC386D22CDA09800027B976 /* ImportView.swift in Sources */,
17021710
D1403E2D25530A0B0076F87C /* PhotoPickerView.swift in Sources */,
17031711
F224FE5927A959E900617AF6 /* ImageListView.swift in Sources */,
17041712
);

iOS/ScriptWidget/App/Settings/ExportImportView.swift

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import SwiftUI
2+
import UniformTypeIdentifiers
3+
4+
struct ExportView: View {
5+
@State private var isExporting = false
6+
@State private var progress: Float = 0
7+
@State private var statusMessage = ""
8+
@State private var exportedFileURL: URL?
9+
@State private var showShareSheet = false
10+
11+
var body: some View {
12+
VStack(spacing: 20) {
13+
// Export button and progress bar
14+
VStack {
15+
Button(action: {
16+
startExport()
17+
}) {
18+
Text("Export")
19+
.font(.title2)
20+
.fontWeight(.bold)
21+
.foregroundColor(.white)
22+
.frame(maxWidth: .infinity)
23+
.padding(.vertical, 16)
24+
.background(Color.blue)
25+
.cornerRadius(15)
26+
}
27+
.padding(.horizontal, 20)
28+
.disabled(isExporting)
29+
30+
if isExporting {
31+
VStack {
32+
ProgressView(value: progress)
33+
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
34+
.scaleEffect(1.5)
35+
.padding(.top, 10)
36+
37+
Text(statusMessage)
38+
.font(.caption)
39+
.padding(.top, 5)
40+
}
41+
}
42+
}
43+
}
44+
.padding()
45+
.sheet(isPresented: $showShareSheet, content: {
46+
if let fileURL = exportedFileURL {
47+
ActivityViewController(activityItems: [fileURL])
48+
}
49+
})
50+
}
51+
52+
private func startExport() {
53+
isExporting = true
54+
progress = 0
55+
statusMessage = "Starting export..."
56+
57+
Task {
58+
do {
59+
let zipFileURL = try await ExportManager.exportAllScripts { currentProgress, message in
60+
DispatchQueue.main.async {
61+
self.progress = currentProgress
62+
self.statusMessage = message
63+
}
64+
}
65+
66+
DispatchQueue.main.async {
67+
self.exportedFileURL = zipFileURL
68+
self.showShareSheet = true
69+
self.isExporting = false
70+
}
71+
} catch {
72+
DispatchQueue.main.async {
73+
self.statusMessage = "Error: \(error.localizedDescription)"
74+
self.isExporting = false
75+
}
76+
}
77+
}
78+
}
79+
}
80+
81+
struct ActivityViewController: UIViewControllerRepresentable {
82+
let activityItems: [Any]
83+
84+
func makeUIViewController(context: Context) -> UIActivityViewController {
85+
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
86+
return controller
87+
}
88+
89+
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
90+
}
91+
92+
struct ExportView_Previews: PreviewProvider {
93+
static var previews: some View {
94+
ExportView()
95+
}
96+
}
97+
98+
// End of file. No additional code.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import SwiftUI
2+
3+
struct ImportView: View {
4+
@State private var isImporting = false
5+
6+
var body: some View {
7+
VStack(spacing: 20) {
8+
// Import button and progress bar
9+
VStack {
10+
Button(action: {
11+
isImporting.toggle()
12+
}) {
13+
Text("Import")
14+
.font(.title2)
15+
.fontWeight(.bold)
16+
.foregroundColor(.white)
17+
.frame(maxWidth: .infinity)
18+
.padding(.vertical, 16)
19+
.background(Color.green)
20+
.cornerRadius(15)
21+
}
22+
.padding(.horizontal, 20)
23+
24+
if isImporting {
25+
ProgressView()
26+
.progressViewStyle(LinearProgressViewStyle(tint: .green))
27+
.scaleEffect(1.5)
28+
.padding(.top, 10)
29+
}
30+
}
31+
}
32+
.padding()
33+
}
34+
35+
36+
}
37+
38+
struct ImportView_Previews: PreviewProvider {
39+
static var previews: some View {
40+
ImportView()
41+
}
42+
}

iOS/ScriptWidget/App/Settings/SettingsView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ struct SettingsView: View {
7070

7171
GroupBox (label: SettingsLabelView(title: "Export & Import", image: "info.circle")) {
7272

73-
NavigationLink(destination: ExportImportView()) {
73+
NavigationLink(destination: ExportView()) {
7474
SettingsTextRowView(name: "Export", content: "")
7575
}
76-
NavigationLink(destination: ExportImportView()) {
76+
NavigationLink(destination: ImportView()) {
7777
SettingsTextRowView(name: "Import", content: "")
7878
}
7979
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// ImportExportManager.swift
3+
// ScriptWidget
4+
//
5+
// Created by eevv on 11/5/24.
6+
//
7+
//
8+
9+
import Foundation
10+
import ZipArchive
11+
12+
class ExportManager {
13+
static func exportAllScripts(progressCallback: @escaping (Float, String) -> Void) async throws -> URL {
14+
// Create a temporary directory for exporting scripts
15+
let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent("scriptwidget-export-all")
16+
let zipFilePath = FileManager.default.temporaryDirectory.appendingPathComponent("scriptwidget-export-all.zip")
17+
18+
// Remove existing temp directory and zip file if they exist
19+
try? FileManager.default.removeItem(at: tempDir)
20+
try? FileManager.default.removeItem(at: zipFilePath)
21+
22+
// Create the temporary directory
23+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true, attributes: nil)
24+
25+
// List all scripts
26+
let scripts = await sharedScriptManager.asyncListScripts()
27+
let totalScripts = Float(scripts.count)
28+
29+
// Export each script to the temporary directory
30+
for (index, script) in scripts.enumerated() {
31+
let scriptPath = tempDir.appendingPathComponent(script.name)
32+
let succeed = sharedScriptManager.exportScript(model: script, toPath: scriptPath)
33+
34+
// Update progress with script name
35+
let progress = Float(index + 1) / totalScripts
36+
await MainActor.run {
37+
progressCallback(progress * 0.9, "Exporting: \(script.name)") // 90% of progress for exporting scripts
38+
}
39+
}
40+
41+
// Create zip file from the temporary directory
42+
await MainActor.run {
43+
progressCallback(0.95, "Creating zip file") // 95% progress when starting zip creation
44+
}
45+
let success = SSZipArchive.createZipFile(atPath: zipFilePath.path, withContentsOfDirectory: tempDir.path)
46+
47+
if success {
48+
// Remove the temporary directory
49+
try? FileManager.default.removeItem(at: tempDir)
50+
51+
// Final progress update
52+
await MainActor.run {
53+
progressCallback(1.0, "Export completed")
54+
}
55+
56+
return zipFilePath
57+
} else {
58+
throw NSError(domain: "ExportManager", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to create zip file"])
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)