Close #21451: Add active search term tab groups on home
This commit is contained in:
parent
f15291757b
commit
8a15e8a681
|
@ -9,24 +9,34 @@ import mozilla.components.browser.state.selector.selectedNormalTab
|
|||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.feature.tabs.ext.hasMediaPlayed
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.tabstray.browser.TabGroup
|
||||
import org.mozilla.fenix.tabstray.browser.maxActiveTime
|
||||
import org.mozilla.fenix.tabstray.ext.isNormalTabActiveWithSearchTerm
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Get the last opened normal tab and the last tab with in progress media, if available.
|
||||
* Get the last opened normal tab, last tab with in progress media and last search term group, if available.
|
||||
*
|
||||
* @return A list of the last opened tab and the last tab with in progress media
|
||||
* @return A list of the last opened tab, last tab with in progress media and last search term group
|
||||
* if distinct and available or an empty list.
|
||||
*/
|
||||
fun BrowserState.asRecentTabs(): List<TabSessionState> {
|
||||
return mutableListOf<TabSessionState>().apply {
|
||||
fun BrowserState.asRecentTabs(): List<RecentTab> {
|
||||
return mutableListOf<RecentTab>().apply {
|
||||
val lastOpenedNormalTab = lastOpenedNormalTab
|
||||
val inProgressMediaTab = inProgressMediaTab
|
||||
|
||||
lastOpenedNormalTab?.let { add(it) }
|
||||
lastOpenedNormalTab?.let { add(RecentTab.Tab(it)) }
|
||||
|
||||
if (inProgressMediaTab == lastOpenedNormalTab) {
|
||||
secondToLastOpenedNormalTab?.let { add(it) }
|
||||
secondToLastOpenedNormalTab?.let { add(RecentTab.Tab(it)) }
|
||||
} else {
|
||||
inProgressMediaTab?.let { add(it) }
|
||||
inProgressMediaTab?.let { add(RecentTab.Tab(it)) }
|
||||
}
|
||||
|
||||
if (FeatureFlags.tabGroupFeature) {
|
||||
lastSearchGroup?.let { add(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,3 +64,48 @@ val BrowserState.inProgressMediaTab: TabSessionState?
|
|||
get() = normalTabs
|
||||
.filter { it.hasMediaPlayed() }
|
||||
.maxByOrNull { it.lastMediaAccessState.lastMediaAccess }
|
||||
|
||||
/**
|
||||
* Get the most recent search term group.
|
||||
*/
|
||||
val BrowserState.lastSearchGroup: RecentTab.SearchGroup?
|
||||
get() {
|
||||
val tabGroup = normalTabs.toSearchGroup().lastOrNull() ?: return null
|
||||
val firstTab = tabGroup.tabs.firstOrNull() ?: return null
|
||||
|
||||
return RecentTab.SearchGroup(
|
||||
tabGroup.searchTerm,
|
||||
firstTab.id,
|
||||
firstTab.content.url,
|
||||
firstTab.content.thumbnail,
|
||||
tabGroup.tabs.count()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search term groups sorted by last access time.
|
||||
*/
|
||||
private fun List<TabSessionState>.toSearchGroup(): List<TabGroup> {
|
||||
val data = filter {
|
||||
it.isNormalTabActiveWithSearchTerm(maxActiveTime)
|
||||
}.groupBy {
|
||||
when {
|
||||
it.content.searchTerms.isNotBlank() -> it.content.searchTerms
|
||||
else -> it.historyMetadata?.searchTerm ?: ""
|
||||
}.lowercase()
|
||||
}
|
||||
|
||||
return data.map { mapEntry ->
|
||||
val searchTerm = mapEntry.key.replaceFirstChar(Char::uppercase)
|
||||
val groupTabs = mapEntry.value
|
||||
val groupMax = groupTabs.fold(0L) { acc, tab ->
|
||||
max(tab.lastAccess, acc)
|
||||
}
|
||||
|
||||
TabGroup(
|
||||
searchTerm = searchTerm,
|
||||
tabs = groupTabs,
|
||||
lastAccess = groupMax
|
||||
)
|
||||
}.sortedBy { it.lastAccess }
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package org.mozilla.fenix.home
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
|
@ -17,6 +16,7 @@ import mozilla.components.service.pocket.PocketRecommendedStory
|
|||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.ext.getFilteredStories
|
||||
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_TO_SHOW_COUNT
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory
|
||||
|
||||
|
@ -50,7 +50,7 @@ data class Tab(
|
|||
* @property tip The current [Tip] to show on the [HomeFragment].
|
||||
* @property showCollectionPlaceholder If true, shows a placeholder when there are no collections.
|
||||
* @property showSetAsDefaultBrowserCard If true, shows the default browser card
|
||||
* @property recentTabs The list of recent [TabSessionState] in the [HomeFragment].
|
||||
* @property recentTabs The list of recent [RecentTab] in the [HomeFragment].
|
||||
* @property recentBookmarks The list of recently saved [BookmarkNode]s to show on the [HomeFragment].
|
||||
* @property historyMetadata The list of [HistoryMetadataGroup].
|
||||
* @property pocketArticles The list of [PocketRecommendedStory].
|
||||
|
@ -63,7 +63,7 @@ data class HomeFragmentState(
|
|||
val tip: Tip? = null,
|
||||
val showCollectionPlaceholder: Boolean = false,
|
||||
val showSetAsDefaultBrowserCard: Boolean = false,
|
||||
val recentTabs: List<TabSessionState> = emptyList(),
|
||||
val recentTabs: List<RecentTab> = emptyList(),
|
||||
val recentBookmarks: List<BookmarkNode> = emptyList(),
|
||||
val historyMetadata: List<HistoryMetadataGroup> = emptyList(),
|
||||
val pocketStories: List<PocketRecommendedStory> = emptyList(),
|
||||
|
@ -77,7 +77,7 @@ sealed class HomeFragmentAction : Action {
|
|||
val collections: List<TabCollection>,
|
||||
val tip: Tip? = null,
|
||||
val showCollectionPlaceholder: Boolean,
|
||||
val recentTabs: List<TabSessionState>,
|
||||
val recentTabs: List<RecentTab>,
|
||||
val recentBookmarks: List<BookmarkNode>,
|
||||
val historyMetadata: List<HistoryMetadataGroup>
|
||||
) :
|
||||
|
@ -90,7 +90,7 @@ sealed class HomeFragmentAction : Action {
|
|||
data class ModeChange(val mode: Mode) : HomeFragmentAction()
|
||||
data class TopSitesChange(val topSites: List<TopSite>) : HomeFragmentAction()
|
||||
data class RemoveTip(val tip: Tip) : HomeFragmentAction()
|
||||
data class RecentTabsChange(val recentTabs: List<TabSessionState>) : HomeFragmentAction()
|
||||
data class RecentTabsChange(val recentTabs: List<RecentTab>) : HomeFragmentAction()
|
||||
data class RecentBookmarksChange(val recentBookmarks: List<BookmarkNode>) : HomeFragmentAction()
|
||||
data class HistoryMetadataChange(val historyMetadata: List<HistoryMetadataGroup>) : HomeFragmentAction()
|
||||
data class SelectPocketStoriesCategory(val categoryName: String) : HomeFragmentAction()
|
||||
|
|
|
@ -4,16 +4,17 @@
|
|||
|
||||
package org.mozilla.fenix.home.recenttabs
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import kotlinx.coroutines.flow.map
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.lib.state.helpers.AbstractBinding
|
||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
|
||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||
import org.mozilla.fenix.ext.asRecentTabs
|
||||
import org.mozilla.fenix.ext.lastOpenedNormalTab
|
||||
import org.mozilla.fenix.home.HomeFragmentAction
|
||||
import org.mozilla.fenix.home.HomeFragmentStore
|
||||
|
||||
|
@ -28,20 +29,39 @@ class RecentTabsListFeature(
|
|||
) : AbstractBinding<BrowserState>(browserStore) {
|
||||
|
||||
override suspend fun onState(flow: Flow<BrowserState>) {
|
||||
// Listen for changes regarding the currently selected tab, in progress media tab
|
||||
// and search term groups.
|
||||
flow
|
||||
// Listen for changes regarding the currently selected tab and the in progress media tab
|
||||
// and also for changes (close, undo) in normal tabs that could involve these.
|
||||
.ifAnyChanged {
|
||||
val lastOpenedNormalTab = it.lastOpenedNormalTab
|
||||
arrayOf(
|
||||
lastOpenedNormalTab?.id,
|
||||
lastOpenedNormalTab?.content?.title,
|
||||
lastOpenedNormalTab?.content?.icon,
|
||||
it.normalTabs
|
||||
)
|
||||
}
|
||||
.map { it.asRecentTabs() }
|
||||
.ifChanged()
|
||||
.collect {
|
||||
homeStore.dispatch(HomeFragmentAction.RecentTabsChange(browserStore.state.asRecentTabs()))
|
||||
homeStore.dispatch(HomeFragmentAction.RecentTabsChange(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RecentTab {
|
||||
/**
|
||||
* A tab that was recently viewed
|
||||
*
|
||||
* @param state Recently viewed [TabSessionState]
|
||||
*/
|
||||
data class Tab(val state: TabSessionState) : RecentTab()
|
||||
|
||||
/**
|
||||
* A search term group that was recently viewed
|
||||
*
|
||||
* @param searchTerm The search term that was recently viewed
|
||||
* @param tabId The id of the tab that was recently viewed
|
||||
* @param url The url that was recently viewed
|
||||
* @param thumbnail The thumbnail of the search term that was recently viewed
|
||||
* @param count The number of tabs in the search term group
|
||||
*/
|
||||
data class SearchGroup(
|
||||
val searchTerm: String,
|
||||
val tabId: String,
|
||||
val url: String,
|
||||
val thumbnail: Bitmap?,
|
||||
val count: Int
|
||||
) : RecentTab()
|
||||
}
|
||||
|
|
|
@ -26,6 +26,11 @@ interface RecentTabController {
|
|||
*/
|
||||
fun handleRecentTabClicked(tabId: String)
|
||||
|
||||
/**
|
||||
* @see [RecentTabInteractor.onRecentSearchGroupClicked]
|
||||
*/
|
||||
fun handleRecentSearchGroupClicked(tabId: String)
|
||||
|
||||
/**
|
||||
* @see [RecentTabInteractor.onRecentTabShowAllClicked]
|
||||
*/
|
||||
|
@ -62,6 +67,11 @@ class DefaultRecentTabsController(
|
|||
navController.navigate(HomeFragmentDirections.actionGlobalTabsTrayFragment())
|
||||
}
|
||||
|
||||
override fun handleRecentSearchGroupClicked(tabId: String) {
|
||||
selectTabUseCase.invoke(tabId)
|
||||
navController.navigate(HomeFragmentDirections.actionGlobalTabsTrayFragment())
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = PRIVATE)
|
||||
fun dismissSearchDialogIfDisplayed() {
|
||||
if (navController.currentDestination?.id == R.id.searchDialogFragment) {
|
||||
|
|
|
@ -15,6 +15,13 @@ interface RecentTabInteractor {
|
|||
*/
|
||||
fun onRecentTabClicked(tabId: String)
|
||||
|
||||
/**
|
||||
* Opens the tabs tray and scroll to the search group. Called when a user clicks on a search group.
|
||||
*
|
||||
* @param tabId The ID of the tab to open.
|
||||
*/
|
||||
fun onRecentSearchGroupClicked(tabId: String)
|
||||
|
||||
/**
|
||||
* Show the tabs tray. Called when a user clicks on the "Show all" button besides the recent
|
||||
* tabs.
|
||||
|
|
|
@ -36,7 +36,8 @@ class RecentTabViewHolder(
|
|||
FirefoxTheme {
|
||||
RecentTabs(
|
||||
recentTabs = recentTabs.value ?: emptyList(),
|
||||
onRecentTabClick = { interactor.onRecentTabClicked(it) }
|
||||
onRecentTabClick = { interactor.onRecentTabClicked(it) },
|
||||
onRecentSearchGroupClicked = { interactor.onRecentSearchGroupClicked(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,47 +24,69 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import mozilla.components.browser.icons.compose.Loader
|
||||
import mozilla.components.browser.icons.compose.Placeholder
|
||||
import mozilla.components.browser.icons.compose.WithIcon
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.support.ktx.kotlin.getRepresentativeSnippet
|
||||
import mozilla.components.ui.colors.PhotonColors
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.components
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
/**
|
||||
* A list of recent tabs to jump back to.
|
||||
*
|
||||
* @param recentTabs List of [TabSessionState] to display.
|
||||
* @param recentTabs List of [RecentTab] to display.
|
||||
* @param onRecentTabClick Invoked when the user clicks on a recent tab.
|
||||
* @param onRecentSearchGroupClicked Invoked when the user clicks on a recent search group.
|
||||
*/
|
||||
@Composable
|
||||
fun RecentTabs(
|
||||
recentTabs: List<TabSessionState>,
|
||||
onRecentTabClick: (String) -> Unit = {}
|
||||
recentTabs: List<RecentTab>,
|
||||
onRecentTabClick: (String) -> Unit = {},
|
||||
onRecentSearchGroupClicked: (String) -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
recentTabs.forEach { tab ->
|
||||
RecentTabItem(
|
||||
tabId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
icon = tab.content.icon,
|
||||
onRecentTabClick = onRecentTabClick
|
||||
)
|
||||
when (tab) {
|
||||
is RecentTab.Tab -> {
|
||||
RecentTabItem(
|
||||
tabId = tab.state.id,
|
||||
url = tab.state.content.url,
|
||||
title = tab.state.content.title,
|
||||
thumbnail = tab.state.content.thumbnail,
|
||||
onRecentTabClick = onRecentTabClick
|
||||
)
|
||||
}
|
||||
is RecentTab.SearchGroup -> {
|
||||
RecentSearchGroupItem(
|
||||
searchTerm = tab.searchTerm,
|
||||
tabId = tab.tabId,
|
||||
url = tab.url,
|
||||
thumbnail = tab.thumbnail,
|
||||
count = tab.count,
|
||||
onSearchGroupClicked = onRecentSearchGroupClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,18 +94,20 @@ fun RecentTabs(
|
|||
/**
|
||||
* A recent tab item.
|
||||
*
|
||||
* @param tabId Tbe id of the tab.
|
||||
* @param tabId The id of the tab.
|
||||
* @param url The loaded URL of the tab.
|
||||
* @param title The title of the tab.
|
||||
* @param icon The icon of the tab.
|
||||
* @param thumbnail The icon of the tab.
|
||||
* @param onRecentTabClick Invoked when the user clicks on a recent tab.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun RecentTabItem(
|
||||
tabId: String,
|
||||
url: String,
|
||||
title: String,
|
||||
icon: Bitmap? = null,
|
||||
thumbnail: Bitmap? = null,
|
||||
onRecentTabClick: (String) -> Unit = {}
|
||||
) {
|
||||
Card(
|
||||
|
@ -101,7 +125,9 @@ private fun RecentTabItem(
|
|||
RecentTabImage(
|
||||
url = url,
|
||||
modifier = Modifier.size(116.dp, 84.dp),
|
||||
icon = icon
|
||||
icon = thumbnail,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.TopCenter
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
@ -112,7 +138,82 @@ private fun RecentTabItem(
|
|||
) {
|
||||
RecentTabTitle(title = title)
|
||||
|
||||
RecentTabSubtitle(url = url)
|
||||
RecentTabSubtitle(subtitle = url)
|
||||
|
||||
Row {
|
||||
RecentTabImage(
|
||||
url = url,
|
||||
modifier = Modifier.size(18.dp, 18.dp),
|
||||
icon = icon
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
RecentTabSubtitle(subtitle = url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent search group item.
|
||||
*
|
||||
* @param searchTerm The search term for the group.
|
||||
* @param tabId The id of the last accessed tab in the group.
|
||||
* @param url The loaded URL of the last accessed tab in the group.
|
||||
* @param thumbnail The icon of the group.
|
||||
* @param count Count of how many tabs belongs to the group.
|
||||
* @param onSearchGroupClicked Invoked when the user clicks on a group.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun RecentSearchGroupItem(
|
||||
searchTerm: String,
|
||||
tabId: String,
|
||||
url: String,
|
||||
thumbnail: Bitmap?,
|
||||
count: Int,
|
||||
onSearchGroupClicked: (String) -> Unit = {}
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(116.dp)
|
||||
.clickable { onSearchGroupClicked(tabId) },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
backgroundColor = FirefoxTheme.colors.surface,
|
||||
elevation = 6.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
RecentTabImage(
|
||||
url = url,
|
||||
modifier = Modifier.size(116.dp, 84.dp),
|
||||
icon = thumbnail,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.TopCenter
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
RecentTabTitle(title = stringResource(R.string.recent_tabs_search_term, searchTerm))
|
||||
|
||||
Row {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_all_tabs),
|
||||
contentDescription = null // decorative element
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
RecentTabSubtitle(subtitle = stringResource(R.string.recent_tabs_search_term_count, count))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +231,8 @@ private fun RecentTabItem(
|
|||
private fun RecentTabImage(
|
||||
url: String,
|
||||
modifier: Modifier = Modifier,
|
||||
contentScale: ContentScale = ContentScale.Fit,
|
||||
alignment: Alignment = Alignment.Center,
|
||||
icon: Bitmap? = null
|
||||
) {
|
||||
if (icon != null) {
|
||||
|
@ -137,6 +240,8 @@ private fun RecentTabImage(
|
|||
painter = BitmapPainter(icon.asImageBitmap()),
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
contentScale = contentScale,
|
||||
alignment = alignment
|
||||
)
|
||||
} else {
|
||||
components.core.icons.Loader(
|
||||
|
@ -157,7 +262,7 @@ private fun RecentTabImage(
|
|||
Image(
|
||||
painter = icon.painter,
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -183,12 +288,12 @@ private fun RecentTabTitle(title: String) {
|
|||
/**
|
||||
* A recent tab subtitle.
|
||||
*
|
||||
* @param url The loaded URL of the tab.
|
||||
* @param subtitle The loaded URL of the tab.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabSubtitle(url: String) {
|
||||
private fun RecentTabSubtitle(subtitle: String) {
|
||||
Text(
|
||||
text = url.getRepresentativeSnippet(),
|
||||
text = subtitle.getRepresentativeSnippet(),
|
||||
color = FirefoxTheme.colors.textSecondary,
|
||||
fontSize = 12.sp,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
|
|
@ -355,6 +355,10 @@ class SessionControlInteractor(
|
|||
recentTabController.handleRecentTabClicked(tabId)
|
||||
}
|
||||
|
||||
override fun onRecentSearchGroupClicked(tabId: String) {
|
||||
recentTabController.handleRecentSearchGroupClicked(tabId)
|
||||
}
|
||||
|
||||
override fun onRecentTabShowAllClicked() {
|
||||
recentTabController.handleRecentTabShowAllClicked()
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import androidx.lifecycle.LifecycleOwner
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
|
@ -25,6 +24,7 @@ import org.mozilla.fenix.home.HomeFragmentStore
|
|||
import org.mozilla.fenix.home.HomeScreenViewModel
|
||||
import org.mozilla.fenix.home.Mode
|
||||
import org.mozilla.fenix.home.OnboardingState
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
// This method got a little complex with the addition of the tab tray feature flag
|
||||
|
@ -40,7 +40,7 @@ internal fun normalModeAdapterItems(
|
|||
recentBookmarks: List<BookmarkNode>,
|
||||
showCollectionsPlaceholder: Boolean,
|
||||
showSetAsDefaultBrowserCard: Boolean,
|
||||
recentTabs: List<TabSessionState>,
|
||||
recentTabs: List<RecentTab>,
|
||||
historyMetadata: List<HistoryMetadataGroup>,
|
||||
pocketStories: List<PocketRecommendedStory>
|
||||
): List<AdapterItem> {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabstray.browser
|
||||
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
|
||||
data class TabGroup(
|
||||
/**
|
||||
* The search term used for the tab group.
|
||||
*/
|
||||
val searchTerm: String,
|
||||
|
||||
/**
|
||||
* The list of tabSessionStates belonging to this tab group.
|
||||
*/
|
||||
val tabs: List<TabSessionState>,
|
||||
|
||||
/**
|
||||
* The last time tabs in this group was accessed.
|
||||
*/
|
||||
val lastAccess: Long
|
||||
)
|
|
@ -20,7 +20,6 @@ import org.mozilla.fenix.components.Components
|
|||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.selection.SelectionHolder
|
||||
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
import org.mozilla.fenix.tabstray.browser.TabGroupAdapter.Group
|
||||
import kotlin.math.max
|
||||
import mozilla.components.concept.tabstray.Tab as TabsTrayTab
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
|
@ -42,8 +41,10 @@ class TabGroupAdapter(
|
|||
private val store: TabsTrayStore,
|
||||
private val featureName: String,
|
||||
delegate: TrayObservable = ObserverRegistry()
|
||||
) : ListAdapter<Group, TabGroupViewHolder>(DiffCallback), TabsTray, TrayObservable by delegate {
|
||||
) : ListAdapter<TabGroupAdapter.Group, TabGroupViewHolder>(DiffCallback), TabsTray, TrayObservable by delegate {
|
||||
|
||||
// TODO use [List<TabSessionState>.toSearchGroup()]
|
||||
// see https://github.com/mozilla-mobile/android-components/issues/11012
|
||||
data class Group(
|
||||
/**
|
||||
* A title for the tab group.
|
||||
|
@ -138,6 +139,6 @@ class TabGroupAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
internal fun Group.containsTabId(tabId: String): Boolean {
|
||||
internal fun TabGroupAdapter.Group.containsTabId(tabId: String): Boolean {
|
||||
return tabs.firstOrNull { it.id == tabId } != null
|
||||
}
|
||||
|
|
10
app/src/main/res/drawable/ic_all_tabs.xml
Normal file
10
app/src/main/res/drawable/ic_all_tabs.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:pathData="M0,2.5C0,1.119 1.119,0 2.5,0H15.5C16.881,0 18,1.119 18,2.5V10.5C18,11.881 16.881,13 15.5,13H2.5C1.119,13 0,11.881 0,10.5V2.5ZM15.7,11.5L16.5,10.7V2.3L15.7,1.5H2.3L1.5,2.3V10.7L2.3,11.5H15.7ZM1.5,15.7L2.3,16.5H15.7L16.5,15.7V14.75C16.5,14.336 16.836,14 17.25,14C17.664,14 18,14.336 18,14.75V15.5C18,16.881 16.881,18 15.5,18H2.5C1.119,18 0,16.881 0,15.5V14.75C0,14.336 0.336,14 0.75,14C1.164,14 1.5,14.336 1.5,14.75V15.7Z"
|
||||
android:fillColor="#15141A"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -118,6 +118,10 @@
|
|||
<string name="recent_tabs_header">Jump back in</string>
|
||||
<!-- Button text for showing all the tabs in the tabs tray -->
|
||||
<string name="recent_tabs_show_all">Show all</string>
|
||||
<!-- Title for showing a group item in the 'Jump back in' section of the new tab -->
|
||||
<string name="recent_tabs_search_term">Your search for \"%1$s\"</string>
|
||||
<!-- Text for the number of tabs in a group in the 'Jump back in' section of the new tab -->
|
||||
<string name="recent_tabs_search_term_count">%1$s sites</string>
|
||||
|
||||
<!-- History Metadata -->
|
||||
<!-- Header text for a section on the home screen that displays grouped highlights from the
|
||||
|
|
|
@ -8,9 +8,11 @@ import io.mockk.mockk
|
|||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.LastMediaAccessState
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.concept.storage.HistoryMetadataKey
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
|
||||
class BrowserStateTest {
|
||||
|
||||
|
@ -47,7 +49,7 @@ class BrowserStateTest {
|
|||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(selectedTab, result[0])
|
||||
assertEquals(selectedTab, (result[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -66,7 +68,7 @@ class BrowserStateTest {
|
|||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(1, result.size)
|
||||
assertEquals(lastAccessedNormalTab, result[0])
|
||||
assertEquals(lastAccessedNormalTab, (result[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -84,8 +86,8 @@ class BrowserStateTest {
|
|||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(2, result.size)
|
||||
assertEquals(selectedTab, result[0])
|
||||
assertEquals(mediaTab, result[1])
|
||||
assertEquals(selectedTab, (result[0] as RecentTab.Tab).state)
|
||||
assertEquals(mediaTab, (result[1] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -109,8 +111,8 @@ class BrowserStateTest {
|
|||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(2, result.size)
|
||||
assertEquals(lastAccessedNormalTab, result[0])
|
||||
assertEquals(mediaTab, result[1])
|
||||
assertEquals(lastAccessedNormalTab, (result[0] as RecentTab.Tab).state)
|
||||
assertEquals(mediaTab, (result[1] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -129,7 +131,64 @@ class BrowserStateTest {
|
|||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(2, result.size)
|
||||
assertEquals(mediaTab, result[0])
|
||||
assertEquals(mediaTab, (result[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a tab group exists WHEN recentTabs is called THEN return a tab group`() {
|
||||
val searchGroupTab = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "1",
|
||||
historyMetadata = HistoryMetadataKey(
|
||||
url = "https://www.mozilla.org",
|
||||
searchTerm = "Test",
|
||||
referrerUrl = "https://www.mozilla.org"
|
||||
)
|
||||
)
|
||||
val browserState = BrowserState(
|
||||
tabs = listOf(searchGroupTab),
|
||||
selectedTabId = searchGroupTab.id
|
||||
)
|
||||
|
||||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(2, result.size)
|
||||
assertEquals(searchGroupTab, (result[0] as RecentTab.Tab).state)
|
||||
assert(result[1] is RecentTab.SearchGroup)
|
||||
assertEquals(searchGroupTab.historyMetadata?.searchTerm, (result[1] as RecentTab.SearchGroup).searchTerm)
|
||||
assertEquals(searchGroupTab.id, (result[1] as RecentTab.SearchGroup).tabId)
|
||||
assertEquals(searchGroupTab.content.url, (result[1] as RecentTab.SearchGroup).url)
|
||||
assertEquals(searchGroupTab.content.thumbnail, (result[1] as RecentTab.SearchGroup).thumbnail)
|
||||
assertEquals(1, (result[1] as RecentTab.SearchGroup).count)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the selected tab is a normal tab and tab group exists WHEN asRecentTabs is called THEN return a list of these tabs`() {
|
||||
val selectedTab = createTab(url = "url", id = "3")
|
||||
val searchGroupTab = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "4",
|
||||
historyMetadata = HistoryMetadataKey(
|
||||
url = "https://www.mozilla.org",
|
||||
searchTerm = "Test",
|
||||
referrerUrl = "https://www.mozilla.org"
|
||||
)
|
||||
)
|
||||
val browserState = BrowserState(
|
||||
tabs = listOf(mockk(relaxed = true), selectedTab, searchGroupTab),
|
||||
selectedTabId = selectedTab.id
|
||||
)
|
||||
|
||||
val result = browserState.asRecentTabs()
|
||||
|
||||
assertEquals(3, result.size)
|
||||
assertEquals(selectedTab, (result[0] as RecentTab.Tab).state)
|
||||
assert(result[2] is RecentTab.SearchGroup)
|
||||
assertEquals(searchGroupTab.historyMetadata?.searchTerm, (result[2] as RecentTab.SearchGroup).searchTerm)
|
||||
assertEquals(searchGroupTab.id, (result[2] as RecentTab.SearchGroup).tabId)
|
||||
assertEquals(searchGroupTab.content.url, (result[2] as RecentTab.SearchGroup).url)
|
||||
assertEquals(searchGroupTab.content.thumbnail, (result[2] as RecentTab.SearchGroup).thumbnail)
|
||||
assertEquals(1, (result[2] as RecentTab.SearchGroup).count)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -10,7 +10,6 @@ import io.mockk.mockk
|
|||
import io.mockk.mockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
|
@ -27,6 +26,7 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
|||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getFilteredStories
|
||||
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.POCKET_STORIES_TO_SHOW_COUNT
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.pocket.PocketRecommendedStoryCategory
|
||||
import org.mozilla.fenix.onboarding.FenixOnboarding
|
||||
|
@ -111,7 +111,7 @@ class HomeFragmentStoreTest {
|
|||
assertEquals(0, homeFragmentStore.state.recentTabs.size)
|
||||
|
||||
// Add 2 TabSessionState to the HomeFragmentStore.
|
||||
val recentTabs: List<TabSessionState> = listOf(mockk(), mockk())
|
||||
val recentTabs: List<RecentTab> = listOf(mockk(), mockk())
|
||||
homeFragmentStore.dispatch(HomeFragmentAction.RecentTabsChange(recentTabs)).join()
|
||||
|
||||
assertEquals(recentTabs, homeFragmentStore.state.recentTabs)
|
||||
|
@ -163,7 +163,7 @@ class HomeFragmentStoreTest {
|
|||
|
||||
val collections: List<TabCollection> = listOf(mockk())
|
||||
val topSites: List<TopSite> = listOf(mockk(), mockk())
|
||||
val recentTabs: List<TabSessionState> = listOf(mockk(), mockk())
|
||||
val recentTabs: List<RecentTab> = listOf(mockk(), mockk())
|
||||
val recentBookmarks: List<BookmarkNode> = listOf(mockk(), mockk())
|
||||
val historyMetadata: List<HistoryMetadataGroup> = listOf(mockk(), mockk())
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.home
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
|
@ -16,6 +17,7 @@ import mozilla.components.browser.state.state.LastMediaAccessState
|
|||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.engine.mediasession.MediaSession
|
||||
import mozilla.components.concept.storage.HistoryMetadataKey
|
||||
import mozilla.components.feature.media.middleware.LastMediaAccessMiddleware
|
||||
import mozilla.components.support.test.ext.joinBlocking
|
||||
import mozilla.components.support.test.libstate.ext.waitUntilIdle
|
||||
|
@ -30,6 +32,7 @@ import org.junit.Before
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.home.HomeFragmentAction.RecentTabsChange
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTabsListFeature
|
||||
|
||||
class RecentTabsListFeatureTest {
|
||||
|
@ -137,8 +140,10 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(2, homeStore.state.recentTabs.size)
|
||||
assertEquals(selectedTab, homeStore.state.recentTabs[0])
|
||||
assertEquals(mediaTab, homeStore.state.recentTabs[1])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(selectedTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
assertTrue(homeStore.state.recentTabs[1] is RecentTab.Tab)
|
||||
assertEquals(mediaTab, (homeStore.state.recentTabs[1] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -162,7 +167,8 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(1, homeStore.state.recentTabs.size)
|
||||
assertEquals(selectedMediaTab, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(selectedMediaTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -192,14 +198,16 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(1, homeStore.state.recentTabs.size)
|
||||
assertEquals(tab1, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(tab1, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
|
||||
browserStore.dispatch(TabListAction.SelectTabAction(tab2.id)).joinBlocking()
|
||||
|
||||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(1, homeStore.state.recentTabs.size)
|
||||
assertEquals(tab2, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(tab2, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -227,22 +235,28 @@ class RecentTabsListFeatureTest {
|
|||
feature.start()
|
||||
homeStore.waitUntilIdle()
|
||||
assertEquals(2, homeStore.state.recentTabs.size)
|
||||
assertEquals(initialMediaTab, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(initialMediaTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
|
||||
browserStore.dispatch(
|
||||
MediaSessionAction.UpdateMediaPlaybackStateAction("2", MediaSession.PlaybackState.PLAYING)
|
||||
).joinBlocking()
|
||||
homeStore.waitUntilIdle()
|
||||
assertEquals(2, homeStore.state.recentTabs.size)
|
||||
assertEquals(initialMediaTab, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(initialMediaTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
// UpdateMediaPlaybackStateAction would set the current timestamp as the new value for lastMediaAccess
|
||||
val updatedLastMediaAccess = homeStore.state.recentTabs[1].lastMediaAccessState.lastMediaAccess
|
||||
val updatedLastMediaAccess =
|
||||
(homeStore.state.recentTabs[1] as RecentTab.Tab).state.lastMediaAccessState.lastMediaAccess
|
||||
assertTrue("expected lastMediaAccess ($updatedLastMediaAccess) > 100", updatedLastMediaAccess > 100)
|
||||
assertEquals("http://mozilla.org", homeStore.state.recentTabs[1].lastMediaAccessState.lastMediaUrl)
|
||||
assertEquals(
|
||||
"http://mozilla.org",
|
||||
(homeStore.state.recentTabs[1] as RecentTab.Tab).state.lastMediaAccessState.lastMediaUrl
|
||||
)
|
||||
// Check that the media tab is updated ignoring just the lastMediaAccess property.
|
||||
assertEquals(
|
||||
newMediaTab,
|
||||
homeStore.state.recentTabs[1].copy(
|
||||
(homeStore.state.recentTabs[1] as RecentTab.Tab).state.copy(
|
||||
lastMediaAccessState = LastMediaAccessState("https://mozilla.com", 100)
|
||||
)
|
||||
)
|
||||
|
@ -282,7 +296,8 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(1, homeStore.state.recentTabs.size)
|
||||
assertEquals(selectedNormalTab, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(selectedNormalTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
|
||||
browserStore.dispatch(TabListAction.SelectTabAction(privateTab.id)).joinBlocking()
|
||||
|
||||
|
@ -290,7 +305,8 @@ class RecentTabsListFeatureTest {
|
|||
|
||||
// If the selected tab is a private tab the feature should show the last accessed normal tab.
|
||||
assertEquals(1, homeStore.state.recentTabs.size)
|
||||
assertEquals(lastAccessedNormalTab, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(lastAccessedNormalTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -316,9 +332,9 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
middleware.assertLastAction(RecentTabsChange::class) {
|
||||
val tab = it.recentTabs.first()
|
||||
assertTrue(tab.content.title.isEmpty())
|
||||
assertNull(tab.content.icon)
|
||||
val tab = it.recentTabs.first() as RecentTab.Tab
|
||||
assertTrue(tab.state.content.title.isEmpty())
|
||||
assertNull(tab.state.content.icon)
|
||||
}
|
||||
|
||||
browserStore.dispatch(UpdateTitleAction("1", "test")).joinBlocking()
|
||||
|
@ -326,9 +342,9 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
middleware.assertLastAction(RecentTabsChange::class) {
|
||||
val tab = it.recentTabs.first()
|
||||
assertEquals("test", tab.content.title)
|
||||
assertNull(tab.content.icon)
|
||||
val tab = it.recentTabs.first() as RecentTab.Tab
|
||||
assertEquals("test", tab.state.content.title)
|
||||
assertNull(tab.state.content.icon)
|
||||
}
|
||||
|
||||
browserStore.dispatch(UpdateIconAction("1", "https://www.mozilla.org", mockk()))
|
||||
|
@ -337,9 +353,9 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
middleware.assertLastAction(RecentTabsChange::class) {
|
||||
val tab = it.recentTabs.first()
|
||||
assertEquals("test", tab.content.title)
|
||||
assertNotNull(tab.content.icon)
|
||||
val tab = it.recentTabs.first() as RecentTab.Tab
|
||||
assertEquals("test", tab.state.content.title)
|
||||
assertNotNull(tab.state.content.icon)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,6 +377,179 @@ class RecentTabsListFeatureTest {
|
|||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(1, homeStore.state.recentTabs.size)
|
||||
assertEquals(selectedTab, homeStore.state.recentTabs[0])
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(selectedTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a selected tab group WHEN the feature starts THEN dispatch the selected tab group as a recent tab list`() {
|
||||
val tab = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "1",
|
||||
historyMetadata = HistoryMetadataKey(
|
||||
url = "https://www.mozilla.org",
|
||||
searchTerm = "test search term",
|
||||
referrerUrl = "https://www.mozilla.org"
|
||||
)
|
||||
)
|
||||
val tabs = listOf(tab)
|
||||
val browserStore = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = tabs,
|
||||
selectedTabId = "1"
|
||||
)
|
||||
)
|
||||
val feature = RecentTabsListFeature(
|
||||
browserStore = browserStore,
|
||||
homeStore = homeStore
|
||||
)
|
||||
|
||||
feature.start()
|
||||
|
||||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(2, homeStore.state.recentTabs.size)
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
val searchGroup = (homeStore.state.recentTabs[1] as RecentTab.SearchGroup)
|
||||
assertEquals(searchGroup.searchTerm, "Test search term")
|
||||
assertEquals(searchGroup.tabId, "1")
|
||||
assertEquals(searchGroup.url, "https://www.mozilla.org")
|
||||
assertEquals(searchGroup.thumbnail, null)
|
||||
assertEquals(searchGroup.count, 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a selected tab group and a selected tab WHEN the feature starts THEN dispatch both the selected tab and the selected tab group as a recent tab list`() {
|
||||
val tab1 = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "1"
|
||||
)
|
||||
val tab2 = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "2",
|
||||
historyMetadata = HistoryMetadataKey(
|
||||
url = "https://www.mozilla.org",
|
||||
searchTerm = "test search term",
|
||||
referrerUrl = "https://www.mozilla.org"
|
||||
)
|
||||
)
|
||||
val tabs = listOf(tab1, tab2)
|
||||
val browserStore = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = tabs,
|
||||
selectedTabId = "1"
|
||||
)
|
||||
)
|
||||
val feature = RecentTabsListFeature(
|
||||
browserStore = browserStore,
|
||||
homeStore = homeStore
|
||||
)
|
||||
|
||||
feature.start()
|
||||
|
||||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(2, homeStore.state.recentTabs.size)
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(tab1, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
val searchGroup = (homeStore.state.recentTabs[1] as RecentTab.SearchGroup)
|
||||
assertEquals(searchGroup.searchTerm, "Test search term")
|
||||
assertEquals(searchGroup.tabId, "2")
|
||||
assertEquals(searchGroup.url, "https://www.mozilla.org")
|
||||
assertEquals(searchGroup.thumbnail, null)
|
||||
assertEquals(searchGroup.count, 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a selected tab group with 2 tabs WHEN the feature starts THEN dispatch both tab in the selected tab group in the recent tab list`() {
|
||||
val tab1 = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "1",
|
||||
historyMetadata = HistoryMetadataKey(
|
||||
url = "https://www.mozilla.org",
|
||||
searchTerm = "test search term",
|
||||
referrerUrl = "https://www.mozilla.org"
|
||||
)
|
||||
)
|
||||
val tab2 = createTab(
|
||||
url = "https://www.getpocket.com",
|
||||
id = "2",
|
||||
historyMetadata = HistoryMetadataKey(
|
||||
url = "https://www.getpocket.com",
|
||||
searchTerm = "Test Search Term",
|
||||
referrerUrl = "https://www.getpocket.com"
|
||||
)
|
||||
)
|
||||
val tabs = listOf(tab1, tab2)
|
||||
val browserStore = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = tabs,
|
||||
selectedTabId = "2"
|
||||
)
|
||||
)
|
||||
val feature = RecentTabsListFeature(
|
||||
browserStore = browserStore,
|
||||
homeStore = homeStore
|
||||
)
|
||||
|
||||
feature.start()
|
||||
|
||||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(2, homeStore.state.recentTabs.size)
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(tab2, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
val searchGroup = (homeStore.state.recentTabs[1] as RecentTab.SearchGroup)
|
||||
assertEquals(searchGroup.searchTerm, "Test search term")
|
||||
assertEquals(searchGroup.tabId, "1")
|
||||
assertEquals(searchGroup.url, "https://www.mozilla.org")
|
||||
assertEquals(searchGroup.thumbnail, null)
|
||||
assertEquals(searchGroup.count, 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a valid inProgressMediaTabId, selected tab and tab group exists WHEN the feature starts THEN dispatch all as as a recent tabs list`() {
|
||||
val mediaTab = createTab(
|
||||
url = "https://mozilla.com", id = "42",
|
||||
lastMediaAccessState = LastMediaAccessState("https://mozilla.com", 123)
|
||||
)
|
||||
val selectedTab = createTab("https://mozilla.com", id = "43")
|
||||
val historyMetadataKey = HistoryMetadataKey(
|
||||
url = "https://www.mozilla.org",
|
||||
searchTerm = "test search term",
|
||||
referrerUrl = "https://www.mozilla.org"
|
||||
)
|
||||
val thumbnail = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
|
||||
val searchTermTab = createTab(
|
||||
url = "https://www.mozilla.org",
|
||||
id = "44",
|
||||
thumbnail = thumbnail,
|
||||
historyMetadata = historyMetadataKey
|
||||
)
|
||||
val browserStore = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = listOf(mediaTab, selectedTab, searchTermTab),
|
||||
selectedTabId = "43"
|
||||
)
|
||||
)
|
||||
val feature = RecentTabsListFeature(
|
||||
browserStore = browserStore,
|
||||
homeStore = homeStore
|
||||
)
|
||||
|
||||
feature.start()
|
||||
homeStore.waitUntilIdle()
|
||||
|
||||
assertEquals(3, homeStore.state.recentTabs.size)
|
||||
assertTrue(homeStore.state.recentTabs[0] is RecentTab.Tab)
|
||||
assertEquals(selectedTab, (homeStore.state.recentTabs[0] as RecentTab.Tab).state)
|
||||
assertTrue(homeStore.state.recentTabs[1] is RecentTab.Tab)
|
||||
assertEquals(mediaTab, (homeStore.state.recentTabs[1] as RecentTab.Tab).state)
|
||||
val searchGroup = (homeStore.state.recentTabs[2] as RecentTab.SearchGroup)
|
||||
assertEquals(searchGroup.searchTerm, "Test search term")
|
||||
assertEquals(searchGroup.tabId, "44")
|
||||
assertEquals(searchGroup.url, "https://www.mozilla.org")
|
||||
assertEquals(searchGroup.thumbnail, thumbnail)
|
||||
assertEquals(searchGroup.count, 1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,6 +158,13 @@ class SessionControlInteractorTest {
|
|||
verify { recentTabController.handleRecentTabClicked(tabId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onRecentSearchGroupClicked() {
|
||||
val tabId = "tabId"
|
||||
interactor.onRecentSearchGroupClicked(tabId)
|
||||
verify { recentTabController.handleRecentSearchGroupClicked(tabId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onRecentTabShowAllClicked() {
|
||||
interactor.onRecentTabShowAllClicked()
|
||||
|
|
|
@ -9,7 +9,6 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import mozilla.components.concept.storage.BookmarkNodeType
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
|
@ -24,6 +23,7 @@ import org.mozilla.fenix.ext.settings
|
|||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.historymetadata.HistoryMetadataGroup
|
||||
import org.mozilla.fenix.home.HomeFragmentState
|
||||
import org.mozilla.fenix.home.recenttabs.RecentTab
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
|
@ -44,7 +44,7 @@ class SessionControlViewTest {
|
|||
|
||||
@Test
|
||||
fun `GIVEN recentTabs WHEN calling shouldShowHomeOnboardingDialog THEN show the dialog `() {
|
||||
val recentTabs = listOf<TabSessionState>(mockk())
|
||||
val recentTabs = listOf<RecentTab>(mockk())
|
||||
val settings: Settings = mockk()
|
||||
|
||||
every { settings.hasShownHomeOnboardingDialog } returns false
|
||||
|
@ -101,7 +101,7 @@ class SessionControlViewTest {
|
|||
interactor,
|
||||
mockk(relaxed = true)
|
||||
)
|
||||
val recentTabs = listOf<TabSessionState>(mockk(relaxed = true))
|
||||
val recentTabs = listOf<RecentTab>(mockk(relaxed = true))
|
||||
|
||||
val state = HomeFragmentState(recentTabs = recentTabs)
|
||||
|
||||
|
@ -140,7 +140,7 @@ class SessionControlViewTest {
|
|||
val expandedCollections = emptySet<Long>()
|
||||
val recentBookmarks =
|
||||
listOf(BookmarkNode(BookmarkNodeType.ITEM, "guid", null, null, null, null, 0, null))
|
||||
val recentTabs = emptyList<TabSessionState>()
|
||||
val recentTabs = emptyList<RecentTab.Tab>()
|
||||
val historyMetadata = emptyList<HistoryMetadataGroup>()
|
||||
val pocketArticles = emptyList<PocketRecommendedStory>()
|
||||
|
||||
|
@ -168,7 +168,7 @@ class SessionControlViewTest {
|
|||
val collections = emptyList<TabCollection>()
|
||||
val expandedCollections = emptySet<Long>()
|
||||
val recentBookmarks = listOf<BookmarkNode>()
|
||||
val recentTabs = listOf<TabSessionState>(mockk())
|
||||
val recentTabs = listOf<RecentTab.Tab>(mockk())
|
||||
val historyMetadata = emptyList<HistoryMetadataGroup>()
|
||||
val pocketArticles = emptyList<PocketRecommendedStory>()
|
||||
|
||||
|
@ -197,7 +197,7 @@ class SessionControlViewTest {
|
|||
val collections = emptyList<TabCollection>()
|
||||
val expandedCollections = emptySet<Long>()
|
||||
val recentBookmarks = listOf<BookmarkNode>()
|
||||
val recentTabs = emptyList<TabSessionState>()
|
||||
val recentTabs = emptyList<RecentTab.Tab>()
|
||||
val historyMetadata = listOf(HistoryMetadataGroup("title", emptyList()))
|
||||
val pocketArticles = emptyList<PocketRecommendedStory>()
|
||||
|
||||
|
@ -226,7 +226,7 @@ class SessionControlViewTest {
|
|||
val collections = emptyList<TabCollection>()
|
||||
val expandedCollections = emptySet<Long>()
|
||||
val recentBookmarks = listOf<BookmarkNode>()
|
||||
val recentTabs = emptyList<TabSessionState>()
|
||||
val recentTabs = emptyList<RecentTab.Tab>()
|
||||
val historyMetadata = emptyList<HistoryMetadataGroup>()
|
||||
val pocketArticles = listOf(PocketRecommendedStory("", "", "", "", 0, ""))
|
||||
val context = spyk(testContext)
|
||||
|
@ -259,7 +259,7 @@ class SessionControlViewTest {
|
|||
val collections = emptyList<TabCollection>()
|
||||
val expandedCollections = emptySet<Long>()
|
||||
val recentBookmarks = listOf<BookmarkNode>()
|
||||
val recentTabs = emptyList<TabSessionState>()
|
||||
val recentTabs = emptyList<RecentTab.Tab>()
|
||||
val historyMetadata = emptyList<HistoryMetadataGroup>()
|
||||
val pocketArticles = emptyList<PocketRecommendedStory>()
|
||||
val context = spyk(testContext)
|
||||
|
|
Loading…
Reference in New Issue
Block a user