Rename send location to share location
This commit is contained in:
parent
1f5a628b13
commit
8c64f704cb
14 changed files with 223 additions and 223 deletions
|
|
@ -14,11 +14,11 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "Send location" screen.
|
* The "Share location" screen.
|
||||||
*
|
*
|
||||||
* Allows a user to share a location message within a room.
|
* Allows a user to share a location message within a room.
|
||||||
*/
|
*/
|
||||||
interface SendLocationEntryPoint : FeatureEntryPoint {
|
interface ShareLocationEntryPoint : FeatureEntryPoint {
|
||||||
fun createNode(
|
fun createNode(
|
||||||
parentNode: Node,
|
parentNode: Node,
|
||||||
buildContext: BuildContext,
|
buildContext: BuildContext,
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Element Creations Ltd.
|
|
||||||
* Copyright 2023-2025 New Vector 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.send
|
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
||||||
|
|
||||||
private const val APP_NAME = "ApplicationName"
|
|
||||||
|
|
||||||
class SendLocationStateProvider : PreviewParameterProvider<SendLocationState> {
|
|
||||||
override val values: Sequence<SendLocationState>
|
|
||||||
get() = sequenceOf(
|
|
||||||
aSendLocationState(
|
|
||||||
permissionDialog = SendLocationState.Dialog.None,
|
|
||||||
mode = SendLocationState.Mode.PinLocation,
|
|
||||||
hasLocationPermission = false,
|
|
||||||
),
|
|
||||||
aSendLocationState(
|
|
||||||
permissionDialog = SendLocationState.Dialog.PermissionDenied,
|
|
||||||
mode = SendLocationState.Mode.PinLocation,
|
|
||||||
hasLocationPermission = false,
|
|
||||||
),
|
|
||||||
aSendLocationState(
|
|
||||||
permissionDialog = SendLocationState.Dialog.PermissionRationale,
|
|
||||||
mode = SendLocationState.Mode.PinLocation,
|
|
||||||
hasLocationPermission = false,
|
|
||||||
),
|
|
||||||
aSendLocationState(
|
|
||||||
permissionDialog = SendLocationState.Dialog.None,
|
|
||||||
mode = SendLocationState.Mode.PinLocation,
|
|
||||||
hasLocationPermission = true,
|
|
||||||
),
|
|
||||||
aSendLocationState(
|
|
||||||
permissionDialog = SendLocationState.Dialog.None,
|
|
||||||
mode = SendLocationState.Mode.SenderLocation,
|
|
||||||
hasLocationPermission = true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun aSendLocationState(
|
|
||||||
permissionDialog: SendLocationState.Dialog,
|
|
||||||
mode: SendLocationState.Mode,
|
|
||||||
hasLocationPermission: Boolean,
|
|
||||||
): SendLocationState {
|
|
||||||
return SendLocationState(
|
|
||||||
permissionDialog = permissionDialog,
|
|
||||||
mode = mode,
|
|
||||||
hasLocationPermission = hasLocationPermission,
|
|
||||||
appName = APP_NAME,
|
|
||||||
eventSink = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -6,26 +6,26 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import dev.zacsweers.metro.AppScope
|
import dev.zacsweers.metro.AppScope
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import io.element.android.features.location.api.SendLocationEntryPoint
|
import io.element.android.features.location.api.ShareLocationEntryPoint
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultSendLocationEntryPoint : SendLocationEntryPoint {
|
class DefaultShareLocationEntryPoint : ShareLocationEntryPoint {
|
||||||
override fun createNode(
|
override fun createNode(
|
||||||
parentNode: Node,
|
parentNode: Node,
|
||||||
buildContext: BuildContext,
|
buildContext: BuildContext,
|
||||||
timelineMode: Timeline.Mode,
|
timelineMode: Timeline.Mode,
|
||||||
): Node {
|
): Node {
|
||||||
return parentNode.createNode<SendLocationNode>(
|
return parentNode.createNode<ShareLocationNode>(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = listOf(SendLocationNode.Inputs(timelineMode))
|
plugins = listOf(ShareLocationNode.Inputs(timelineMode))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,15 +6,15 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
|
|
||||||
sealed interface SendLocationEvents {
|
sealed interface ShareLocationEvents {
|
||||||
data class SendLocation(
|
data class ShareLocation(
|
||||||
val cameraPosition: CameraPosition,
|
val cameraPosition: CameraPosition,
|
||||||
val location: Location?,
|
val location: Location?,
|
||||||
) : SendLocationEvents {
|
) : ShareLocationEvents {
|
||||||
data class CameraPosition(
|
data class CameraPosition(
|
||||||
val lat: Double,
|
val lat: Double,
|
||||||
val lon: Double,
|
val lon: Double,
|
||||||
|
|
@ -22,9 +22,9 @@ sealed interface SendLocationEvents {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data object SwitchToMyLocationMode : SendLocationEvents
|
data object SwitchToMyLocationMode : ShareLocationEvents
|
||||||
data object SwitchToPinLocationMode : SendLocationEvents
|
data object SwitchToPinLocationMode : ShareLocationEvents
|
||||||
data object DismissDialog : SendLocationEvents
|
data object DismissDialog : ShareLocationEvents
|
||||||
data object RequestPermissions : SendLocationEvents
|
data object RequestPermissions : ShareLocationEvents
|
||||||
data object OpenAppSettings : SendLocationEvents
|
data object OpenAppSettings : ShareLocationEvents
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -26,10 +26,10 @@ import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
||||||
@ContributesNode(RoomScope::class)
|
@ContributesNode(RoomScope::class)
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class SendLocationNode(
|
class ShareLocationNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
presenterFactory: SendLocationPresenter.Factory,
|
presenterFactory: ShareLocationPresenter.Factory,
|
||||||
analyticsService: AnalyticsService,
|
analyticsService: AnalyticsService,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
|
|
@ -48,7 +48,7 @@ class SendLocationNode(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
SendLocationView(
|
ShareLocationView(
|
||||||
state = presenter.present(),
|
state = presenter.present(),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
navigateUp = ::navigateUp,
|
navigateUp = ::navigateUp,
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
|
@ -38,7 +38,7 @@ import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class SendLocationPresenter(
|
class ShareLocationPresenter(
|
||||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||||
private val room: JoinedRoom,
|
private val room: JoinedRoom,
|
||||||
@Assisted private val timelineMode: Timeline.Mode,
|
@Assisted private val timelineMode: Timeline.Mode,
|
||||||
|
|
@ -46,60 +46,60 @@ class SendLocationPresenter(
|
||||||
private val messageComposerContext: MessageComposerContext,
|
private val messageComposerContext: MessageComposerContext,
|
||||||
private val locationActions: LocationActions,
|
private val locationActions: LocationActions,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) : Presenter<SendLocationState> {
|
) : Presenter<ShareLocationState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
fun interface Factory {
|
fun interface Factory {
|
||||||
fun create(timelineMode: Timeline.Mode): SendLocationPresenter
|
fun create(timelineMode: Timeline.Mode): ShareLocationPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
private val permissionsPresenter = permissionsPresenterFactory.create(MapDefaults.permissions)
|
private val permissionsPresenter = permissionsPresenterFactory.create(MapDefaults.permissions)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): SendLocationState {
|
override fun present(): ShareLocationState {
|
||||||
val permissionsState: PermissionsState = permissionsPresenter.present()
|
val permissionsState: PermissionsState = permissionsPresenter.present()
|
||||||
var mode: SendLocationState.Mode by remember {
|
var mode: ShareLocationState.Mode by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
if (permissionsState.isAnyGranted) {
|
if (permissionsState.isAnyGranted) {
|
||||||
SendLocationState.Mode.SenderLocation
|
ShareLocationState.Mode.SenderLocation
|
||||||
} else {
|
} else {
|
||||||
SendLocationState.Mode.PinLocation
|
ShareLocationState.Mode.PinLocation
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val appName by remember { derivedStateOf { buildMeta.applicationName } }
|
val appName by remember { derivedStateOf { buildMeta.applicationName } }
|
||||||
var permissionDialog: SendLocationState.Dialog by remember {
|
var permissionDialog: ShareLocationState.Dialog by remember {
|
||||||
mutableStateOf(SendLocationState.Dialog.None)
|
mutableStateOf(ShareLocationState.Dialog.None)
|
||||||
}
|
}
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
LaunchedEffect(permissionsState.permissions) {
|
LaunchedEffect(permissionsState.permissions) {
|
||||||
if (permissionsState.isAnyGranted) {
|
if (permissionsState.isAnyGranted) {
|
||||||
mode = SendLocationState.Mode.SenderLocation
|
mode = ShareLocationState.Mode.SenderLocation
|
||||||
permissionDialog = SendLocationState.Dialog.None
|
permissionDialog = ShareLocationState.Dialog.None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvent(event: SendLocationEvents) {
|
fun handleEvent(event: ShareLocationEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is SendLocationEvents.SendLocation -> scope.launch {
|
is ShareLocationEvents.ShareLocation -> scope.launch {
|
||||||
sendLocation(event, mode)
|
shareLocation(event, mode)
|
||||||
}
|
}
|
||||||
SendLocationEvents.SwitchToMyLocationMode -> when {
|
ShareLocationEvents.SwitchToMyLocationMode -> when {
|
||||||
permissionsState.isAnyGranted -> mode = SendLocationState.Mode.SenderLocation
|
permissionsState.isAnyGranted -> mode = ShareLocationState.Mode.SenderLocation
|
||||||
permissionsState.shouldShowRationale -> permissionDialog = SendLocationState.Dialog.PermissionRationale
|
permissionsState.shouldShowRationale -> permissionDialog = ShareLocationState.Dialog.PermissionRationale
|
||||||
else -> permissionDialog = SendLocationState.Dialog.PermissionDenied
|
else -> permissionDialog = ShareLocationState.Dialog.PermissionDenied
|
||||||
}
|
}
|
||||||
SendLocationEvents.SwitchToPinLocationMode -> mode = SendLocationState.Mode.PinLocation
|
ShareLocationEvents.SwitchToPinLocationMode -> mode = ShareLocationState.Mode.PinLocation
|
||||||
SendLocationEvents.DismissDialog -> permissionDialog = SendLocationState.Dialog.None
|
ShareLocationEvents.DismissDialog -> permissionDialog = ShareLocationState.Dialog.None
|
||||||
SendLocationEvents.OpenAppSettings -> {
|
ShareLocationEvents.OpenAppSettings -> {
|
||||||
locationActions.openSettings()
|
locationActions.openSettings()
|
||||||
permissionDialog = SendLocationState.Dialog.None
|
permissionDialog = ShareLocationState.Dialog.None
|
||||||
}
|
}
|
||||||
SendLocationEvents.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
ShareLocationEvents.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SendLocationState(
|
return ShareLocationState(
|
||||||
permissionDialog = permissionDialog,
|
permissionDialog = permissionDialog,
|
||||||
mode = mode,
|
mode = mode,
|
||||||
hasLocationPermission = permissionsState.isAnyGranted,
|
hasLocationPermission = permissionsState.isAnyGranted,
|
||||||
|
|
@ -108,14 +108,14 @@ class SendLocationPresenter(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendLocation(
|
private suspend fun shareLocation(
|
||||||
event: SendLocationEvents.SendLocation,
|
event: ShareLocationEvents.ShareLocation,
|
||||||
mode: SendLocationState.Mode,
|
mode: ShareLocationState.Mode,
|
||||||
) {
|
) {
|
||||||
val replyMode = messageComposerContext.composerMode as? MessageComposerMode.Reply
|
val replyMode = messageComposerContext.composerMode as? MessageComposerMode.Reply
|
||||||
val inReplyToEventId = replyMode?.eventId
|
val inReplyToEventId = replyMode?.eventId
|
||||||
when (mode) {
|
when (mode) {
|
||||||
SendLocationState.Mode.PinLocation -> {
|
ShareLocationState.Mode.PinLocation -> {
|
||||||
val geoUri = event.cameraPosition.toGeoUri()
|
val geoUri = event.cameraPosition.toGeoUri()
|
||||||
getTimeline().flatMap {
|
getTimeline().flatMap {
|
||||||
it.sendLocation(
|
it.sendLocation(
|
||||||
|
|
@ -136,7 +136,7 @@ class SendLocationPresenter(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SendLocationState.Mode.SenderLocation -> {
|
ShareLocationState.Mode.SenderLocation -> {
|
||||||
val geoUri = event.toGeoUri()
|
val geoUri = event.toGeoUri()
|
||||||
getTimeline().flatMap {
|
getTimeline().flatMap {
|
||||||
it.sendLocation(
|
it.sendLocation(
|
||||||
|
|
@ -168,8 +168,8 @@ class SendLocationPresenter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SendLocationEvents.SendLocation.toGeoUri(): String = location?.toGeoUri() ?: cameraPosition.toGeoUri()
|
private fun ShareLocationEvents.ShareLocation.toGeoUri(): String = location?.toGeoUri() ?: cameraPosition.toGeoUri()
|
||||||
|
|
||||||
private fun SendLocationEvents.SendLocation.CameraPosition.toGeoUri(): String = "geo:$lat,$lon"
|
private fun ShareLocationEvents.ShareLocation.CameraPosition.toGeoUri(): String = "geo:$lat,$lon"
|
||||||
|
|
||||||
private fun generateBody(uri: String): String = "Location was shared at $uri"
|
private fun generateBody(uri: String): String = "Location was shared at $uri"
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
data class SendLocationState(
|
data class ShareLocationState(
|
||||||
val permissionDialog: Dialog,
|
val permissionDialog: Dialog,
|
||||||
val mode: Mode,
|
val mode: Mode,
|
||||||
val hasLocationPermission: Boolean,
|
val hasLocationPermission: Boolean,
|
||||||
val appName: String,
|
val appName: String,
|
||||||
val eventSink: (SendLocationEvents) -> Unit,
|
val eventSink: (ShareLocationEvents) -> Unit,
|
||||||
) {
|
) {
|
||||||
sealed interface Mode {
|
sealed interface Mode {
|
||||||
data object SenderLocation : Mode
|
data object SenderLocation : Mode
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Element Creations Ltd.
|
||||||
|
* Copyright 2023-2025 New Vector 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 androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
|
||||||
|
private const val APP_NAME = "ApplicationName"
|
||||||
|
|
||||||
|
class ShareLocationStateProvider : PreviewParameterProvider<ShareLocationState> {
|
||||||
|
override val values: Sequence<ShareLocationState>
|
||||||
|
get() = sequenceOf(
|
||||||
|
aShareLocationState(
|
||||||
|
permissionDialog = ShareLocationState.Dialog.None,
|
||||||
|
mode = ShareLocationState.Mode.PinLocation,
|
||||||
|
hasLocationPermission = false,
|
||||||
|
),
|
||||||
|
aShareLocationState(
|
||||||
|
permissionDialog = ShareLocationState.Dialog.PermissionDenied,
|
||||||
|
mode = ShareLocationState.Mode.PinLocation,
|
||||||
|
hasLocationPermission = false,
|
||||||
|
),
|
||||||
|
aShareLocationState(
|
||||||
|
permissionDialog = ShareLocationState.Dialog.PermissionRationale,
|
||||||
|
mode = ShareLocationState.Mode.PinLocation,
|
||||||
|
hasLocationPermission = false,
|
||||||
|
),
|
||||||
|
aShareLocationState(
|
||||||
|
permissionDialog = ShareLocationState.Dialog.None,
|
||||||
|
mode = ShareLocationState.Mode.PinLocation,
|
||||||
|
hasLocationPermission = true,
|
||||||
|
),
|
||||||
|
aShareLocationState(
|
||||||
|
permissionDialog = ShareLocationState.Dialog.None,
|
||||||
|
mode = ShareLocationState.Mode.SenderLocation,
|
||||||
|
hasLocationPermission = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun aShareLocationState(
|
||||||
|
permissionDialog: ShareLocationState.Dialog,
|
||||||
|
mode: ShareLocationState.Mode,
|
||||||
|
hasLocationPermission: Boolean,
|
||||||
|
): ShareLocationState {
|
||||||
|
return ShareLocationState(
|
||||||
|
permissionDialog = permissionDialog,
|
||||||
|
mode = mode,
|
||||||
|
hasLocationPermission = hasLocationPermission,
|
||||||
|
appName = APP_NAME,
|
||||||
|
eventSink = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
@ -56,25 +56,25 @@ import org.maplibre.android.camera.CameraPosition
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SendLocationView(
|
fun ShareLocationView(
|
||||||
state: SendLocationState,
|
state: ShareLocationState,
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
state.eventSink(SendLocationEvents.RequestPermissions)
|
state.eventSink(ShareLocationEvents.RequestPermissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (state.permissionDialog) {
|
when (state.permissionDialog) {
|
||||||
SendLocationState.Dialog.None -> Unit
|
ShareLocationState.Dialog.None -> Unit
|
||||||
SendLocationState.Dialog.PermissionDenied -> PermissionDeniedDialog(
|
ShareLocationState.Dialog.PermissionDenied -> PermissionDeniedDialog(
|
||||||
onContinue = { state.eventSink(SendLocationEvents.OpenAppSettings) },
|
onContinue = { state.eventSink(ShareLocationEvents.OpenAppSettings) },
|
||||||
onDismiss = { state.eventSink(SendLocationEvents.DismissDialog) },
|
onDismiss = { state.eventSink(ShareLocationEvents.DismissDialog) },
|
||||||
appName = state.appName,
|
appName = state.appName,
|
||||||
)
|
)
|
||||||
SendLocationState.Dialog.PermissionRationale -> PermissionRationaleDialog(
|
ShareLocationState.Dialog.PermissionRationale -> PermissionRationaleDialog(
|
||||||
onContinue = { state.eventSink(SendLocationEvents.RequestPermissions) },
|
onContinue = { state.eventSink(ShareLocationEvents.RequestPermissions) },
|
||||||
onDismiss = { state.eventSink(SendLocationEvents.DismissDialog) },
|
onDismiss = { state.eventSink(ShareLocationEvents.DismissDialog) },
|
||||||
appName = state.appName,
|
appName = state.appName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -85,10 +85,10 @@ fun SendLocationView(
|
||||||
|
|
||||||
LaunchedEffect(state.mode) {
|
LaunchedEffect(state.mode) {
|
||||||
when (state.mode) {
|
when (state.mode) {
|
||||||
SendLocationState.Mode.PinLocation -> {
|
ShareLocationState.Mode.PinLocation -> {
|
||||||
cameraPositionState.cameraMode = CameraMode.NONE
|
cameraPositionState.cameraMode = CameraMode.NONE
|
||||||
}
|
}
|
||||||
SendLocationState.Mode.SenderLocation -> {
|
ShareLocationState.Mode.SenderLocation -> {
|
||||||
cameraPositionState.position = CameraPosition.Builder()
|
cameraPositionState.position = CameraPosition.Builder()
|
||||||
.zoom(MapDefaults.DEFAULT_ZOOM)
|
.zoom(MapDefaults.DEFAULT_ZOOM)
|
||||||
.build()
|
.build()
|
||||||
|
|
@ -99,7 +99,7 @@ fun SendLocationView(
|
||||||
|
|
||||||
LaunchedEffect(cameraPositionState.isMoving) {
|
LaunchedEffect(cameraPositionState.isMoving) {
|
||||||
if (cameraPositionState.cameraMoveStartedReason == CameraMoveStartedReason.GESTURE) {
|
if (cameraPositionState.cameraMoveStartedReason == CameraMoveStartedReason.GESTURE) {
|
||||||
state.eventSink(SendLocationEvents.SwitchToPinLocationMode)
|
state.eventSink(ShareLocationEvents.SwitchToPinLocationMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,8 +114,8 @@ fun SendLocationView(
|
||||||
Text(
|
Text(
|
||||||
stringResource(
|
stringResource(
|
||||||
when (state.mode) {
|
when (state.mode) {
|
||||||
SendLocationState.Mode.PinLocation -> CommonStrings.screen_share_this_location_action
|
ShareLocationState.Mode.PinLocation -> CommonStrings.screen_share_this_location_action
|
||||||
SendLocationState.Mode.SenderLocation -> CommonStrings.screen_share_my_location_action
|
ShareLocationState.Mode.SenderLocation -> CommonStrings.screen_share_my_location_action
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -125,8 +125,8 @@ fun SendLocationView(
|
||||||
enabled = cameraPositionState.position.target != null
|
enabled = cameraPositionState.position.target != null
|
||||||
) {
|
) {
|
||||||
state.eventSink(
|
state.eventSink(
|
||||||
SendLocationEvents.SendLocation(
|
ShareLocationEvents.ShareLocation(
|
||||||
cameraPosition = SendLocationEvents.SendLocation.CameraPosition(
|
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
|
||||||
lat = cameraPositionState.position.target!!.latitude,
|
lat = cameraPositionState.position.target!!.latitude,
|
||||||
lon = cameraPositionState.position.target!!.longitude,
|
lon = cameraPositionState.position.target!!.longitude,
|
||||||
zoom = cameraPositionState.position.zoom,
|
zoom = cameraPositionState.position.zoom,
|
||||||
|
|
@ -190,8 +190,8 @@ fun SendLocationView(
|
||||||
modifier = Modifier.centerBottomEdge(this),
|
modifier = Modifier.centerBottomEdge(this),
|
||||||
)
|
)
|
||||||
LocationFloatingActionButton(
|
LocationFloatingActionButton(
|
||||||
isMapCenteredOnUser = state.mode == SendLocationState.Mode.SenderLocation,
|
isMapCenteredOnUser = state.mode == ShareLocationState.Mode.SenderLocation,
|
||||||
onClick = { state.eventSink(SendLocationEvents.SwitchToMyLocationMode) },
|
onClick = { state.eventSink(ShareLocationEvents.SwitchToMyLocationMode) },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
.padding(end = 18.dp, bottom = 72.dp + navBarPadding),
|
.padding(end = 18.dp, bottom = 72.dp + navBarPadding),
|
||||||
|
|
@ -202,10 +202,10 @@ fun SendLocationView(
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SendLocationViewPreview(
|
internal fun ShareLocationViewPreview(
|
||||||
@PreviewParameter(SendLocationStateProvider::class) state: SendLocationState
|
@PreviewParameter(ShareLocationStateProvider::class) state: ShareLocationState
|
||||||
) = ElementPreview {
|
) = ElementPreview {
|
||||||
SendLocationView(
|
ShareLocationView(
|
||||||
state = state,
|
state = state,
|
||||||
navigateUp = {},
|
navigateUp = {},
|
||||||
)
|
)
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
|
|
@ -22,19 +22,19 @@ import io.element.android.tests.testutils.node.TestParentNode
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class DefaultSendLocationEntryPointTest {
|
class DefaultShareLocationEntryPointTest {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test node builder`() {
|
fun `test node builder`() {
|
||||||
val entryPoint = DefaultSendLocationEntryPoint()
|
val entryPoint = DefaultShareLocationEntryPoint()
|
||||||
val parentNode = TestParentNode.create { buildContext, plugins ->
|
val parentNode = TestParentNode.create { buildContext, plugins ->
|
||||||
SendLocationNode(
|
ShareLocationNode(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
presenterFactory = { timelineMode: Timeline.Mode ->
|
presenterFactory = { timelineMode: Timeline.Mode ->
|
||||||
SendLocationPresenter(
|
ShareLocationPresenter(
|
||||||
permissionsPresenterFactory = { FakePermissionsPresenter() },
|
permissionsPresenterFactory = { FakePermissionsPresenter() },
|
||||||
room = FakeJoinedRoom(),
|
room = FakeJoinedRoom(),
|
||||||
timelineMode = timelineMode,
|
timelineMode = timelineMode,
|
||||||
|
|
@ -53,7 +53,7 @@ class DefaultSendLocationEntryPointTest {
|
||||||
buildContext = BuildContext.root(null),
|
buildContext = BuildContext.root(null),
|
||||||
timelineMode = timelineMode,
|
timelineMode = timelineMode,
|
||||||
)
|
)
|
||||||
assertThat(result).isInstanceOf(SendLocationNode::class.java)
|
assertThat(result).isInstanceOf(ShareLocationNode::class.java)
|
||||||
assertThat(result.plugins).contains(SendLocationNode.Inputs(timelineMode))
|
assertThat(result.plugins).contains(ShareLocationNode.Inputs(timelineMode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.location.impl.send
|
package io.element.android.features.location.impl.share
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionMode
|
import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
|
|
@ -40,7 +40,7 @@ import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class SendLocationPresenterTest {
|
class ShareLocationPresenterTest {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val warmUpRule = WarmUpRule()
|
val warmUpRule = WarmUpRule()
|
||||||
|
|
||||||
|
|
@ -50,9 +50,9 @@ class SendLocationPresenterTest {
|
||||||
private val fakeLocationActions = FakeLocationActions()
|
private val fakeLocationActions = FakeLocationActions()
|
||||||
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
||||||
|
|
||||||
private fun createSendLocationPresenter(
|
private fun createShareLocationPresenter(
|
||||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||||
): SendLocationPresenter = SendLocationPresenter(
|
): ShareLocationPresenter = ShareLocationPresenter(
|
||||||
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
permissionsPresenterFactory = object : PermissionsPresenter.Factory {
|
||||||
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
override fun create(permissions: List<String>): PermissionsPresenter = fakePermissionsPresenter
|
||||||
},
|
},
|
||||||
|
|
@ -66,7 +66,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions granted`() = runTest {
|
fun `initial state with permissions granted`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.AllGranted,
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
|
@ -75,25 +75,25 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(initialState.mode).isEqualTo(SendLocationState.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(SendLocationEvents.SwitchToPinLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToPinLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(myLocationState.hasLocationPermission).isTrue()
|
assertThat(myLocationState.hasLocationPermission).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions partially granted`() = runTest {
|
fun `initial state with permissions partially granted`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.SomeGranted,
|
permissions = PermissionsState.Permissions.SomeGranted,
|
||||||
|
|
@ -102,25 +102,25 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(initialState.mode).isEqualTo(SendLocationState.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(SendLocationEvents.SwitchToPinLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToPinLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(myLocationState.hasLocationPermission).isTrue()
|
assertThat(myLocationState.hasLocationPermission).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions denied`() = runTest {
|
fun `initial state with permissions denied`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -129,25 +129,25 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(initialState.mode).isEqualTo(SendLocationState.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(SendLocationEvents.SwitchToMyLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
assertThat(myLocationState.hasLocationPermission).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions denied once`() = runTest {
|
fun `initial state with permissions denied once`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -156,25 +156,25 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(initialState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(initialState.mode).isEqualTo(SendLocationState.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(SendLocationEvents.SwitchToMyLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
assertThat(myLocationState.hasLocationPermission).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `rationale dialog dismiss`() = runTest {
|
fun `rationale dialog dismiss`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -183,30 +183,30 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Click on the button to switch mode
|
// Click on the button to switch mode
|
||||||
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.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(SendLocationEvents.DismissDialog)
|
myLocationState.eventSink(ShareLocationEvents.DismissDialog)
|
||||||
val dialogDismissedState = awaitItem()
|
val dialogDismissedState = awaitItem()
|
||||||
assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
|
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `rationale dialog continue`() = runTest {
|
fun `rationale dialog continue`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -215,27 +215,27 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Click on the button to switch mode
|
// Click on the button to switch mode
|
||||||
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionRationale)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(myLocationState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(myLocationState.hasLocationPermission).isFalse()
|
assertThat(myLocationState.hasLocationPermission).isFalse()
|
||||||
|
|
||||||
// Continue the dialog sends permission request to the permissions presenter
|
// Continue the dialog sends permission request to the permissions presenter
|
||||||
myLocationState.eventSink(SendLocationEvents.RequestPermissions)
|
myLocationState.eventSink(ShareLocationEvents.RequestPermissions)
|
||||||
assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
|
assertThat(fakePermissionsPresenter.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `permission denied dialog dismiss`() = runTest {
|
fun `permission denied dialog dismiss`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -244,23 +244,23 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Click on the button to switch mode
|
// Click on the button to switch mode
|
||||||
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
|
||||||
val myLocationState = awaitItem()
|
val myLocationState = awaitItem()
|
||||||
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied)
|
assertThat(myLocationState.permissionDialog).isEqualTo(ShareLocationState.Dialog.PermissionDenied)
|
||||||
assertThat(myLocationState.mode).isEqualTo(SendLocationState.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(SendLocationEvents.DismissDialog)
|
myLocationState.eventSink(ShareLocationEvents.DismissDialog)
|
||||||
val dialogDismissedState = awaitItem()
|
val dialogDismissedState = awaitItem()
|
||||||
assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
|
assertThat(dialogDismissedState.mode).isEqualTo(ShareLocationState.Mode.PinLocation)
|
||||||
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
|
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +275,7 @@ class SendLocationPresenterTest {
|
||||||
sendLocationLambda = sendLocationResult
|
sendLocationLambda = sendLocationResult
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
val sendLocationPresenter = createSendLocationPresenter(joinedRoom)
|
val shareLocationPresenter = createShareLocationPresenter(joinedRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.AllGranted,
|
permissions = PermissionsState.Permissions.AllGranted,
|
||||||
|
|
@ -284,15 +284,15 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Send location
|
// Send location
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
SendLocationEvents.SendLocation(
|
ShareLocationEvents.ShareLocation(
|
||||||
cameraPosition = SendLocationEvents.SendLocation.CameraPosition(
|
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
|
||||||
lat = 0.0,
|
lat = 0.0,
|
||||||
lon = 1.0,
|
lon = 1.0,
|
||||||
zoom = 2.0,
|
zoom = 2.0,
|
||||||
|
|
@ -339,7 +339,7 @@ class SendLocationPresenterTest {
|
||||||
sendLocationLambda = sendLocationResult
|
sendLocationLambda = sendLocationResult
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
val sendLocationPresenter = createSendLocationPresenter(joinedRoom)
|
val shareLocationPresenter = createShareLocationPresenter(joinedRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -348,15 +348,15 @@ class SendLocationPresenterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Send location
|
// Send location
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
SendLocationEvents.SendLocation(
|
ShareLocationEvents.ShareLocation(
|
||||||
cameraPosition = SendLocationEvents.SendLocation.CameraPosition(
|
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
|
||||||
lat = 0.0,
|
lat = 0.0,
|
||||||
lon = 1.0,
|
lon = 1.0,
|
||||||
zoom = 2.0,
|
zoom = 2.0,
|
||||||
|
|
@ -403,7 +403,7 @@ class SendLocationPresenterTest {
|
||||||
sendLocationLambda = sendLocationResult
|
sendLocationLambda = sendLocationResult
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
val sendLocationPresenter = createSendLocationPresenter(joinedRoom)
|
val shareLocationPresenter = createShareLocationPresenter(joinedRoom)
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -418,15 +418,15 @@ class SendLocationPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
// Send location
|
// Send location
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
SendLocationEvents.SendLocation(
|
ShareLocationEvents.ShareLocation(
|
||||||
cameraPosition = SendLocationEvents.SendLocation.CameraPosition(
|
cameraPosition = ShareLocationEvents.ShareLocation.CameraPosition(
|
||||||
lat = 0.0,
|
lat = 0.0,
|
||||||
lon = 1.0,
|
lon = 1.0,
|
||||||
zoom = 2.0,
|
zoom = 2.0,
|
||||||
|
|
@ -451,7 +451,7 @@ class SendLocationPresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `open settings activity`() = runTest {
|
fun `open settings activity`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
aPermissionsState(
|
aPermissionsState(
|
||||||
permissions = PermissionsState.Permissions.NoneGranted,
|
permissions = PermissionsState.Permissions.NoneGranted,
|
||||||
|
|
@ -466,28 +466,28 @@ class SendLocationPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
// Skip initial state
|
// Skip initial state
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
|
||||||
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
|
initialState.eventSink(ShareLocationEvents.SwitchToMyLocationMode)
|
||||||
val dialogShownState = awaitItem()
|
val dialogShownState = awaitItem()
|
||||||
|
|
||||||
// Open settings
|
// Open settings
|
||||||
dialogShownState.eventSink(SendLocationEvents.OpenAppSettings)
|
dialogShownState.eventSink(ShareLocationEvents.OpenAppSettings)
|
||||||
val settingsOpenedState = awaitItem()
|
val settingsOpenedState = awaitItem()
|
||||||
|
|
||||||
assertThat(settingsOpenedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
|
assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShareLocationState.Dialog.None)
|
||||||
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
|
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `application name is in state`() = runTest {
|
fun `application name is in state`() = runTest {
|
||||||
val sendLocationPresenter = createSendLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
sendLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.appName).isEqualTo("app name")
|
assertThat(initialState.appName).isEqualTo("app name")
|
||||||
|
|
@ -10,11 +10,11 @@ package io.element.android.features.location.test
|
||||||
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import io.element.android.features.location.api.SendLocationEntryPoint
|
import io.element.android.features.location.api.ShareLocationEntryPoint
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
|
|
||||||
class FakeSendLocationEntryPoint : SendLocationEntryPoint {
|
class FakeShareLocationEntryPoint : ShareLocationEntryPoint {
|
||||||
override fun createNode(
|
override fun createNode(
|
||||||
parentNode: Node,
|
parentNode: Node,
|
||||||
buildContext: BuildContext,
|
buildContext: BuildContext,
|
||||||
|
|
@ -30,7 +30,7 @@ import io.element.android.features.forward.api.ForwardEntryPoint
|
||||||
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
|
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.api.LocationService
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.api.SendLocationEntryPoint
|
import io.element.android.features.location.api.ShareLocationEntryPoint
|
||||||
import io.element.android.features.location.api.ShowLocationEntryPoint
|
import io.element.android.features.location.api.ShowLocationEntryPoint
|
||||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
import io.element.android.features.messages.impl.attachments.Attachment
|
import io.element.android.features.messages.impl.attachments.Attachment
|
||||||
|
|
@ -102,7 +102,7 @@ class MessagesFlowNode(
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val roomListService: RoomListService,
|
private val roomListService: RoomListService,
|
||||||
private val sessionId: SessionId,
|
private val sessionId: SessionId,
|
||||||
private val sendLocationEntryPoint: SendLocationEntryPoint,
|
private val shareLocationEntryPoint: ShareLocationEntryPoint,
|
||||||
private val showLocationEntryPoint: ShowLocationEntryPoint,
|
private val showLocationEntryPoint: ShowLocationEntryPoint,
|
||||||
private val createPollEntryPoint: CreatePollEntryPoint,
|
private val createPollEntryPoint: CreatePollEntryPoint,
|
||||||
private val elementCallEntryPoint: ElementCallEntryPoint,
|
private val elementCallEntryPoint: ElementCallEntryPoint,
|
||||||
|
|
@ -374,7 +374,7 @@ class MessagesFlowNode(
|
||||||
createNode<ReportMessageNode>(buildContext, listOf(inputs))
|
createNode<ReportMessageNode>(buildContext, listOf(inputs))
|
||||||
}
|
}
|
||||||
is NavTarget.SendLocation -> {
|
is NavTarget.SendLocation -> {
|
||||||
sendLocationEntryPoint.createNode(
|
shareLocationEntryPoint.createNode(
|
||||||
parentNode = this,
|
parentNode = this,
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
timelineMode = navTarget.timelineMode,
|
timelineMode = navTarget.timelineMode,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import io.element.android.features.call.test.FakeElementCallEntryPoint
|
||||||
import io.element.android.features.forward.test.FakeForwardEntryPoint
|
import io.element.android.features.forward.test.FakeForwardEntryPoint
|
||||||
import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint
|
import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint
|
||||||
import io.element.android.features.location.test.FakeLocationService
|
import io.element.android.features.location.test.FakeLocationService
|
||||||
import io.element.android.features.location.test.FakeSendLocationEntryPoint
|
import io.element.android.features.location.test.FakeShareLocationEntryPoint
|
||||||
import io.element.android.features.location.test.FakeShowLocationEntryPoint
|
import io.element.android.features.location.test.FakeShowLocationEntryPoint
|
||||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
import io.element.android.features.messages.impl.pinned.banner.createPinnedEventsTimelineProvider
|
import io.element.android.features.messages.impl.pinned.banner.createPinnedEventsTimelineProvider
|
||||||
|
|
@ -62,7 +62,7 @@ class DefaultMessagesEntryPointTest {
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
roomListService = FakeRoomListService(),
|
roomListService = FakeRoomListService(),
|
||||||
sessionId = A_SESSION_ID,
|
sessionId = A_SESSION_ID,
|
||||||
sendLocationEntryPoint = FakeSendLocationEntryPoint(),
|
shareLocationEntryPoint = FakeShareLocationEntryPoint(),
|
||||||
showLocationEntryPoint = FakeShowLocationEntryPoint(),
|
showLocationEntryPoint = FakeShowLocationEntryPoint(),
|
||||||
createPollEntryPoint = FakeCreatePollEntryPoint(),
|
createPollEntryPoint = FakeCreatePollEntryPoint(),
|
||||||
elementCallEntryPoint = FakeElementCallEntryPoint(),
|
elementCallEntryPoint = FakeElementCallEntryPoint(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue