Bug 1815765 - Add tab auto close banner to Tabs Tray rewrite

fenix/118.0
Noah Bond 11 months ago committed by mergify[bot]
parent aaadeab883
commit 7ca8aab648

@ -57,6 +57,9 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
* @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 shouldShowTabAutoCloseBanner Whether the tab auto closer banner should be displayed.
* @param shouldShowInactiveTabsAutoCloseDialog Whether the inactive tabs auto close dialog should be displayed.
* @param onTabPageClick Invoked when the user clicks on the Normal, Private, or Synced tabs page button.
* @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.
@ -85,6 +88,9 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
* @param onDeleteSelectedTabsClick Invoked when the user clicks on the close selected tabs banner menu item.
* @param onForceSelectedTabsAsInactiveClick Invoked when the user clicks on the make inactive banner menu item.
* @param onTabsTrayDismiss Invoked when accessibility services or UI automation requests dismissal.
* @param onTabAutoCloseBannerViewOptionsClick Invoked when the user clicks to view the auto close options.
* @param onTabAutoCloseBannerDismiss Invoked when the user clicks to dismiss the auto close banner.
* @param onTabAutoCloseBannerShown Invoked when the auto close banner has been shown to the user.
*/
@Suppress("LongMethod", "LongParameterList", "ComplexMethod")
@Composable
@ -94,6 +100,7 @@ fun TabsTray(
tabsTrayStore: TabsTrayStore,
displayTabsInGrid: Boolean,
isInDebugMode: Boolean,
shouldShowTabAutoCloseBanner: Boolean,
shouldShowInactiveTabsAutoCloseDialog: (Int) -> Boolean,
onTabPageClick: (Page) -> Unit,
onTabClose: (TabSessionState) -> Unit,
@ -119,6 +126,9 @@ fun TabsTray(
onDeleteSelectedTabsClick: () -> Unit,
onForceSelectedTabsAsInactiveClick: () -> Unit,
onTabsTrayDismiss: () -> Unit,
onTabAutoCloseBannerViewOptionsClick: () -> Unit,
onTabAutoCloseBannerDismiss: () -> Unit,
onTabAutoCloseBannerShown: () -> Unit,
) {
val multiselectMode = tabsTrayStore
.observeAsComposableState { state -> state.mode }.value ?: TabsTrayState.Mode.Normal
@ -148,6 +158,7 @@ fun TabsTray(
TabsTrayBanner(
tabsTrayStore = tabsTrayStore,
isInDebugMode = isInDebugMode,
shouldShowTabAutoCloseBanner = shouldShowTabAutoCloseBanner,
onTabPageIndicatorClicked = onTabPageClick,
onSaveToCollectionClick = onSaveToCollectionClick,
onShareSelectedTabsClick = onShareSelectedTabsClick,
@ -160,6 +171,9 @@ fun TabsTray(
onDeleteSelectedTabsClick = onDeleteSelectedTabsClick,
onForceSelectedTabsAsInactiveClick = onForceSelectedTabsAsInactiveClick,
onDismissClick = onTabsTrayDismiss,
onTabAutoCloseBannerViewOptionsClick = onTabAutoCloseBannerViewOptionsClick,
onTabAutoCloseBannerDismiss = onTabAutoCloseBannerDismiss,
onTabAutoCloseBannerShown = onTabAutoCloseBannerShown,
)
}
@ -435,6 +449,15 @@ private fun TabsTraySyncedTabsPreview() {
)
}
@LightDarkPreview
@Composable
private fun TabsTrayAutoCloseBannerPreview() {
TabsTrayPreviewRoot(
normalTabs = generateFakeTabsList(),
showTabAutoCloseBanner = true,
)
}
@Suppress("LongMethod", "LongParameterList")
@Composable
private fun TabsTrayPreviewRoot(
@ -448,6 +471,7 @@ private fun TabsTrayPreviewRoot(
syncedTabs: List<SyncedTabsListItem> = emptyList(),
inactiveTabsExpanded: Boolean = false,
showInactiveTabsAutoCloseDialog: Boolean = false,
showTabAutoCloseBanner: Boolean = false,
) {
var selectedPageState by remember { mutableStateOf(selectedPage) }
val normalTabsState = remember { normalTabs.toMutableStateList() }
@ -487,6 +511,7 @@ private fun TabsTrayPreviewRoot(
displayTabsInGrid = displayTabsInGrid,
isInDebugMode = false,
shouldShowInactiveTabsAutoCloseDialog = { true },
shouldShowTabAutoCloseBanner = showTabAutoCloseBanner,
onTabPageClick = { page ->
selectedPageState = page
},
@ -538,6 +563,9 @@ private fun TabsTrayPreviewRoot(
onBookmarkSelectedTabsClick = {},
onForceSelectedTabsAsInactiveClick = {},
onTabsTrayDismiss = {},
onTabAutoCloseBannerViewOptionsClick = {},
onTabAutoCloseBannerDismiss = {},
onTabAutoCloseBannerShown = {},
)
}
}

@ -50,21 +50,26 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.lib.state.ext.observeAsComposableState
import mozilla.components.ui.tabcounter.TabCounter
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.Banner
import org.mozilla.fenix.compose.BottomSheetHandle
import org.mozilla.fenix.compose.ContextualMenu
import org.mozilla.fenix.compose.Divider
import org.mozilla.fenix.compose.MenuItem
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.theme.FirefoxTheme
import kotlin.math.max
private val ICON_SIZE = 24.dp
private const val MAX_WIDTH_TAB_ROW_PERCENT = 0.5f
private const val BOTTOM_SHEET_HANDLE_WIDTH_PERCENT = 0.1f
private const val TAB_COUNT_SHOW_CFR = 6
/**
* Top-level UI for displaying the banner in [TabsTray].
*
* @param tabsTrayStore [TabsTrayStore] used to listen for changes to [TabsTrayState].
* @param isInDebugMode True for debug variant or if secret menu is enabled for this session.
* @param shouldShowTabAutoCloseBanner Whether the tab auto closer banner should be displayed.
* @param onTabPageIndicatorClicked Invoked when the user clicks on a tab page indicator.
* @param onSaveToCollectionClick Invoked when the user clicks on the save to collection button from
* the multi select banner.
@ -78,12 +83,16 @@ private const val BOTTOM_SHEET_HANDLE_WIDTH_PERCENT = 0.1f
* @param onBookmarkSelectedTabsClick Invoked when user interacts with the bookmark menu item.
* @param onForceSelectedTabsAsInactiveClick Invoked when user interacts with the make inactive menu item.
* @param onDismissClick Invoked when accessibility services or UI automation requests dismissal.
* @param onTabAutoCloseBannerViewOptionsClick Invoked when the user clicks to view the auto close options.
* @param onTabAutoCloseBannerDismiss Invoked when the user clicks to dismiss the auto close banner.
* @param onTabAutoCloseBannerShown Invoked when the auto close banner has been shown to the user.
*/
@Suppress("LongParameterList")
@Composable
fun TabsTrayBanner(
tabsTrayStore: TabsTrayStore,
isInDebugMode: Boolean,
shouldShowTabAutoCloseBanner: Boolean,
onTabPageIndicatorClicked: (Page) -> Unit,
onSaveToCollectionClick: () -> Unit,
onShareSelectedTabsClick: () -> Unit,
@ -96,6 +105,9 @@ fun TabsTrayBanner(
onBookmarkSelectedTabsClick: () -> Unit,
onForceSelectedTabsAsInactiveClick: () -> Unit,
onDismissClick: () -> Unit,
onTabAutoCloseBannerViewOptionsClick: () -> Unit,
onTabAutoCloseBannerDismiss: () -> Unit,
onTabAutoCloseBannerShown: () -> Unit,
) {
val normalTabCount = tabsTrayStore.observeAsComposableState { state ->
state.normalTabs.size + state.inactiveTabs.size
@ -106,32 +118,58 @@ fun TabsTrayBanner(
.observeAsComposableState { state -> state.mode }.value ?: TabsTrayState.Mode.Normal
val selectedPage = tabsTrayStore
.observeAsComposableState { state -> state.selectedPage }.value ?: Page.NormalTabs
val showTabAutoCloseBanner = tabsTrayStore.observeAsComposableState { state ->
shouldShowTabAutoCloseBanner && max(state.normalTabs.size, state.privateTabs.size) >= TAB_COUNT_SHOW_CFR
}.value ?: false
var hasAcknowledgedBanner by remember { mutableStateOf(false) }
Column {
if (multiselectMode is TabsTrayState.Mode.Select) {
MultiSelectBanner(
selectedTabCount = multiselectMode.selectedTabs.size,
shouldShowInactiveButton = isInDebugMode,
onExitSelectModeClick = { tabsTrayStore.dispatch(TabsTrayAction.ExitSelectMode) },
onSaveToCollectionsClick = onSaveToCollectionClick,
onShareSelectedTabs = onShareSelectedTabsClick,
onBookmarkSelectedTabsClick = onBookmarkSelectedTabsClick,
onCloseSelectedTabsClick = onDeleteSelectedTabsClick,
onMakeSelectedTabsInactive = onForceSelectedTabsAsInactiveClick,
)
} else {
SingleSelectBanner(
onTabPageIndicatorClicked = onTabPageIndicatorClicked,
selectedPage = selectedPage,
normalTabCount = normalTabCount,
privateTabCount = privateTabCount,
onEnterMultiselectModeClick = { tabsTrayStore.dispatch(TabsTrayAction.EnterSelectMode) },
onShareAllTabsClick = onShareAllTabsClick,
onTabSettingsClick = onTabSettingsClick,
onRecentlyClosedClick = onRecentlyClosedClick,
onAccountSettingsClick = onAccountSettingsClick,
onDeleteAllTabsClick = onDeleteAllTabsClick,
onDismissClick = onDismissClick,
)
}
if (multiselectMode is TabsTrayState.Mode.Select) {
MultiSelectBanner(
selectedTabCount = multiselectMode.selectedTabs.size,
shouldShowInactiveButton = isInDebugMode,
onExitSelectModeClick = { tabsTrayStore.dispatch(TabsTrayAction.ExitSelectMode) },
onSaveToCollectionsClick = onSaveToCollectionClick,
onShareSelectedTabs = onShareSelectedTabsClick,
onBookmarkSelectedTabsClick = onBookmarkSelectedTabsClick,
onCloseSelectedTabsClick = onDeleteSelectedTabsClick,
onMakeSelectedTabsInactive = onForceSelectedTabsAsInactiveClick,
)
} else {
SingleSelectBanner(
onTabPageIndicatorClicked = onTabPageIndicatorClicked,
selectedPage = selectedPage,
normalTabCount = normalTabCount,
privateTabCount = privateTabCount,
onEnterMultiselectModeClick = { tabsTrayStore.dispatch(TabsTrayAction.EnterSelectMode) },
onShareAllTabsClick = onShareAllTabsClick,
onTabSettingsClick = onTabSettingsClick,
onRecentlyClosedClick = onRecentlyClosedClick,
onAccountSettingsClick = onAccountSettingsClick,
onDeleteAllTabsClick = onDeleteAllTabsClick,
onDismissClick = onDismissClick,
)
if (!hasAcknowledgedBanner && showTabAutoCloseBanner) {
onTabAutoCloseBannerShown()
Divider()
Banner(
message = stringResource(id = R.string.tab_tray_close_tabs_banner_message),
button1Text = stringResource(id = R.string.tab_tray_close_tabs_banner_negative_button_text),
button2Text = stringResource(id = R.string.tab_tray_close_tabs_banner_positive_button_text),
onButton1Click = {
hasAcknowledgedBanner = true
onTabAutoCloseBannerViewOptionsClick()
},
onButton2Click = {
hasAcknowledgedBanner = true
onTabAutoCloseBannerDismiss()
},
)
}
}
}
@ -537,6 +575,14 @@ private fun TabsTrayBannerInfinityPreview() {
)
}
@LightDarkPreview
@Composable
private fun TabsTrayBannerAutoClosePreview() {
TabsTrayBannerPreviewRoot(
shouldShowTabAutoCloseBanner = true,
)
}
@LightDarkPreview
@Composable
private fun TabsTrayBannerMultiselectPreview() {
@ -566,6 +612,7 @@ private fun TabsTrayBannerPreviewRoot(
selectedPage: Page = Page.NormalTabs,
normalTabCount: Int = 10,
privateTabCount: Int = 10,
shouldShowTabAutoCloseBanner: Boolean = false,
) {
val normalTabs = generateFakeTabsList(normalTabCount)
val privateTabs = generateFakeTabsList(privateTabCount)
@ -584,6 +631,7 @@ private fun TabsTrayBannerPreviewRoot(
TabsTrayBanner(
tabsTrayStore = tabsTrayStore,
isInDebugMode = true,
shouldShowTabAutoCloseBanner = shouldShowTabAutoCloseBanner,
onTabPageIndicatorClicked = { page ->
tabsTrayStore.dispatch(TabsTrayAction.PageSelected(page))
},
@ -598,6 +646,9 @@ private fun TabsTrayBannerPreviewRoot(
onDeleteSelectedTabsClick = {},
onForceSelectedTabsAsInactiveClick = {},
onDismissClick = {},
onTabAutoCloseBannerViewOptionsClick = {},
onTabAutoCloseBannerDismiss = {},
onTabAutoCloseBannerShown = {},
)
}
}

@ -243,6 +243,8 @@ class TabsTrayFragment : AppCompatDialogFragment() {
displayTabsInGrid = requireContext().settings().gridTabView,
isInDebugMode = Config.channel.isDebug ||
requireComponents.settings.showSecretDebugMenuThisSession,
shouldShowTabAutoCloseBanner = requireContext().settings().shouldShowAutoCloseTabsBanner &&
requireContext().settings().canShowCfr,
shouldShowInactiveTabsAutoCloseDialog =
requireContext().settings()::shouldShowInactiveTabsAutoCloseDialog,
onTabPageClick = { page ->
@ -291,6 +293,16 @@ class TabsTrayFragment : AppCompatDialogFragment() {
onBookmarkSelectedTabsClick = tabsTrayInteractor::onBookmarkSelectedTabsClicked,
onForceSelectedTabsAsInactiveClick = tabsTrayInteractor::onForceSelectedTabsAsInactiveClicked,
onTabsTrayDismiss = ::onTabsTrayDismissed,
onTabAutoCloseBannerViewOptionsClick = {
navigationInteractor.onTabSettingsClicked()
requireContext().settings().shouldShowAutoCloseTabsBanner = false
},
onTabAutoCloseBannerDismiss = {
requireContext().settings().shouldShowAutoCloseTabsBanner = false
},
onTabAutoCloseBannerShown = {
requireContext().settings().lastCfrShownTimeInMillis = System.currentTimeMillis()
},
)
}
}

Loading…
Cancel
Save