Bug 1816522 - Add multi selection banner UI

fenix/114.1.0
Alexandru2909 1 year ago committed by mergify[bot]
parent 3f318de778
commit fc40edf578

@ -17,6 +17,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.theme.FirefoxTheme
@ -28,6 +29,7 @@ import org.mozilla.fenix.theme.FirefoxTheme
* @param showMenu Whether or not the menu is currently displayed to the user.
* @param onDismissRequest Invoked when user dismisses the menu or on orientation changes.
* @param modifier Modifier to be applied to the menu.
* @param offset Offset to be added to the position of the menu.
*/
@Composable
fun DropdownMenu(
@ -35,6 +37,7 @@ fun DropdownMenu(
showMenu: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset.Zero,
) {
DisposableEffect(LocalConfiguration.current.orientation) {
onDispose { onDismissRequest() }
@ -44,6 +47,7 @@ fun DropdownMenu(
DropdownMenu(
expanded = showMenu && menuItems.isNotEmpty(),
onDismissRequest = { onDismissRequest() },
offset = offset,
modifier = Modifier
.background(color = FirefoxTheme.colors.layer2)
.then(modifier),

@ -50,6 +50,7 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
* @param browserStore [BrowserStore] used to listen for changes to [BrowserState].
* @param tabsTrayStore [TabsTrayStore] used to listen for changes to [TabsTrayState].
* @param displayTabsInGrid Whether the normal and private tabs should be displayed in a grid.
* @param isInDebugMode True for debug variant or if secret menu is enabled for this session.
* @param onTabClose Invoked when the user clicks to close a tab.
* @param onTabMediaClick Invoked when the user interacts with a tab's media controls.
* @param onTabClick Invoked when the user clicks on a tab.
@ -68,13 +69,14 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
* @param onSyncedTabClick Invoked when the user clicks on a synced tab.
*/
@OptIn(ExperimentalPagerApi::class, ExperimentalComposeUiApi::class)
@Suppress("LongMethod", "LongParameterList")
@Suppress("LongMethod", "LongParameterList", "ComplexMethod")
@Composable
fun TabsTray(
appStore: AppStore,
browserStore: BrowserStore,
tabsTrayStore: TabsTrayStore,
displayTabsInGrid: Boolean,
isInDebugMode: Boolean,
shouldShowInactiveTabsAutoCloseDialog: (Int) -> Boolean,
onTabPageClick: (Page) -> Unit,
onTabClose: (TabSessionState) -> Unit,
@ -116,6 +118,12 @@ fun TabsTray(
}
}
val shapeModifier = if (isInMultiSelectMode) {
Modifier
} else {
Modifier.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
}
LaunchedEffect(selectedPage) {
pagerState.animateScrollToPage(selectedPage.ordinal)
}
@ -123,14 +131,15 @@ fun TabsTray(
Column(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
.then(shapeModifier)
.background(FirefoxTheme.colors.layer1),
) {
Box(modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
TabsTrayBanner(
isInMultiSelectMode = isInMultiSelectMode,
selectMode = multiselectMode,
selectedPage = selectedPage,
normalTabCount = normalTabs.size + inactiveTabs.size,
isInDebugMode = isInDebugMode,
onTabPageIndicatorClicked = onTabPageClick,
)
}
@ -341,6 +350,7 @@ private fun TabsTrayPreviewRoot(
browserStore = browserStore,
tabsTrayStore = tabsTrayStore,
displayTabsInGrid = displayTabsInGrid,
isInDebugMode = false,
shouldShowInactiveTabsAutoCloseDialog = { true },
onTabPageClick = { page ->
selectedPageState = page

@ -39,29 +39,42 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.ui.tabcounter.TabCounter
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.DropdownMenu
import org.mozilla.fenix.compose.MenuItem
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.theme.FirefoxTheme
private val ICON_SIZE = 24.dp
/**
* Top-level UI for displaying the banner in [TabsTray].
*
* @param isInMultiSelectMode Whether the tab list is in multi-select mode.
* @param selectMode Current [TabsTrayState.Mode] used in the tabs tray.
* @param selectedPage The active [Page] of the Tabs Tray.
* @param normalTabCount The total amount of normal browsing tabs currently open.
* @param isInDebugMode True for debug variant or if secret menu is enabled for this session.
* @param onTabPageIndicatorClicked Invoked when the user clicks on a tab page indicator.
*/
@Composable
fun TabsTrayBanner(
isInMultiSelectMode: Boolean,
selectMode: TabsTrayState.Mode,
selectedPage: Page,
normalTabCount: Int,
isInDebugMode: Boolean,
onTabPageIndicatorClicked: (Page) -> Unit,
) {
if (isInMultiSelectMode) {
MultiSelectBanner()
if (selectMode is TabsTrayState.Mode.Select) {
MultiSelectBanner(
selectedTabCount = selectMode.selectedTabs.size,
shouldShowInactiveButton = isInDebugMode,
)
} else {
SingleSelectBanner(
onTabPageIndicatorClicked = onTabPageIndicatorClicked,
@ -198,20 +211,94 @@ private fun NormalTabsTabIcon(normalTabCount: Int) {
}
}
/**
* Banner displayed in multi select mode.
*
* @param selectedTabCount Number of selected tabs.
* @param shouldShowInactiveButton Whether or not to show the inactive tabs menu item.
*/
@Suppress("LongMethod")
@Composable
private fun MultiSelectBanner() {
Box(
private fun MultiSelectBanner(
selectedTabCount: Int,
shouldShowInactiveButton: Boolean,
) {
var showMenu by remember { mutableStateOf(false) }
val menuItems = mutableListOf(
MenuItem(
title = stringResource(R.string.tab_tray_multiselect_menu_item_bookmark),
) {},
MenuItem(
title = stringResource(R.string.tab_tray_multiselect_menu_item_close),
) {},
)
if (shouldShowInactiveButton) {
menuItems.add(
MenuItem(
title = stringResource(R.string.inactive_tabs_menu_item),
) {},
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.height(88.dp)
.background(color = FirefoxTheme.colors.layerAccent),
contentAlignment = Alignment.Center,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(onClick = {}) {
Icon(
painter = painterResource(id = R.drawable.ic_close),
contentDescription = stringResource(id = R.string.tab_tray_close_multiselect_content_description),
tint = FirefoxTheme.colors.iconOnColor,
)
}
Text(
text = "Multi selection mode",
color = FirefoxTheme.colors.textOnColorPrimary,
text = stringResource(R.string.tab_tray_multi_select_title, selectedTabCount),
style = FirefoxTheme.typography.body1,
color = FirefoxTheme.colors.textOnColorPrimary,
fontSize = 20.sp,
fontWeight = FontWeight.W500,
)
Spacer(modifier = Modifier.weight(1.0f))
IconButton(onClick = {}) {
Icon(
painter = painterResource(id = R.drawable.ic_tab_collection),
contentDescription = stringResource(
id = R.string.tab_tray_collection_button_multiselect_content_description,
),
tint = FirefoxTheme.colors.iconOnColor,
)
}
IconButton(onClick = {}) {
Icon(
painter = painterResource(id = R.drawable.ic_share),
contentDescription = stringResource(
id = R.string.tab_tray_multiselect_share_content_description,
),
tint = FirefoxTheme.colors.iconOnColor,
)
}
IconButton(onClick = { showMenu = true }) {
Icon(
painter = painterResource(id = R.drawable.ic_menu),
contentDescription = stringResource(id = R.string.tab_tray_multiselect_menu_content_description),
tint = FirefoxTheme.colors.iconOnColor,
)
DropdownMenu(
menuItems = menuItems,
showMenu = showMenu,
offset = DpOffset(x = 0.dp, y = -ICON_SIZE),
onDismissRequest = { showMenu = false },
)
}
}
}
@ -236,13 +323,28 @@ private fun TabsTrayBannerInfinityPreview() {
@Composable
private fun TabsTrayBannerMultiselectPreview() {
TabsTrayBannerPreviewRoot(
isInMultiSelectMode = true,
selectMode = TabsTrayState.Mode.Select(
setOf(
TabSessionState(
id = "1",
content = ContentState(
url = "www.mozilla.com",
),
),
TabSessionState(
id = "2",
content = ContentState(
url = "www.mozilla.com",
),
),
),
),
)
}
@Composable
private fun TabsTrayBannerPreviewRoot(
isInMultiSelectMode: Boolean = false,
selectMode: TabsTrayState.Mode = TabsTrayState.Mode.Normal,
selectedPage: Page = Page.NormalTabs,
normalTabCount: Int = 10,
) {
@ -251,9 +353,10 @@ private fun TabsTrayBannerPreviewRoot(
FirefoxTheme {
Box(modifier = Modifier.background(color = FirefoxTheme.colors.layer1)) {
TabsTrayBanner(
isInMultiSelectMode = isInMultiSelectMode,
selectMode = selectMode,
selectedPage = selectedPageState,
normalTabCount = normalTabCount,
isInDebugMode = true,
onTabPageIndicatorClicked = { page ->
selectedPageState = page
},

@ -32,6 +32,7 @@ import mozilla.components.feature.downloads.ui.DownloadCancelDialogFragment
import mozilla.components.feature.tabs.tabstray.TabsFeature
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.Config
import org.mozilla.fenix.GleanMetrics.TabsTray
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
@ -223,6 +224,8 @@ class TabsTrayFragment : AppCompatDialogFragment() {
browserStore = requireComponents.core.store,
tabsTrayStore = tabsTrayStore,
displayTabsInGrid = requireContext().settings().gridTabView,
isInDebugMode = Config.channel.isDebug ||
requireComponents.settings.showSecretDebugMenuThisSession,
shouldShowInactiveTabsAutoCloseDialog =
requireContext().settings()::shouldShowInactiveTabsAutoCloseDialog,
onTabPageClick = { page ->

Loading…
Cancel
Save