Rename ShareLocationEvents -> ShareLocationEvent
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user