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
@@ -78,6 +78,20 @@ public struct ProfileCard: View {
QRCodeSheet(profileName: profile.name, remoteURL: remoteURL)
}
}
.sheet(
isPresented: $viewModel.showQRSShare,
onDismiss: {
viewModel.qrsShareData = nil
viewModel.qrsShareProfileName = nil
},
content: {
if let data = viewModel.qrsShareData, let name = viewModel.qrsShareProfileName {
QRSSheet(profileName: name, profileData: data)
} else {
ProgressView()
}
}
)
#else
.sheet(isPresented: $viewModel.showNewProfile, onDismiss: {
environments.profileUpdate.send()
@@ -98,11 +112,20 @@ public struct ProfileCard: View {
}
}
#endif
.sheet(isPresented: $viewModel.showQRSShare) {
if let profile = selectedProfile, let data = try? profile.origin.toContent().encode() {
QRSSheet(profileName: profile.name, profileData: data)
.sheet(
isPresented: $viewModel.showQRSShare,
onDismiss: {
viewModel.qrsShareData = nil
viewModel.qrsShareProfileName = nil
},
content: {
if let data = viewModel.qrsShareData, let name = viewModel.qrsShareProfileName {
QRSSheet(profileName: name, profileData: data)
} else {
ProgressView()
}
}
}
)
.fileExporter(
isPresented: $viewModel.showExporter,
document: viewModel.exportDocument,
@@ -253,12 +276,10 @@ public struct ProfileCard: View {
}
}
if let data = try? profile.origin.toContent().encode() {
FormNavigationLink {
QRSSheet(profileName: profile.name, profileData: data)
} label: {
Label("Share as QRS Code", systemImage: "qrcode")
}
Button {
prepareQRSShare(profile)
} label: {
Label("Share as QRS Code", systemImage: "qrcode")
}
} label: {
Image(systemName: "square.and.arrow.up")
@@ -301,7 +322,7 @@ public struct ProfileCard: View {
}
Button {
viewModel.showQRSShare = true
prepareQRSShare(profile)
} label: {
Label("Share as QRS Code", systemImage: "qrcode")
}
@@ -333,42 +354,52 @@ public struct ProfileCard: View {
#if !os(tvOS)
private func shareProfile(_ profile: ProfilePreview, type: ShareItemType) {
do {
let url: URL
switch type {
case .file:
url = try profile.origin.toContent().generateShareFile()
case .json:
url = try profile.origin.read().generateShareFile(name: "\(profile.name).json")
Task {
do {
let url: URL
switch type {
case .file:
url = try await profile.origin.generateShareFileAsync()
case .json:
url = try await profile.origin.generateJSONShareFileAsync(name: "\(profile.name).json")
}
#if os(iOS)
presentShareController(url)
#elseif os(macOS)
let anchorView = viewModel.shareButtonView ?? NSApp.keyWindow?.contentView ?? NSView()
NSSharingServicePicker(items: [url]).show(
relativeTo: .zero,
of: anchorView,
preferredEdge: .minY
)
#endif
} catch {
viewModel.alert = AlertState(error: error)
}
#if os(iOS)
presentShareController(url)
#elseif os(macOS)
let anchorView = viewModel.shareButtonView ?? NSApp.keyWindow?.contentView ?? NSView()
NSSharingServicePicker(items: [url]).show(
relativeTo: .zero,
of: anchorView,
preferredEdge: .minY
)
#endif
} catch {
viewModel.alert = AlertState(error: error)
}
}
private func exportProfile(_ profile: ProfilePreview, type: ExportItemType) {
do {
switch type {
case .file:
let doc = try ProfileExportDocument(content: profile.origin.toContent())
viewModel.exportDocument = ProfileAnyExportDocument(profile: doc)
case .json:
let doc = try ProfileJSONExportDocument(jsonContent: profile.origin.read(), name: profile.name)
viewModel.exportDocument = ProfileAnyExportDocument(json: doc)
Task {
do {
let document: ProfileAnyExportDocument
switch type {
case .file:
let data = try await profile.origin.encodedContentDataAsync()
document = ProfileAnyExportDocument(data: data, filename: "\(profile.name).bpf", contentType: .data)
case .json:
let content = try await profile.origin.readAsync()
document = ProfileAnyExportDocument(data: Data(content.utf8), filename: "\(profile.name).json", contentType: .json)
}
await MainActor.run {
viewModel.exportDocument = document
viewModel.showExporter = true
}
} catch {
await MainActor.run {
viewModel.alert = AlertState(error: error)
}
}
viewModel.showExporter = true
} catch {
viewModel.alert = AlertState(error: error)
}
}
@@ -391,6 +422,20 @@ public struct ProfileCard: View {
#endif
#endif
private func prepareQRSShare(_ profile: ProfilePreview) {
viewModel.qrsShareProfileName = profile.name
viewModel.qrsShareData = nil
viewModel.showQRSShare = true
Task {
do {
viewModel.qrsShareData = try await profile.origin.encodedContentDataAsync()
} catch {
viewModel.alert = AlertState(error: error)
viewModel.showQRSShare = false
}
}
}
@ViewBuilder
private func profileInfo(for profile: ProfilePreview) -> some View {
HStack(spacing: 8) {
@@ -474,6 +519,8 @@ extension ProfileCard {
@Published var showProfilePicker = false
@Published var showQRCode = false
@Published var showQRSShare = false
@Published var qrsShareData: Data?
@Published var qrsShareProfileName: String?
@Published var isUpdating = false
@Published var alert: AlertState?
@Published var profileToEdit: Profile?