Allow picking duration for the live location share

This commit is contained in:
ganfra 2026-02-18 19:13:47 +01:00
parent a129d1f9ca
commit 7472f889cf
8 changed files with 113 additions and 28 deletions

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.location.impl.share
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
enum class LiveLocationDuration(
val duration: Duration,
val label: String,
) {
FifteenMinutes(15.minutes, "15 minutes"),
OneHour(1.hours, "1 hour"),
EightHours(8.hours, "8 hours");
}

View file

@ -9,6 +9,7 @@
package io.element.android.features.location.impl.share package io.element.android.features.location.impl.share
import io.element.android.features.location.api.Location import io.element.android.features.location.api.Location
import kotlin.time.Duration
sealed interface ShareLocationEvent { sealed interface ShareLocationEvent {
data class ShareStaticLocation( data class ShareStaticLocation(
@ -22,7 +23,8 @@ sealed interface ShareLocationEvent {
) )
} }
data object SelectLiveLocationDuration: ShareLocationEvent data object SelectLiveLocationDuration : ShareLocationEvent
data class StartLiveLocationShare(val duration: Duration) : ShareLocationEvent
data object SwitchToMyLocationMode : ShareLocationEvent data object SwitchToMyLocationMode : ShareLocationEvent
data object SwitchToPinLocationMode : ShareLocationEvent data object SwitchToPinLocationMode : ShareLocationEvent

View file

@ -74,7 +74,7 @@ class ShareLocationPresenter(
featureFlagService.isFeatureEnabledFlow(FeatureFlags.LiveLocationSharing) featureFlagService.isFeatureEnabledFlow(FeatureFlags.LiveLocationSharing)
}.collectAsState(false) }.collectAsState(false)
val appName by remember { derivedStateOf { buildMeta.applicationName } } val appName by remember { derivedStateOf { buildMeta.applicationName } }
var permissionDialog: ShareLocationState.Dialog by remember { var dialogState: ShareLocationState.Dialog by remember {
mutableStateOf(ShareLocationState.Dialog.None) mutableStateOf(ShareLocationState.Dialog.None)
} }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@ -82,7 +82,7 @@ class ShareLocationPresenter(
LaunchedEffect(permissionsState.permissions) { LaunchedEffect(permissionsState.permissions) {
if (permissionsState.isAnyGranted) { if (permissionsState.isAnyGranted) {
mode = ShareLocationState.Mode.SenderLocation mode = ShareLocationState.Mode.SenderLocation
permissionDialog = ShareLocationState.Dialog.None dialogState = ShareLocationState.Dialog.None
} }
} }
@ -93,22 +93,30 @@ class ShareLocationPresenter(
} }
ShareLocationEvent.SwitchToMyLocationMode -> when { ShareLocationEvent.SwitchToMyLocationMode -> when {
permissionsState.isAnyGranted -> mode = ShareLocationState.Mode.SenderLocation permissionsState.isAnyGranted -> mode = ShareLocationState.Mode.SenderLocation
permissionsState.shouldShowRationale -> permissionDialog = ShareLocationState.Dialog.PermissionRationale permissionsState.shouldShowRationale -> dialogState = ShareLocationState.Dialog.PermissionRationale
else -> permissionDialog = ShareLocationState.Dialog.PermissionDenied else -> dialogState = ShareLocationState.Dialog.PermissionDenied
} }
ShareLocationEvent.SwitchToPinLocationMode -> mode = ShareLocationState.Mode.PinLocation ShareLocationEvent.SwitchToPinLocationMode -> mode = ShareLocationState.Mode.PinLocation
ShareLocationEvent.DismissDialog -> permissionDialog = ShareLocationState.Dialog.None ShareLocationEvent.DismissDialog -> dialogState = ShareLocationState.Dialog.None
ShareLocationEvent.OpenAppSettings -> { ShareLocationEvent.OpenAppSettings -> {
locationActions.openSettings() locationActions.openSettings()
permissionDialog = ShareLocationState.Dialog.None dialogState = ShareLocationState.Dialog.None
} }
ShareLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions) ShareLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions)
ShareLocationEvent.SelectLiveLocationDuration -> Unit ShareLocationEvent.SelectLiveLocationDuration -> dialogState = when {
permissionsState.isAnyGranted -> ShareLocationState.Dialog.LiveLocationDuration
permissionsState.shouldShowRationale -> ShareLocationState.Dialog.PermissionRationale
else -> ShareLocationState.Dialog.PermissionDenied
}
is ShareLocationEvent.StartLiveLocationShare -> scope.launch {
dialogState = ShareLocationState.Dialog.None
//room.startLiveLocationShare(event.duration.inWholeMilliseconds)
}
} }
} }
return ShareLocationState( return ShareLocationState(
permissionDialog = permissionDialog, dialogState = dialogState,
mode = mode, mode = mode,
hasLocationPermission = permissionsState.isAnyGranted, hasLocationPermission = permissionsState.isAnyGranted,
canShareLiveLocation = isLiveLocationSharingEnabled, canShareLiveLocation = isLiveLocationSharingEnabled,

View file

@ -9,7 +9,7 @@
package io.element.android.features.location.impl.share package io.element.android.features.location.impl.share
data class ShareLocationState( data class ShareLocationState(
val permissionDialog: Dialog, val dialogState: Dialog,
val mode: Mode, val mode: Mode,
val hasLocationPermission: Boolean, val hasLocationPermission: Boolean,
val appName: String, val appName: String,
@ -25,5 +25,6 @@ data class ShareLocationState(
data object None : Dialog data object None : Dialog
data object PermissionRationale : Dialog data object PermissionRationale : Dialog
data object PermissionDenied : Dialog data object PermissionDenied : Dialog
data object LiveLocationDuration : Dialog
} }
} }

View file

@ -40,6 +40,12 @@ class ShareLocationStateProvider : PreviewParameterProvider<ShareLocationState>
mode = ShareLocationState.Mode.SenderLocation, mode = ShareLocationState.Mode.SenderLocation,
hasLocationPermission = true, hasLocationPermission = true,
), ),
aShareLocationState(
permissionDialog = ShareLocationState.Dialog.LiveLocationDuration,
mode = ShareLocationState.Mode.SenderLocation,
hasLocationPermission = true,
canShareLiveLocation = true,
),
) )
} }
@ -50,7 +56,7 @@ private fun aShareLocationState(
canShareLiveLocation: Boolean = false, canShareLiveLocation: Boolean = false,
): ShareLocationState { ): ShareLocationState {
return ShareLocationState( return ShareLocationState(
permissionDialog = permissionDialog, dialogState = permissionDialog,
mode = mode, mode = mode,
hasLocationPermission = hasLocationPermission, hasLocationPermission = hasLocationPermission,
canShareLiveLocation = canShareLiveLocation, canShareLiveLocation = canShareLiveLocation,

View file

@ -9,6 +9,7 @@
package io.element.android.features.location.impl.share package io.element.android.features.location.impl.share
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
@ -22,8 +23,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetValue import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.material3.rememberStandardBottomSheetState import androidx.compose.material3.rememberStandardBottomSheetState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -40,7 +46,9 @@ import io.element.android.features.location.impl.common.PermissionDeniedDialog
import io.element.android.features.location.impl.common.PermissionRationaleDialog import io.element.android.features.location.impl.common.PermissionRationaleDialog
import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ListDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.list.RadioButtonListItem
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold
@ -57,6 +65,7 @@ import io.element.android.libraries.maplibre.compose.MapLibreMap
import io.element.android.libraries.maplibre.compose.rememberCameraPositionState import io.element.android.libraries.maplibre.compose.rememberCameraPositionState
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import org.maplibre.android.camera.CameraPosition import org.maplibre.android.camera.CameraPosition
import kotlin.time.Duration
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -69,7 +78,7 @@ fun ShareLocationView(
state.eventSink(ShareLocationEvent.RequestPermissions) state.eventSink(ShareLocationEvent.RequestPermissions)
} }
when (state.permissionDialog) { when (state.dialogState) {
ShareLocationState.Dialog.None -> Unit ShareLocationState.Dialog.None -> Unit
ShareLocationState.Dialog.PermissionDenied -> PermissionDeniedDialog( ShareLocationState.Dialog.PermissionDenied -> PermissionDeniedDialog(
onContinue = { state.eventSink(ShareLocationEvent.OpenAppSettings) }, onContinue = { state.eventSink(ShareLocationEvent.OpenAppSettings) },
@ -81,6 +90,13 @@ fun ShareLocationView(
onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) }, onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) },
appName = state.appName, appName = state.appName,
) )
ShareLocationState.Dialog.LiveLocationDuration -> LiveLocationDurationDialog(
onSelectDuration = { duration ->
state.eventSink(ShareLocationEvent.StartLiveLocationShare(duration))
navigateUp()
},
onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) },
)
} }
val cameraPositionState = rememberCameraPositionState { val cameraPositionState = rememberCameraPositionState {
@ -226,7 +242,7 @@ private fun StaticLocationItem(
@Composable @Composable
private fun LiveLocationItem( private fun LiveLocationItem(
onClick: ()->Unit, onClick: () -> Unit,
) { ) {
ListItem( ListItem(
headlineContent = { headlineContent = {
@ -242,6 +258,32 @@ private fun LiveLocationItem(
) )
} }
@Composable
private fun LiveLocationDurationDialog(
onSelectDuration: (Duration) -> Unit,
onDismiss: () -> Unit,
) {
var selectedIndex by remember { mutableIntStateOf(0) }
ListDialog(
title = "Choose how long to share your live location.",
submitText = stringResource(CommonStrings.action_continue),
onSubmit = { onSelectDuration(LiveLocationDuration.entries[selectedIndex].duration) },
onDismissRequest = onDismiss,
applyPaddingToContents = false,
verticalArrangement = Arrangement.Top
) {
itemsIndexed(LiveLocationDuration.entries) { index, duration ->
RadioButtonListItem(
headline = duration.label,
selected = index == selectedIndex,
onSelect = { selectedIndex = index },
compactLayout = true,
modifier = Modifier.padding(start = 8.dp)
)
}
}
}
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun ShareLocationViewPreview( internal fun ShareLocationViewPreview(

View file

@ -78,14 +78,14 @@ class ShareLocationPresenterTest {
shareLocationPresenter.present() shareLocationPresenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.SenderLocation) assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.SenderLocation)
assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.hasLocationPermission).isTrue()
// Swipe the map to switch mode // Swipe the map to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isTrue() assertThat(myLocationState.hasLocationPermission).isTrue()
} }
@ -105,14 +105,14 @@ class ShareLocationPresenterTest {
shareLocationPresenter.present() shareLocationPresenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.SenderLocation) assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.SenderLocation)
assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.hasLocationPermission).isTrue()
// Swipe the map to switch mode // Swipe the map to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isTrue() assertThat(myLocationState.hasLocationPermission).isTrue()
} }
@ -132,14 +132,14 @@ class ShareLocationPresenterTest {
shareLocationPresenter.present() shareLocationPresenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.hasLocationPermission).isFalse()
// Click on the button to switch mode // Click on the button to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionDenied) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse() assertThat(myLocationState.hasLocationPermission).isFalse()
} }
@ -159,14 +159,14 @@ class ShareLocationPresenterTest {
shareLocationPresenter.present() shareLocationPresenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(initialState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.hasLocationPermission).isFalse()
// Click on the button to switch mode // Click on the button to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse() assertThat(myLocationState.hasLocationPermission).isFalse()
} }
@ -191,14 +191,14 @@ class ShareLocationPresenterTest {
// Click on the button to switch mode // Click on the button to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse() assertThat(myLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog // Dismiss the dialog
myLocationState.eventSink(ShareLocationEvent.DismissDialog) myLocationState.eventSink(ShareLocationEvent.DismissDialog)
val dialogDismissedState = awaitItem() val dialogDismissedState = awaitItem()
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(dialogDismissedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(dialogDismissedState.hasLocationPermission).isFalse() assertThat(dialogDismissedState.hasLocationPermission).isFalse()
} }
@ -223,7 +223,7 @@ class ShareLocationPresenterTest {
// Click on the button to switch mode // Click on the button to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse() assertThat(myLocationState.hasLocationPermission).isFalse()
@ -252,14 +252,14 @@ class ShareLocationPresenterTest {
// Click on the button to switch mode // Click on the button to switch mode
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode) initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem() val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionDenied) assertThat(myLocationState.dialogState).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse() assertThat(myLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog // Dismiss the dialog
myLocationState.eventSink(ShareLocationEvent.DismissDialog) myLocationState.eventSink(ShareLocationEvent.DismissDialog)
val dialogDismissedState = awaitItem() val dialogDismissedState = awaitItem()
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(dialogDismissedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation) assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(dialogDismissedState.hasLocationPermission).isFalse() assertThat(dialogDismissedState.hasLocationPermission).isFalse()
} }
@ -478,7 +478,7 @@ class ShareLocationPresenterTest {
dialogShownState.eventSink(ShareLocationEvent.OpenAppSettings) dialogShownState.eventSink(ShareLocationEvent.OpenAppSettings)
val settingsOpenedState = awaitItem() val settingsOpenedState = awaitItem()
assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None) assertThat(settingsOpenedState.dialogState).isEqualTo(ShareLocationState.Dialog.None)
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1) assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
} }
} }

View file

@ -9,6 +9,7 @@
package io.element.android.libraries.designsystem.components.dialogs package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.HorizontalOrVertical
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
@ -42,6 +43,7 @@ fun ListDialog(
enabled: Boolean = true, enabled: Boolean = true,
applyPaddingToContents: Boolean = true, applyPaddingToContents: Boolean = true,
destructiveSubmit: Boolean = false, destructiveSubmit: Boolean = false,
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(16.dp),
listItems: LazyListScope.() -> Unit, listItems: LazyListScope.() -> Unit,
) { ) {
val decoratedSubtitle: @Composable (() -> Unit)? = subtitle?.let { val decoratedSubtitle: @Composable (() -> Unit)? = subtitle?.let {
@ -67,6 +69,7 @@ fun ListDialog(
listItems = listItems, listItems = listItems,
applyPaddingToContents = applyPaddingToContents, applyPaddingToContents = applyPaddingToContents,
destructiveSubmit = destructiveSubmit, destructiveSubmit = destructiveSubmit,
verticalArrangement = verticalArrangement,
) )
} }
} }
@ -82,6 +85,7 @@ private fun ListDialogContent(
enabled: Boolean, enabled: Boolean,
applyPaddingToContents: Boolean, applyPaddingToContents: Boolean,
destructiveSubmit: Boolean, destructiveSubmit: Boolean,
verticalArrangement: Arrangement.Vertical,
subtitle: @Composable (() -> Unit)? = null, subtitle: @Composable (() -> Unit)? = null,
) { ) {
SimpleAlertDialogContent( SimpleAlertDialogContent(
@ -99,7 +103,7 @@ private fun ListDialogContent(
val horizontalPadding = if (applyPaddingToContents) 0.dp else 8.dp val horizontalPadding = if (applyPaddingToContents) 0.dp else 8.dp
LazyColumn( LazyColumn(
modifier = Modifier.padding(horizontal = horizontalPadding), modifier = Modifier.padding(horizontal = horizontalPadding),
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = verticalArrangement,
) { listItems() } ) { listItems() }
} }
} }
@ -126,6 +130,7 @@ internal fun ListDialogContentPreview() {
enabled = true, enabled = true,
destructiveSubmit = false, destructiveSubmit = false,
applyPaddingToContents = true, applyPaddingToContents = true,
verticalArrangement = Arrangement.spacedBy(16.dp),
) )
} }
} }