Fix main-thread blocking I/O

This commit is contained in:
世界
2026-02-05 02:37:02 +08:00
parent 4ae421cd04
commit 2e682d746c
17 changed files with 643 additions and 237 deletions
@@ -35,22 +35,23 @@ public struct ProfileShareButton<Label>: View where Label: View {
private var bodyCompat: some View {
ShareButtonCompat(alert, label: label) {
try profile.toContent().generateShareFile()
try await profile.generateShareFileAsync()
}
}
}
public struct ShareButtonCompat<Label>: View where Label: View {
private let label: () -> Label
private let itemURL: () throws -> URL
private let itemURL: () async throws -> URL
@Binding private var alert: AlertState?
#if os(macOS)
@State private var sharePresented = false
@State private var shareItemURL: URL?
#endif
public init(_ alert: Binding<AlertState?>, @ViewBuilder label: @escaping () -> Label, itemURL: @escaping () throws -> URL) {
public init(_ alert: Binding<AlertState?>, @ViewBuilder label: @escaping () -> Label, itemURL: @escaping () async throws -> URL) {
_alert = alert
self.label = label
self.itemURL = itemURL
@@ -60,7 +61,7 @@ public struct ShareButtonCompat<Label>: View where Label: View {
Button(action: shareItem, label: label)
.buttonStyle(.plain)
#if os(macOS)
.background(SharingServicePicker($sharePresented, $alert, itemURL))
.background(SharingServicePicker($sharePresented, $alert, $shareItemURL))
#endif
}
@@ -70,7 +71,9 @@ public struct ShareButtonCompat<Label>: View where Label: View {
await shareItemAsync()
}
#elseif os(macOS)
sharePresented = true
Task {
await shareItemAsync()
}
#endif
}
@@ -103,6 +106,21 @@ public struct ShareButtonCompat<Label>: View where Label: View {
animated: true
)
}
#elseif os(macOS)
private nonisolated func shareItemAsync() async {
do {
let shareItem = try await itemURL()
await MainActor.run {
shareItemURL = shareItem
sharePresented = true
}
} catch {
await MainActor.run {
alert = AlertState(error: error)
}
}
}
#endif
}
@@ -110,12 +128,12 @@ public struct ShareButtonCompat<Label>: View where Label: View {
private struct SharingServicePicker: NSViewRepresentable {
@Binding private var isPresented: Bool
@Binding private var alert: AlertState?
private let item: () throws -> URL
@Binding private var item: URL?
init(_ isPresented: Binding<Bool>, _ alert: Binding<AlertState?>, _ item: @escaping () throws -> URL) {
init(_ isPresented: Binding<Bool>, _ alert: Binding<AlertState?>, _ item: Binding<URL?>) {
_isPresented = isPresented
_alert = alert
self.item = item
_item = item
}
func makeNSView(context _: Context) -> NSView {
@@ -125,14 +143,15 @@ public struct ShareButtonCompat<Label>: View where Label: View {
func updateNSView(_ nsView: NSView, context: Context) {
if isPresented {
do {
let picker = try NSSharingServicePicker(items: [item()])
picker.delegate = context.coordinator
DispatchQueue.main.async {
picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY)
}
} catch {
alert = AlertState(error: error)
guard let item else {
return
}
let picker = NSSharingServicePicker(items: [item])
picker.delegate = context.coordinator
picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY)
DispatchQueue.main.async {
isPresented = false
self.item = nil
}
}
}