Rename ShareLocationEvents -> ShareLocationEvent

This commit is contained in:
ganfra
2026-02-18 17:24:20 +01:00
parent 8c64f704cb
commit add737b646
4 changed files with 122 additions and 82 deletions

View File

@@ -10,11 +10,11 @@ package io.element.android.features.location.impl.share
import io.element.android.features.location.api.Location
sealed interface ShareLocationEvents {
data class ShareLocation(
sealed interface ShareLocationEvent {
data class ShareStaticLocation(
val cameraPosition: CameraPosition,
val location: Location?,
) : ShareLocationEvents {
) : ShareLocationEvent {
data class CameraPosition(
val lat: Double,
val lon: Double,
@@ -22,9 +22,11 @@ sealed interface ShareLocationEvents {
)
}
data object SwitchToMyLocationMode : ShareLocationEvents
data object SwitchToPinLocationMode : ShareLocationEvents
data object DismissDialog : ShareLocationEvents
data object RequestPermissions : ShareLocationEvents
data object OpenAppSettings : ShareLocationEvents
data object SelectLiveLocationDuration: ShareLocationEvent
data object SwitchToMyLocationMode : ShareLocationEvent
data object SwitchToPinLocationMode : ShareLocationEvent
data object DismissDialog : ShareLocationEvent
data object RequestPermissions : ShareLocationEvent
data object OpenAppSettings : ShareLocationEvent
}

View File

@@ -79,23 +79,24 @@ class ShareLocationPresenter(
}
}
fun handleEvent(event: ShareLocationEvents) {
fun handleEvent(event: ShareLocationEvent) {
when (event) {
is ShareLocationEvents.ShareLocation -> scope.launch {
is ShareLocationEvent.ShareStaticLocation -> scope.launch {
shareLocation(event, mode)
}
ShareLocationEvents.SwitchToMyLocationMode -> when {
ShareLocationEvent.SwitchToMyLocationMode -> when {
permissionsState.isAnyGranted -> mode = ShareLocationState.Mode.SenderLocation
permissionsState.shouldShowRationale -> permissionDialog = ShareLocationState.Dialog.PermissionRationale
else -> permissionDialog = ShareLocationState.Dialog.PermissionDenied
}
ShareLocationEvents.SwitchToPinLocationMode -> mode = ShareLocationState.Mode.PinLocation
ShareLocationEvents.DismissDialog -> permissionDialog = ShareLocationState.Dialog.None
ShareLocationEvents.OpenAppSettings -> {
ShareLocationEvent.SwitchToPinLocationMode -> mode = ShareLocationState.Mode.PinLocation
ShareLocationEvent.DismissDialog -> permissionDialog = ShareLocationState.Dialog.None
ShareLocationEvent.OpenAppSettings -> {
locationActions.openSettings()
permissionDialog = ShareLocationState.Dialog.None
}
ShareLocationEvents.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions)
ShareLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions)
ShareLocationEvent.SelectLiveLocationDuration -> Unit
}
}
@@ -109,7 +110,7 @@ class ShareLocationPresenter(
}
private suspend fun shareLocation(
event: ShareLocationEvents.ShareLocation,
event: ShareLocationEvent.ShareStaticLocation,
mode: ShareLocationState.Mode,
) {
val replyMode = messageComposerContext.composerMode as? MessageComposerMode.Reply
@@ -168,8 +169,8 @@ class ShareLocationPresenter(
}
}
private fun ShareLocationEvents.ShareLocation.toGeoUri(): String = location?.toGeoUri() ?: cameraPosition.toGeoUri()
private fun ShareLocationEvent.ShareStaticLocation.toGeoUri(): String = location?.toGeoUri() ?: cameraPosition.toGeoUri()
private fun ShareLocationEvents.ShareLocation.CameraPosition.toGeoUri(): String = "geo:$lat,$lon"
private fun ShareLocationEvent.ShareStaticLocation.CameraPosition.toGeoUri(): String = "geo:$lat,$lon"
private fun generateBody(uri: String): String = "Location was shared at $uri"

View File

@@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.material3.rememberStandardBottomSheetState
@@ -31,24 +30,29 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.location.api.Location
import io.element.android.features.location.api.internal.centerBottomEdge
import io.element.android.features.location.api.internal.rememberTileStyleUrl
import io.element.android.features.location.impl.R
import io.element.android.features.location.impl.common.MapDefaults
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.ui.LocationFloatingActionButton
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementPreview
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.Icon
import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.maplibre.compose.CameraMode
import io.element.android.libraries.maplibre.compose.CameraMoveStartedReason
import io.element.android.libraries.maplibre.compose.CameraPositionState
import io.element.android.libraries.maplibre.compose.MapLibreMap
import io.element.android.libraries.maplibre.compose.rememberCameraPositionState
import io.element.android.libraries.ui.strings.CommonStrings
@@ -62,19 +66,19 @@ fun ShareLocationView(
modifier: Modifier = Modifier,
) {
LaunchedEffect(Unit) {
state.eventSink(ShareLocationEvents.RequestPermissions)
state.eventSink(ShareLocationEvent.RequestPermissions)
}
when (state.permissionDialog) {
ShareLocationState.Dialog.None -> Unit
ShareLocationState.Dialog.PermissionDenied -> PermissionDeniedDialog(
onContinue = { state.eventSink(ShareLocationEvents.OpenAppSettings) },
onDismiss = { state.eventSink(ShareLocationEvents.DismissDialog) },
onContinue = { state.eventSink(ShareLocationEvent.OpenAppSettings) },
onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) },
appName = state.appName,
)
ShareLocationState.Dialog.PermissionRationale -> PermissionRationaleDialog(
onContinue = { state.eventSink(ShareLocationEvents.RequestPermissions) },
onDismiss = { state.eventSink(ShareLocationEvents.DismissDialog) },
onContinue = { state.eventSink(ShareLocationEvent.RequestPermissions) },
onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) },
appName = state.appName,
)
}
@@ -99,7 +103,7 @@ fun ShareLocationView(
LaunchedEffect(cameraPositionState.isMoving) {
if (cameraPositionState.cameraMoveStartedReason == CameraMoveStartedReason.GESTURE) {
state.eventSink(ShareLocationEvents.SwitchToPinLocationMode)
state.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
}
}
@@ -108,48 +112,35 @@ fun ShareLocationView(
BottomSheetScaffold(
sheetContent = {
Spacer(modifier = Modifier.height(16.dp))
Spacer(Modifier.height(20.dp))
ListItem(
headlineContent = {
Text(
stringResource(
when (state.mode) {
ShareLocationState.Mode.PinLocation -> CommonStrings.screen_share_this_location_action
ShareLocationState.Mode.SenderLocation -> CommonStrings.screen_share_my_location_action
}
)
text = "Sharing options",
style = ElementTheme.typography.fontBodyLgMedium,
)
},
modifier = Modifier.clickable(
// target is null when the map hasn't loaded (or api key is wrong) so we disable the button
enabled = cameraPositionState.position.target != null
) {
state.eventSink(
ShareLocationEvents.ShareLocation(
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
lat = cameraPositionState.position.target!!.latitude,
lon = cameraPositionState.position.target!!.longitude,
zoom = cameraPositionState.position.zoom,
),
location = cameraPositionState.location?.let {
Location(
lat = it.latitude,
lon = it.longitude,
accuracy = it.accuracy,
)
}
)
)
navigateUp()
},
leadingContent = {
Icon(
resourceId = R.drawable.pin_small,
contentDescription = null,
tint = Color.Unspecified,
)
},
}
)
StaticLocationItem(state.mode, cameraPositionState){
val positionTarget = cameraPositionState.position.target ?: return@StaticLocationItem
state.eventSink(
ShareLocationEvent.ShareStaticLocation(
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
lat = positionTarget.latitude,
lon = positionTarget.longitude,
zoom = cameraPositionState.position.zoom,
),
location = cameraPositionState.location?.let {
Location(
lat = it.latitude,
lon = it.longitude,
accuracy = it.accuracy,
)
}
)
)
navigateUp()
}
Spacer(modifier = Modifier.height(16.dp + navBarPadding))
},
modifier = modifier,
@@ -191,7 +182,7 @@ fun ShareLocationView(
)
LocationFloatingActionButton(
isMapCenteredOnUser = state.mode == ShareLocationState.Mode.SenderLocation,
onClick = { state.eventSink(ShareLocationEvents.SwitchToMyLocationMode) },
onClick = { state.eventSink(ShareLocationEvent.SwitchToMyLocationMode) },
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(end = 18.dp, bottom = 72.dp + navBarPadding),
@@ -200,6 +191,52 @@ fun ShareLocationView(
}
}
@Composable
private fun StaticLocationItem(
mode: ShareLocationState.Mode,
cameraPositionState: CameraPositionState,
onClick: ()->Unit,
) {
ListItem(
headlineContent = {
Text(
stringResource(
when (mode) {
ShareLocationState.Mode.PinLocation -> CommonStrings.screen_share_this_location_action
ShareLocationState.Mode.SenderLocation -> CommonStrings.screen_share_my_location_action
}
)
)
},
modifier = Modifier.clickable(
// target is null when the map hasn't loaded (or api key is wrong) so we disable the button
enabled = cameraPositionState.position.target != null,
onClick = onClick
),
leadingContent = ListItemContent.Icon(
iconSource = IconSource.Vector(CompoundIcons.LocationNavigatorCentred())
)
)
}
@Composable
private fun LiveLocationItem(
onClick: ()->Unit,
) {
ListItem(
headlineContent = {
Text("Share live location")
},
modifier = Modifier.clickable(
onClick = onClick
),
leadingContent = ListItemContent.Icon(
iconSource = IconSource.Vector(CompoundIcons.LocationPinSolid()),
tintColor = ElementTheme.colors.iconAccentPrimary,
)
)
}
@PreviewsDayNight
@Composable
internal fun ShareLocationViewPreview(

View File

@@ -83,7 +83,7 @@ class ShareLocationPresenterTest {
assertThat(initialState.hasLocationPermission).isTrue()
// Swipe the map to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToPinLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
@@ -110,7 +110,7 @@ class ShareLocationPresenterTest {
assertThat(initialState.hasLocationPermission).isTrue()
// Swipe the map to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToPinLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToPinLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
@@ -137,7 +137,7 @@ class ShareLocationPresenterTest {
assertThat(initialState.hasLocationPermission).isFalse()
// Click on the button to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
@@ -164,7 +164,7 @@ class ShareLocationPresenterTest {
assertThat(initialState.hasLocationPermission).isFalse()
// Click on the button to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
@@ -189,14 +189,14 @@ class ShareLocationPresenterTest {
val initialState = awaitItem()
// Click on the button to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog
myLocationState.eventSink(ShareLocationEvents.DismissDialog)
myLocationState.eventSink(ShareLocationEvent.DismissDialog)
val dialogDismissedState = awaitItem()
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
@@ -221,14 +221,14 @@ class ShareLocationPresenterTest {
val initialState = awaitItem()
// Click on the button to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
// Continue the dialog sends permission request to the permissions presenter
myLocationState.eventSink(ShareLocationEvents.RequestPermissions)
myLocationState.eventSink(ShareLocationEvent.RequestPermissions)
assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
}
}
@@ -250,14 +250,14 @@ class ShareLocationPresenterTest {
val initialState = awaitItem()
// Click on the button to switch mode
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val myLocationState = awaitItem()
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog
myLocationState.eventSink(ShareLocationEvents.DismissDialog)
myLocationState.eventSink(ShareLocationEvent.DismissDialog)
val dialogDismissedState = awaitItem()
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
@@ -291,8 +291,8 @@ class ShareLocationPresenterTest {
// Send location
initialState.eventSink(
ShareLocationEvents.ShareLocation(
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
ShareLocationEvent.ShareStaticLocation(
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
lat = 0.0,
lon = 1.0,
zoom = 2.0,
@@ -355,8 +355,8 @@ class ShareLocationPresenterTest {
// Send location
initialState.eventSink(
ShareLocationEvents.ShareLocation(
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
ShareLocationEvent.ShareStaticLocation(
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
lat = 0.0,
lon = 1.0,
zoom = 2.0,
@@ -425,8 +425,8 @@ class ShareLocationPresenterTest {
// Send location
initialState.eventSink(
ShareLocationEvents.ShareLocation(
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
ShareLocationEvent.ShareStaticLocation(
cameraPosition = ShareLocationEvent.ShareStaticLocation.CameraPosition(
lat = 0.0,
lon = 1.0,
zoom = 2.0,
@@ -471,11 +471,11 @@ class ShareLocationPresenterTest {
// Skip initial state
val initialState = awaitItem()
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
initialState.eventSink(ShareLocationEvent.SwitchToMyLocationMode)
val dialogShownState = awaitItem()
// Open settings
dialogShownState.eventSink(ShareLocationEvents.OpenAppSettings)
dialogShownState.eventSink(ShareLocationEvent.OpenAppSettings)
val settingsOpenedState = awaitItem()
assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)