Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
fun AttachmentItemContent(
attachmentItem: VaultItemState.ViewState.Content.Common.AttachmentItem,
onAttachmentDownloadClick: (VaultItemState.ViewState.Content.Common.AttachmentItem) -> Unit,
onAttachmentPreviewClick: (VaultItemState.ViewState.Content.Common.AttachmentItem) -> Unit,
cardStyle: CardStyle,
modifier: Modifier = Modifier,
) {
Expand All @@ -42,7 +43,7 @@ fun AttachmentItemContent(
Row(
modifier = modifier
.defaultMinSize(minHeight = 60.dp)
.cardStyle(cardStyle = cardStyle, paddingStart = 16.dp)
.cardStyle(cardStyle = cardStyle, paddingStart = 16.dp, paddingEnd = 8.dp)
.testTag("CipherAttachment"),
verticalAlignment = Alignment.CenterVertically,
) {
Expand All @@ -69,6 +70,22 @@ fun AttachmentItemContent(

Spacer(modifier = Modifier.width(8.dp))

if (attachmentItem.isPreviewable) {
BitwardenStandardIconButton(
vectorIconRes = BitwardenDrawable.ic_preview,
contentDescription = stringResource(id = BitwardenString.preview),
onClick = {
if (!attachmentItem.isDownloadAllowed) {
shouldShowPremiumWarningDialog = true
return@BitwardenStandardIconButton
}
onAttachmentPreviewClick(attachmentItem)
},
modifier = Modifier
.testTag("AttachmentPreviewButton"),
)
}

BitwardenStandardIconButton(
vectorIconRes = BitwardenDrawable.ic_download,
contentDescription = stringResource(id = BitwardenString.download),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ fun VaultItemCardContent(
attachmentItem = attachmentItem,
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
.onAttachmentDownloadClick,
onAttachmentPreviewClick = vaultCommonItemTypeHandlers
.onAttachmentPreviewClick,
cardStyle = attachments.toListItemCardStyle(index = index),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ fun VaultItemIdentityContent(
attachmentItem = attachmentItem,
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
.onAttachmentDownloadClick,
onAttachmentPreviewClick = vaultCommonItemTypeHandlers
.onAttachmentPreviewClick,
cardStyle = attachments.toListItemCardStyle(index = index),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ fun VaultItemLoginContent(
cardStyle = attachments.toListItemCardStyle(index = index),
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
.onAttachmentDownloadClick,
)
onAttachmentPreviewClick = vaultCommonItemTypeHandlers
.onAttachmentPreviewClick,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditArgs
import com.x8bit.bitwarden.ui.vault.feature.item.component.AttachmentPreviewDialog
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCardItemTypeHandlers
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers
Expand Down Expand Up @@ -121,6 +122,9 @@ fun VaultItemScreen(
onDismissRequest = remember(viewModel) {
{ viewModel.trySendAction(VaultItemAction.Common.DismissDialogClick) }
},
onPreviewLoaded = remember(viewModel) {
{ viewModel.trySendAction(VaultItemAction.Internal.AttachmentPreviewLoaded) }
},
onConfirmDeleteClick = remember(viewModel) {
{ viewModel.trySendAction(VaultItemAction.Common.ConfirmDeleteClick) }
},
Expand Down Expand Up @@ -274,6 +278,7 @@ fun VaultItemScreen(
private fun VaultItemDialogs(
dialog: VaultItemState.DialogState?,
onDismissRequest: () -> Unit,
onPreviewLoaded: () -> Unit,
onConfirmDeleteClick: () -> Unit,
onConfirmCloneWithoutFido2Credential: () -> Unit,
onConfirmRestoreAction: () -> Unit,
Expand Down Expand Up @@ -324,6 +329,14 @@ private fun VaultItemDialogs(
onDismissRequest = onDismissRequest,
)

is VaultItemState.DialogState.AttachmentPreview -> {
AttachmentPreviewDialog(
attachmentFile = dialog.file,
onDismissRequest = onDismissRequest,
onLoaded = onPreviewLoaded
)
}

null -> Unit
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ fun VaultItemSecureNoteContent(
attachmentItem = attachmentItem,
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
.onAttachmentDownloadClick,
onAttachmentPreviewClick = vaultCommonItemTypeHandlers
.onAttachmentPreviewClick,
cardStyle = attachments.toListItemCardStyle(index = index),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ fun VaultItemSshKeyContent(
attachmentItem = attachmentItem,
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
.onAttachmentDownloadClick,
onAttachmentPreviewClick = vaultCommonItemTypeHandlers
.onAttachmentPreviewClick,
cardStyle = attachments.toListItemCardStyle(index = index),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ class VaultItemViewModel @Inject constructor(
handleAttachmentDownloadClick(action)
}

is VaultItemAction.Common.AttachmentPreviewClick -> {
handleAttachmentPreviewClick(action)
}

is VaultItemAction.Common.AttachmentFileLocationReceive -> {
handleAttachmentFileLocationReceive(action)
}
Expand Down Expand Up @@ -378,6 +382,33 @@ class VaultItemViewModel @Inject constructor(
VaultItemAction.Internal.AttachmentDecryptReceive(
result = result,
fileName = action.attachment.title,
isPreview = false
),
)
}
}
}

private fun handleAttachmentPreviewClick(
action: VaultItemAction.Common.AttachmentPreviewClick,
) {
onContent { content ->
updateDialogState(
VaultItemState.DialogState.Loading(BitwardenString.loading.asText()),
)

viewModelScope.launch {
val result = vaultRepository
.downloadAttachment(
cipherView = requireNotNull(content.common.currentCipher),
attachmentId = action.attachment.id,
)

trySendAction(
VaultItemAction.Internal.AttachmentDecryptReceive(
result = result,
fileName = action.attachment.title,
isPreview = true
),
)
}
Expand Down Expand Up @@ -952,6 +983,10 @@ class VaultItemViewModel @Inject constructor(
handleAttachmentFinishedSavingToDisk(action)
}

is VaultItemAction.Internal.AttachmentPreviewLoaded -> {
handleAttachmentPreviewLoaded()
}

is VaultItemAction.Internal.IsIconLoadingDisabledUpdateReceive -> {
handleIsIconLoadingDisabledUpdateReceive(action)
}
Expand Down Expand Up @@ -1142,11 +1177,15 @@ class VaultItemViewModel @Inject constructor(

is DownloadAttachmentResult.Success -> {
temporaryAttachmentData = result.file
sendEvent(
VaultItemEvent.NavigateToSelectAttachmentSaveLocation(
fileName = action.fileName,
),
)
if (action.isPreview) {
updateDialogState(VaultItemState.DialogState.AttachmentPreview(result.file))
} else {
sendEvent(
VaultItemEvent.NavigateToSelectAttachmentSaveLocation(
fileName = action.fileName,
),
)
}
}
}
}
Expand All @@ -1169,6 +1208,12 @@ class VaultItemViewModel @Inject constructor(
}
}

private fun handleAttachmentPreviewLoaded() {
viewModelScope.launch {
temporaryAttachmentData?.let { fileManager.delete(it) }
}
}

private fun handleRestoreItemClicked() {
updateDialogState(VaultItemState.DialogState.RestoreItemDialog)
}
Expand Down Expand Up @@ -1425,6 +1470,7 @@ data class VaultItemState(
val url: String,
val isLargeFile: Boolean,
val isDownloadAllowed: Boolean,
val isPreviewable: Boolean,
) : Parcelable

/**
Expand Down Expand Up @@ -1727,6 +1773,12 @@ data class VaultItemState(
*/
@Parcelize
data object RestoreItemDialog : DialogState()

/**
* Displays a preview of an image attachment.
*/
@Parcelize
data class AttachmentPreview(val file: File) : DialogState()
}
}

Expand Down Expand Up @@ -1913,6 +1965,13 @@ sealed class VaultItemAction {
val attachment: VaultItemState.ViewState.Content.Common.AttachmentItem,
) : Common()

/**
* The user has clicked the preview button.
*/
data class AttachmentPreviewClick(
val attachment: VaultItemState.ViewState.Content.Common.AttachmentItem,
) : Common()

/**
* The user has selected a location to save the file.
*/
Expand Down Expand Up @@ -2153,6 +2212,7 @@ sealed class VaultItemAction {
data class AttachmentDecryptReceive(
val result: DownloadAttachmentResult,
val fileName: String,
val isPreview: Boolean,
) : Internal()

/**
Expand All @@ -2164,6 +2224,12 @@ sealed class VaultItemAction {
val file: File,
) : Internal()

/**
* Indicates the attachment file has been loaded into memory and the
* temporary file on disk can be deleted.
*/
data object AttachmentPreviewLoaded : Internal()

/**
* Indicates the `isIconLoadingDisabled` setting has changed.
*/
Expand Down
Loading
Loading