mirror of
https://github.com/bitwarden/android.git
synced 2025-12-11 04:39:19 -06:00
PM-24035: Add tooltip for website icons (#5554)
This commit is contained in:
parent
3342ebf139
commit
9ed59e61a3
@ -21,12 +21,14 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.components.model.TooltipData
|
||||
import com.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
|
||||
@ -35,7 +37,9 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.util.displayLabel
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@ -48,11 +52,15 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
fun AppearanceScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: AppearanceViewModel = hiltViewModel(),
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
AppearanceEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
AppearanceEvent.NavigateToWebsiteIconsHelp -> {
|
||||
intentManager.launchUri("https://bitwarden.com/help/website-icons/".toUri())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,6 +142,12 @@ fun AppearanceScreen(
|
||||
onCheckedChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AppearanceAction.ShowWebsiteIconsToggle(it)) }
|
||||
},
|
||||
tooltip = TooltipData(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AppearanceAction.ShowWebsiteIconsTooltipClick) }
|
||||
},
|
||||
contentDescription = stringResource(id = R.string.show_website_icons_help),
|
||||
),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ShowWebsiteIconsSwitch")
|
||||
|
||||
@ -22,6 +22,7 @@ private const val KEY_STATE = "state"
|
||||
/**
|
||||
* View model for the appearance screen.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
@HiltViewModel
|
||||
class AppearanceViewModel @Inject constructor(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
@ -58,6 +59,7 @@ class AppearanceViewModel @Inject constructor(
|
||||
AppearanceAction.BackClick -> handleBackClicked()
|
||||
is AppearanceAction.LanguageChange -> handleLanguageChanged(action)
|
||||
is AppearanceAction.ShowWebsiteIconsToggle -> handleShowWebsiteIconsToggled(action)
|
||||
AppearanceAction.ShowWebsiteIconsTooltipClick -> handleShowWebsiteIconsTooltipClick()
|
||||
is AppearanceAction.ThemeChange -> handleThemeChanged(action)
|
||||
is AppearanceAction.DynamicColorsToggle -> handleDynamicColorsToggled(action)
|
||||
AppearanceAction.DismissDialog -> handleDismissDialog()
|
||||
@ -68,6 +70,7 @@ class AppearanceViewModel @Inject constructor(
|
||||
is AppearanceAction.Internal.AppLanguageStateUpdateReceive -> {
|
||||
handleLanguageStateChange(action)
|
||||
}
|
||||
|
||||
is AppearanceAction.Internal.DynamicColorsStateUpdateReceive -> {
|
||||
handleDynamicColorsStateChange(action)
|
||||
}
|
||||
@ -106,6 +109,10 @@ class AppearanceViewModel @Inject constructor(
|
||||
settingsRepository.isIconLoadingDisabled = !action.showWebsiteIcons
|
||||
}
|
||||
|
||||
private fun handleShowWebsiteIconsTooltipClick() {
|
||||
sendEvent(AppearanceEvent.NavigateToWebsiteIconsHelp)
|
||||
}
|
||||
|
||||
private fun handleThemeChanged(action: AppearanceAction.ThemeChange) {
|
||||
mutableStateFlow.update { it.copy(theme = action.theme) }
|
||||
settingsRepository.appTheme = action.theme
|
||||
@ -170,6 +177,11 @@ sealed class AppearanceEvent {
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : AppearanceEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the website icons help URL.
|
||||
*/
|
||||
data object NavigateToWebsiteIconsHelp : AppearanceEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,6 +207,11 @@ sealed class AppearanceAction {
|
||||
val showWebsiteIcons: Boolean,
|
||||
) : AppearanceAction()
|
||||
|
||||
/**
|
||||
* User clicked the website icons tooltip.
|
||||
*/
|
||||
data object ShowWebsiteIconsTooltipClick : AppearanceAction()
|
||||
|
||||
/**
|
||||
* Indicates that the user selected a new theme.
|
||||
*/
|
||||
|
||||
@ -265,6 +265,7 @@ Scanning will happen automatically.</string>
|
||||
<string name="address">Address</string>
|
||||
<string name="expiration">Expiration</string>
|
||||
<string name="show_website_icons">Show website icons</string>
|
||||
<string name="show_website_icons_help">Show website icons help</string>
|
||||
<string name="show_website_icons_description">Show a recognizable image next to each login</string>
|
||||
<string name="icons_url">Icons server URL</string>
|
||||
<string name="vault_is_locked">Vault is locked</string>
|
||||
|
||||
@ -10,15 +10,19 @@ import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.core.net.toUri
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.core.util.isBuildVersionAtLeast
|
||||
import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
|
||||
import com.bitwarden.ui.util.assertNoDialogExists
|
||||
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.runs
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -37,12 +41,17 @@ class AppearanceScreenTest : BitwardenComposeTest() {
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
}
|
||||
private val intentManager: IntentManager = mockk {
|
||||
every { launchUri(uri = any()) } just runs
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic(::isBuildVersionAtLeast)
|
||||
every { isBuildVersionAtLeast(any()) } returns true
|
||||
setContent {
|
||||
setContent(
|
||||
intentManager = intentManager,
|
||||
) {
|
||||
AppearanceScreen(
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
viewModel = viewModel,
|
||||
@ -172,12 +181,31 @@ class AppearanceScreenTest : BitwardenComposeTest() {
|
||||
verify { viewModel.trySendAction(AppearanceAction.ShowWebsiteIconsToggle(true)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on show website icons tooltip click should send ShowWebsiteIconsToggled`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Show website icons help")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(AppearanceAction.ShowWebsiteIconsTooltipClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateBack should call onNavigateBack`() {
|
||||
mutableEventFlow.tryEmit(AppearanceEvent.NavigateBack)
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToWebsiteIconsHelp should call launchUri`() {
|
||||
mutableEventFlow.tryEmit(AppearanceEvent.NavigateToWebsiteIconsHelp)
|
||||
verify {
|
||||
intentManager.launchUri("https://bitwarden.com/help/website-icons/".toUri())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dynamic colors should be displayed based on state`() {
|
||||
composeTestRule.onNodeWithText("Dynamic colors")
|
||||
|
||||
@ -136,6 +136,15 @@ class AppearanceViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on ShowWebsiteIconsTooltipClick should emit NavigateToWebsiteIconsHelp`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AppearanceAction.ShowWebsiteIconsTooltipClick)
|
||||
assertEquals(AppearanceEvent.NavigateToWebsiteIconsHelp, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on ThemeChange should update state and set theme in SettingsRepository`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user