mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-17 15:26:23 +00:00
[fenix] for https://github.com/mozilla-mobile/fenix/issues/24177: add ui wireframe for synced recent tab
This commit is contained in:
parent
13af68853e
commit
da6d15fc81
@ -48,6 +48,21 @@ sealed class RecentTab {
|
||||
*/
|
||||
data class Tab(val state: TabSessionState) : RecentTab()
|
||||
|
||||
/**
|
||||
* A tab that was recently viewed on a synced device.
|
||||
*
|
||||
* @param deviceDisplayName The device the tab was viewed on.
|
||||
* @param title The title of the tab.
|
||||
* @param url The url of the tab.
|
||||
* @param previewImageUrl The url used to retrieve the preview image of the tab.
|
||||
*/
|
||||
data class SyncedTab(
|
||||
val deviceDisplayName: String,
|
||||
val title: String,
|
||||
val url: String,
|
||||
val previewImageUrl: String?,
|
||||
) : RecentTab()
|
||||
|
||||
/**
|
||||
* A search term group that was recently viewed
|
||||
*
|
||||
|
@ -46,7 +46,7 @@ class RecentTabViewHolder(
|
||||
RecentTabs(
|
||||
recentTabs = recentTabs.value ?: emptyList(),
|
||||
onRecentTabClick = { interactor.onRecentTabClicked(it) },
|
||||
onRecentSearchGroupClicked = { interactor.onRecentSearchGroupClicked(it) },
|
||||
onRecentSearchGroupClick = { interactor.onRecentSearchGroupClicked(it) },
|
||||
menuItems = listOf(
|
||||
RecentTabMenuItem(
|
||||
title = stringResource(id = R.string.recent_tab_menu_item_remove),
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
@file:Suppress("MagicNumber")
|
||||
@file:Suppress("MagicNumber", "TooManyFunctions")
|
||||
|
||||
package org.mozilla.fenix.home.recenttabs.view
|
||||
|
||||
@ -16,6 +16,7 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@ -26,6 +27,8 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
@ -50,6 +53,7 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@ -59,6 +63,7 @@ import mozilla.components.browser.icons.compose.WithIcon
|
||||
import mozilla.components.concept.base.images.ImageLoadRequest
|
||||
import mozilla.components.support.ktx.kotlin.getRepresentativeSnippet
|
||||
import mozilla.components.ui.colors.PhotonColors
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.components
|
||||
import org.mozilla.fenix.compose.Image
|
||||
@ -71,14 +76,19 @@ import org.mozilla.fenix.theme.FirefoxTheme
|
||||
* @param recentTabs List of [RecentTab] to display.
|
||||
* @param menuItems List of [RecentTabMenuItem] shown long clicking a [RecentTab].
|
||||
* @param onRecentTabClick Invoked when the user clicks on a recent tab.
|
||||
* @param onRecentSearchGroupClicked Invoked when the user clicks on a recent search group.
|
||||
* @param onRecentSearchGroupClick Invoked when the user clicks on a recent search group.
|
||||
* @param onRecentSyncedTabClick Invoked when the user clicks on the recent synced tab.
|
||||
* @param onSyncedTabSeeAllButtonClick Invoked when user clicks on the "See all" button in the synced tab card.
|
||||
*/
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
fun RecentTabs(
|
||||
recentTabs: List<RecentTab>,
|
||||
menuItems: List<RecentTabMenuItem>,
|
||||
onRecentTabClick: (String) -> Unit = {},
|
||||
onRecentSearchGroupClicked: (String) -> Unit = {}
|
||||
onRecentSearchGroupClick: (String) -> Unit = {},
|
||||
onRecentSyncedTabClick: (RecentTab.SyncedTab) -> Unit = {},
|
||||
onSyncedTabSeeAllButtonClick: () -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@ -99,7 +109,16 @@ fun RecentTabs(
|
||||
searchTerm = tab.searchTerm,
|
||||
tabId = tab.tabId,
|
||||
count = tab.count,
|
||||
onSearchGroupClicked = onRecentSearchGroupClicked
|
||||
onSearchGroupClick = onRecentSearchGroupClick
|
||||
)
|
||||
}
|
||||
}
|
||||
is RecentTab.SyncedTab -> {
|
||||
if (FeatureFlags.taskContinuityFeature) {
|
||||
RecentSyncedTabItem(
|
||||
tab,
|
||||
onRecentSyncedTabClick,
|
||||
onSyncedTabSeeAllButtonClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -158,9 +177,7 @@ private fun RecentTabItem(
|
||||
Row {
|
||||
RecentTabIcon(
|
||||
url = tab.state.content.url,
|
||||
modifier = Modifier
|
||||
.size(18.dp, 18.dp)
|
||||
.clip(RoundedCornerShape(2.dp)),
|
||||
modifier = Modifier.size(18.dp).clip(RoundedCornerShape(2.dp)),
|
||||
icon = tab.state.content.icon
|
||||
)
|
||||
|
||||
@ -186,7 +203,7 @@ private fun RecentTabItem(
|
||||
* @param searchTerm The search term for the group.
|
||||
* @param tabId The id of the last accessed tab in the group.
|
||||
* @param count Count of how many tabs belongs to the group.
|
||||
* @param onSearchGroupClicked Invoked when the user clicks on a group.
|
||||
* @param onSearchGroupClick Invoked when the user clicks on a group.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
@ -194,13 +211,13 @@ private fun RecentSearchGroupItem(
|
||||
searchTerm: String,
|
||||
tabId: String,
|
||||
count: Int,
|
||||
onSearchGroupClicked: (String) -> Unit = {}
|
||||
onSearchGroupClick: (String) -> Unit = {}
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(112.dp)
|
||||
.clickable { onSearchGroupClicked(tabId) },
|
||||
.clickable { onSearchGroupClick(tabId) },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
backgroundColor = FirefoxTheme.colors.layer2,
|
||||
elevation = 6.dp
|
||||
@ -244,25 +261,139 @@ private fun RecentSearchGroupItem(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent synced tab.
|
||||
*
|
||||
* @param tab Optional synced tab. If null, displays placeholders.
|
||||
* @param onRecentSyncedTabClick Invoked when item is clicked.
|
||||
* @param onSeeAllButtonClick Invoked when "See all" button is clicked.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun RecentSyncedTabItem(
|
||||
tab: RecentTab.SyncedTab?,
|
||||
onRecentSyncedTabClick: (RecentTab.SyncedTab) -> Unit,
|
||||
onSeeAllButtonClick: () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(180.dp)
|
||||
.clickable { tab?.let { onRecentSyncedTabClick(tab) } },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
backgroundColor = FirefoxTheme.colors.layer2,
|
||||
elevation = 6.dp
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
|
||||
if (tab == null) {
|
||||
RecentTabImagePlaceholder()
|
||||
} else {
|
||||
RecentTabImage(
|
||||
tab = tab,
|
||||
modifier = Modifier
|
||||
.size(108.dp, 80.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
) {
|
||||
if (tab == null) {
|
||||
RecentTabTitlePlaceholder()
|
||||
} else {
|
||||
RecentTabTitle(title = tab.title)
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (tab == null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(FirefoxTheme.colors.layer3)
|
||||
.size(18.dp)
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_synced_tabs),
|
||||
contentDescription = stringResource(
|
||||
R.string.recent_tabs_synced_device_icon_content_description
|
||||
),
|
||||
modifier = Modifier.size(18.dp, 18.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
if (tab == null) {
|
||||
TextLinePlaceHolder()
|
||||
} else {
|
||||
RecentTabSubtitle(subtitle = tab.deviceDisplayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Button(
|
||||
onClick = onSeeAllButtonClick,
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
backgroundColor = if (tab == null) {
|
||||
FirefoxTheme.colors.layer3
|
||||
} else {
|
||||
FirefoxTheme.colors.actionSecondary
|
||||
}
|
||||
),
|
||||
elevation = ButtonDefaults.elevation(
|
||||
defaultElevation = 0.dp,
|
||||
pressedElevation = 0.dp
|
||||
),
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
if (tab != null) {
|
||||
Text(
|
||||
text = stringResource(R.string.recent_tabs_see_all_synced_tabs_button_text),
|
||||
textAlign = TextAlign.Center,
|
||||
color = FirefoxTheme.colors.textActionSecondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent tab image.
|
||||
*
|
||||
* @param tab [RecentTab.Tab] that was recently viewed.
|
||||
* @param tab [RecentTab] that was recently viewed.
|
||||
* @param modifier [Modifier] used to draw the image content.
|
||||
* @param contentScale [ContentScale] used to draw image content.
|
||||
* @param alignment [Alignment] used to draw the image content.
|
||||
* is null.
|
||||
*/
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
private fun RecentTabImage(
|
||||
tab: RecentTab.Tab,
|
||||
tab: RecentTab,
|
||||
modifier: Modifier = Modifier,
|
||||
contentScale: ContentScale = ContentScale.FillWidth,
|
||||
alignment: Alignment = Alignment.TopCenter
|
||||
) {
|
||||
val previewImageUrl = tab.state.content.previewImageUrl
|
||||
val thumbnail = tab.state.content.thumbnail
|
||||
val (previewImageUrl, loaderUrl, key) = when (tab) {
|
||||
is RecentTab.Tab -> Triple(
|
||||
tab.state.content.previewImageUrl,
|
||||
tab.state.content.url,
|
||||
tab.state.id
|
||||
)
|
||||
is RecentTab.SyncedTab -> Triple(tab.previewImageUrl, tab.url, tab.url)
|
||||
else -> return
|
||||
}
|
||||
val thumbnail = (tab as? RecentTab.Tab)?.state?.content?.thumbnail
|
||||
|
||||
when {
|
||||
!previewImageUrl.isNullOrEmpty() -> {
|
||||
@ -287,15 +418,10 @@ private fun RecentTabImage(
|
||||
modifier = modifier,
|
||||
backgroundColor = colorResource(id = R.color.photonGrey20)
|
||||
) {
|
||||
components.core.icons.Loader(tab.state.content.url) {
|
||||
components.core.icons.Loader(loaderUrl) {
|
||||
Placeholder {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = when (isSystemInDarkTheme()) {
|
||||
true -> PhotonColors.DarkGrey30
|
||||
false -> PhotonColors.LightGrey30
|
||||
}
|
||||
)
|
||||
modifier = Modifier.background(color = FirefoxTheme.colors.layer3)
|
||||
)
|
||||
}
|
||||
|
||||
@ -317,7 +443,7 @@ private fun RecentTabImage(
|
||||
}
|
||||
|
||||
ThumbnailImage(
|
||||
tabId = tab.state.id,
|
||||
key = key,
|
||||
modifier = modifier,
|
||||
contentScale = contentScale,
|
||||
alignment = alignment
|
||||
@ -327,6 +453,19 @@ private fun RecentTabImage(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder for a recent tab image.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabImagePlaceholder() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(108.dp, 80.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(color = FirefoxTheme.colors.layer3)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu shown for a [RecentTab.Tab].
|
||||
*
|
||||
@ -444,6 +583,20 @@ private fun RecentTabTitle(title: String) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder for a tab title.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabTitlePlaceholder() {
|
||||
Column {
|
||||
TextLinePlaceHolder()
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
TextLinePlaceHolder()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent tab subtitle.
|
||||
*
|
||||
@ -465,18 +618,18 @@ private fun RecentTabSubtitle(subtitle: String) {
|
||||
|
||||
@Composable
|
||||
private fun ThumbnailImage(
|
||||
tabId: String,
|
||||
key: String,
|
||||
modifier: Modifier,
|
||||
contentScale: ContentScale,
|
||||
alignment: Alignment
|
||||
) {
|
||||
val rememberBitmap = remember(tabId) { mutableStateOf<ImageBitmap?>(null) }
|
||||
val rememberBitmap = remember(key) { mutableStateOf<ImageBitmap?>(null) }
|
||||
val size = LocalDensity.current.run { 108.dp.toPx().toInt() }
|
||||
val request = ImageLoadRequest(tabId, size)
|
||||
val request = ImageLoadRequest(key, size)
|
||||
val storage = components.core.thumbnailStorage
|
||||
val bitmap = rememberBitmap.value
|
||||
|
||||
LaunchedEffect(tabId) {
|
||||
LaunchedEffect(key) {
|
||||
rememberBitmap.value = storage.loadThumbnail(request).await()?.asImageBitmap()
|
||||
}
|
||||
|
||||
@ -491,3 +644,13 @@ private fun ThumbnailImage(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextLinePlaceHolder() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(12.dp)
|
||||
.fillMaxWidth()
|
||||
.background(FirefoxTheme.colors.layer3)
|
||||
)
|
||||
}
|
||||
|
@ -127,6 +127,10 @@
|
||||
<!-- Text for the number of tabs in a group in the 'Jump back in' section of the new tab
|
||||
%d is a placeholder for the number of sites in the group. This number will always be more than one. -->
|
||||
<string name="recent_tabs_search_term_count_2">%d sites</string>
|
||||
<!-- Text for button in synced tab card that opens synced tabs tray -->
|
||||
<string name="recent_tabs_see_all_synced_tabs_button_text">See all synced tabs</string>
|
||||
<!-- Accessibility description for icon for recent synced tab -->
|
||||
<string name="recent_tabs_synced_device_icon_content_description">Synced device</string>
|
||||
<!-- Text for the menu button to remove a grouped highlight from the user's browsing history
|
||||
in the Recently visited section -->
|
||||
<string name="recent_tab_menu_item_remove">Remove</string>
|
||||
|
Loading…
Reference in New Issue
Block a user