mirror of
https://github.com/bitwarden/android.git
synced 2026-03-12 05:04:17 -05:00
PM-22397: Remove custom deletion date (#5311)
This commit is contained in:
@@ -62,6 +62,7 @@ fun BitwardenTextSelectionButton(
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.clearAndSetSemantics {
|
||||
role = semanticRole
|
||||
contentDescription = supportingText
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManagerImp
|
||||
import com.x8bit.bitwarden.ui.platform.manager.review.AppReviewManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.review.AppReviewManagerImpl
|
||||
import com.x8bit.bitwarden.ui.platform.model.FeatureFlagsState
|
||||
import java.time.Clock
|
||||
|
||||
/**
|
||||
* Helper [Composable] that wraps a [content] and provides manager classes via [CompositionLocal].
|
||||
@@ -43,8 +44,9 @@ fun LocalManagerProvider(
|
||||
appResumeStateManager: AppResumeStateManager = AppResumeStateManagerImpl(),
|
||||
appReviewManager: AppReviewManager = AppReviewManagerImpl(activity = activity),
|
||||
biometricsManager: BiometricsManager = BiometricsManagerImpl(activity = activity),
|
||||
clock: Clock = Clock.systemDefaultZone(),
|
||||
exitManager: ExitManager = ExitManagerImpl(activity = activity),
|
||||
intentManager: IntentManager = IntentManagerImpl(context = activity),
|
||||
intentManager: IntentManager = IntentManagerImpl(context = activity, clock = clock),
|
||||
credentialProviderCompletionManager: CredentialProviderCompletionManager =
|
||||
createCredentialProviderCompletionManager(activity = activity),
|
||||
keyChainManager: KeyChainManager = KeyChainManagerImpl(activity = activity),
|
||||
@@ -57,6 +59,7 @@ fun LocalManagerProvider(
|
||||
LocalAppResumeStateManager provides appResumeStateManager,
|
||||
LocalAppReviewManager provides appReviewManager,
|
||||
LocalBiometricsManager provides biometricsManager,
|
||||
LocalClock provides clock,
|
||||
LocalExitManager provides exitManager,
|
||||
LocalCredentialProviderCompletionManager provides credentialProviderCompletionManager,
|
||||
LocalIntentManager provides intentManager,
|
||||
@@ -83,6 +86,11 @@ val LocalBiometricsManager: ProvidableCompositionLocal<BiometricsManager> = comp
|
||||
error("CompositionLocal BiometricsManager not present")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the clock throughout the app.
|
||||
*/
|
||||
val LocalClock: ProvidableCompositionLocal<Clock> = compositionLocalOf { Clock.systemDefaultZone() }
|
||||
|
||||
/**
|
||||
* Provides access to the exit manager throughout the app.
|
||||
*/
|
||||
|
||||
@@ -12,6 +12,7 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.time.Clock
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
@@ -25,9 +26,11 @@ class PlatformUiManagerModule {
|
||||
@Singleton
|
||||
fun provideIntentManager(
|
||||
@ApplicationContext context: Context,
|
||||
clock: Clock,
|
||||
): IntentManager =
|
||||
IntentManagerImpl(
|
||||
context = context,
|
||||
clock = clock,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -85,7 +85,7 @@ const val EXTRA_KEY_UV_PERFORMED_DURING_UNLOCK: String = "uv_performed_during_un
|
||||
@OmitFromCoverage
|
||||
class IntentManagerImpl(
|
||||
private val context: Context,
|
||||
private val clock: Clock = Clock.systemDefaultZone(),
|
||||
private val clock: Clock,
|
||||
) : IntentManager {
|
||||
override fun startActivity(intent: Intent) {
|
||||
try {
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenExpandingHeader
|
||||
@@ -51,6 +50,8 @@ import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.stepper.BitwardenStepper
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addedit.components.AddEditSendCustomDateChooser
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addedit.components.AddEditSendDeletionDateChooser
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.addedit.handlers.AddEditSendHandlers
|
||||
|
||||
/**
|
||||
@@ -138,48 +139,23 @@ fun AddEditSendContent(
|
||||
|
||||
if (isAddMode) {
|
||||
AddEditSendDeletionDateChooser(
|
||||
onDateSelect = addSendHandlers.onDeletionDateChange,
|
||||
isEnabled = !policyDisablesSend,
|
||||
modifier = Modifier
|
||||
.testTag("SendDeletionOptionsPicker")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
onDateSelect = addSendHandlers.onDeletionDateChange,
|
||||
isEnabled = !policyDisablesSend,
|
||||
)
|
||||
} else {
|
||||
Column(
|
||||
AddEditSendCustomDateChooser(
|
||||
originalSelection = state.common.deletionDate,
|
||||
isEnabled = !policyDisablesSend,
|
||||
onDateSelect = addSendHandlers.onDeletionDateChange,
|
||||
modifier = Modifier
|
||||
.testTag("SendCustomDeletionDatePicker")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardStyle(cardStyle = CardStyle.Full, paddingVertical = 0.dp),
|
||||
) {
|
||||
AddEditSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.testTag("SendCustomDeletionDatePicker")
|
||||
.fillMaxWidth(),
|
||||
dateLabel = stringResource(id = R.string.deletion_date),
|
||||
timeLabel = stringResource(id = R.string.deletion_time),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
isEnabled = !policyDisablesSend,
|
||||
onDateSelect = { addSendHandlers.onDeletionDateChange(requireNotNull(it)) },
|
||||
)
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.deletion_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
AddEditSendOptions(
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.addedit
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenDateSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTimeSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.util.orNow
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
/**
|
||||
* Displays a UI for selecting a customizable date and time.
|
||||
*
|
||||
* @param dateLabel The display label for the date selection field.
|
||||
* @param timeLabel The display label for the time selection field.
|
||||
* @param currentZonedDateTime The currently selected time, `null` when no time is selected yet.
|
||||
* @param dateFormatPattern The pattern to use when displaying the date.
|
||||
* @param timeFormatPattern The pattern for displaying the time.
|
||||
* @param onDateSelect The callback for being notified of updates to the selected date and time.
|
||||
* This will only be `null` when there is no selected time.
|
||||
* @param isEnabled Whether the button is enabled.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
*/
|
||||
@Composable
|
||||
fun AddEditSendCustomDateChooser(
|
||||
dateLabel: String,
|
||||
timeLabel: String,
|
||||
currentZonedDateTime: ZonedDateTime?,
|
||||
dateFormatPattern: String,
|
||||
timeFormatPattern: String,
|
||||
onDateSelect: (ZonedDateTime?) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// This tracks the date component (year, month, and day) and ignores lower level
|
||||
// components.
|
||||
var date: ZonedDateTime? by remember { mutableStateOf(currentZonedDateTime) }
|
||||
// This tracks just the time component (hours and minutes) and ignores the higher level
|
||||
// components. 0 representing midnight and counting up from there.
|
||||
var timeMillis: Long by remember {
|
||||
mutableLongStateOf(
|
||||
currentZonedDateTime.orNow().let {
|
||||
it.hour.hours.inWholeMilliseconds + it.minute.minutes.inWholeMilliseconds
|
||||
},
|
||||
)
|
||||
}
|
||||
val derivedDateTimeMillis: ZonedDateTime? by remember {
|
||||
derivedStateOf { date?.plus(timeMillis, ChronoUnit.MILLIS) }
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
) {
|
||||
BitwardenDateSelectButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
label = dateLabel,
|
||||
formatPattern = dateFormatPattern,
|
||||
currentZonedDateTime = currentZonedDateTime,
|
||||
isEnabled = isEnabled,
|
||||
onDateSelect = {
|
||||
date = it
|
||||
onDateSelect(derivedDateTimeMillis)
|
||||
},
|
||||
cardStyle = null,
|
||||
)
|
||||
BitwardenTimeSelectButton(
|
||||
modifier = Modifier.weight(1f),
|
||||
label = timeLabel,
|
||||
formatPattern = timeFormatPattern,
|
||||
currentZonedDateTime = currentZonedDateTime,
|
||||
isEnabled = isEnabled,
|
||||
onTimeSelect = { hour, minute ->
|
||||
timeMillis = hour.hours.inWholeMilliseconds + minute.minutes.inWholeMilliseconds
|
||||
onDateSelect(derivedDateTimeMillis)
|
||||
},
|
||||
cardStyle = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.addedit
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.base.util.cardStyle
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
|
||||
/**
|
||||
* Displays UX for choosing deletion date of a send.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun AddEditSendDeletionDateChooser(
|
||||
currentZonedDateTime: ZonedDateTime,
|
||||
dateFormatPattern: String,
|
||||
timeFormatPattern: String,
|
||||
onDateSelect: (ZonedDateTime) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val defaultOption = DeletionOptions.SEVEN_DAYS
|
||||
val options = DeletionOptions.entries.associateWith { it.text() }
|
||||
var selectedOption: DeletionOptions by rememberSaveable { mutableStateOf(defaultOption) }
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardStyle(cardStyle = CardStyle.Full, paddingVertical = 0.dp),
|
||||
) {
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.deletion_date),
|
||||
isEnabled = isEnabled,
|
||||
options = options.values.toImmutableList(),
|
||||
selectedOption = selectedOption.text(),
|
||||
onOptionSelected = { selected ->
|
||||
selectedOption = options.entries.first { it.value == selected }.key
|
||||
if (selectedOption != DeletionOptions.CUSTOM) {
|
||||
onDateSelect(
|
||||
// Add the appropriate milliseconds offset based on the selected option
|
||||
ZonedDateTime.now().plus(selectedOption.offsetMillis, ChronoUnit.MILLIS),
|
||||
)
|
||||
}
|
||||
},
|
||||
insets = PaddingValues(top = 6.dp, bottom = 4.dp),
|
||||
cardStyle = null,
|
||||
)
|
||||
AnimatedVisibility(visible = selectedOption == DeletionOptions.CUSTOM) {
|
||||
Column {
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
AddEditSendCustomDateChooser(
|
||||
dateLabel = stringResource(id = R.string.deletion_date),
|
||||
timeLabel = stringResource(id = R.string.deletion_time),
|
||||
currentZonedDateTime = currentZonedDateTime,
|
||||
dateFormatPattern = dateFormatPattern,
|
||||
timeFormatPattern = timeFormatPattern,
|
||||
onDateSelect = { onDateSelect(requireNotNull(it)) },
|
||||
isEnabled = isEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.deletion_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
}
|
||||
|
||||
private enum class DeletionOptions(
|
||||
val text: Text,
|
||||
val offsetMillis: Long,
|
||||
) {
|
||||
ONE_HOUR(
|
||||
text = R.string.one_hour.asText(),
|
||||
offsetMillis = 1.hours.inWholeMilliseconds,
|
||||
),
|
||||
ONE_DAY(
|
||||
text = R.string.one_day.asText(),
|
||||
offsetMillis = 1.days.inWholeMilliseconds,
|
||||
),
|
||||
TWO_DAYS(
|
||||
text = R.string.two_days.asText(),
|
||||
offsetMillis = 2.days.inWholeMilliseconds,
|
||||
),
|
||||
THREE_DAYS(
|
||||
text = R.string.three_days.asText(),
|
||||
offsetMillis = 3.days.inWholeMilliseconds,
|
||||
),
|
||||
SEVEN_DAYS(
|
||||
text = R.string.seven_days.asText(),
|
||||
offsetMillis = 7.days.inWholeMilliseconds,
|
||||
),
|
||||
THIRTY_DAYS(
|
||||
text = R.string.thirty_days.asText(),
|
||||
offsetMillis = 30.days.inWholeMilliseconds,
|
||||
),
|
||||
CUSTOM(
|
||||
text = R.string.custom.asText(),
|
||||
offsetMillis = -1L,
|
||||
),
|
||||
}
|
||||
@@ -759,11 +759,7 @@ data class AddEditSendState(
|
||||
val expirationDate: ZonedDateTime?,
|
||||
val sendUrl: String?,
|
||||
val hasPassword: Boolean,
|
||||
) : Parcelable {
|
||||
val dateFormatPattern: String get() = "M/d/yyyy"
|
||||
|
||||
val timeFormatPattern: String get() = "hh:mm a"
|
||||
}
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* Models what type the user is trying to send.
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.addedit.components
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.x8bit.bitwarden.ui.platform.util.toFormattedPattern
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.time.Clock
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
|
||||
/**
|
||||
* Displays a UI for selecting a customizable date and time.
|
||||
*
|
||||
* @param originalSelection The originally selected time value, this cannot be changed after being
|
||||
* set.
|
||||
* @param onDateSelect The callback for being notified of updates to the selected date and time.
|
||||
* @param isEnabled Whether the button is enabled.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
* @param clock The clock used for formatting and timezone purposes.
|
||||
*/
|
||||
@Composable
|
||||
fun AddEditSendCustomDateChooser(
|
||||
originalSelection: ZonedDateTime,
|
||||
onDateSelect: (ZonedDateTime) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
clock: Clock = LocalClock.current,
|
||||
) {
|
||||
val originalSelectionOption: CustomDeletionOption.Current = rememberSaveable {
|
||||
CustomDeletionOption.Current(time = originalSelection)
|
||||
}
|
||||
val options = persistentMapOf(
|
||||
originalSelectionOption to originalSelectionOption.getText(clock = clock)(),
|
||||
CustomDeletionOption.OneHour to CustomDeletionOption.OneHour.getText(clock = clock)(),
|
||||
CustomDeletionOption.OneDay to CustomDeletionOption.OneDay.getText(clock = clock)(),
|
||||
CustomDeletionOption.TwoDays to CustomDeletionOption.TwoDays.getText(clock = clock)(),
|
||||
CustomDeletionOption.ThreeDays to CustomDeletionOption.ThreeDays.getText(clock = clock)(),
|
||||
CustomDeletionOption.SevenDays to CustomDeletionOption.SevenDays.getText(clock = clock)(),
|
||||
CustomDeletionOption.ThirtyDays to CustomDeletionOption.ThirtyDays.getText(clock = clock)(),
|
||||
)
|
||||
var currentSelectionOption: CustomDeletionOption by rememberSaveable(originalSelectionOption) {
|
||||
mutableStateOf(value = originalSelectionOption)
|
||||
}
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.deletion_date),
|
||||
isEnabled = isEnabled,
|
||||
options = options.values.toImmutableList(),
|
||||
selectedOption = currentSelectionOption.getText(clock = clock).invoke(),
|
||||
onOptionSelected = { selected ->
|
||||
currentSelectionOption = options.entries.first { it.value == selected }.key
|
||||
onDateSelect(
|
||||
(currentSelectionOption as? CustomDeletionOption.Current)
|
||||
?.time
|
||||
?: ZonedDateTime
|
||||
.now(clock)
|
||||
.plus(currentSelectionOption.offsetMillis, ChronoUnit.MILLIS),
|
||||
)
|
||||
},
|
||||
supportingText = stringResource(id = R.string.deletion_date_info),
|
||||
insets = PaddingValues(top = 6.dp, bottom = 4.dp),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
private sealed class CustomDeletionOption : Parcelable {
|
||||
abstract val offsetMillis: Long
|
||||
abstract fun getText(clock: Clock): Text
|
||||
|
||||
@Parcelize
|
||||
data class Current(
|
||||
val time: ZonedDateTime,
|
||||
) : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 0L
|
||||
|
||||
override fun getText(
|
||||
clock: Clock,
|
||||
): Text = time.toFormattedPattern(pattern = "d MMM, yyyy, h:mma", clock = clock).asText()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object OneHour : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 1.hours.inWholeMilliseconds
|
||||
override fun getText(clock: Clock): Text = R.string.one_hour.asText()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object OneDay : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 1.days.inWholeMilliseconds
|
||||
override fun getText(clock: Clock): Text = R.string.one_day.asText()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object TwoDays : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 2.days.inWholeMilliseconds
|
||||
override fun getText(clock: Clock): Text = R.string.two_days.asText()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object ThreeDays : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 3.days.inWholeMilliseconds
|
||||
override fun getText(clock: Clock): Text = R.string.three_days.asText()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object SevenDays : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 7.days.inWholeMilliseconds
|
||||
override fun getText(clock: Clock): Text = R.string.seven_days.asText()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data object ThirtyDays : CustomDeletionOption() {
|
||||
override val offsetMillis: Long get() = 30.days.inWholeMilliseconds
|
||||
override fun getText(clock: Clock): Text = R.string.thirty_days.asText()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.x8bit.bitwarden.ui.tools.feature.send.addedit.components
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalClock
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.time.Clock
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
|
||||
/**
|
||||
* Displays UX for choosing deletion date of a send.
|
||||
*/
|
||||
@Composable
|
||||
fun AddEditSendDeletionDateChooser(
|
||||
onDateSelect: (ZonedDateTime) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
clock: Clock = LocalClock.current,
|
||||
) {
|
||||
val options = DeletionOption.entries.associateWith { it.text() }
|
||||
var selectedOption: DeletionOption by rememberSaveable {
|
||||
mutableStateOf(value = DeletionOption.SEVEN_DAYS)
|
||||
}
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.deletion_date),
|
||||
isEnabled = isEnabled,
|
||||
options = options.values.toImmutableList(),
|
||||
selectedOption = selectedOption.text(),
|
||||
onOptionSelected = { selected ->
|
||||
selectedOption = options.entries.first { it.value == selected }.key
|
||||
onDateSelect(
|
||||
ZonedDateTime.now(clock).plus(selectedOption.offsetMillis, ChronoUnit.MILLIS),
|
||||
)
|
||||
},
|
||||
supportingText = stringResource(id = R.string.deletion_date_info),
|
||||
insets = PaddingValues(top = 6.dp, bottom = 4.dp),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
private enum class DeletionOption(
|
||||
val text: Text,
|
||||
val offsetMillis: Long,
|
||||
) {
|
||||
ONE_HOUR(
|
||||
text = R.string.one_hour.asText(),
|
||||
offsetMillis = 1.hours.inWholeMilliseconds,
|
||||
),
|
||||
ONE_DAY(
|
||||
text = R.string.one_day.asText(),
|
||||
offsetMillis = 1.days.inWholeMilliseconds,
|
||||
),
|
||||
TWO_DAYS(
|
||||
text = R.string.two_days.asText(),
|
||||
offsetMillis = 2.days.inWholeMilliseconds,
|
||||
),
|
||||
THREE_DAYS(
|
||||
text = R.string.three_days.asText(),
|
||||
offsetMillis = 3.days.inWholeMilliseconds,
|
||||
),
|
||||
SEVEN_DAYS(
|
||||
text = R.string.seven_days.asText(),
|
||||
offsetMillis = 7.days.inWholeMilliseconds,
|
||||
),
|
||||
THIRTY_DAYS(
|
||||
text = R.string.thirty_days.asText(),
|
||||
offsetMillis = 30.days.inWholeMilliseconds,
|
||||
),
|
||||
}
|
||||
@@ -16,6 +16,9 @@ import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.review.AppReviewManager
|
||||
import com.x8bit.bitwarden.ui.platform.model.FeatureFlagsState
|
||||
import io.mockk.mockk
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
|
||||
abstract class BitwardenComposeTest : BaseComposeTest() {
|
||||
|
||||
@@ -32,6 +35,7 @@ abstract class BitwardenComposeTest : BaseComposeTest() {
|
||||
appResumeStateManager: AppResumeStateManager = mockk(),
|
||||
appReviewManager: AppReviewManager = mockk(),
|
||||
biometricsManager: BiometricsManager = mockk(),
|
||||
clock: Clock = Clock.fixed(Instant.parse("2023-10-27T12:00:00Z"), ZoneOffset.UTC),
|
||||
exitManager: ExitManager = mockk(),
|
||||
intentManager: IntentManager = mockk(),
|
||||
credentialProviderCompletionManager: CredentialProviderCompletionManager = mockk(),
|
||||
@@ -46,6 +50,7 @@ abstract class BitwardenComposeTest : BaseComposeTest() {
|
||||
appResumeStateManager = appResumeStateManager,
|
||||
appReviewManager = appReviewManager,
|
||||
biometricsManager = biometricsManager,
|
||||
clock = clock,
|
||||
exitManager = exitManager,
|
||||
intentManager = intentManager,
|
||||
credentialProviderCompletionManager = credentialProviderCompletionManager,
|
||||
|
||||
Reference in New Issue
Block a user