Skip to content

Commit dac2136

Browse files
committed
refactor
1 parent b8b0c60 commit dac2136

File tree

3 files changed

+170
-75
lines changed

3 files changed

+170
-75
lines changed

Examples/Examples.xcodeproj/project.pbxproj

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
79615DF92C3DA92C005AE6E0 /* CustomDump in Frameworks */ = {isa = PBXBuildFile; productRef = 79615DF82C3DA92C005AE6E0 /* CustomDump */; };
3737
796298992AEBBA77000AA957 /* MFAFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796298982AEBBA77000AA957 /* MFAFlow.swift */; };
3838
7962989D2AEBC6F9000AA957 /* SVGView in Frameworks */ = {isa = PBXBuildFile; productRef = 7962989C2AEBC6F9000AA957 /* SVGView */; };
39+
796B32BE2C4559E900DDD7B4 /* IdentifiedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 796B32BD2C4559E900DDD7B4 /* IdentifiedCollections */; };
3940
79719ECE2ADF26C400737804 /* Supabase in Frameworks */ = {isa = PBXBuildFile; productRef = 79719ECD2ADF26C400737804 /* Supabase */; };
4041
797D664A2B46A1D8007592ED /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797D66492B46A1D8007592ED /* Dependencies.swift */; };
4142
797EFB662BABD82A00098D6B /* BucketList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797EFB652BABD82A00098D6B /* BucketList.swift */; };
@@ -163,6 +164,7 @@
163164
isa = PBXFrameworksBuildPhase;
164165
buildActionMask = 2147483647;
165166
files = (
167+
796B32BE2C4559E900DDD7B4 /* IdentifiedCollections in Frameworks */,
166168
792404E32C3473EC002959B3 /* Supabase in Frameworks */,
167169
79615DF92C3DA92C005AE6E0 /* CustomDump in Frameworks */,
168170
);
@@ -403,6 +405,7 @@
403405
packageProductDependencies = (
404406
792404E22C3473EC002959B3 /* Supabase */,
405407
79615DF82C3DA92C005AE6E0 /* CustomDump */,
408+
796B32BD2C4559E900DDD7B4 /* IdentifiedCollections */,
406409
);
407410
productName = SupaDrive;
408411
productReference = 792404B72C3454A9002959B3 /* SupaDrive.app */;
@@ -1191,6 +1194,11 @@
11911194
package = 7962989B2AEBC6F9000AA957 /* XCRemoteSwiftPackageReference "SVGView" */;
11921195
productName = SVGView;
11931196
};
1197+
796B32BD2C4559E900DDD7B4 /* IdentifiedCollections */ = {
1198+
isa = XCSwiftPackageProductDependency;
1199+
package = 7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */;
1200+
productName = IdentifiedCollections;
1201+
};
11941202
79719ECD2ADF26C400737804 /* Supabase */ = {
11951203
isa = XCSwiftPackageProductDependency;
11961204
productName = Supabase;

Examples/SupaDrive/AppView.swift

+159-74
Original file line numberDiff line numberDiff line change
@@ -8,58 +8,98 @@
88
import CustomDump
99
import Supabase
1010
import SwiftUI
11+
import IdentifiedCollections
12+
13+
@MainActor
14+
@Observable
15+
final class AppModel {
16+
var panels: IdentifiedArrayOf<PanelModel> {
17+
didSet {
18+
bindPanelModels()
19+
}
20+
}
1121

12-
struct AppView: View {
13-
@State var path: [String]
14-
@State var selectedItemPerPath: [String: FileObject] = [:]
22+
init(panels: IdentifiedArrayOf<PanelModel>) {
23+
self.panels = panels
24+
bindPanelModels()
25+
}
26+
27+
var path: String {
28+
panels.last?.path ?? ""
29+
}
30+
31+
var pathComponents: [String] {
32+
path.components(separatedBy: "/")
33+
}
34+
35+
var selectedFile: FileObject? {
36+
panels.last?.selectedItem
37+
}
38+
39+
private func bindPanelModels() {
40+
for panel in panels {
41+
panel.onSelectItem = { [weak self, weak panel] item in
42+
guard let self, let panel else { return }
43+
44+
// self.panels.append(PanelModel(path: self.path.appending))
45+
//
46+
// if let name = item.name, item.id == nil {
47+
// self.panels.replaceSubrange(
48+
// (index + 1)...,
49+
// with: [PanelModel(path: self.path.appending("/\(name)"))]
50+
// )
51+
// }
52+
}
53+
}
54+
}
55+
}
1556

16-
@State var reload = UUID()
57+
struct AppView: View {
58+
@Bindable var model: AppModel
1759

1860
var body: some View {
1961
VStack(alignment: .leading, spacing: 0) {
2062
breadcrump
2163

2264
ScrollView(.horizontal) {
2365
HStack {
24-
ForEach(path.indices, id: \.self) { pathIndex in
66+
ForEach(model.panels) { panel in
2567
PanelView(
26-
path: path[0 ... pathIndex].joined(separator: "/"),
27-
selectedItem: Binding(
28-
get: {
29-
selectedItemPerPath[path[pathIndex]]
30-
},
31-
set: { newValue in
32-
selectedItemPerPath[path[pathIndex]] = newValue
33-
34-
if let newValue, let name = newValue.name, newValue.id == nil {
35-
path.replaceSubrange((pathIndex + 1)..., with: [name])
36-
} else {
37-
path.replaceSubrange((pathIndex + 1)..., with: [])
38-
}
39-
}
40-
)
68+
model: panel
69+
// model: PanelModel(path: path[0 ... pathIndex].joined(separator: "/"))
70+
// path: path[0 ... pathIndex].joined(separator: "/"),
71+
// selectedItem: Binding(
72+
// get: {
73+
// selectedItemPerPath[path[pathIndex]]
74+
// },
75+
// set: { newValue in
76+
// selectedItemPerPath[path[pathIndex]] = newValue
77+
//
78+
// if let newValue, let name = newValue.name, newValue.id == nil {
79+
// path.replaceSubrange((pathIndex + 1)..., with: [name])
80+
// } else {
81+
// path.replaceSubrange((pathIndex + 1)..., with: [])
82+
// }
83+
// }
84+
// )
4185
)
4286
.frame(width: 200)
4387
}
4488
}
4589
}
4690
}
4791
.overlay(alignment: .trailing) {
48-
if
49-
let lastPath = path.last,
50-
let selectedItem = selectedItemPerPath[lastPath],
51-
selectedItem.id != nil
52-
{
92+
if let selectedFile = model.selectedFile {
5393
Form {
54-
Text(selectedItem.name ?? "")
94+
Text(selectedFile.name ?? "")
5595
.font(.title2)
5696
Divider()
5797

58-
if let contentLenth = selectedItem.metadata?["contentLength"]?.intValue {
98+
if let contentLenth = selectedFile.metadata?["contentLength"]?.intValue {
5999
LabeledContent("Size", value: "\(contentLenth)")
60100
}
61101

62-
if let mimeType = selectedItem.metadata?["mimetype"]?.stringValue {
102+
if let mimeType = selectedFile.metadata?["mimetype"]?.stringValue {
63103
LabeledContent("MIME Type", value: mimeType)
64104
}
65105
}
@@ -70,56 +110,116 @@ struct AppView: View {
70110
.transition(.move(edge: .trailing))
71111
}
72112
}
73-
.animation(.default, value: path)
74-
.animation(.default, value: selectedItemPerPath)
113+
.animation(.default, value: model.path)
114+
.animation(.default, value: model.selectedFile)
75115
}
76116

77117
var breadcrump: some View {
78118
HStack {
79-
ForEach(Array(zip(path.indices, path)), id: \.0) { idx, path in
119+
ForEach(Array(zip(model.pathComponents.indices, model.pathComponents)), id: \.0) { idx, path in
80120
Button(path) {
81-
self.path.replaceSubrange((idx + 1)..., with: [])
121+
// self.path.replaceSubrange((idx + 1)..., with: [])
82122
}
83123
.buttonStyle(.plain)
84124

85-
if idx != self.path.indices.last {
86-
Text(">")
87-
}
125+
// if idx != self.path.indices.last {
126+
// Text(">")
127+
// }
88128
}
89129
}
90130
.padding()
91131
}
92132
}
93133

134+
struct DragValue: Codable {
135+
let path: String
136+
let object: FileObject
137+
}
138+
139+
@MainActor
140+
@Observable
141+
final class PanelModel: Identifiable {
142+
let path: String
143+
var selectedItem: FileObject?
144+
145+
var items: [FileObject] = []
146+
147+
@ObservationIgnored
148+
var onSelectItem: @MainActor (FileObject) -> Void = { _ in }
149+
150+
init(path: String) {
151+
self.path = path
152+
}
153+
154+
func load() async {
155+
do {
156+
let files = try await supabase.storage.from("main").list(path: path)
157+
items = files.filter { $0.name?.hasPrefix(".") == false }
158+
} catch {
159+
dump(error)
160+
}
161+
}
162+
163+
func didSelectItem(_ item: FileObject) {
164+
self.selectedItem = item
165+
onSelectItem(item)
166+
}
167+
168+
func newFolderButtonTapped() async {
169+
do {
170+
try await supabase.storage.from("main")
171+
.upload(path: "\(path)/Untiltled/.dummy", file: Data())
172+
} catch {
173+
174+
}
175+
}
176+
177+
func uploadFile(at url: URL) async {
178+
let path = url.lastPathComponent
179+
180+
do {
181+
let file = try Data(contentsOf: url)
182+
try await supabase.storage.from("main")
183+
.upload(path: "\(self.path)/\(path)", file: file)
184+
} catch {}
185+
}
186+
}
187+
94188
struct PanelView: View {
95-
var path: String
96-
@Binding var selectedItem: FileObject?
189+
@Bindable var model: PanelModel
97190

98191
@State private var isDraggingOver = false
99-
@State private var items: [FileObject] = []
100-
101-
@State private var reload = UUID()
102192

103193
var body: some View {
104194
List {
105-
ForEach(items) { item in
106-
Button {
107-
selectedItem = item
108-
} label: {
109-
Text(item.name ?? "")
110-
.bold(selectedItem == item)
195+
ForEach(model.items) { item in
196+
Text(item.name ?? "")
197+
.bold(model.selectedItem == item)
198+
.onTapGesture {
199+
model.didSelectItem(item)
200+
}
201+
.onDrag {
202+
let data = try! JSONEncoder().encode(DragValue(path: model.path, object: item))
203+
let string = String(decoding: data, as: UTF8.self)
204+
return NSItemProvider(object: string as NSString)
205+
}
206+
}
207+
.onInsert(of: ["public.text"]) { index, items in
208+
for item in items {
209+
Task {
210+
guard let data = try await item.loadItem(forTypeIdentifier: "public.text") as? Data,
211+
let value = try? JSONDecoder().decode(DragValue.self, from: data) else {
212+
return
213+
}
214+
215+
self.model.items.insert(value.object, at: index)
216+
}
111217
}
218+
print(index, items)
112219
}
113-
.buttonStyle(.plain)
114220
}
115-
.task(id: reload) {
116-
do {
117-
let files = try await supabase.storage.from("main").list(path: path)
118-
119-
items = files.filter { $0.name?.hasPrefix(".") == false }
120-
} catch {
121-
dump(error)
122-
}
221+
.task {
222+
await model.load()
123223
}
124224
.onDrop(of: [.fileURL], isTargeted: $isDraggingOver) { providers in
125225
for provider in providers {
@@ -128,13 +228,8 @@ struct PanelView: View {
128228
return
129229
}
130230

131-
Task { @MainActor in
132-
let path = url.lastPathComponent
133-
let file = try! Data(contentsOf: url)
134-
try! await supabase.storage.from("main")
135-
.upload(path: "\(self.path)/\(path)", file: file)
136-
137-
reload = UUID()
231+
Task {
232+
await model.uploadFile(at: url)
138233
}
139234
}
140235
}
@@ -148,23 +243,13 @@ struct PanelView: View {
148243
.contextMenu {
149244
Button("New folder") {
150245
Task {
151-
try! await supabase.storage.from("main")
152-
.upload(path: "\(path)/Untiltled/.dummy", file: Data())
153-
reload = UUID()
246+
await model.newFolderButtonTapped()
154247
}
155248
}
156249
}
157250
}
158251
}
159252

160-
extension FileObject {
161-
var metadataDump: String {
162-
var output = ""
163-
customDump(metadata, to: &output)
164-
return output
165-
}
166-
}
167-
168253
#Preview {
169-
AppView(path: [])
254+
AppView(model: AppModel(panels: []))
170255
}

Examples/SupaDrive/SupaDriveApp.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ struct SupaDriveApp: App {
2525
var body: some Scene {
2626
WindowGroup {
2727
AuthView { session in
28-
AppView(path: [session.user.id.uuidString.lowercased()])
28+
AppView(
29+
model: AppModel(panels: [PanelModel(path: session.user.id.uuidString.lowercased())])
30+
)
2931
}
3032
}
3133
}

0 commit comments

Comments
 (0)