feat(send): multi-file shares and receivers polish

Allow selecting multiple files on Android, desktop, and iOS and share them
as one transfer through the core multi-source path. Update the composer UI
for multi-file review, hide unfinished iOS receive methods, and surface
refusal reasons on the receivers list.
This commit is contained in:
2026-07-12 17:03:53 +02:00
parent ceec383473
commit d3a5f19ecb
21 changed files with 285 additions and 115 deletions

View File

@@ -16,18 +16,22 @@ import androidx.compose.ui.platform.LocalContext
@Composable
actual fun rememberShareFilePicker(
onFilePicked: (PickedShareFile) -> Unit,
onFilesPicked: (List<PickedShareFile>) -> Unit,
onError: (String) -> Unit,
): ShareFilePicker {
val context = LocalContext.current
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (uri != null) {
onFilePicked(context.pickedShareFile(uri))
}
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
if (uris.isEmpty()) return@rememberLauncherForActivityResult
runCatching {
uris.map { uri -> context.pickedShareFile(uri) }
}.fold(
onSuccess = onFilesPicked,
onFailure = { onError(it.message ?: "Could not open the selected files") },
)
}
return remember(launcher) {
object : ShareFilePicker {
override fun pickFile() {
override fun pickFiles() {
launcher.launch(arrayOf("*/*"))
}
}

View File

@@ -63,22 +63,30 @@ private class AndroidFileSystemService(
ReceiveFolderKind.IosSecurityScopedUrl -> null
}
override suspend fun sharePickedFile(
override suspend fun sharePickedFiles(
repository: CoreGateway,
file: PickedShareFile,
files: List<PickedShareFile>,
transferName: String,
senderName: String,
accessPolicy: ShareAccessPolicy,
): Result<Share> = runCatching {
context.contentResolver.openFileDescriptor(Uri.parse(file.value), "r").use { descriptor ->
checkNotNull(descriptor) { "Could not open selected file descriptor" }
repository.shareFileDescriptor(
fd = descriptor.fd,
displayName = file.displayName,
transferName = transferName,
senderName = senderName,
accessPolicy = accessPolicy,
).getOrThrow()
require(files.isNotEmpty()) { "Select at least one file to share" }
val descriptors = files.map { file ->
context.contentResolver.openFileDescriptor(Uri.parse(file.value), "r")
?: error("Could not open selected file descriptor for ${file.displayName}")
}
try {
val sources = files.zip(descriptors) { file, descriptor ->
uniffi.vnidrop.ShareSource(
kind = uniffi.vnidrop.SourceKind.FILE_DESCRIPTOR,
value = descriptor.fd.toString(),
displayName = file.displayName,
isDirectory = false,
)
}
repository.shareSources(sources, transferName, senderName, accessPolicy).getOrThrow()
} finally {
descriptors.forEach { it.close() }
}
}