Skip to content

Store one image per surface for the remote messages and clear the new tab page surface image when the message is dismissed#7696

Merged
catalinradoiu merged 4 commits intodevelopfrom
feature/cradoiu/store-one-image-per-surface
Feb 9, 2026
Merged

Store one image per surface for the remote messages and clear the new tab page surface image when the message is dismissed#7696
catalinradoiu merged 4 commits intodevelopfrom
feature/cradoiu/store-one-image-per-surface

Conversation

@catalinradoiu
Copy link
Contributor

@catalinradoiu catalinradoiu commented Feb 6, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/task/1212974313804281

Description

This PR enhances the remote messaging image handling by implementing surface-specific image storage and cleanup. Now, images are stored per surface (NEW_TAB_PAGE, MODAL) and are properly cleaned up when messages are dismissed or actions are taken.

Steps to test this PR

Follow the steps in this task to test the PR: https://app.asana.com/1/137249556945/task/1213116538171640

UI changes

No visual changes, only behavior improvements


Note

Medium Risk
Touches shared remote-messaging APIs and image storage behavior, which could cause missing/stale images across surfaces if call sites aren’t updated or surface selection is incorrect.

Overview
Remote message image handling is now surface-specific: getRemoteMessageImageFile takes a Surface and a new clearMessageImage(surface) API clears only that surface’s cached file.

The image store now saves separate files per surface (e.g., active_message_remote_image_<surface>.png) and callers (NewTabPageViewModel, RemoteMessageViewModel, CardsListRemoteMessageViewModel) request/clear images for NEW_TAB_PAGE vs MODAL; new-tab flows clear the image when a message is dismissed or an action is taken, except for Action.Share.

Tests are updated/added to cover the new surface-parameterized APIs and the Share “don’t clear image” behavior.

Written by Cursor Bugbot for commit ec575e8. This will update automatically on new commits. Configure here.

Copy link
Contributor Author

@catalinradoiu catalinradoiu marked this pull request as ready for review February 6, 2026 18:26
}
val cardsList = message?.content as? Content.CardsList
val imageFile = remoteMessagingModel.getRemoteMessageImageFile()
val imageFile = remoteMessagingModel.getRemoteMessageImageFile(Surface.MODAL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MODAL surface images never cleaned up when dismissed

Medium Severity

CardsListRemoteMessageViewModel fetches images using Surface.MODAL but never calls clearMessageImage(Surface.MODAL) in its action handlers (onCloseButtonClicked, onActionButtonClicked, onItemClicked). Unlike NewTabPageViewModel and RemoteMessageViewModel which explicitly call clearMessageImage(Surface.NEW_TAB_PAGE) when dismissing messages, the MODAL equivalent is missing. Since dismissMessage() no longer clears images automatically (removed in this PR), MODAL surface images will accumulate on disk.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to persist the image for the modals so that we don’t download them again when accessing the modal from the “What’s New” entrypoint from settings.

}

override suspend fun clearMessageImage(surface: Surface) {
withContext(dispatchers.io()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withContext(dispatchers.io()) can be removed for consistency with other methods here + is not needed as it is handled in deleteStoredImage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed


deleteStoredImage()
message?.surfaces?.forEach { surface ->
withContext(dispatcherProvider.io()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withContext(dispatcherProvider.io()) can be removed as it's not needed here. deleteStoredImage handles context switching internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

logcat { "RMF: Prefetching image: $imageUrl for message: ${message.id}" }
// Store one image per surface
message.surfaces.forEach { surface ->
withContext(dispatcherProvider.io()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move withContext(dispatcherProvider.io()) above the forEach loop to have a single context switch for all surfaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

logcat { "RMF: Successfully saved image to permanent storage: ${permanentFile.absolutePath}" }
}.onFailure { error ->
logcat { "RMF: Failed to prefetch image $imageUrl for surface ${surface.jsonValue}: ${error.message}" }
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image downloaded redundantly for each surface

Low Severity

The fetchAndStoreImage method downloads the same image from Glide once per surface inside the forEach loop. When a message targets multiple surfaces (e.g., both MODAL and NEW_TAB_PAGE), this triggers redundant network requests for the same imageUrl. The Glide download (submit().get()) belongs outside the loop, with only the copyTo operation inside the loop to create surface-specific copies of the same downloaded file.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually a message will target one surface.

Copy link
Contributor

@anikiki anikiki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and works as expected! 🎉

@catalinradoiu catalinradoiu merged commit 91c5e70 into develop Feb 9, 2026
21 checks passed
@catalinradoiu catalinradoiu deleted the feature/cradoiu/store-one-image-per-surface branch February 9, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants