Skip to content

Commit 000a7d1

Browse files
PM-17660: Add additional context for the sync feature (#5243)
1 parent c9d4d35 commit 000a7d1

File tree

11 files changed

+360
-76
lines changed

11 files changed

+360
-76
lines changed

authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt

Lines changed: 138 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@ import android.widget.Toast
88
import androidx.compose.foundation.Image
99
import androidx.compose.foundation.layout.Arrangement
1010
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.Row
1112
import androidx.compose.foundation.layout.Spacer
1213
import androidx.compose.foundation.layout.fillMaxSize
1314
import androidx.compose.foundation.layout.fillMaxWidth
1415
import androidx.compose.foundation.layout.height
1516
import androidx.compose.foundation.layout.padding
1617
import androidx.compose.foundation.layout.size
18+
import androidx.compose.foundation.layout.width
1719
import androidx.compose.foundation.lazy.LazyColumn
1820
import androidx.compose.foundation.lazy.items
1921
import androidx.compose.foundation.rememberScrollState
22+
import androidx.compose.foundation.shape.RoundedCornerShape
2023
import androidx.compose.foundation.verticalScroll
24+
import androidx.compose.material3.Card
25+
import androidx.compose.material3.CardDefaults
2126
import androidx.compose.material3.ExperimentalMaterial3Api
2227
import androidx.compose.material3.FabPosition
2328
import androidx.compose.material3.HorizontalDivider
@@ -56,7 +61,9 @@ import com.bitwarden.authenticator.ui.authenticator.feature.itemlisting.model.Ve
5661
import com.bitwarden.authenticator.ui.platform.components.appbar.BitwardenMediumTopAppBar
5762
import com.bitwarden.authenticator.ui.platform.components.appbar.BitwardenTopAppBar
5863
import com.bitwarden.authenticator.ui.platform.components.appbar.action.BitwardenSearchActionItem
64+
import com.bitwarden.authenticator.ui.platform.components.button.BitwardenFilledButton
5965
import com.bitwarden.authenticator.ui.platform.components.button.BitwardenFilledTonalButton
66+
import com.bitwarden.authenticator.ui.platform.components.button.BitwardenTextButton
6067
import com.bitwarden.authenticator.ui.platform.components.card.BitwardenActionCard
6168
import com.bitwarden.authenticator.ui.platform.components.dialog.BasicDialogState
6269
import com.bitwarden.authenticator.ui.platform.components.dialog.BitwardenBasicDialog
@@ -139,6 +146,10 @@ fun ItemListingScreen(
139146
)
140147
}
141148

149+
ItemListingEvent.NavigateToSyncInformation -> {
150+
intentManager.launchUri("https://bitwarden.com/help/totp-sync".toUri())
151+
}
152+
142153
ItemListingEvent.NavigateToBitwardenSettings -> {
143154
intentManager.startMainBitwardenAppAccountSettings()
144155
}
@@ -230,6 +241,9 @@ fun ItemListingScreen(
230241
viewModel.trySendAction(ItemListingAction.SyncWithBitwardenDismiss)
231242
}
232243
},
244+
onSyncLearnMoreClick = remember(viewModel) {
245+
{ viewModel.trySendAction(ItemListingAction.SyncLearnMoreClick) }
246+
},
233247
)
234248
}
235249

@@ -270,6 +284,9 @@ fun ItemListingScreen(
270284
viewModel.trySendAction(ItemListingAction.SyncWithBitwardenClick)
271285
}
272286
},
287+
onSyncLearnMoreClick = remember(viewModel) {
288+
{ viewModel.trySendAction(ItemListingAction.SyncLearnMoreClick) }
289+
},
273290
onDismissSyncWithBitwardenClick = remember(viewModel) {
274291
{
275292
viewModel.trySendAction(ItemListingAction.SyncWithBitwardenDismiss)
@@ -339,6 +356,7 @@ private fun ItemListingContent(
339356
onDismissDownloadBitwardenClick: () -> Unit,
340357
onSyncWithBitwardenClick: () -> Unit,
341358
onDismissSyncWithBitwardenClick: () -> Unit,
359+
onSyncLearnMoreClick: () -> Unit,
342360
) {
343361
BitwardenScaffold(
344362
modifier = Modifier
@@ -402,23 +420,15 @@ private fun ItemListingContent(
402420
) {
403421
LazyColumn {
404422
item {
405-
when (state.actionCard) {
406-
ItemListingState.ActionCardState.DownloadBitwardenApp ->
407-
DownloadBitwardenActionCard(
408-
modifier = Modifier.padding(horizontal = 16.dp),
409-
onDownloadBitwardenClick = onDownloadBitwardenClick,
410-
onDismissClick = onDismissDownloadBitwardenClick,
411-
)
412-
413-
ItemListingState.ActionCardState.SyncWithBitwarden ->
414-
SyncWithBitwardenActionCard(
415-
modifier = Modifier.padding(16.dp),
416-
onSyncWithBitwardenClick = onSyncWithBitwardenClick,
417-
onDismissClick = onDismissSyncWithBitwardenClick,
418-
)
419-
420-
ItemListingState.ActionCardState.None -> Unit
421-
}
423+
ActionCard(
424+
actionCardState = state.actionCard,
425+
onDownloadBitwardenClick = onDownloadBitwardenClick,
426+
onDownloadBitwardenDismissClick = onDismissDownloadBitwardenClick,
427+
onSyncWithBitwardenClick = onSyncWithBitwardenClick,
428+
onSyncWithBitwardenDismissClick = onDismissSyncWithBitwardenClick,
429+
onSyncLearnMoreClick = onSyncLearnMoreClick,
430+
modifier = Modifier.padding(all = 16.dp),
431+
)
422432
}
423433
if (state.favoriteItems.isNotEmpty()) {
424434
item {
@@ -572,6 +582,7 @@ fun EmptyItemListingContent(
572582
onDownloadBitwardenClick: () -> Unit,
573583
onDismissDownloadBitwardenClick: () -> Unit,
574584
onSyncWithBitwardenClick: () -> Unit,
585+
onSyncLearnMoreClick: () -> Unit,
575586
onDismissSyncWithBitwardenClick: () -> Unit,
576587
) {
577588
BitwardenScaffold(
@@ -635,23 +646,14 @@ fun EmptyItemListingContent(
635646
ItemListingState.ActionCardState.SyncWithBitwarden -> Arrangement.Top
636647
},
637648
) {
638-
when (actionCardState) {
639-
ItemListingState.ActionCardState.DownloadBitwardenApp ->
640-
DownloadBitwardenActionCard(
641-
modifier = Modifier.padding(16.dp),
642-
onDismissClick = onDismissDownloadBitwardenClick,
643-
onDownloadBitwardenClick = onDownloadBitwardenClick,
644-
)
645-
646-
ItemListingState.ActionCardState.SyncWithBitwarden ->
647-
SyncWithBitwardenActionCard(
648-
modifier = Modifier.padding(16.dp),
649-
onDismissClick = onDismissSyncWithBitwardenClick,
650-
onSyncWithBitwardenClick = onSyncWithBitwardenClick,
651-
)
652-
653-
ItemListingState.ActionCardState.None -> Unit
654-
}
649+
ActionCard(
650+
actionCardState = actionCardState,
651+
onDownloadBitwardenClick = onDownloadBitwardenClick,
652+
onDownloadBitwardenDismissClick = onDismissDownloadBitwardenClick,
653+
onSyncWithBitwardenClick = onSyncWithBitwardenClick,
654+
onSyncWithBitwardenDismissClick = onDismissSyncWithBitwardenClick,
655+
onSyncLearnMoreClick = onSyncLearnMoreClick,
656+
)
655657

656658
// Add a spacer if an action card is showing:
657659
when (actionCardState) {
@@ -735,32 +737,114 @@ private fun DownloadBitwardenActionCard(
735737
},
736738
)
737739

740+
@Suppress("LongMethod")
738741
@Composable
739742
private fun SyncWithBitwardenActionCard(
740743
modifier: Modifier = Modifier,
741744
onDismissClick: () -> Unit,
745+
onAppSettingsClick: () -> Unit,
746+
onLearnMoreClick: () -> Unit,
747+
) {
748+
Card(
749+
modifier = modifier,
750+
shape = RoundedCornerShape(size = 16.dp),
751+
colors = CardDefaults.cardColors(
752+
containerColor = MaterialTheme.colorScheme.surfaceContainer,
753+
disabledContainerColor = MaterialTheme.colorScheme.surfaceContainer,
754+
),
755+
elevation = CardDefaults.elevatedCardElevation(),
756+
) {
757+
Spacer(Modifier.height(height = 4.dp))
758+
Row(modifier = Modifier.fillMaxWidth()) {
759+
Spacer(Modifier.width(width = 16.dp))
760+
Row(
761+
modifier = Modifier.padding(top = 12.dp),
762+
verticalAlignment = Alignment.CenterVertically,
763+
) {
764+
Icon(
765+
painter = rememberVectorPainter(id = R.drawable.ic_bitwarden),
766+
contentDescription = null,
767+
tint = MaterialTheme.colorScheme.primary,
768+
modifier = Modifier.size(size = 20.dp),
769+
)
770+
Spacer(Modifier.width(width = 16.dp))
771+
Text(
772+
text = stringResource(id = R.string.sync_with_the_bitwarden_app),
773+
style = MaterialTheme.typography.bodyLarge,
774+
color = MaterialTheme.colorScheme.onSurface,
775+
)
776+
}
777+
Spacer(Modifier.weight(weight = 1f))
778+
Spacer(Modifier.width(width = 16.dp))
779+
IconButton(onClick = onDismissClick) {
780+
Icon(
781+
painter = painterResource(id = R.drawable.ic_close),
782+
contentDescription = stringResource(id = R.string.close),
783+
tint = MaterialTheme.colorScheme.onSurfaceVariant,
784+
modifier = Modifier.size(size = 24.dp),
785+
)
786+
}
787+
Spacer(Modifier.width(width = 4.dp))
788+
}
789+
Text(
790+
text = stringResource(id = R.string.sync_with_bitwarden_action_card_message),
791+
style = MaterialTheme.typography.bodyMedium,
792+
color = MaterialTheme.colorScheme.onSurfaceVariant,
793+
modifier = Modifier
794+
.padding(horizontal = 16.dp)
795+
.padding(start = 36.dp, end = 48.dp)
796+
.fillMaxWidth(),
797+
)
798+
Spacer(Modifier.height(height = 16.dp))
799+
BitwardenFilledButton(
800+
label = stringResource(id = R.string.take_me_to_app_settings),
801+
onClick = onAppSettingsClick,
802+
modifier = Modifier
803+
.padding(horizontal = 16.dp)
804+
.fillMaxWidth(),
805+
)
806+
BitwardenTextButton(
807+
label = stringResource(id = R.string.learn_more),
808+
onClick = onLearnMoreClick,
809+
modifier = Modifier
810+
.padding(horizontal = 16.dp)
811+
.fillMaxWidth(),
812+
)
813+
Spacer(Modifier.height(height = 4.dp))
814+
}
815+
}
816+
817+
@Composable
818+
private fun ActionCard(
819+
actionCardState: ItemListingState.ActionCardState,
820+
onDownloadBitwardenClick: () -> Unit,
821+
onDownloadBitwardenDismissClick: () -> Unit,
742822
onSyncWithBitwardenClick: () -> Unit,
743-
) = BitwardenActionCard(
744-
modifier = modifier,
745-
actionIcon = rememberVectorPainter(R.drawable.ic_refresh),
746-
actionText = stringResource(R.string.sync_with_bitwarden_action_card_message),
747-
callToActionText = stringResource(R.string.go_to_settings),
748-
titleText = stringResource(R.string.sync_with_the_bitwarden_app),
749-
onCardClicked = onSyncWithBitwardenClick,
750-
trailingContent = {
751-
IconButton(
752-
onClick = onDismissClick,
753-
) {
754-
Icon(
755-
painter = painterResource(id = R.drawable.ic_close),
756-
contentDescription = stringResource(id = R.string.close),
757-
tint = MaterialTheme.colorScheme.onSurfaceVariant,
758-
modifier = Modifier
759-
.size(24.dp),
823+
onSyncWithBitwardenDismissClick: () -> Unit,
824+
onSyncLearnMoreClick: () -> Unit,
825+
modifier: Modifier = Modifier,
826+
) {
827+
when (actionCardState) {
828+
ItemListingState.ActionCardState.DownloadBitwardenApp -> {
829+
DownloadBitwardenActionCard(
830+
modifier = modifier,
831+
onDownloadBitwardenClick = onDownloadBitwardenClick,
832+
onDismissClick = onDownloadBitwardenDismissClick,
760833
)
761834
}
762-
},
763-
)
835+
836+
ItemListingState.ActionCardState.SyncWithBitwarden -> {
837+
SyncWithBitwardenActionCard(
838+
modifier = modifier,
839+
onAppSettingsClick = onSyncWithBitwardenClick,
840+
onDismissClick = onSyncWithBitwardenDismissClick,
841+
onLearnMoreClick = onSyncLearnMoreClick,
842+
)
843+
}
844+
845+
ItemListingState.ActionCardState.None -> Unit
846+
}
847+
}
764848

765849
@OptIn(ExperimentalMaterial3Api::class)
766850
@Composable
@@ -776,6 +860,7 @@ private fun EmptyListingContentPreview() {
776860
onDownloadBitwardenClick = { },
777861
onDismissDownloadBitwardenClick = { },
778862
onSyncWithBitwardenClick = { },
863+
onSyncLearnMoreClick = { },
779864
onDismissSyncWithBitwardenClick = { },
780865
)
781866
}

authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingViewModel.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ class ItemListingViewModel @Inject constructor(
153153
ItemListingAction.SyncWithBitwardenDismiss -> {
154154
handleSyncWithBitwardenDismiss()
155155
}
156+
157+
ItemListingAction.SyncLearnMoreClick -> {
158+
handleSyncLearnMoreClick()
159+
}
156160
}
157161
}
158162

@@ -564,6 +568,10 @@ class ItemListingViewModel @Inject constructor(
564568
}
565569
}
566570

571+
private fun handleSyncLearnMoreClick() {
572+
sendEvent(ItemListingEvent.NavigateToSyncInformation)
573+
}
574+
567575
/**
568576
* Converts a [SharedVerificationCodesState] into an action card for display.
569577
*/
@@ -794,6 +802,11 @@ sealed class ItemListingEvent {
794802
*/
795803
data object NavigateToAppSettings : ItemListingEvent()
796804

805+
/**
806+
* Navigate to the sync information web page.
807+
*/
808+
data object NavigateToSyncInformation : ItemListingEvent()
809+
797810
/**
798811
* Navigate to Bitwarden play store listing.
799812
*/
@@ -872,6 +885,11 @@ sealed class ItemListingAction {
872885
*/
873886
data object SyncWithBitwardenClick : ItemListingAction()
874887

888+
/**
889+
* The user tapped the learn more button on the sync action card.
890+
*/
891+
data object SyncLearnMoreClick : ItemListingAction()
892+
875893
/**
876894
* The user dismissed sync Bitwarden action card.
877895
*/
@@ -886,7 +904,7 @@ sealed class ItemListingAction {
886904
* Represents an action triggered when the user clicks an item in the dropdown menu.
887905
*
888906
* @param menuAction The action selected from the dropdown menu.
889-
* @param id The identifier of the item on which the action is being performed.
907+
* @param item The item on which the action is being performed.
890908
*/
891909
data class DropdownMenuClick(
892910
val menuAction: VaultDropdownMenuAction,

authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/components/card/BitwardenActionCard.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ fun BitwardenActionCard(
4444
onClick = onCardClicked,
4545
shape = RoundedCornerShape(size = 16.dp),
4646
colors = CardDefaults.cardColors(
47-
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
48-
disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
47+
containerColor = MaterialTheme.colorScheme.surfaceContainer,
48+
disabledContainerColor = MaterialTheme.colorScheme.surfaceContainer,
4949
),
5050
modifier = modifier,
5151
elevation = CardDefaults.elevatedCardElevation(),

authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/platform/components/row/BitwardenTextRow.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.compose.runtime.remember
1818
import androidx.compose.ui.Alignment
1919
import androidx.compose.ui.Modifier
2020
import androidx.compose.ui.semantics.semantics
21+
import androidx.compose.ui.text.AnnotatedString
2122
import androidx.compose.ui.unit.dp
2223

2324
/**
@@ -37,7 +38,7 @@ fun BitwardenTextRow(
3738
text: String,
3839
onClick: () -> Unit,
3940
modifier: Modifier = Modifier,
40-
description: String? = null,
41+
description: AnnotatedString? = null,
4142
withDivider: Boolean = false,
4243
content: (@Composable () -> Unit)? = null,
4344
) {

0 commit comments

Comments
 (0)