mirror of
https://github.com/bitwarden/android.git
synced 2026-06-08 08:06:32 -05:00
Modify Add Sends UI to allow for editing existing sends (#575)
This commit is contained in:
committed by
Álison Fernandes
parent
7e0a14d3a0
commit
9e6c49fb7c
@@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -36,9 +38,11 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenSegmentedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenStepper
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenWideSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.SegmentedButtonState
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialTypography
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.handlers.AddSendHandlers
|
||||
|
||||
/**
|
||||
@@ -48,6 +52,7 @@ import com.x8bit.bitwarden.ui.tools.feature.send.addsend.handlers.AddSendHandler
|
||||
@Composable
|
||||
fun AddSendContent(
|
||||
state: AddSendState.ViewState.Content,
|
||||
isAddMode: Boolean,
|
||||
addSendHandlers: AddSendHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -65,34 +70,36 @@ fun AddSendContent(
|
||||
onValueChange = addSendHandlers.onNamChange,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.type),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
if (isAddMode) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.type),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSegmentedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
options = listOf(
|
||||
SegmentedButtonState(
|
||||
text = stringResource(id = R.string.file),
|
||||
onClick = addSendHandlers.onFileTypeSelect,
|
||||
isChecked = state.selectedType is AddSendState.ViewState.Content.SendType.File,
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSegmentedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
options = listOf(
|
||||
SegmentedButtonState(
|
||||
text = stringResource(id = R.string.file),
|
||||
onClick = addSendHandlers.onFileTypeSelect,
|
||||
isChecked = state.isFileType,
|
||||
),
|
||||
SegmentedButtonState(
|
||||
text = stringResource(id = R.string.text),
|
||||
onClick = addSendHandlers.onTextTypeSelect,
|
||||
isChecked = state.isTextType,
|
||||
),
|
||||
),
|
||||
SegmentedButtonState(
|
||||
text = stringResource(id = R.string.text),
|
||||
onClick = addSendHandlers.onTextTypeSelect,
|
||||
isChecked = state.selectedType is AddSendState.ViewState.Content.SendType.Text,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
when (val type = state.selectedType) {
|
||||
is AddSendState.ViewState.Content.SendType.File -> {
|
||||
BitwardenListHeaderText(
|
||||
@@ -161,6 +168,7 @@ fun AddSendContent(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
AddSendOptions(
|
||||
state = state,
|
||||
isAddMode = isAddMode,
|
||||
addSendHandlers = addSendHandlers,
|
||||
)
|
||||
|
||||
@@ -173,12 +181,15 @@ fun AddSendContent(
|
||||
* Displays a collapsable set of new send options.
|
||||
*
|
||||
* @param state The content state.
|
||||
* @param isAddMode When `true`, indicates that we are creating a new send and `false` when editing
|
||||
* an existing send.
|
||||
* @param addSendHandlers THe handlers various events.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun AddSendOptions(
|
||||
state: AddSendState.ViewState.Content,
|
||||
isAddMode: Boolean,
|
||||
addSendHandlers: AddSendHandlers,
|
||||
) {
|
||||
var isExpanded by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -221,25 +232,92 @@ private fun AddSendOptions(
|
||||
modifier = Modifier.clipToBounds(),
|
||||
) {
|
||||
Column {
|
||||
SendDeletionDateChooser(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
onDateSelect = addSendHandlers.onDeletionDateChange,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
SendExpirationDateChooser(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.expirationDate,
|
||||
onDateSelect = addSendHandlers.onExpirationDateChange,
|
||||
)
|
||||
if (isAddMode) {
|
||||
SendDeletionDateChooser(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
onDateSelect = addSendHandlers.onDeletionDateChange,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
SendExpirationDateChooser(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.expirationDate,
|
||||
onDateSelect = addSendHandlers.onExpirationDateChange,
|
||||
)
|
||||
} else {
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.deletion_date),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AddSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
onDateSelect = { addSendHandlers.onDeletionDateChange(requireNotNull(it)) },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.deletion_date_info),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.expiration_date),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AddSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.expirationDate,
|
||||
onDateSelect = addSendHandlers.onExpirationDateChange,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.expiration_date_info),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.clear),
|
||||
onClick = addSendHandlers.onClearExpirationDateClick,
|
||||
isEnabled = state.common.expirationDate != null,
|
||||
modifier = Modifier.wrapContentWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenStepper(
|
||||
label = stringResource(id = R.string.maximum_access_count),
|
||||
@@ -260,6 +338,30 @@ private fun AddSendOptions(
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
)
|
||||
if (!isAddMode) {
|
||||
state.common.currentAccessCount?.let {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.current_access_count) + ":",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = it.toString(),
|
||||
style = LocalNonMaterialTypography.current.bodySmallProminent,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.new_password),
|
||||
|
||||
@@ -135,6 +135,7 @@ fun AddSendScreen(
|
||||
when (val viewState = state.viewState) {
|
||||
is AddSendState.ViewState.Content -> AddSendContent(
|
||||
state = viewState,
|
||||
isAddMode = state.isAddMode,
|
||||
addSendHandlers = remember(viewModel) { AddSendHandlers.create(viewModel) },
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
@@ -3,18 +3,24 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.model.AddSendType
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.util.toSendView
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.util.toViewState
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.util.toSendUrl
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -50,6 +56,7 @@ class AddSendViewModel @Inject constructor(
|
||||
AddSendType.AddItem -> AddSendState.ViewState.Content(
|
||||
common = AddSendState.ViewState.Content.Common(
|
||||
name = "",
|
||||
currentAccessCount = null,
|
||||
maxAccessCount = null,
|
||||
passwordInput = "",
|
||||
noteInput = "",
|
||||
@@ -62,6 +69,7 @@ class AddSendViewModel @Inject constructor(
|
||||
.truncatedTo(ChronoUnit.DAYS)
|
||||
.plusWeeks(1),
|
||||
expirationDate = null,
|
||||
sendUrl = null,
|
||||
),
|
||||
selectedType = AddSendState.ViewState.Content.SendType.Text(
|
||||
input = "",
|
||||
@@ -69,9 +77,7 @@ class AddSendViewModel @Inject constructor(
|
||||
),
|
||||
)
|
||||
|
||||
is AddSendType.EditItem -> AddSendState.ViewState.Error(
|
||||
"Not yet implemented".asText(),
|
||||
)
|
||||
is AddSendType.EditItem -> AddSendState.ViewState.Loading
|
||||
},
|
||||
dialogState = null,
|
||||
isPremiumUser = authRepo.userStateFlow.value?.activeAccount?.isPremium == true,
|
||||
@@ -85,6 +91,19 @@ class AddSendViewModel @Inject constructor(
|
||||
.onEach { savedStateHandle[KEY_STATE] = it }
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
when (val addSendType = state.addSendType) {
|
||||
AddSendType.AddItem -> Unit
|
||||
is AddSendType.EditItem -> {
|
||||
vaultRepo
|
||||
.getSendStateFlow(addSendType.sendItemId)
|
||||
// We'll stop getting updates as soon as we get some loaded data.
|
||||
.takeUntilLoaded()
|
||||
.map { AddSendAction.Internal.SendDataReceive(it) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
}
|
||||
|
||||
authRepo
|
||||
.userStateFlow
|
||||
.map { AddSendAction.Internal.UserStateReceive(it) }
|
||||
@@ -100,6 +119,7 @@ class AddSendViewModel @Inject constructor(
|
||||
is AddSendAction.CloseClick -> handleCloseClick()
|
||||
is AddSendAction.DeletionDateChange -> handleDeletionDateChange(action)
|
||||
is AddSendAction.ExpirationDateChange -> handleExpirationDateChange(action)
|
||||
AddSendAction.ClearExpirationDate -> handleClearExpirationDate()
|
||||
AddSendAction.DismissDialogClick -> handleDismissDialogClick()
|
||||
is AddSendAction.SaveClick -> handleSaveClick()
|
||||
is AddSendAction.FileTypeClick -> handleFileTypeClick()
|
||||
@@ -118,7 +138,9 @@ class AddSendViewModel @Inject constructor(
|
||||
|
||||
private fun handleInternalAction(action: AddSendAction.Internal): Unit = when (action) {
|
||||
is AddSendAction.Internal.CreateSendResultReceive -> handleCreateSendResultReceive(action)
|
||||
is AddSendAction.Internal.UpdateSendResultReceive -> handleUpdateSendResultReceive(action)
|
||||
is AddSendAction.Internal.UserStateReceive -> handleUserStateReceive(action)
|
||||
is AddSendAction.Internal.SendDataReceive -> handleSendDataReceive(action)
|
||||
}
|
||||
|
||||
private fun handleCreateSendResultReceive(
|
||||
@@ -148,12 +170,113 @@ class AddSendViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateSendResultReceive(
|
||||
action: AddSendAction.Internal.UpdateSendResultReceive,
|
||||
) {
|
||||
when (val result = action.result) {
|
||||
is UpdateSendResult.Error -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.an_error_has_occurred.asText(),
|
||||
message = result
|
||||
.errorMessage
|
||||
?.asText()
|
||||
?: R.string.generic_error_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is UpdateSendResult.Success -> {
|
||||
mutableStateFlow.update { it.copy(dialogState = null) }
|
||||
sendEvent(AddSendEvent.NavigateBack)
|
||||
sendEvent(
|
||||
AddSendEvent.ShowShareSheet(
|
||||
message = result.sendView.toSendUrl(state.baseWebSendUrl),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUserStateReceive(action: AddSendAction.Internal.UserStateReceive) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isPremiumUser = action.userState?.activeAccount?.isPremium == true)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun handleSendDataReceive(action: AddSendAction.Internal.SendDataReceive) {
|
||||
when (val sendDataState = action.sendDataState) {
|
||||
is DataState.Error -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = AddSendState.ViewState.Error(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is DataState.Loaded -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = sendDataState
|
||||
.data
|
||||
?.toViewState(
|
||||
clock = clock,
|
||||
baseWebSendUrl = environmentRepo
|
||||
.environment
|
||||
.environmentUrlData
|
||||
.baseWebSendUrl,
|
||||
)
|
||||
?: AddSendState.ViewState.Error(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DataState.Loading -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = AddSendState.ViewState.Loading)
|
||||
}
|
||||
}
|
||||
|
||||
is DataState.NoNetwork -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = AddSendState.ViewState.Error(
|
||||
message = R.string.internet_connection_required_title
|
||||
.asText()
|
||||
.concat(R.string.internet_connection_required_message.asText()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is DataState.Pending -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
viewState = sendDataState
|
||||
.data
|
||||
?.toViewState(
|
||||
clock = clock,
|
||||
baseWebSendUrl = environmentRepo
|
||||
.environment
|
||||
.environmentUrlData
|
||||
.baseWebSendUrl,
|
||||
)
|
||||
?: AddSendState.ViewState.Error(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyLinkClick() {
|
||||
// TODO Add copy link support (BIT-1435)
|
||||
sendEvent(AddSendEvent.ShowToast("Not yet implemented"))
|
||||
@@ -212,6 +335,10 @@ class AddSendViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleClearExpirationDate() {
|
||||
updateCommonContent { it.copy(expirationDate = null) }
|
||||
}
|
||||
|
||||
private fun handleSaveClick() {
|
||||
onContent { content ->
|
||||
if (content.common.name.isBlank()) {
|
||||
@@ -235,8 +362,20 @@ class AddSendViewModel @Inject constructor(
|
||||
)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
val result = vaultRepo.createSend(content.toSendView(clock))
|
||||
sendAction(AddSendAction.Internal.CreateSendResultReceive(result))
|
||||
when (val addSendType = state.addSendType) {
|
||||
AddSendType.AddItem -> {
|
||||
val result = vaultRepo.createSend(content.toSendView(clock))
|
||||
sendAction(AddSendAction.Internal.CreateSendResultReceive(result))
|
||||
}
|
||||
|
||||
is AddSendType.EditItem -> {
|
||||
val result = vaultRepo.updateSend(
|
||||
sendId = addSendType.sendItemId,
|
||||
sendView = content.toSendView(clock),
|
||||
)
|
||||
sendAction(AddSendAction.Internal.UpdateSendResultReceive(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,12 +541,23 @@ data class AddSendState(
|
||||
val selectedType: SendType,
|
||||
) : ViewState() {
|
||||
|
||||
/**
|
||||
* Helper method to indicate if the selected type is [SendType.File].
|
||||
*/
|
||||
val isFileType: Boolean get() = selectedType is SendType.File
|
||||
|
||||
/**
|
||||
* Helper method to indicate if the selected type is [SendType.Text].
|
||||
*/
|
||||
val isTextType: Boolean get() = selectedType is SendType.Text
|
||||
|
||||
/**
|
||||
* Content data that is common for all item types.
|
||||
*/
|
||||
@Parcelize
|
||||
data class Common(
|
||||
val name: String,
|
||||
val currentAccessCount: Int?,
|
||||
val maxAccessCount: Int?,
|
||||
val passwordInput: String,
|
||||
val noteInput: String,
|
||||
@@ -415,6 +565,7 @@ data class AddSendState(
|
||||
val isDeactivateChecked: Boolean,
|
||||
val deletionDate: ZonedDateTime,
|
||||
val expirationDate: ZonedDateTime?,
|
||||
val sendUrl: String?,
|
||||
) : Parcelable {
|
||||
val dateFormatPattern: String get() = "M/d/yyyy"
|
||||
|
||||
@@ -592,6 +743,11 @@ sealed class AddSendAction {
|
||||
*/
|
||||
data class ExpirationDateChange(val expirationDate: ZonedDateTime?) : AddSendAction()
|
||||
|
||||
/**
|
||||
* The user has cleared the expiration date.
|
||||
*/
|
||||
data object ClearExpirationDate : AddSendAction()
|
||||
|
||||
/**
|
||||
* Models actions that the [AddSendViewModel] itself might send.
|
||||
*/
|
||||
@@ -605,5 +761,15 @@ sealed class AddSendAction {
|
||||
* Indicates a result for creating a send has been received.
|
||||
*/
|
||||
data class CreateSendResultReceive(val result: CreateSendResult) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for updating a send has been received.
|
||||
*/
|
||||
data class UpdateSendResultReceive(val result: UpdateSendResult) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates that the send item data has been received.
|
||||
*/
|
||||
data class SendDataReceive(val sendDataState: DataState<SendView?>) : Internal()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ data class AddSendHandlers(
|
||||
val onDeactivateSendToggle: (Boolean) -> Unit,
|
||||
val onDeletionDateChange: (ZonedDateTime) -> Unit,
|
||||
val onExpirationDateChange: (ZonedDateTime?) -> Unit,
|
||||
val onClearExpirationDateClick: () -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -57,6 +58,9 @@ data class AddSendHandlers(
|
||||
onExpirationDateChange = {
|
||||
viewModel.trySendAction(AddSendAction.ExpirationDateChange(it))
|
||||
},
|
||||
onClearExpirationDateClick = {
|
||||
viewModel.trySendAction(AddSendAction.ClearExpirationDate)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.addsend.util
|
||||
|
||||
import com.bitwarden.core.SendType
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.AddSendState
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.util.toSendUrl
|
||||
import java.time.Clock
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* Transforms [SendView] into [AddSendState.ViewState.Content].
|
||||
*/
|
||||
fun SendView.toViewState(
|
||||
clock: Clock,
|
||||
baseWebSendUrl: String,
|
||||
): AddSendState.ViewState.Content =
|
||||
AddSendState.ViewState.Content(
|
||||
common = AddSendState.ViewState.Content.Common(
|
||||
name = this.name,
|
||||
currentAccessCount = this.accessCount.toInt(),
|
||||
maxAccessCount = this.maxAccessCount?.toInt(),
|
||||
// We do not set the password here
|
||||
// We only allow them to create new passwords, not view old ones
|
||||
passwordInput = "",
|
||||
noteInput = this.notes.orEmpty(),
|
||||
isHideEmailChecked = this.hideEmail,
|
||||
isDeactivateChecked = this.disabled,
|
||||
deletionDate = ZonedDateTime.ofInstant(this.deletionDate, clock.zone),
|
||||
expirationDate = this.expirationDate?.let { ZonedDateTime.ofInstant(it, clock.zone) },
|
||||
sendUrl = this.toSendUrl(baseWebSendUrl),
|
||||
),
|
||||
selectedType = when (type) {
|
||||
SendType.TEXT -> {
|
||||
AddSendState.ViewState.Content.SendType.Text(
|
||||
input = this.text?.text.orEmpty(),
|
||||
isHideByDefaultChecked = this.text?.hidden == true,
|
||||
)
|
||||
}
|
||||
|
||||
SendType.FILE -> AddSendState.ViewState.Content.SendType.File
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user