Attachment picker components API update#6130
Conversation
SDK Size Comparison 📏
|
✅ Actions performedReview triggered.
|
WalkthroughRedesigns the attachment picker from a tab-factory model to a mode/config-driven system: introduces AttachmentPickerMode and AttachmentPickerConfig, replaces tab factories with AttachmentPickerMenu and component-factory wiring, updates view models and composables, removes legacy tab-factory API, and updates samples, tests, and theming to the new mode-based surface. Changes
Sequence Diagram(s)sequenceDiagram
participant Composer as MessageComposer
participant Menu as AttachmentPickerMenu
participant VM as AttachmentsPickerViewModel
participant Factory as ChatComponentFactory
participant System as SystemPicker/Launchers
participant Location as LocationViewModelFactory
Composer->>Menu: onAttachmentsClick()
Menu->>VM: changePickerMode(mode) / changeAttachmentState(show)
VM->>Factory: request AttachmentPicker (pickerMode, attachments, commands)
Factory->>System: launch system pickers or in-app pickers (File/Media/Camera/Poll)
System-->>Factory: attachments metadata
Factory-->>VM: onAttachmentsSubmitted(meta)
VM-->>Composer: getSelectedAttachmentsAsync -> deliver attachments to composer
Note right of Location: LocationComponentFactory hooks into Factory when location mode selected
(Note: colored rectangles not applicable in this simple sequence.) Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Actions performedReview triggered.
|
gpunto
left a comment
There was a problem hiding this comment.
Left some comments, but looks good to me!
.../main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPickerMenu.kt
Outdated
Show resolved
Hide resolved
...ain/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentSystemPicker.kt
Outdated
Show resolved
Hide resolved
...in/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPickerContent.kt
Show resolved
Hide resolved
.../main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt
Show resolved
Hide resolved
7b83148 to
d916300
Compare
|
@CodeRabbit full review |
✅ Actions performedFull review triggered. |
…prevent the view model to overriding picker mode configuration for the initial selected mode
…nly when the access state changes.
…ted `permission` sub-package
Renamed `selectedAttachmentPickerMode` to `selectedMode` and `additionalContent` to `trailingContent` in the `AttachmentTypePicker` and `AttachmentTypeSystemPicker` composables for better clarity and consistency.
The default for `useSystemPicker` and `useDefaultSystemMediaPicker` has been changed from `false` to `true`. This makes the system's native file/media picker the default option, which doesn't require explicit storage permissions in the app. The in-app picker can still be used by setting this flag to `false`. Documentation and sample apps have been updated to reflect this change and include the necessary permissions for when the in-app picker is enabled.
This change introduces support for single file selection within the attachment picker, in addition to the existing multiple selection functionality. Key changes include: * The `FilesPicker` now accepts an `allowMultipleSelection` flag, which determines whether to display checkboxes (for multi-select) or radio buttons (for single-select). * The `AttachmentsPickerViewModel` has been updated to handle both single and multiple selection logic when an item is selected. * The `SelectFilesContract` is now passed a boolean to control whether the system's file picker allows multiple file selection. * Added and updated tests and snapshot previews for both single and multiple selection modes.
Adds an `allowMultipleSelection` parameter to the `ImagesPicker` and `AttachmentMediaPicker` Composables. When `allowMultipleSelection` is `false`, the selected item indicator changes from a numbered circle to a checkmark, providing a better user experience for single-selection scenarios. The `pickerMode` parameter in `AttachmentMediaPicker` is now used to control this behavior.
The `AttachmentPicker` composable now accepts a `modifier` parameter, allowing for easier customization of its layout from the outside.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
stream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/compose/messages/AttachmentsPicker.kt (1)
119-157:⚠️ Potential issue | 🟡 MinorCustomization snippet no longer demonstrates any customization.
The "Customization" section (linked to the Customization docs) now shows the exact same
AttachmentPickerusage as the "Usage" snippet — there's no custom tab, custom factory, or any configuration override. If the oldAttachmentsPickerCustomTabFactory/FullScreenPickerExamplewere removed because the factory pattern is gone, this snippet should be updated to showcase the new customization surface (e.g., providing a customAttachmentPickerConfigwith specific modes, or overridingChatComponentFactorycomposables). Otherwise the docs section will be misleading.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentFilePicker.kt (1)
261-303:⚠️ Potential issue | 🟡 MinorUpdate preview pattern to match documented guidelines.
The preview does not follow the pattern documented in
stream-chat-android-compose/GUIDELINES.md. Per guidelines, previews should use@Preview(name = "...")and wrap withChatThemeinstead ofChatPreviewTheme(showBackground = true):`@Preview`(name = "AttachmentFilePickerSingleSelection Preview") `@Composable` private fun AttachmentFilePickerSingleSelectionPreview() { ChatTheme { AttachmentFilePickerSingleSelection() } }stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentCommandPicker.kt (1)
53-59:⚠️ Potential issue | 🟡 MinorAlign preview theme with similar attachment pickers: use
ChatPreviewThemeinstead ofChatTheme.This preview uses
ChatTheme { ... }directly, butAttachmentFilePicker,AttachmentMediaPicker, andAttachmentSystemPickerall useChatPreviewTheme { ... }, which initializesChatClientfor preview rendering. Aligning with this established pattern ensures consistency across attachment picker previews.Switch to ChatPreviewTheme
`@Preview`(showBackground = true) `@Composable` private fun AttachmentCommandPickerPreview() { - ChatTheme { + ChatPreviewTheme { AttachmentCommandPicker() } }stream-chat-android-compose/api/stream-chat-android-compose.api (1)
356-459:⚠️ Potential issue | 🟡 MinorFallback handling for custom
AttachmentPickerModeimplementations is safe but silent.The code includes an
else -> Unitfallback inAttachmentPickerContent(lines 43–71), so custom implementations won't crash the UI. However, this means unknown modes silently render nothing. The sample app demonstrates the correct pattern: overrideChatComponentFactory.AttachmentPickerContent()to handle custom modes. While this design is documented in the code comments, custom mode support is not immediately discoverable. Consider making the override pattern more prominent in public documentation or KDoc.
🤖 Fix all issues with AI agents
In
`@stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/location/LocationComponentFactory.kt`:
- Around line 57-59: Update the incorrect KDoc above the
LocationComponentFactory: replace the copy-pasted "Factory for creating
components related to deleting messages for the current user." with a concise
docstring that accurately describes this factory as creating location-sharing
components (e.g., location picker, map preview, or location message UI) for the
sample app; ensure the KDoc is placed immediately above the
LocationComponentFactory declaration and references its purpose for location
sharing.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt`:
- Around line 89-91: The LaunchedEffect currently does
onModeSelected(modes.first()) which will throw if modes is empty and will re-run
on every modes change, resetting selection; update the LaunchedEffect to guard
for modes.isNotEmpty() before calling onModeSelected and only auto-select once
on initial appearance (e.g., use LaunchedEffect(Unit) or track a remembered flag
like hasAutoSelected) so subsequent changes to modes (from filterByCapabilities
or capability updates) don't overwrite the user's current selection; reference
the AttachmentTypePicker.kt LaunchedEffect, onModeSelected, modes.first(),
filterByCapabilities, AttachmentPickerConfig and PollPickerMode when locating
where to add the empty-check and the one-time auto-select guard.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModel.kt`:
- Around line 95-102: The KDoc for changePickerMode refers to `@param` pickerMode
but the function signature uses attachmentPickerMode; update either the
parameter name or the KDoc so they match. Specifically, in the function
changePickerMode(attachmentPickerMode: AttachmentPickerMode) make the parameter
name pickerMode to align with the existing KDoc, or change the KDoc `@param` to
`@param` attachmentPickerMode — ensure the parameter identifier in the function
signature and the KDoc entry are identical.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle.kt`:
- Around line 98-99: The default for AttachmentsPickerDialogStyle has been
changed so useDefaultSystemMediaPicker = true which reverses prior in-app picker
behavior and causes a breaking default; restore backward compatibility by
changing the default back to false on the AttachmentsPickerDialogStyle data
class (symbol: AttachmentsPickerDialogStyle, parameter:
useDefaultSystemMediaPicker) so existing callers keep the original in-app picker
behavior, and note the intentional change in the migration/changelog or add a
clear comment on the class if you intend to keep the new default.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerViewStyle.kt`:
- Around line 1439-1442: The default for the XML attribute
MessageComposerView_streamUiMessageComposerAttachmentsPickerSystemPickerEnabled
was changed to true causing a breaking behavioral change; restore the previous
default of false in MessageComposerViewStyle by setting the fallback value for
useDefaultSystemMediaPicker to false (reference the val
useDefaultSystemMediaPicker in MessageComposerViewStyle and the
R.styleable.MessageComposerView_streamUiMessageComposerAttachmentsPickerSystemPickerEnabled
attribute), and if you still want Compose-aligned defaults keep that change only
in Compose codepaths and add a clear upgrade/migration note documenting the
default difference for XML-based integrations.
🧹 Nitpick comments (17)
stream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/compose/messages/AttachmentsPicker.kt (1)
145-145: Unusedattachmentslambda parameter.The named parameter
attachmentson line 145 is captured but never referenced in the body. Either use_(like the usage snippet on line 51 which omits the name entirely) or show a usage example for consistency.- onAttachmentsSelected = { attachments -> + onAttachmentsSelected = {stream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/compose/messages/MessageComposer.kt (1)
89-92:contentPaddingis unused — consider suppressing or prefixing with underscore.The named parameter improves readability for the docs, but it will generate an "unused variable" warning. Since this is a documentation snippet, a minor nit:
- ) { contentPadding -> + ) { _ ->Or keep the name for documentation clarity and add
@Suppress("UNUSED_PARAMETER")— either way is fine for a snippet file.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt (2)
76-78: Selection comparison via::classmay match unintended mode instances.
selectedMode::class == mode::classtreats all instances of the same mode class as identical. Today this is fine because only one instance of each mode class appears in the list. However, if a future configuration included twoGalleryPickerModeinstances with differentmediaTypevalues, the selection highlight would match both. Consider comparing by equality (selectedMode == mode) if the mode types are data classes, which would be both simpler and more precise.Suggested simplification
- val isSelected = selectedMode != null && - selectedMode::class == mode::class + val isSelected = selectedMode == modeThis works correctly when
selectedModeisnull(evaluates tofalse) and gives exact-instance matching if modes are data classes.
55-92: Wrapmodescomputation withrememberto avoid recomputing the filtered list on every recomposition.
ChatTheme.attachmentPickerConfig.modes.filterByCapabilities(...)creates a new list instance on each recomposition via thefilter()call. Sincemodesis used as theLaunchedEffectkey inAttachmentTypePicker, this causes the effect to re-fire unnecessarily, resetting the selection on every recomposition. Even inAttachmentTypeSystemPicker(which lacks the effect), the allocation is wasteful. Both functions should useremember(channel, messageMode)to memoize the filtered list.Suggested change for AttachmentTypePicker (apply similarly to AttachmentTypeSystemPicker)
+ val modes = remember(channel, messageMode) { + ChatTheme.attachmentPickerConfig.modes.filterByCapabilities( + channel = channel, + messageMode = messageMode, + ) + } - val modes = ChatTheme.attachmentPickerConfig.modes.filterByCapabilities( - channel = channel, - messageMode = messageMode, - )stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.kt (1)
199-211: Consider addingcontentDescriptionto the checkmark icon for accessibility.Line 208:
contentDescription = nullon the checkmark means screen readers won't announce the selected state in single-selection mode. The multi-selection branch (position number asText) has a similar gap, but numeric text is at least readable. A short description like"Selected"(viastringResource) on theIconwould improve the experience for assistive technology users.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/permission/RequiredPermission.kt (1)
133-152: Previews should use@StreamPreviewhelpers.The preview functions use
@PreviewwithChatTheme { ... }instead of the@StreamPreviewhelpers. As per coding guidelines for Compose files instream-chat-android-compose: "Compose previews should useStreamPreviewhelpers."stream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/cookbook/ui/CustomComposerAndAttachmentsPicker.kt (1)
413-421: Preview function visibility could be narrowed toprivate.
PreviewCustomAttachmentPickerOptionsis currentlypublic. Preview composables are typicallyprivateto avoid polluting the public API surface.Suggested fix
`@Preview`(showBackground = true) `@Composable` -fun PreviewCustomAttachmentPickerOptions() { +private fun PreviewCustomAttachmentPickerOptions() {stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/permission/VisualMediaAccessAsState.kt (1)
45-69:onAccessChangecan become stale if the caller recomposes with a new lambda.The
onAccessChangelambda is captured by theproduceStatecoroutine but is not included as a key. If the caller recomposes with a different lambda instance, the stale closure will continue to be invoked from the lifecycle observer. Consider wrapping it withrememberUpdatedStateto keep the reference current:Proposed fix
+import androidx.compose.runtime.rememberUpdatedState + `@Composable` internal fun visualMediaAccessAsState( context: Context, lifecycleOwner: LifecycleOwner, onAccessChange: (VisualMediaAccess) -> Unit, -): State<VisualMediaAccess> = - produceState( +): State<VisualMediaAccess> { + val currentOnAccessChange by rememberUpdatedState(onAccessChange) + return produceState( initialValue = resolveVisualMediaAccessState(context), context, lifecycleOwner, ) { - onAccessChange(value) + currentOnAccessChange(value) val eventObserver = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { val newAccess = resolveVisualMediaAccessState(context) if (newAccess != value) { value = newAccess - onAccessChange(newAccess) + currentOnAccessChange(newAccess) } } } lifecycleOwner.lifecycle.addObserver(eventObserver) awaitDispose { lifecycleOwner.lifecycle.removeObserver(eventObserver) } } +}The same pattern applies to
FilesAccessAsState.kt.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt (1)
196-257: Consider using@StreamPreviewhelpers for previews.The preview functions use
@Preview(showBackground = true)directly. The project's coding guidelines suggest using@StreamPreviewhelpers for Compose previews. This is a minor consistency nit—the current approach works fine. As per coding guidelines: "Compose previews should useStreamPreviewhelpers."stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentCameraPicker.kt (1)
94-98: Unnecessary safe call on non-nullable launcher.
rememberCaptureMediaLauncher(Line 158) accepts a non-nullableCaptureMediaContract.Modeand returns a non-nullableManagedActivityResultLauncher. The?.on Line 97 is therefore redundant. Note the difference with the variant inAttachmentSystemPicker.ktwhich takes a nullablemodeand can returnnull.Use non-null call
if (showRequiredCameraPermission) { RequiredCameraPermission() } else { - AttachmentCameraPickerContent { captureMediaLauncher?.launch(Unit) } + AttachmentCameraPickerContent { captureMediaLauncher.launch(Unit) } }stream-chat-android-compose/api/stream-chat-android-compose.api (2)
2218-2218: AlignAttachmentPickerparameter order with factory override.
The composable uses(viewModel, modifier, …)whileChatComponentFactory.AttachmentPickeruses(modifier, viewModel, …). This mismatch increases the risk of mis‑ordered args in custom factory implementations; consider aligning the order or calling out named‑argument usage.
301-315: Consider defensively copyingmodesinAttachmentPickerConfigfor stricter immutability.While the parameter is typed as
List(immutable interface) and the default useslistOf(), a caller could theoretically pass aMutableListimplementation and modify it externally, bypassing the data class's equality contract. Defensively copying to an immutable list in the constructor would provide stricter guarantees, though the current type signature and defaults already provide reasonable protection.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPickerMenu.kt (2)
114-119: Consider adding a brief comment explaining theelse -> truedefault.The fallback to
trueis safe because onlyFilePickerModeandGalleryPickerModedrive item selection callbacks, while camera/poll/command modes use different interaction paths. A short inline comment would help future maintainers (and custom-mode authors) understand this implicit contract.📝 Suggested comment
val allowMultipleSelection = when (val mode = attachmentsPickerViewModel.pickerMode) { is FilePickerMode -> mode.allowMultipleSelection is GalleryPickerMode -> mode.allowMultipleSelection - else -> true + // Other modes (camera, poll, command) don't use item selection; + // default to multiple for any custom modes. + else -> true }
128-141:AttachmentPickerCreatePollClickfalls through thewhensilently — intentional but opaque.The
whenstatement handlesAttachmentPickerPollCreationandAttachmentPickerCommandSelect, whileAttachmentPickerCreatePollClickintentionally bypasses both branches and only setsisShowingDialog = trueon Line 140. This state-machine flow is correct but easy to misread. An explicit branch (even if empty/commented) would make the contract clearer.📝 Suggested explicit branch
onAttachmentPickerAction = { action -> when (action) { is AttachmentPickerPollCreation -> { attachmentsPickerViewModel.changeAttachmentState(showAttachments = false) composerViewModel.createPoll(action.pollConfig) } - is AttachmentPickerCommandSelect -> { attachmentsPickerViewModel.changeAttachmentState(showAttachments = false) composerViewModel.selectCommand(action.command) } + // Poll-create click only opens the dialog; no picker action needed. + is AttachmentPickerCreatePollClick -> Unit } isShowingDialog = action is AttachmentPickerCreatePollClick },stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/location/LocationComponentFactory.kt (1)
86-91: Hardcoded content description string.
"Share Location"is hardcoded in English. For a sample app this is acceptable, but if this pattern is expected to be lifted into the main library or localized, it should use a string resource.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModel.kt (1)
178-188: Duplicated deselection logic withremoveSelectedAttachment.
deselectAttachment(Lines 178–188) replicates the same position-shifting deselection logic that appears inremoveSelectedAttachment(Lines 130–150). Consider extracting a shared private helper to avoid the duplication.♻️ Sketch — shared deselection helper
+ private fun deselectAttachmentAt(itemIndex: Int): List<AttachmentPickerItemState> { + val removedPosition = (attachments[itemIndex].selection as Selection.Selected).position + return attachments.mapIndexed { index, item -> + when { + index == itemIndex -> item.copy(selection = Selection.Unselected) + item.selection is Selection.Selected && item.selection.position > removedPosition -> + item.copy(selection = Selection.Selected(position = item.selection.position - 1)) + else -> item + } + } + } public fun removeSelectedAttachment(attachment: Attachment) { val itemIndex = attachments.indexOfFirst { item -> item.attachmentMetaData.title == attachment.name } if (itemIndex == -1) return val currentItem = attachments[itemIndex] if (currentItem.selection !is Selection.Selected) return - val removedPosition = currentItem.selection.position - attachments = attachments.mapIndexed { index, item -> - when { - index == itemIndex -> item.copy(selection = Selection.Unselected) - item.selection is Selection.Selected && item.selection.position > removedPosition -> - item.copy(selection = Selection.Selected(position = item.selection.position - 1)) - else -> item - } - } + attachments = deselectAttachmentAt(itemIndex) } - private fun deselectAttachment(itemIndex: Int): List<AttachmentPickerItemState> { - val removedPosition = (attachments[itemIndex].selection as Selection.Selected).position - return attachments.mapIndexed { index, item -> - when { - index == itemIndex -> item.copy(selection = Selection.Unselected) - item.selection is Selection.Selected && item.selection.position > removedPosition -> - item.copy(selection = Selection.Selected(position = item.selection.position - 1)) - else -> item - } - } - } + private fun deselectAttachment(itemIndex: Int): List<AttachmentPickerItemState> = + deselectAttachmentAt(itemIndex)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentSystemPicker.kt (1)
262-270: Minor duplication withAttachmentCameraPicker.kt:156-162.This
rememberCaptureMediaLauncherhas nearly identical logic to the one inAttachmentCameraPicker.kt, differing only in the nullable wrapper. Consider extracting a shared helper if the two continue to evolve together, though the current duplication is small and justified by the different nullability semantics.
...c/main/java/io/getstream/chat/android/compose/sample/ui/location/LocationComponentFactory.kt
Show resolved
Hide resolved
.../main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt
Show resolved
Hide resolved
...main/java/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModel.kt
Show resolved
Hide resolved
.../chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogStyle.kt
Show resolved
Hide resolved
...in/kotlin/io/getstream/chat/android/ui/feature/messages/composer/MessageComposerViewStyle.kt
Show resolved
Hide resolved
d916300 to
59dbdba
Compare
VelikovPetar
left a comment
There was a problem hiding this comment.
Left some discussion points, maybe not directly to this PR.
But looks pretty good, nice job!
...va/io/getstream/chat/android/compose/state/messages/attachments/AttachmentPickerItemState.kt
Show resolved
Hide resolved
...in/java/io/getstream/chat/android/compose/state/messages/attachments/AttachmentPickerMode.kt
Show resolved
Hide resolved
.../java/io/getstream/chat/android/compose/state/messages/attachments/AttachmentPickerConfig.kt
Show resolved
Hide resolved
.../main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPickerMenu.kt
Outdated
Show resolved
Hide resolved
...ain/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentSystemPicker.kt
Show resolved
Hide resolved
.../main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt
Show resolved
Hide resolved
...oid-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.kt
Show resolved
Hide resolved
…tion` to centralize the logic for checking if multiple selections are supported.
|


🎯 Goal
Simplify and modernize the Attachment Picker API.
The existing attachment picker used a factory pattern with 7 factory classes (
AttachmentsPickerTabFactory+ 6 implementations), scattered configuration, and a sealed class hierarchy that limited extensibility. This PR introduces a cleaner, more intuitive API that:AttachmentPickerConfigdata classChatComponentFactorycomposable overrides🛠 Implementation details
New API Structure:
AttachmentPickerModeinterface - Replaces theAttachmentsPickerModesealed classGalleryPickerMode(allowMultipleSelection, mediaType)- Pick images/videos from galleryFilePickerMode(allowMultipleSelection)- Pick files from storageCameraPickerMode(captureMode)- Capture photos/videosPollPickerMode(autoShowCreateDialog)- Create pollsCommandPickerMode- Select commandsAttachmentPickerConfig- Centralized picker configurationuseSystemPicker: Boolean- Toggle between in-app and system pickermodes: List<AttachmentPickerMode>- Configure available picker modesChatComponentFactoryextensions - New factory methods for customization:AttachmentPickerMenu(),AttachmentPicker(),AttachmentTypePicker()AttachmentPickerContent(),AttachmentMediaPicker(),AttachmentFilePicker()AttachmentCameraPicker(),AttachmentPollPicker(),AttachmentCommandPicker()AttachmentSystemPicker(),AttachmentTypeSystemPicker()Removed:
AttachmentsPickerTabFactoryinterface and all implementationsAttachmentsPickerTabFactoriesstatic factory methodsAttachmentsProcessingViewModelfrom factory package (moved to viewmodel package)CaptureMediaLauncher.ktAttachmentsPickerModesealed classChatTheme.useDefaultSystemMediaPickerparameter and propertyChatComponentFactory.AttachmentsPickerSendButton()DefaultAttachmentsPickerSendButton()SystemAttachmentsPickerConfigRefactored:
permissionsub-packageAttachmentsPickerViewModelupdated to use nullablepickerModefor proper initializationPermissions:
🎨 UI Changes
No visual changes - this is an API refactoring. The attachment picker UI remains identical.
🧪 Testing
Manual Testing:
useSystemPicker = trueinAttachmentPickerConfigAutomated Testing:
RequiredPermissionlogicAttachmentsPickerTabFactoriesTest,AttachmentsPickerPollTabFactoryTest,AttachmentsProcessingViewModelTest)🎉 GIF
Summary by CodeRabbit
New Features
Breaking Changes