fix: multi-select for gallery and file picker

Gallery uses PickMultipleVisualMedia (image + video), file picker uses
OpenMultipleDocuments — both now allow selecting multiple items at once.
Camera stays single-shot (one photo per shutter, standard behavior).
Each selected item uploads in parallel as before.
This commit is contained in:
Bruno Deanoz 2026-06-22 16:29:35 +02:00
parent 875a319ddd
commit ad11c0ee46

View file

@ -77,6 +77,7 @@ import dev.kaizen.app.net.WireMessage
import dev.kaizen.app.settings.SettingsScreen import dev.kaizen.app.settings.SettingsScreen
import dev.kaizen.app.settings.SettingsViewModel import dev.kaizen.app.settings.SettingsViewModel
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -161,13 +162,24 @@ fun ChatScreen(
} }
} }
val filePicker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> val galleryPicker = rememberLauncherForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) { uris ->
uri ?: return@rememberLauncherForActivityResult
val cr = context.contentResolver val cr = context.contentResolver
val mimeType = cr.getType(uri) ?: "application/octet-stream" for (uri in uris) {
val fileName = uri.lastPathSegment?.substringAfterLast('/') ?: "file" val mimeType = cr.getType(uri) ?: "image/jpeg"
val bytes = cr.openInputStream(uri)?.use { it.readBytes() } ?: return@rememberLauncherForActivityResult val fileName = uri.lastPathSegment?.substringAfterLast('/') ?: "image_${System.currentTimeMillis()}"
uploadPickedFile(fileName, mimeType, bytes) val bytes = cr.openInputStream(uri)?.use { it.readBytes() } ?: continue
uploadPickedFile(fileName, mimeType, bytes)
}
}
val filePicker = rememberLauncherForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
val cr = context.contentResolver
for (uri in uris) {
val mimeType = cr.getType(uri) ?: "application/octet-stream"
val fileName = uri.lastPathSegment?.substringAfterLast('/') ?: "file"
val bytes = cr.openInputStream(uri)?.use { it.readBytes() } ?: continue
uploadPickedFile(fileName, mimeType, bytes)
}
} }
val cameraPicker = rememberLauncherForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap -> val cameraPicker = rememberLauncherForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap ->
@ -859,8 +871,8 @@ fun ChatScreen(
) { ) {
AttachmentMenu( AttachmentMenu(
onCamera = { showAttachMenu = false; cameraPicker.launch(null) }, onCamera = { showAttachMenu = false; cameraPicker.launch(null) },
onGallery = { showAttachMenu = false; filePicker.launch("image/*") }, onGallery = { showAttachMenu = false; galleryPicker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)) },
onFile = { showAttachMenu = false; filePicker.launch("*/*") }, onFile = { showAttachMenu = false; filePicker.launch(arrayOf("*/*")) },
) )
} }