Conversation
…ver time (#6199) * Fix createdLocallyAt using NTP-style server clock offset estimation Co-Authored-By: Claude <noreply@anthropic.com> * Pr remarks * Adjust thread message createdLocallyAt. * Ensure exceedsSyncThreshold is compared against estimated server time (where applicable). * Add max allowed offset. --------- Co-authored-by: Claude <noreply@anthropic.com>
…ers.IO)` (#6284) Co-authored-by: Claude <noreply@anthropic.com>
* Update `DependencyResolverTest` to verify error handling when dependency resolution races with disconnection. * Prevent race conditions during disconnects in `ChatClient`.
- Update `StorageHelper` and `AttachmentMetaDataMapper` to safely handle cases where content URIs (e.g. cloud-backed files) cannot be opened. - Introduce `hasUnresolvedAttachments` state in `AttachmentsPickerViewModel` to track failed attachment resolutions. - Show a toast message in both View-based and Compose attachment pickers when files are unavailable and need to be downloaded to the device. - Add `clearUnresolvedAttachments` to reset the error state after it has been consumed by the UI. - Add unit tests for unresolved attachment scenarios in `AttachmentsPickerViewModelTest`.
…l` (#6280) * Deprecate imagePreviewUrl and use type-specific attachment URL fields Co-Authored-By: Claude <noreply@anthropic.com> * Extract common extensions. --------- Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: adasiewiczr <17440581+adasiewiczr@users.noreply.github.com>
* Add new CDN contract. * Add CDN for document files. * Add CDN support for downloading attachments. * Deprecate current CDN methods. * Add progress indicator snackbar. * Add useDocumentGView config flag. * Add file sharing cache handling. * Add file sharing cache handling. * Remove CDNResponse.kt * Add tests * PR remarks
# Conflicts: # README.md # gradle.properties # metrics/size.json # stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt # stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/state/ChatClientStateExtensions.kt # stream-chat-android-compose/api/stream-chat-android-compose.api # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/ImageAttachmentPreviewContent.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/LinkAttachmentContent.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentPreviewContent.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentQuotedContent.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/DocumentAttachmentPreviewHandler.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/attachments/ChannelMediaAttachmentsScreen.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/ComposerLinkPreview.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentsPicker.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/StorageHelperWrapper.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/extensions/internal/AttachmentExtensions.kt # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModel.kt # stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModelTest.kt # stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/Attachment.kt # stream-chat-android-ui-components/api/stream-chat-android-ui-components.api # stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt # stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/internal/AttachmentMetaDataMapper.kt # stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/MessageListView.kt # stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/navigation/destinations/AttachmentDestination.kt # stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
SDK Size Comparison 📏
|
WalkthroughA comprehensive CDN abstraction is introduced alongside server clock offset synchronization. The changes integrate CDN-based URL rewriting and header injection across media playback, image loading, HTTP requests, and file downloads. Multiple legacy customization interfaces are deprecated in favor of the new CDN configuration model. Attachment URL handling is refactored to use specific fields instead of a generic Changes
Sequence DiagramsequenceDiagram
actor User
participant App as Application
participant Client as ChatClient
participant CDN as CDN Interface
participant Network as Network Layer<br/>(OkHttp/Coil)
participant Media as Media System<br/>(ExoPlayer)
participant Server as Stream Server
User->>App: Request image/file/media
App->>Client: downloadAttachment() or loadMedia()
Client->>CDN: imageRequest(url) or fileRequest(url)
alt CDN Transformation
CDN-->>Client: CDNRequest{newUrl, headers}
Client->>Network: Request with transformed URL + headers
else No CDN
CDN-->>Client: CDNRequest{originalUrl, null}
Client->>Network: Request with original URL
end
Network->>Server: HTTP GET (CDN URL + headers)
Server-->>Network: Media/File content
Network-->>Media: Stream data
Media-->>App: Render/Playback
App-->>User: Display content
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/StorageHelper.kt (1)
271-275:⚠️ Potential issue | 🟡 MinorBug: Wrong column index used in null check for duration.
The condition checks
cursor.isNull(fileSizeIndex)but should checkcursor.isNull(durationIndex).🐛 Proposed fix
- val duration = if (durationIndex != -1 && !cursor.isNull(fileSizeIndex)) { + val duration = if (durationIndex != -1 && !cursor.isNull(durationIndex)) { cursor.getLong(durationIndex) } else { 0L }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/StorageHelper.kt` around lines 271 - 275, The null-check for the duration column uses the wrong index: change the condition in StorageHelper (where durationIndex and fileSizeIndex are used) to call cursor.isNull(durationIndex) instead of cursor.isNull(fileSizeIndex) so the code verifies the duration column before calling cursor.getLong(durationIndex); update the conditional that sets duration to use the correct durationIndex null check.
🧹 Nitpick comments (10)
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt (1)
208-209: Consider adding KDoc for the public API.The
completeRecordingfunction is part of the public API and now accepts a completion callback. Per coding guidelines, public APIs should be documented with KDoc. While other recording functions in this file also lack KDoc, this new parameter would benefit from documentation explaining whenonCompleteis invoked and whatResult<Attachment>contains.Suggested KDoc
+ /** + * Completes the current audio recording. + * + * `@param` onComplete Optional callback invoked with the resulting [Attachment] on success + * or an error on failure. + */ public fun completeRecording(onComplete: ((Result<Attachment>) -> Unit)? = null): Unit = messageComposerController.completeRecording(onComplete)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt` around lines 208 - 209, Add KDoc to the public function MessageComposerViewModel.completeRecording to document the new onComplete parameter: explain when the callback is invoked (e.g., after recording successfully completes or fails), describe the Result<Attachment> contents for success (the created Attachment) and failure (the error/exception), and note threading/dispatch expectations and any nullability/optional behavior; reference the delegation to messageComposerController.completeRecording in the docs so callers understand behavior is forwarded to that controller.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/overview/internal/MediaAttachmentAdapter.kt (1)
191-193: Consider aligning DiffUtil comparison with URL selection logic.The
areItemsTheSameuses(imageUrl ?: thumbUrl)uniformly, whileloadImageselects the URL based on attachment type (imageUrlfor images,thumbUrlotherwise). This inconsistency is unlikely to cause issues in practice since attachment types don't change, but for consistency the comparison could mirror the type-aware selection.Optional: Type-aware comparison
override fun areItemsTheSame(oldItem: AttachmentGalleryItem, newItem: AttachmentGalleryItem): Boolean { - return (oldItem.attachment.imageUrl ?: oldItem.attachment.thumbUrl) == - (newItem.attachment.imageUrl ?: newItem.attachment.thumbUrl) && + val oldUrl = if (oldItem.attachment.isImage()) oldItem.attachment.imageUrl else oldItem.attachment.thumbUrl + val newUrl = if (newItem.attachment.isImage()) newItem.attachment.imageUrl else newItem.attachment.thumbUrl + return oldUrl == newUrl && oldItem.createdAt == newItem.createdAt }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/overview/internal/MediaAttachmentAdapter.kt` around lines 191 - 193, The DiffUtil comparison in MediaAttachmentAdapter::areItemsTheSame currently compares (imageUrl ?: thumbUrl) but loadImage selects the URL based on attachment type; update areItemsTheSame to mirror loadImage's type-aware URL selection (use attachment.type or the same predicate loadImage uses to decide image vs thumbnail) when comparing URLs and keep the createdAt check; modify the comparison logic in areItemsTheSame to call the same helper or replicate the same conditional used by loadImage so both routines pick the same URL for comparison.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt (1)
94-101: Consider: User feedback for unresolved attachments in XML UI.The Compose
AttachmentPickershows a toast when attachments cannot be resolved, but the XMLAttachmentsPickerDialogFragmentsilently drops them. This could confuse users when some selected files don't appear in the composer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt` around lines 94 - 101, The XML picker silently drops unresolved attachments in AttachmentsPickerDialogFragment.onDismiss when style.saveAttachmentsOnDismiss is true; change the logic to detect which selectedAttachments failed to convert (e.g., compare selectedAttachments to the result of selectedAttachments.mapNotNull { it.toAttachment(requireContext()) } or collect failed items via selectedAttachments.filter { it.toAttachment(requireContext()) == null }), call attachmentsSelectionListener?.onAttachmentsSelected(...) with the successfully resolved attachments as before, and then show a user-facing Toast (using requireContext()) that indicates how many or which attachments could not be resolved so users get feedback about dropped files.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageAssetTransformer.kt (1)
37-54: Add thread-expectation note totransformKDoc.Please document the expected calling thread/context for
transform(asset: Any)(and any state assumptions), so custom implementations are safe.As per coding guidelines
**/*.kt: “Document public APIs with KDoc, including thread expectations and state notes”.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageAssetTransformer.kt` around lines 37 - 54, The KDoc for ImageAssetTransformer.transform(asset: Any) is missing the expected calling thread and state assumptions; update the KDoc for the public function transform in ImageAssetTransformer to explicitly state which thread/context callers must use (e.g., "Called on main/UI thread" or "May be called from background threads; implementations must be thread-safe"), and document any state assumptions (e.g., no mutation of shared state, no long-running work, must be fast or offloaded). Mention whether implementations may access UI-only resources or must avoid blocking I/O so custom implementations implement correct synchronization or offloading.stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/factory/StreamStatePluginFactory.kt (1)
130-143: Add a focused non-zero-offset regression test for this wiring.This is the handoff that makes sync staleness depend on the shared
ServerClockOffset, so a small test proving behavior with a non-zero offset would make this much harder to regress.If you add it, I’d keep it under
runTestwith virtual time because this path is timing-sensitive. Based on learnings: Applies to /src/test//*.kt : Use deterministic tests withrunTest+ virtual time for concurrency-sensitive logic (uploads, sync, message state).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/factory/StreamStatePluginFactory.kt` around lines 130 - 143, Add a deterministic regression test that verifies SyncManager's staleness logic respects a non-zero ServerClockOffset: create a test under src/test/**/*.kt that uses runTest with virtual time, construct the same wiring as in StreamStatePluginFactory (instantiate SyncManager with chatClient.serverClockOffset set to a non-zero value and the now = { System.currentTimeMillis() } replacement if needed), drive the timing-sensitive path (trigger sync/reconnect) and assert that sync staleness/behavior changes according to the provided offset; keep the test focused, use virtual time to avoid flakiness, and target the SyncManager/ServerClockOffset wiring to prevent regressions.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/DocumentAttachmentPreviewHandler.kt (1)
25-37: Document the Activity-context/UI-call expectation for this handler.The new behavior switch is clear, but this public KDoc still doesn't spell out the required call context even though
handleAttachmentPreviewstarts activities. Please add the expected thread/call context and state notes so consumers know what kind ofContextis valid here.As per coding guidelines,
**/*.kt: Document public APIs with KDoc, including thread expectations and state notes.Also applies to: 57-63
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/DocumentAttachmentPreviewHandler.kt` around lines 25 - 37, Update the KDoc for DocumentAttachmentPreviewHandler to state that the provided Context must be an Activity (or an unwrapped Context capable of starting activities) and that handleAttachmentPreview will start activities (via Intents/CustomTabs/Google Docs Viewer) so it must be invoked on the main/UI thread when the Activity is in a valid, not-destroyed state; reference the constructor parameter useDocumentGView and the public method handleAttachmentPreview in the note so callers know which behavior depends on that flag and when to supply an Activity-context.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DownloadAttachmentUriGenerator.kt (1)
22-30: Add thread/state notes to the deprecation KDoc.These types are still public API until removal, and the new deprecation docs don't yet capture the expected thread/state contract for
generateDownloadUri.As per coding guidelines,
**/*.kt: Document public APIs with KDoc, including thread expectations and state notes.Also applies to: 42-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DownloadAttachmentUriGenerator.kt` around lines 22 - 30, Update the deprecation KDoc for the public interface DownloadAttachmentUriGenerator (and its generateDownloadUri contract) to include thread and state expectations: state whether implementations may be called on any thread or must be called on the main/UI thread, whether it must be non-blocking, and whether it may be invoked after related message/attachment objects are recycled or mutated; add a brief "State/Threading" note describing these expectations so callers and implementers know concurrency and lifecycle guarantees. Reference the DownloadAttachmentUriGenerator interface and its generateDownloadUri method when adding this KDoc note, and mirror the same change for the analogous KDoc block at the other occurrence mentioned.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.kt (1)
201-212: Resize video thumbnail URLs here as well.Images already go through
applyStreamCdnImageResizingIfEnabled, andAttachment.imagePreviewDatanow does the same for video thumbnails. Quoted video replies still use the rawthumbUrl, so they skip the CDN downsizing path.💡 Suggested change
type == AttachmentType.VIDEO -> { videoCount++ fileCount++ - mediaPreviewData = attachment.upload ?: attachment.thumbUrl + mediaPreviewData = attachment.upload ?: attachment.thumbUrl + ?.applyStreamCdnImageResizingIfEnabled(streamCdnImageResizing) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.kt` around lines 201 - 212, The video branch in QuotedMessageBodyBuilder sets mediaPreviewData to attachment.upload ?: attachment.thumbUrl but does not apply CDN image resizing; update the AttachmentType.VIDEO branch so that when attachment.upload is null you pass attachment.thumbUrl through applyStreamCdnImageResizingIfEnabled(streamCdnImageResizing) (same helper used for images) so mediaPreviewData receives the resized thumb URL; adjust the assignment in the video case where mediaPreviewData is set (and ensure you reference the same helper used for images and imagePreviewData).stream-chat-android-client/src/test/java/io/getstream/chat/android/client/cdn/internal/CDNOkHttpInterceptorTest.kt (1)
65-82: Add a same-key header override regression test.This suite proves additive merging, but it never locks in what should happen when the original request already has
Authorization. Without that case, a regression that keeps both values would still pass here.♻️ Suggested test addition
+ `@Test` + fun `intercept CDN headers override existing headers for same key`() { + val cdn = object : CDN { + override suspend fun fileRequest(url: String) = + CDNRequest(url, headers = mapOf("Authorization" to "new-token")) + } + val interceptor = CDNOkHttpInterceptor(cdn) + val originalRequest = Request.Builder() + .url("https://original.com/file.mp4") + .addHeader("Authorization", "old-token") + .build() + val chain = FakeChain(FakeResponse(200), request = originalRequest) + + val response = interceptor.intercept(chain) + + assertEquals("new-token", response.request.header("Authorization")) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/cdn/internal/CDNOkHttpInterceptorTest.kt` around lines 65 - 82, Add a regression test that verifies same-key header overriding for Authorization: create a CDN stub (implementing CDN.fileRequest) that returns a CDNRequest with an "Authorization" header, build an original Request with an existing "Authorization" header value, run CDNOkHttpInterceptor.intercept using a FakeChain/FakeResponse, and assert that response.request.header("Authorization") equals the CDN-provided value (and not the original), ensuring the interceptor replaces the original Authorization header rather than keeping both.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManager.kt (1)
95-95: Use a share-specific cache prefix before relying on prefix-scoped eviction.Line 95 now evicts by
cacheFilePrefix, while Lines 150 and 169 still default to"TMP". That prefix is generic enough to let this manager clean up unrelated cache entries if anything else in the app uses the same convention. A feature-specific prefix keeps the TTL/size policy scoped to share files only.♻️ Suggested diff
public data class ShareCacheConfig( - val cacheFilePrefix: String = "TMP", + val cacheFilePrefix: String = "stream_share_", val bitmapShareFilename: String = "shared_image.png", val bitmapQuality: Int = 90, val cacheTtlMs: Long = 5 * 60 * 1000L, val maxCacheSizeBytes: Long = 25L * 1024 * 1024, )Also applies to: 150-150, 168-170
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManager.kt` at line 95, StreamShareFileManager is evicting and creating cache files using a generic prefix ("TMP" / config.cacheFilePrefix) which can collide with other cache users; change the calls in StreamShareFileManager (the evictCacheFiles call and places that default to "TMP" around file creation) to use a share-specific prefix (e.g., derive a constant like SHARE_CACHE_PREFIX or append a suffix to config.cacheFilePrefix such as "${config.cacheFilePrefix}_SHARE") so eviction and TTL/size policies only target share files; update every usage in this class (the evictCacheFiles invocation and the code paths that currently use "TMP") to use that new share-specific prefix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Line 5: The hero image tag <img
src="/docs/stream-chat-android-github-cover.png"/> is missing an alt attribute;
update that element in README.md to include a concise descriptive alt text (for
example alt="Stream Chat Android GitHub cover") so screen readers can interpret
the image and satisfy markdownlint MD045.
In
`@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/cdn/internal/CDNDataSourceFactory.kt`:
- Around line 71-78: The catch block in CDNDataSourceFactory around runBlocking
{ cdn.fileRequest(url) } must not swallow thread interruptions or coroutine
cancellations; change the exception handling so that InterruptedException and
CancellationException (or any Throwable that represents cancellation) are
re-thrown immediately and only non-cancellation exceptions are logged and cause
fallback to CDNRequest(url). Update the catch-site for the generic exception
suppression (`@Suppress`("TooGenericExceptionCaught")) to include a brief comment
explaining why it remains (per coding guidelines) or narrow the caught type to
Exception after rethrowing cancellations, and keep the logger.e(...) and
fallback to CDNRequest only for non-cancellation failures.
In
`@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt`:
- Around line 411-420: The early snapshot of plugins in resolvePluginDependency
breaks when initialization completes after the snapshot; instead of using the
pre-wait currentPlugins, re-read the plugins list after
awaitInitializationState(RESOLVE_DEPENDENCY_TIMEOUT) (or hold the same
synchronization used by connect/disconnect) so that the resolver lookup uses the
up-to-date plugins; specifically update the code around
currentPlugins/awaitInitializationState/InitializationState.COMPLETE and the
subsequent resolver lookup to fetch plugins again (or perform the lookup under
the same lock) to avoid stale-plugin "was not found" errors.
In `@stream-chat-android-compose/api/stream-chat-android-compose.api`:
- Around line 7053-7054: Add a migration note documenting the breaking change:
the JVM no-arg entry point for MessageComposerViewModel.completeRecording was
removed and now requires a callback parameter (see completeRecording and
completeRecording$default signatures); update CHANGELOG.md and the v7 migration
guide to explain that Kotlin callers will keep no-arg calls after recompilation
(due to default parameters) but Java callers must now supply a Function1
callback or use the synthetic default helper, include an example of the new Java
call shape and a short snippet explaining how to migrate existing Java usages.
- Around line 720-721: Add an entry to the v7 CHANGELOG under
stream-chat-android-compose that documents the breaking JVM signature changes:
list the new signatures for
AttachmentPreviewHandler.defaultAttachmentHandlers(Context, boolean), the
DocumentAttachmentPreviewHandler constructor (now taking useDocumentGView
boolean), and ChatTheme (with useDocumentGView), explain the upgrade path for
Java callers and existing binaries — the new useDocumentGView flag defaults to
true, existing behavior is preserved, and Java callers can pass false to opt
into CDN-aware document handling — and include a short code snippet example
showing how Java callers should call the updated
AttachmentPreviewHandler.defaultAttachmentHandlers(...) and construct
DocumentAttachmentPreviewHandler with useDocumentGView=false to opt out.
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt`:
- Around line 1021-1037: The current completeRecording implementation adds the
finished Attachment into selected attachments via
addAttachments(listOf(result.value)), which duplicates/stages the recording
because recordingState already sets _recordingAttachment and syncAttachments()
appends that field; remove the explicit staging to _selectedAttachments: in
MessageComposerController.completeRecording (and the branch that calls
audioRecordingController.completeRecordingSync()), stop calling
addAttachments(...) on successful Result.Success and instead rely on the
existing _recordingAttachment flow (recordingState, _recordingAttachment,
syncAttachments()) so the attachment is only appended once.
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageAssetTransformer.kt`:
- Around line 58-65: Update the KDoc for DefaultImageAssetTransformer to reflect
that it is a pass-through transformer (it returns the asset unchanged) instead
of saying it "doesn't provide any headers"; locate the KDoc block above the
object declaration for DefaultImageAssetTransformer (implementing
ImageAssetTransformer) and replace the outdated phrase with wording like
"returns the asset unchanged" or "pass-through asset transformer" while
preserving the existing deprecation note.
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/images/internal/CDNImageInterceptor.kt`:
- Around line 52-59: The catch block around cdn.imageRequest(...) in
CDNImageInterceptor currently catches all Exceptions and swallows
CancellationException, preventing coroutine cancellation from propagating;
modify the handler so it rethrows CancellationException (e.g., if (e is
CancellationException) throw e) before logging and falling back to
chain.proceed(), ensuring cancellation is propagated for cdn.imageRequest, and
only non-cancellation exceptions are logged and trigger the fallback behavior.
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManager.kt`:
- Line 63: The code currently always compresses to PNG in
StreamShareFileManager.bitmap.compress(...), so the exposed config.bitmapQuality
has no effect; update the config and compress call so the format is configurable
and the quality is honored for lossy formats: add a bitmapFormat (e.g.,
Bitmap.CompressFormat) option to the existing configuration (or replace
bitmapQuality with a format+quality pair), use that bitmapFormat in the
bitmap.compress call instead of hardcoding PNG, and preserve the existing
bitmapQuality for formats that support it (or ignore it when bitmapFormat == PNG
with a short comment); adjust any KDoc to reflect the new configurable format
and behavior.
In
`@stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManagerTest.kt`:
- Around line 361-414: The tests use a hard-coded 6*60*1000L TTL which may not
exercise the expiration branch; update both tests to compute the stale timestamp
using ShareCacheConfig().cacheTtlMs (e.g. lastModified =
System.currentTimeMillis() - (ShareCacheConfig().cacheTtlMs + 1)) so the cached
file is definitely expired when shareFileManager calls
fileManager.getFileFromCache; also strengthen assertions by verifying the
miss-specific interactions: for writeAttachmentToShareableFile assert that
chatClient.downloadFile() and fileManager.writeFileInCache(...) are invoked (or
that writeFileInCache is called with the downloaded file) and for
getShareableUriForAttachment assert that no valid URI is returned (isFailure)
and optionally verify download/write are not called or are called according to
expected behavior.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/viewholder/impl/GiphyViewHolder.kt`:
- Around line 76-77: The bindData in GiphyViewHolder currently uses a non-local
return via "?: return" when resolving the GIF URL, which skips the rest of bind
logic (including setting giphyQueryTextView) and can leave recycled row state
stale; change the logic to avoid returning early by assigning the URL to a
nullable local (e.g., val url = it.giphyInfo(... ) ?:
it.giphyFallbackPreviewUrl) and then handle the null case inline (clear or hide
the image view, set a placeholder, but still proceed to set
giphyQueryTextView.text and other UI state). Ensure any image loading calls (the
code that used the URL) are guarded with an if (url != null) block and that
giphyQueryTextView and other fields on GiphyViewHolder are always updated
regardless of URL presence.
---
Outside diff comments:
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/StorageHelper.kt`:
- Around line 271-275: The null-check for the duration column uses the wrong
index: change the condition in StorageHelper (where durationIndex and
fileSizeIndex are used) to call cursor.isNull(durationIndex) instead of
cursor.isNull(fileSizeIndex) so the code verifies the duration column before
calling cursor.getLong(durationIndex); update the conditional that sets duration
to use the correct durationIndex null check.
---
Nitpick comments:
In
`@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/factory/StreamStatePluginFactory.kt`:
- Around line 130-143: Add a deterministic regression test that verifies
SyncManager's staleness logic respects a non-zero ServerClockOffset: create a
test under src/test/**/*.kt that uses runTest with virtual time, construct the
same wiring as in StreamStatePluginFactory (instantiate SyncManager with
chatClient.serverClockOffset set to a non-zero value and the now = {
System.currentTimeMillis() } replacement if needed), drive the timing-sensitive
path (trigger sync/reconnect) and assert that sync staleness/behavior changes
according to the provided offset; keep the test focused, use virtual time to
avoid flakiness, and target the SyncManager/ServerClockOffset wiring to prevent
regressions.
In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/cdn/internal/CDNOkHttpInterceptorTest.kt`:
- Around line 65-82: Add a regression test that verifies same-key header
overriding for Authorization: create a CDN stub (implementing CDN.fileRequest)
that returns a CDNRequest with an "Authorization" header, build an original
Request with an existing "Authorization" header value, run
CDNOkHttpInterceptor.intercept using a FakeChain/FakeResponse, and assert that
response.request.header("Authorization") equals the CDN-provided value (and not
the original), ensuring the interceptor replaces the original Authorization
header rather than keeping both.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/DocumentAttachmentPreviewHandler.kt`:
- Around line 25-37: Update the KDoc for DocumentAttachmentPreviewHandler to
state that the provided Context must be an Activity (or an unwrapped Context
capable of starting activities) and that handleAttachmentPreview will start
activities (via Intents/CustomTabs/Google Docs Viewer) so it must be invoked on
the main/UI thread when the Activity is in a valid, not-destroyed state;
reference the constructor parameter useDocumentGView and the public method
handleAttachmentPreview in the note so callers know which behavior depends on
that flag and when to supply an Activity-context.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.kt`:
- Around line 201-212: The video branch in QuotedMessageBodyBuilder sets
mediaPreviewData to attachment.upload ?: attachment.thumbUrl but does not apply
CDN image resizing; update the AttachmentType.VIDEO branch so that when
attachment.upload is null you pass attachment.thumbUrl through
applyStreamCdnImageResizingIfEnabled(streamCdnImageResizing) (same helper used
for images) so mediaPreviewData receives the resized thumb URL; adjust the
assignment in the video case where mediaPreviewData is set (and ensure you
reference the same helper used for images and imagePreviewData).
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DownloadAttachmentUriGenerator.kt`:
- Around line 22-30: Update the deprecation KDoc for the public interface
DownloadAttachmentUriGenerator (and its generateDownloadUri contract) to include
thread and state expectations: state whether implementations may be called on
any thread or must be called on the main/UI thread, whether it must be
non-blocking, and whether it may be invoked after related message/attachment
objects are recycled or mutated; add a brief "State/Threading" note describing
these expectations so callers and implementers know concurrency and lifecycle
guarantees. Reference the DownloadAttachmentUriGenerator interface and its
generateDownloadUri method when adding this KDoc note, and mirror the same
change for the analogous KDoc block at the other occurrence mentioned.
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageAssetTransformer.kt`:
- Around line 37-54: The KDoc for ImageAssetTransformer.transform(asset: Any) is
missing the expected calling thread and state assumptions; update the KDoc for
the public function transform in ImageAssetTransformer to explicitly state which
thread/context callers must use (e.g., "Called on main/UI thread" or "May be
called from background threads; implementations must be thread-safe"), and
document any state assumptions (e.g., no mutation of shared state, no
long-running work, must be fast or offloaded). Mention whether implementations
may access UI-only resources or must avoid blocking I/O so custom
implementations implement correct synchronization or offloading.
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManager.kt`:
- Line 95: StreamShareFileManager is evicting and creating cache files using a
generic prefix ("TMP" / config.cacheFilePrefix) which can collide with other
cache users; change the calls in StreamShareFileManager (the evictCacheFiles
call and places that default to "TMP" around file creation) to use a
share-specific prefix (e.g., derive a constant like SHARE_CACHE_PREFIX or append
a suffix to config.cacheFilePrefix such as "${config.cacheFilePrefix}_SHARE") so
eviction and TTL/size policies only target share files; update every usage in
this class (the evictCacheFiles invocation and the code paths that currently use
"TMP") to use that new share-specific prefix.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/overview/internal/MediaAttachmentAdapter.kt`:
- Around line 191-193: The DiffUtil comparison in
MediaAttachmentAdapter::areItemsTheSame currently compares (imageUrl ?:
thumbUrl) but loadImage selects the URL based on attachment type; update
areItemsTheSame to mirror loadImage's type-aware URL selection (use
attachment.type or the same predicate loadImage uses to decide image vs
thumbnail) when comparing URLs and keep the createdAt check; modify the
comparison logic in areItemsTheSame to call the same helper or replicate the
same conditional used by loadImage so both routines pick the same URL for
comparison.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.kt`:
- Around line 94-101: The XML picker silently drops unresolved attachments in
AttachmentsPickerDialogFragment.onDismiss when style.saveAttachmentsOnDismiss is
true; change the logic to detect which selectedAttachments failed to convert
(e.g., compare selectedAttachments to the result of
selectedAttachments.mapNotNull { it.toAttachment(requireContext()) } or collect
failed items via selectedAttachments.filter { it.toAttachment(requireContext())
== null }), call attachmentsSelectionListener?.onAttachmentsSelected(...) with
the successfully resolved attachments as before, and then show a user-facing
Toast (using requireContext()) that indicates how many or which attachments
could not be resolved so users get feedback about dropped files.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt`:
- Around line 208-209: Add KDoc to the public function
MessageComposerViewModel.completeRecording to document the new onComplete
parameter: explain when the callback is invoked (e.g., after recording
successfully completes or fails), describe the Result<Attachment> contents for
success (the created Attachment) and failure (the error/exception), and note
threading/dispatch expectations and any nullability/optional behavior; reference
the delegation to messageComposerController.completeRecording in the docs so
callers understand behavior is forwarded to that controller.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c0c45f42-3bdb-4741-8651-252eba521771
⛔ Files ignored due to path filters (2)
docs/sdk-hero-android.pngis excluded by!**/*.pngdocs/stream-chat-android-github-cover.pngis excluded by!**/*.png
📒 Files selected for processing (90)
README.mdstream-chat-android-client/api/stream-chat-android-client.apistream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/state/ChatClientStateExtensions.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/audio/NativeMediaPlayer.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/cdn/CDN.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/cdn/CDNRequest.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/cdn/internal/CDNDataSourceFactory.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/cdn/internal/CDNOkHttpInterceptor.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/cdn/internal/StreamMediaDataSource.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/di/ChatModule.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/file/StreamFileManager.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/factory/StreamStatePluginFactory.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/sync/internal/SyncManager.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/socket/ChatSocket.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/utils/internal/ServerClockOffset.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/ChatClientConnectionTests.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/ChatClientTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/DependencyResolverTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/MockClientBuilder.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/cdn/internal/CDNDataSourceTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/cdn/internal/CDNOkHttpInterceptorTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/chatclient/BaseChatClientTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/debugger/ChatClientDebuggerTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/file/StreamFileManagerTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/internal/SyncManagerTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/socket/FakeChatSocket.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/utils/internal/ServerClockOffsetTest.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/channel/attachments/ChannelMediaAttachmentsActivity.ktstream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.ktstream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/LinkAttachmentContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/AttachmentPreviewHandler.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/handler/DocumentAttachmentPreviewHandler.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPage.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/StreamMediaPlayerContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/attachments/ChannelMediaAttachmentsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/ComposerLinkPreview.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ImageHeadersInterceptor.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/extensions/internal/AttachmentExtensions.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/channel/ChannelMediaAttachmentsPreviewViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilderTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/viewmodel/messages/AttachmentsPickerViewModelTest.ktstream-chat-android-ui-common/src/main/AndroidManifest.xmlstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/documents/AttachmentDocumentActivity.javastream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/documents/DocumentAttachmentHandler.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/AsyncImageHeadersProvider.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DownloadAttachmentUriGenerator.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/DownloadRequestInterceptor.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageAssetTransformer.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageHeadersProvider.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/VideoHeadersProvider.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/StorageHelper.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/images/StreamImageLoaderFactory.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/images/internal/CDNImageInterceptor.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/images/internal/StreamCoil.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManager.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/Attachment.ktstream-chat-android-ui-common/src/main/res/values/strings.xmlstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/images/internal/CDNImageInterceptorTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManagerTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/utils/extensions/AttachmentExtensionsTest.ktstream-chat-android-ui-components-sample/src/main/kotlin/io/getstream/chat/ui/sample/feature/chat/info/shared/media/ChatInfoSharedMediaFragment.ktstream-chat-android-ui-components/api/stream-chat-android-ui-components.apistream-chat-android-ui-components/detekt-baseline.xmlstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/ChatUI.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/AttachmentMediaActivity.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/internal/AttachmentGalleryImagePageFragment.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/internal/AttachmentGalleryPagerAdapter.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/internal/AttachmentGalleryVideoPageFragment.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/overview/internal/MediaAttachmentAdapter.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/AttachmentsPickerDialogFragment.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/internal/AttachmentMetaDataMapper.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/MessageListView.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/DefaultQuotedAttachmentView.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/FileAttachmentsView.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/GiphyMediaAttachmentView.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/LinkAttachmentView.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/MediaAttachmentView.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/viewholder/impl/GiphyViewHolder.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/navigation/destinations/AttachmentDestination.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt
💤 Files with no reviewable changes (3)
- stream-chat-android-ui-common/src/main/AndroidManifest.xml
- stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/images/StreamImageLoaderFactory.kt
- stream-chat-android-ui-components/detekt-baseline.xml
...d-client/src/main/java/io/getstream/chat/android/client/cdn/internal/CDNDataSourceFactory.kt
Show resolved
Hide resolved
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt
Show resolved
Hide resolved
...i-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/ImageAssetTransformer.kt
Show resolved
Hide resolved
...n/src/main/kotlin/io/getstream/chat/android/ui/common/images/internal/CDNImageInterceptor.kt
Show resolved
Hide resolved
.../src/main/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManager.kt
Show resolved
Hide resolved
.../test/kotlin/io/getstream/chat/android/ui/common/internal/file/StreamShareFileManagerTest.kt
Show resolved
Hide resolved
...o/getstream/chat/android/ui/feature/messages/list/adapter/viewholder/impl/GiphyViewHolder.kt
Show resolved
Hide resolved
gpunto
left a comment
There was a problem hiding this comment.
Looks good on my side, but I'm not familiar with some of the changes that went into develop
| if (attachments.size < metadata.size) { | ||
| hasUnresolvedAttachments = true | ||
| } |
There was a problem hiding this comment.
Hey, I think this check can never be true.
storageHelper.toAttachments(metadata) uses .map internally, so it always returns the same number of items as metadata. It never drops anything. It just stores the content URI in extraData for later resolution. So attachments.size < metadata.size is always false, and hasUnresolvedAttachments will never be set here.
On develop, this worked because the equivalent code was calling getAttachmentsForUpload(), which eagerly cached files using getCachedFileFromUri() + mapNotNull. So items that couldn't be resolved were actually dropped at that point. But in v7, the file resolution is deferred to send time (resolveAttachmentFiles), so nothing gets filtered here anymore.
In practice this means: if a user picks a Google Drive file via the system picker, the toast "Some files could not be loaded and were skipped" never shows. The attachment gets staged in the composer and then silently dropped when sending.
Let me think about how to properly fix that and will get back to you.
There was a problem hiding this comment.
Ah I see, I missed this part. yes if this is not a performance issue, I think we can take this direction.
...-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/StorageHelper.kt
Outdated
Show resolved
Hide resolved
…ickerViewModel` and `AttachmentStorageHelper`. - Add `isUriResolvable` to `StorageHelper` to verify if a content URI can be opened for reading. - Implement `partitionResolvable` in `AttachmentStorageHelper` to separate metadata based on URI accessibility. - Update `AttachmentsPickerViewModel.resolveAndSubmitUris` to exclude inaccessible URIs (e.g., undownloaded cloud files) from the submission. - Ensure `hasUnresolvedAttachments` is correctly set when URIs are inaccessible, independent of file type support. - Add unit tests in `AttachmentStorageHelperTest` and `AttachmentsPickerViewModelTest` to verify partitioning logic and view model state updates.
# Conflicts: # stream-chat-android-compose/api/stream-chat-android-compose.api # stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.kt # stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentStorageHelperTest.kt
|


Goal
Merge develop to V7
Implementation
Merge develop to V7
Testing
There should be no regressions, and the latest additions from develop should be present on V7
Summary by CodeRabbit
New Features
Improvements
Deprecations
AsyncImageHeadersProvider,DownloadAttachmentUriGenerator, and related legacy interfaces deprecated; use CDN configuration instead.