Skip to content

Commit 3f25053

Browse files
committed
Add public API for file uploads on FormModel
1 parent d01e5ff commit 3f25053

File tree

3 files changed

+67
-26
lines changed

3 files changed

+67
-26
lines changed

Sources/LiveViewNative/Stylesheets/Modifiers/Files/FileImporterModifier.swift

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,31 +67,11 @@ struct _FileImporterModifier<R: RootRegistry>: ViewModifier {
6767
) { result in
6868
let id = id.resolve(on: element, in: context)
6969

70-
guard let liveChannel = context.coordinator.liveChannel
71-
else { return }
72-
7370
Task {
7471
do {
75-
let files = try result.get().map({ url in
76-
LiveFile(
77-
try Data(contentsOf: url),
78-
UTType(filenameExtension: url.pathExtension)!.preferredMIMEType!,
79-
url.lastPathComponent,
80-
id
81-
)
82-
})
83-
for file in files {
84-
let replyPayload = try await liveChannel.validateUpload(file)
85-
try await context.coordinator.handleEventReplyPayload(replyPayload)
72+
for url in try result.get() {
73+
try await formModel?.queueFileUpload(id: id, url: url, coordinator: context.coordinator)
8674
}
87-
self.formModel?.fileUploads.append(
88-
contentsOf: files.map({ file in
89-
{
90-
try await liveChannel.uploadFile(file)
91-
print("upload complete")
92-
}
93-
})
94-
)
9575
} catch {
9676
logger.log(level: .error, "\(error.localizedDescription)")
9777
}

Sources/LiveViewNative/ViewModel.swift

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Foundation
99
import Combine
1010
import SwiftUI
1111
import LiveViewNativeCore
12+
import UniformTypeIdentifiers
1213

1314
/// The working-copy data model for a ``LiveView``.
1415
///
@@ -35,6 +36,15 @@ public class LiveViewModel: ObservableObject {
3536
func clearForms() {
3637
self.forms.removeAll()
3738
}
39+
40+
func fileUpload(id: String) -> FormModel.FileUpload? {
41+
for (_, form) in forms {
42+
guard let file = form.fileUploads.first(where: { $0.id == id })
43+
else { continue }
44+
return file
45+
}
46+
return nil
47+
}
3848
}
3949

4050
/// A form model stores the working copy of the data for a specific `<form>` element.
@@ -59,7 +69,12 @@ public class FormModel: ObservableObject, CustomDebugStringConvertible {
5969
/// A publisher that emits a value before sending the form submission event.
6070
var formWillSubmit = PassthroughSubject<(), Never>()
6171

62-
var fileUploads: [() async throws -> ()] = []
72+
var fileUploads: [FileUpload] = []
73+
struct FileUpload {
74+
let id: String
75+
let data: Data
76+
let upload: () async throws -> ()
77+
}
6378

6479
init(elementID: String) {
6580
self.elementID = elementID
@@ -99,10 +114,8 @@ public class FormModel: ObservableObject, CustomDebugStringConvertible {
99114
public func sendSubmitEvent() async throws {
100115
formWillSubmit.send(())
101116
for fileUpload in fileUploads {
102-
print("Upload...")
103-
try await fileUpload()
117+
try await fileUpload.upload()
104118
}
105-
print("All uploads done")
106119
if let submitEvent = submitEvent {
107120
try await pushFormEvent(submitEvent)
108121
} else if let submitAction {
@@ -229,6 +242,44 @@ public class FormModel: ObservableObject, CustomDebugStringConvertible {
229242
data = [:]
230243
}
231244

245+
public func queueFileUpload(
246+
id: String,
247+
url: URL,
248+
coordinator: LiveViewCoordinator<some RootRegistry>
249+
) async throws {
250+
return try await queueFileUpload(
251+
id: id,
252+
contents: try Data(contentsOf: url),
253+
fileType: UTType(filenameExtension: url.pathExtension)!,
254+
name: url.lastPathComponent,
255+
coordinator: coordinator
256+
)
257+
}
258+
259+
public func queueFileUpload(
260+
id: String,
261+
contents: Data,
262+
fileType: UTType,
263+
name: String,
264+
coordinator: LiveViewCoordinator<some RootRegistry>
265+
) async throws {
266+
guard let liveChannel = coordinator.liveChannel
267+
else { return }
268+
269+
let file = LiveFile(
270+
contents,
271+
fileType.preferredMIMEType!,
272+
name,
273+
id
274+
)
275+
let replyPayload = try await liveChannel.validateUpload(file)
276+
try await coordinator.handleEventReplyPayload(replyPayload)
277+
self.fileUploads.append(.init(
278+
id: id,
279+
data: contents,
280+
upload: { try await liveChannel.uploadFile(file) }
281+
))
282+
}
232283
}
233284

234285
private extension URLComponents {

Sources/LiveViewNative/Views/Images/ImageView.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ struct ImageView<Root: RootRegistry>: View {
7676
@LiveElementIgnored
7777
@ClassModifiers<Root> private var modifiers
7878
let overrideImage: SwiftUI.Image?
79+
80+
@LiveAttribute("data-phx-upload-ref")
81+
private var phxUploadRef: String?
82+
83+
@LiveElementIgnored
84+
@EnvironmentObject private var liveViewModel: LiveViewModel
7985

8086
init() {
8187
self.overrideImage = nil
@@ -106,6 +112,10 @@ struct ImageView<Root: RootRegistry>: View {
106112
var image: SwiftUI.Image? {
107113
if let overrideImage {
108114
return overrideImage
115+
} else if let phxUploadRef,
116+
let image = liveViewModel.fileUpload(id: phxUploadRef).flatMap({ UIImage(data: $0.data) })
117+
{
118+
return SwiftUI.Image(uiImage: image)
109119
} else if let systemName {
110120
return SwiftUI.Image(systemName: systemName, variableValue: variableValue)
111121
} else if let name {

0 commit comments

Comments
 (0)