mirror of
https://github.com/bitwarden/android.git
synced 2026-05-31 09:46:08 -05:00
BIT-2287: Display a dialog for unassigned items (#1358)
This commit is contained in:
committed by
Álison Fernandes
parent
8e1ecd1e6c
commit
04ac479e7d
@@ -73,6 +73,17 @@ interface SettingsDiskSource {
|
||||
*/
|
||||
fun clearData(userId: String)
|
||||
|
||||
/**
|
||||
* Retrieves the preference indicating whether we should check for unassigned organization
|
||||
* ciphers.
|
||||
*/
|
||||
fun getShouldCheckOrgUnassignedItems(userId: String): Boolean?
|
||||
|
||||
/**
|
||||
* Stores the given [shouldCheckOrgUnassignedItems] for the given [userId].
|
||||
*/
|
||||
fun storeShouldCheckOrgUnassignedItems(userId: String, shouldCheckOrgUnassignedItems: Boolean?)
|
||||
|
||||
/**
|
||||
* Retrieves the biometric integrity validity for the given [userId] and
|
||||
* [systemBioIntegrityState].
|
||||
|
||||
@@ -33,6 +33,7 @@ private const val CRASH_LOGGING_ENABLED_KEY = "crashLoggingEnabled"
|
||||
private const val CLEAR_CLIPBOARD_INTERVAL_KEY = "clearClipboard"
|
||||
private const val INITIAL_AUTOFILL_DIALOG_SHOWN = "addSitePromptShown"
|
||||
private const val HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY = "hasUserLoggedInOrCreatedAccount"
|
||||
private const val SHOULD_CHECK_ORG_UNASSIGNED_ITEMS = "shouldCheckOrganizationUnassignedItems"
|
||||
|
||||
/**
|
||||
* Primary implementation of [SettingsDiskSource].
|
||||
@@ -154,6 +155,7 @@ class SettingsDiskSourceImpl(
|
||||
storeBlockedAutofillUris(userId = userId, blockedAutofillUris = null)
|
||||
storeLastSyncTime(userId = userId, lastSyncTime = null)
|
||||
storeClearClipboardFrequencySeconds(userId = userId, frequency = null)
|
||||
storeShouldCheckOrgUnassignedItems(userId = userId, shouldCheckOrgUnassignedItems = null)
|
||||
removeWithPrefix(prefix = ACCOUNT_BIOMETRIC_INTEGRITY_VALID_KEY.appendIdentifier(userId))
|
||||
|
||||
// The following are intentionally not cleared so they can be
|
||||
@@ -184,6 +186,19 @@ class SettingsDiskSourceImpl(
|
||||
)
|
||||
}
|
||||
|
||||
override fun getShouldCheckOrgUnassignedItems(userId: String): Boolean? =
|
||||
getBoolean(key = SHOULD_CHECK_ORG_UNASSIGNED_ITEMS.appendIdentifier(userId))
|
||||
|
||||
override fun storeShouldCheckOrgUnassignedItems(
|
||||
userId: String,
|
||||
shouldCheckOrgUnassignedItems: Boolean?,
|
||||
) {
|
||||
putBoolean(
|
||||
key = SHOULD_CHECK_ORG_UNASSIGNED_ITEMS.appendIdentifier(userId),
|
||||
value = shouldCheckOrgUnassignedItems,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getAutoCopyTotpDisabled(userId: String): Boolean? =
|
||||
getBoolean(key = DISABLE_AUTO_TOTP_COPY_KEY.appendIdentifier(userId))
|
||||
|
||||
|
||||
@@ -327,7 +327,18 @@ interface VaultRepository : VaultLockManager {
|
||||
suspend fun updateFolder(folderId: String, folderView: FolderView): UpdateFolderResult
|
||||
|
||||
/**
|
||||
* Attempt to get the user's vault data for export.
|
||||
* Attempt to get the user's vault data for export.
|
||||
*/
|
||||
suspend fun exportVaultDataToString(format: ExportFormat): ExportVaultDataResult
|
||||
|
||||
/**
|
||||
* Checks if the user should see the unassigned items message.
|
||||
*/
|
||||
suspend fun shouldShowUnassignedItemsInfo(): Boolean
|
||||
|
||||
/**
|
||||
* Sets the value indicating that the user has or has not acknowledged that their organization
|
||||
* has unassigned items.
|
||||
*/
|
||||
fun acknowledgeUnassignedItemsInfo(hasAcknowledged: Boolean)
|
||||
}
|
||||
|
||||
@@ -1258,6 +1258,26 @@ class VaultRepositoryImpl(
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
override suspend fun shouldShowUnassignedItemsInfo(): Boolean {
|
||||
val userId = activeUserId ?: return false
|
||||
if (settingsDiskSource.getShouldCheckOrgUnassignedItems(userId = userId) == false) {
|
||||
return false
|
||||
}
|
||||
return ciphersService.hasUnassignedCiphers().fold(
|
||||
onFailure = { false },
|
||||
onSuccess = { it },
|
||||
)
|
||||
}
|
||||
|
||||
override fun acknowledgeUnassignedItemsInfo(hasAcknowledged: Boolean) {
|
||||
val userId = activeUserId ?: return
|
||||
settingsDiskSource.storeShouldCheckOrgUnassignedItems(
|
||||
userId = userId,
|
||||
shouldCheckOrgUnassignedItems = !hasAcknowledged,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [userId] has an associated encrypted PIN key but not a pin-protected user
|
||||
* key. This indicates a scenario in which a user has requested PIN unlocking but requires
|
||||
|
||||
@@ -355,6 +355,18 @@ private fun VaultDialogs(
|
||||
onDismissRequest = vaultHandlers.dialogDismiss,
|
||||
)
|
||||
|
||||
VaultState.DialogState.UnassignedItems -> BitwardenTwoButtonDialog(
|
||||
title = stringResource(id = R.string.notice),
|
||||
message = stringResource(
|
||||
id = R.string.organization_unassigned_items_message_useu_description_long,
|
||||
),
|
||||
confirmButtonText = stringResource(id = R.string.remind_me_later),
|
||||
dismissButtonText = stringResource(id = R.string.ok),
|
||||
onConfirmClick = vaultHandlers.dialogDismiss,
|
||||
onDismissClick = vaultHandlers.unassignedItemsAcknowledgeClick,
|
||||
onDismissRequest = vaultHandlers.dialogDismiss,
|
||||
)
|
||||
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,11 @@ class VaultViewModel @Inject constructor(
|
||||
sendAction(VaultAction.Internal.UserStateUpdateReceive(userState = it))
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepository.shouldShowUnassignedItemsInfo()
|
||||
sendAction(VaultAction.Internal.ReceiveUnassignedItemsResult(result))
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleAction(action: VaultAction) {
|
||||
@@ -154,6 +159,7 @@ class VaultViewModel @Inject constructor(
|
||||
handleMasterPasswordRepromptSubmit(action)
|
||||
}
|
||||
|
||||
VaultAction.UnassignedItemsAcknowledgeClick -> handleUnassignedItemsAcknowledgeClick()
|
||||
is VaultAction.Internal -> handleInternalAction(action)
|
||||
}
|
||||
}
|
||||
@@ -350,6 +356,11 @@ class VaultViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUnassignedItemsAcknowledgeClick() {
|
||||
vaultRepository.acknowledgeUnassignedItemsInfo(hasAcknowledged = true)
|
||||
mutableStateFlow.update { it.copy(dialog = null) }
|
||||
}
|
||||
|
||||
private fun handleCopyNoteClick(action: ListingItemOverflowAction.VaultAction.CopyNoteClick) {
|
||||
clipboardManager.setText(action.notes)
|
||||
}
|
||||
@@ -409,6 +420,10 @@ class VaultViewModel @Inject constructor(
|
||||
handlePullToRefreshEnableReceive(action)
|
||||
}
|
||||
|
||||
is VaultAction.Internal.ReceiveUnassignedItemsResult -> {
|
||||
handleReceiveUnassignedItemsResult(action)
|
||||
}
|
||||
|
||||
is VaultAction.Internal.UserStateUpdateReceive -> handleUserStateUpdateReceive(action)
|
||||
is VaultAction.Internal.VaultDataReceive -> handleVaultDataReceive(action)
|
||||
is VaultAction.Internal.IconLoadingSettingReceive -> handleIconLoadingSettingReceive(
|
||||
@@ -440,6 +455,14 @@ class VaultViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReceiveUnassignedItemsResult(
|
||||
action: VaultAction.Internal.ReceiveUnassignedItemsResult,
|
||||
) {
|
||||
if (action.result) {
|
||||
mutableStateFlow.update { it.copy(dialog = VaultState.DialogState.UnassignedItems) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUserStateUpdateReceive(action: VaultAction.Internal.UserStateUpdateReceive) {
|
||||
// Leave the current data alone if there is no UserState; we are in the process of logging
|
||||
// out.
|
||||
@@ -518,7 +541,13 @@ class VaultViewModel @Inject constructor(
|
||||
hasMasterPassword = state.hasMasterPassword,
|
||||
vaultFilterType = vaultFilterTypeOrDefault,
|
||||
),
|
||||
dialog = null,
|
||||
dialog = when (it.dialog) {
|
||||
VaultState.DialogState.UnassignedItems -> VaultState.DialogState.UnassignedItems
|
||||
is VaultState.DialogState.Error,
|
||||
VaultState.DialogState.Syncing,
|
||||
null,
|
||||
-> null
|
||||
},
|
||||
)
|
||||
}
|
||||
sendEvent(VaultEvent.DismissPullToRefresh)
|
||||
@@ -900,6 +929,12 @@ data class VaultState(
|
||||
val title: Text,
|
||||
val message: Text,
|
||||
) : DialogState()
|
||||
|
||||
/**
|
||||
* Represents a dialog indicating that the user has unassigned items.
|
||||
*/
|
||||
@Parcelize
|
||||
data object UnassignedItems : DialogState()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,6 +1149,11 @@ sealed class VaultAction {
|
||||
val password: String,
|
||||
) : VaultAction()
|
||||
|
||||
/**
|
||||
* The user has acknowledged the unassigned items and we do not want to show the message again.
|
||||
*/
|
||||
data object UnassignedItemsAcknowledgeClick : VaultAction()
|
||||
|
||||
/**
|
||||
* Models actions that the [VaultViewModel] itself might send.
|
||||
*/
|
||||
@@ -1138,6 +1178,14 @@ sealed class VaultAction {
|
||||
*/
|
||||
data class PullToRefreshEnableReceive(val isPullToRefreshEnabled: Boolean) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates that we have received the data concerning whether we should display the
|
||||
* unassigned items dialog.
|
||||
*/
|
||||
data class ReceiveUnassignedItemsResult(
|
||||
val result: Boolean,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a change in user state has been received.
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ data class VaultHandlers(
|
||||
val dialogDismiss: () -> Unit,
|
||||
val overflowOptionClick: (ListingItemOverflowAction.VaultAction) -> Unit,
|
||||
val masterPasswordRepromptSubmit: (ListingItemOverflowAction.VaultAction, String) -> Unit,
|
||||
val unassignedItemsAcknowledgeClick: () -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -88,6 +89,9 @@ data class VaultHandlers(
|
||||
),
|
||||
)
|
||||
},
|
||||
unassignedItemsAcknowledgeClick = {
|
||||
viewModel.trySendAction(VaultAction.UnassignedItemsAcknowledgeClick)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user