commit fc157dc79e2b97ed4f5b9c6ea2c73dfdd2dfc174
parent 6225c88e92cc2f2d71265600329aa1191aa76290
Author: fmasalha <fmasalha@mozilla.com>
Date: Wed, 10 Dec 2025 19:46:29 +0000
Bug 2000269 - Added snackbar confirmation on moved bookmarks r=android-reviewers,android-l10n-reviewers,flod,matt-tighe
Differential Revision: https://phabricator.services.mozilla.com/D272695
Diffstat:
5 files changed, 85 insertions(+), 0 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/bookmarks/BookmarksReducer.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/bookmarks/BookmarksReducer.kt
@@ -269,6 +269,10 @@ private fun BookmarksState.updateSelectedFolder(folder: SelectFolderItem): Bookm
} else {
alwaysTryUpdate.copy(
bookmarksEditBookmarkState = bookmarksEditBookmarkState.copy(folder = folder.folder, edited = true),
+ bookmarksSnackbarState = BookmarksSnackbarState.BookmarkMoved(
+ formatBookmarkTitle(bookmarksEditBookmarkState.bookmark.title),
+ folder.folder.title,
+ ),
)
}
}
@@ -276,6 +280,15 @@ private fun BookmarksState.updateSelectedFolder(folder: SelectFolderItem): Bookm
else -> this
}
+internal fun formatBookmarkTitle(raw: String, max: Int = 25): String {
+ val cleaned = raw
+ .removePrefix("https://")
+ .removePrefix("http://")
+ .removePrefix("www.")
+
+ return if (cleaned.length <= max) cleaned else cleaned.take(max) + "…"
+}
+
private fun BookmarksState.toggleSelectionOf(item: BookmarkItem): BookmarksState =
if (selectedItems.any { it.guid == item.guid }) {
copy(selectedItems = selectedItems - item)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/bookmarks/BookmarksScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/bookmarks/BookmarksScreen.kt
@@ -273,6 +273,18 @@ private fun BookmarksList(
val (id, titleOrCount) = state.undoSnackbarText()
stringResource(id, titleOrCount)
}
+ is BookmarksSnackbarState.BookmarkMoved -> {
+ stringResource(
+ R.string.bookmark_moved_single_item,
+ formatArgs = arrayOf(
+ (state.bookmarksSnackbarState as BookmarksSnackbarState.BookmarkMoved).from,
+ (state.bookmarksSnackbarState as BookmarksSnackbarState.BookmarkMoved).to,
+ ),
+ )
+ }
+ BookmarksSnackbarState.SelectFolderFailed -> {
+ stringResource(R.string.bookmark_error_select_folder)
+ }
else -> ""
}
@@ -304,6 +316,18 @@ private fun BookmarksList(
onDismissPerformed = { store.dispatch(SnackbarAction.Dismissed) },
)
}
+ is BookmarksSnackbarState.BookmarkMoved -> scope.launch {
+ snackbarHostState.displaySnackbar(
+ message = snackbarMessage,
+ onDismissPerformed = { store.dispatch(SnackbarAction.Dismissed) },
+ )
+ }
+ BookmarksSnackbarState.SelectFolderFailed -> scope.launch {
+ snackbarHostState.displaySnackbar(
+ message = snackbarMessage,
+ onDismissPerformed = { store.dispatch(SnackbarAction.Dismissed) },
+ )
+ }
}
}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/bookmarks/BookmarksState.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/bookmarks/BookmarksState.kt
@@ -193,6 +193,8 @@ internal sealed class BookmarksSnackbarState {
data object None : BookmarksSnackbarState()
data object CantEditDesktopFolders : BookmarksSnackbarState()
data class UndoDeletion(val guidsToDelete: List<String>) : BookmarksSnackbarState()
+ data class BookmarkMoved(val from: String, val to: String) : BookmarksSnackbarState()
+ data object SelectFolderFailed : BookmarksSnackbarState()
}
internal fun BookmarksSnackbarState.addGuidToDelete(guid: String) = when (this) {
diff --git a/mobile/android/fenix/app/src/main/res/values/strings.xml b/mobile/android/fenix/app/src/main/res/values/strings.xml
@@ -1737,6 +1737,8 @@
<string name="bookmark_empty_list_folder_description">Add bookmarks as you browse so you can find your favorite sites later.</string>
<!-- Description for the add new folder button when selecting a folder. -->
<string name="bookmark_select_folder_new_folder_button_title">New folder</string>
+ <!-- Bookmark snackbar message for moving a single item. %1$s is the title of the item being moved and %2$s is the new parent folder of the bookmark -->
+ <string name="bookmark_moved_single_item">Moved %1$s to %2$s</string>
<!-- Site Permissions -->
<!-- Button label that take the user to the Android App setting -->
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/bookmarks/BookmarksReducerTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/bookmarks/BookmarksReducerTest.kt
@@ -10,6 +10,7 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.mozilla.fenix.components.appstate.AppAction
class BookmarksReducerTest {
@Test
@@ -588,12 +589,55 @@ class BookmarksReducerTest {
bookmarksSelectFolderState = state.bookmarksSelectFolderState?.copy(
outerSelectionGuid = "guid0",
),
+ bookmarksSnackbarState = BookmarksSnackbarState.BookmarkMoved("title", "Nested 0"),
)
assertEquals(expected, result)
}
@Test
+ fun `GIVEN we are on the select folder screen and the bookmark has a url longer than 25 characters WHEN a folder is clicked THEN update the truncate the title and display a snackbar with the truncated title`() {
+ val longTitle = "https://www.firefox.com/en-US/?utm_source=www.mozilla.org&utm_medium=referral&utm_campaign=nav&utm_content=firefox"
+ val state = BookmarksState.default.copy(
+ bookmarksEditBookmarkState = BookmarksEditBookmarkState(
+ bookmark = generateBookmark().copy(title = longTitle),
+ folder = BookmarkItem.Folder("Bookmarks", "guid0", null),
+ ),
+ bookmarksSelectFolderState = BookmarksSelectFolderState(
+ outerSelectionGuid = "guid0",
+ folders = listOf(
+ SelectFolderItem(0, BookmarkItem.Folder("Bookmarks", "guid0", null)),
+ SelectFolderItem(0, BookmarkItem.Folder("Nested 0", "guid0", null)),
+ ),
+ ),
+ )
+
+ val result = bookmarksReducer(
+ state = state,
+ action = SelectFolderAction.ItemClicked(
+ folder = SelectFolderItem(0, BookmarkItem.Folder("Nested 0", "guid0", null)),
+ ),
+ )
+
+ val expected = state.copy(
+ bookmarksEditBookmarkState = state.bookmarksEditBookmarkState?.copy(
+ folder = BookmarkItem.Folder("Nested 0", "guid0", null),
+ edited = true,
+ ),
+ bookmarksSelectFolderState = state.bookmarksSelectFolderState?.copy(
+ outerSelectionGuid = "guid0",
+ ),
+ bookmarksSnackbarState = BookmarksSnackbarState.BookmarkMoved(formatBookmarkTitle(longTitle), "Nested 0"),
+ )
+
+ assertEquals(expected, result)
+
+ val finalResult = bookmarksReducer(result, BackClicked)
+
+ assertEquals(BookmarksSnackbarState.BookmarkMoved(formatBookmarkTitle(longTitle), "Nested 0"), finalResult.bookmarksSnackbarState)
+ }
+
+ @Test
fun `GIVEN a bookmark WHEN a edit is made THEN the edited state is persisted`() {
val bookmarkItem = generateBookmark()
val folderItem = SelectFolderItem(0, BookmarkItem.Folder("Bookmarks", "guid0", null))