mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-07 19:29:57 +02:00
feat(apple): send files to a remembered device
Adds the Send files action to the device detail, routing the picked selection through sendToContact. Rather than a second picker path, sharePickedFiles now takes a ShareDestination, so the macOS security-scoped access handling covers both routes. A contact destination carries no access policy, matching the core's rule that an offer share is never public.
This commit is contained in:
@@ -59,15 +59,23 @@ struct IosFileSystemService: FileSystemService {
|
||||
files: [PickedShareFile],
|
||||
transferName: String,
|
||||
senderName: String,
|
||||
accessPolicy: ShareAccessPolicy
|
||||
destination: ShareDestination
|
||||
) async -> Result<Share, Error> {
|
||||
guard !files.isEmpty else {
|
||||
return .failure(InvitationError.shareEmpty)
|
||||
}
|
||||
let sources = files.map { $0.toIosShareSource() }
|
||||
return await repository.shareSources(
|
||||
sources, transferName: transferName, senderName: senderName, accessPolicy: accessPolicy
|
||||
)
|
||||
switch destination {
|
||||
case .invitation(let accessPolicy):
|
||||
return await repository.shareSources(
|
||||
sources, transferName: transferName, senderName: senderName, accessPolicy: accessPolicy
|
||||
)
|
||||
case .contact(let endpointId):
|
||||
return await repository.sendToContact(
|
||||
endpointId: endpointId, sources: sources,
|
||||
transferName: transferName, senderName: senderName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func validateSecurityScopedUrl(_ value: String) -> FolderAccessStatus {
|
||||
|
||||
@@ -39,7 +39,7 @@ struct MacFileSystemService: FileSystemService {
|
||||
files: [PickedShareFile],
|
||||
transferName: String,
|
||||
senderName: String,
|
||||
accessPolicy: ShareAccessPolicy
|
||||
destination: ShareDestination
|
||||
) async -> Result<Share, Error> {
|
||||
guard !files.isEmpty else {
|
||||
return .failure(InvitationError.shareEmpty)
|
||||
@@ -63,9 +63,17 @@ struct MacFileSystemService: FileSystemService {
|
||||
let sources = files.map {
|
||||
ShareSource(kind: .path, value: $0.value, displayName: $0.displayName, isDirectory: $0.isDirectory)
|
||||
}
|
||||
return await repository.shareSources(
|
||||
sources, transferName: transferName, senderName: senderName, accessPolicy: accessPolicy
|
||||
)
|
||||
switch destination {
|
||||
case .invitation(let accessPolicy):
|
||||
return await repository.shareSources(
|
||||
sources, transferName: transferName, senderName: senderName, accessPolicy: accessPolicy
|
||||
)
|
||||
case .contact(let endpointId):
|
||||
return await repository.sendToContact(
|
||||
endpointId: endpointId, sources: sources,
|
||||
transferName: transferName, senderName: senderName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -70,6 +70,33 @@ struct SendPickers: ViewModifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// File picker for "send to this device", reusing the share picker's selection
|
||||
/// handling so security-scoped bookmarks are captured the same way.
|
||||
struct ContactSendPickers: ViewModifier {
|
||||
@ObservedObject var model: ContactsModel
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.fileImporter(
|
||||
isPresented: $model.pendingFilePick,
|
||||
allowedContentTypes: [.item],
|
||||
allowsMultipleSelection: true
|
||||
) { result in
|
||||
switch result {
|
||||
case .success(let urls):
|
||||
let files = urls.compactMap { PickerSupport.pickedFile(from: $0, isDirectory: false) }
|
||||
if files.isEmpty {
|
||||
model.onFilePickFailed("The selected document could not be opened")
|
||||
} else {
|
||||
Task { await model.onFilesPicked(files) }
|
||||
}
|
||||
case .failure(let error):
|
||||
if !error.isUserCancellation { model.onFilePickFailed(error.technicalDetail) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PickerSupport {
|
||||
static func receiveFolder(from url: URL) -> ReceiveFolder {
|
||||
#if os(iOS)
|
||||
@@ -129,4 +156,8 @@ extension View {
|
||||
func sendPickers(model: SendModel) -> some View {
|
||||
modifier(SendPickers(model: model))
|
||||
}
|
||||
|
||||
func contactSendPickers(model: ContactsModel) -> some View {
|
||||
modifier(ContactSendPickers(model: model))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user