mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Merge pull request #6886 from element-hq/feature/bma/customMapStyle
Read map tiler custom style from matrix .well-known file
This commit is contained in:
+13
-3
@@ -25,8 +25,18 @@ internal class MapTilerTileServerStyleUriBuilder(
|
|||||||
darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun build(darkMode: Boolean): String {
|
override fun build(
|
||||||
val mapId = if (darkMode) darkMapId else lightMapId
|
customMapStyleUrl: String?,
|
||||||
return "$baseUrl/$mapId/style.json?key=$apiKey"
|
darkMode: Boolean,
|
||||||
|
): String {
|
||||||
|
return buildString {
|
||||||
|
if (customMapStyleUrl.isNullOrBlank()) {
|
||||||
|
val mapId = if (darkMode) darkMapId else lightMapId
|
||||||
|
append("$baseUrl/$mapId/style.json")
|
||||||
|
} else {
|
||||||
|
append(customMapStyleUrl)
|
||||||
|
}
|
||||||
|
append("?key=$apiKey")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -19,21 +19,25 @@ import io.element.android.compound.theme.ElementTheme
|
|||||||
*/
|
*/
|
||||||
interface TileServerStyleUriBuilder {
|
interface TileServerStyleUriBuilder {
|
||||||
fun build(
|
fun build(
|
||||||
|
customMapStyleUrl: String?,
|
||||||
darkMode: Boolean,
|
darkMode: Boolean,
|
||||||
): String
|
): String
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TileServerStyleUriBuilder(): TileServerStyleUriBuilder = MapTilerTileServerStyleUriBuilder()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides and remembers a style URI for a MapLibre compatible tile server.
|
* Provides and remembers a style URI for a MapLibre compatible tile server.
|
||||||
*
|
*
|
||||||
* Used for rendering dynamic maps.
|
* Used for rendering dynamic maps.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberTileStyleUrl(): String {
|
fun rememberTileStyleUrl(
|
||||||
|
customMapStyleUrl: String?,
|
||||||
|
): String {
|
||||||
val darkMode = !ElementTheme.isLightTheme
|
val darkMode = !ElementTheme.isLightTheme
|
||||||
return remember(darkMode) {
|
return remember(darkMode, customMapStyleUrl) {
|
||||||
TileServerStyleUriBuilder().build(darkMode)
|
MapTilerTileServerStyleUriBuilder().build(
|
||||||
|
customMapStyleUrl = customMapStyleUrl,
|
||||||
|
darkMode = darkMode,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-2
@@ -22,14 +22,40 @@ class MapTilerTileServerStyleUriBuilderTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `light map uri`() {
|
fun `light map uri`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(darkMode = false)
|
builder.build(
|
||||||
|
customMapStyleUrl = null,
|
||||||
|
darkMode = false,
|
||||||
|
)
|
||||||
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `dark map uri`() {
|
fun `dark map uri`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(darkMode = true)
|
builder.build(
|
||||||
|
customMapStyleUrl = null,
|
||||||
|
darkMode = true,
|
||||||
|
)
|
||||||
).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey")
|
).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `custom map uri light`() {
|
||||||
|
assertThat(
|
||||||
|
builder.build(
|
||||||
|
customMapStyleUrl = "https://custom.url/style.json",
|
||||||
|
darkMode = false,
|
||||||
|
)
|
||||||
|
).isEqualTo("https://custom.url/style.json?key=anApiKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `custom map uri dark`() {
|
||||||
|
assertThat(
|
||||||
|
builder.build(
|
||||||
|
customMapStyleUrl = "https://custom.url/style.json",
|
||||||
|
darkMode = true,
|
||||||
|
)
|
||||||
|
).isEqualTo("https://custom.url/style.json?key=anApiKey")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-7
@@ -37,6 +37,7 @@ import androidx.compose.runtime.derivedStateOf
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
@@ -44,9 +45,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.max
|
import androidx.compose.ui.unit.max
|
||||||
import io.element.android.features.location.api.internal.rememberTileStyleUrl
|
import io.element.android.features.location.api.internal.rememberTileStyleUrl
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
import io.element.android.libraries.designsystem.text.toDp
|
import io.element.android.libraries.designsystem.text.toDp
|
||||||
import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold
|
import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
import org.maplibre.compose.camera.CameraState
|
import org.maplibre.compose.camera.CameraState
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
import org.maplibre.compose.map.MapOptions
|
import org.maplibre.compose.map.MapOptions
|
||||||
@@ -63,6 +66,7 @@ import kotlin.math.roundToInt
|
|||||||
* - Updating camera position padding based on sheet height
|
* - Updating camera position padding based on sheet height
|
||||||
* - Rendering the MaplibreMap with proper ornament positioning
|
* - Rendering the MaplibreMap with proper ornament positioning
|
||||||
*
|
*
|
||||||
|
* @param customMapStyleUrl Optional custom style URL for the map
|
||||||
* @param modifier Modifier for the root layout
|
* @param modifier Modifier for the root layout
|
||||||
* @param scaffoldState State for the bottom sheet scaffold
|
* @param scaffoldState State for the bottom sheet scaffold
|
||||||
* @param cameraState The camera state for the map
|
* @param cameraState The camera state for the map
|
||||||
@@ -79,6 +83,7 @@ import kotlin.math.roundToInt
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MapBottomSheetScaffold(
|
fun MapBottomSheetScaffold(
|
||||||
|
customMapStyleUrl: AsyncData<String?>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
||||||
bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.PartiallyExpanded)
|
bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.PartiallyExpanded)
|
||||||
@@ -130,13 +135,22 @@ fun MapBottomSheetScaffold(
|
|||||||
val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding)
|
val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding)
|
||||||
val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions)
|
val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions)
|
||||||
Box {
|
Box {
|
||||||
MaplibreMap(
|
when (customMapStyleUrl) {
|
||||||
options = mapOptions,
|
is AsyncData.Success -> {
|
||||||
baseStyle = BaseStyle.Uri(rememberTileStyleUrl()),
|
MaplibreMap(
|
||||||
modifier = Modifier.fillMaxSize(),
|
options = mapOptions,
|
||||||
cameraState = cameraState,
|
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapStyleUrl.data)),
|
||||||
content = mapContent,
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
cameraState = cameraState,
|
||||||
|
content = mapContent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
overlayContent(sheetPadding)
|
overlayContent(sheetPadding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -14,6 +14,7 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -35,6 +36,7 @@ import io.element.android.features.location.impl.common.toDialogState
|
|||||||
import io.element.android.features.location.impl.live.LiveLocationStore
|
import io.element.android.features.location.impl.live.LiveLocationStore
|
||||||
import io.element.android.features.messages.api.MessageComposerContext
|
import io.element.android.features.messages.api.MessageComposerContext
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
import io.element.android.libraries.core.extensions.flatMap
|
import io.element.android.libraries.core.extensions.flatMap
|
||||||
@@ -86,6 +88,10 @@ class ShareLocationPresenter(
|
|||||||
}
|
}
|
||||||
val startLiveLocationAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
val startLiveLocationAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||||
val currentUser by client.userProfile.collectAsState()
|
val currentUser by client.userProfile.collectAsState()
|
||||||
|
val customMapStyleUrl by produceState(AsyncData.Loading()) {
|
||||||
|
// Ignore errors
|
||||||
|
value = AsyncData.Success(client.getMapStyleUrl().getOrNull())
|
||||||
|
}
|
||||||
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
||||||
perms.sendLiveLocationPermissions()
|
perms.sendLiveLocationPermissions()
|
||||||
}
|
}
|
||||||
@@ -161,6 +167,7 @@ class ShareLocationPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
|
customMapStyleUrl = customMapStyleUrl,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
|
|||||||
+2
@@ -10,10 +10,12 @@ package io.element.android.features.location.impl.share
|
|||||||
|
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ShareLocationState(
|
data class ShareLocationState(
|
||||||
|
val customMapStyleUrl: AsyncData<String?>,
|
||||||
val currentUser: MatrixUser,
|
val currentUser: MatrixUser,
|
||||||
val dialogState: Dialog,
|
val dialogState: Dialog,
|
||||||
val trackUserLocation: Boolean,
|
val trackUserLocation: Boolean,
|
||||||
|
|||||||
+6
@@ -11,6 +11,7 @@ package io.element.android.features.location.impl.share
|
|||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
@@ -76,10 +77,14 @@ class ShareLocationStateProvider : PreviewParameterProvider<ShareLocationState>
|
|||||||
hasLocationPermission = true,
|
hasLocationPermission = true,
|
||||||
canShareLiveLocation = true,
|
canShareLiveLocation = true,
|
||||||
),
|
),
|
||||||
|
aShareLocationState(
|
||||||
|
customMapStyleUrl = AsyncData.Loading(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aShareLocationState(
|
fun aShareLocationState(
|
||||||
|
customMapStyleUrl: AsyncData<String?> = AsyncData.Success(null),
|
||||||
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
|
||||||
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
|
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
|
||||||
trackUserPosition: Boolean = false,
|
trackUserPosition: Boolean = false,
|
||||||
@@ -90,6 +95,7 @@ fun aShareLocationState(
|
|||||||
eventSink: (ShareLocationEvent) -> Unit = {},
|
eventSink: (ShareLocationEvent) -> Unit = {},
|
||||||
): ShareLocationState {
|
): ShareLocationState {
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
|
customMapStyleUrl = customMapStyleUrl,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
|
|||||||
+1
@@ -117,6 +117,7 @@ fun ShareLocationView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
|
customMapStyleUrl = state.customMapStyleUrl,
|
||||||
cameraState = cameraState,
|
cameraState = cameraState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
scaffoldState = scaffoldState,
|
scaffoldState = scaffoldState,
|
||||||
|
|||||||
+9
@@ -33,6 +33,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsP
|
|||||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||||
import io.element.android.features.location.impl.common.toDialogState
|
import io.element.android.features.location.impl.common.toDialogState
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.mapState
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
@@ -40,6 +41,7 @@ import io.element.android.libraries.dateformatter.api.DateFormatter
|
|||||||
import io.element.android.libraries.dateformatter.api.DateFormatterMode
|
import io.element.android.libraries.dateformatter.api.DateFormatterMode
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.getBestName
|
import io.element.android.libraries.matrix.api.room.getBestName
|
||||||
import io.element.android.libraries.matrix.api.room.joinedRoomMembers
|
import io.element.android.libraries.matrix.api.room.joinedRoomMembers
|
||||||
@@ -58,6 +60,7 @@ class ShowLocationPresenter(
|
|||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
private val dateFormatter: DateFormatter,
|
private val dateFormatter: DateFormatter,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
|
private val client: MatrixClient,
|
||||||
private val joinedRoom: JoinedRoom,
|
private val joinedRoom: JoinedRoom,
|
||||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
) : Presenter<ShowLocationState> {
|
) : Presenter<ShowLocationState> {
|
||||||
@@ -78,6 +81,11 @@ class ShowLocationPresenter(
|
|||||||
mutableStateOf(LocationConstraintsDialogState.None)
|
mutableStateOf(LocationConstraintsDialogState.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val customMapStyleUrl by produceState(AsyncData.Loading()) {
|
||||||
|
// Ignore errors
|
||||||
|
value = AsyncData.Success(client.getMapStyleUrl().getOrNull())
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(permissionsState.permissions) {
|
LaunchedEffect(permissionsState.permissions) {
|
||||||
if (permissionsState.isAnyGranted) {
|
if (permissionsState.isAnyGranted) {
|
||||||
dialogState = LocationConstraintsDialogState.None
|
dialogState = LocationConstraintsDialogState.None
|
||||||
@@ -188,6 +196,7 @@ class ShowLocationPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
|
customMapStyleUrl = customMapStyleUrl,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
locationShares = locationShares,
|
locationShares = locationShares,
|
||||||
focusedLocation = focusedLocation,
|
focusedLocation = focusedLocation,
|
||||||
|
|||||||
+2
@@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show
|
|||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.PinVariant
|
import io.element.android.libraries.designsystem.components.PinVariant
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
@@ -18,6 +19,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ShowLocationState(
|
data class ShowLocationState(
|
||||||
|
val customMapStyleUrl: AsyncData<String?>,
|
||||||
val isLive: Boolean,
|
val isLive: Boolean,
|
||||||
val dialogState: LocationConstraintsDialogState,
|
val dialogState: LocationConstraintsDialogState,
|
||||||
val locationShares: ImmutableList<LocationShareItem>,
|
val locationShares: ImmutableList<LocationShareItem>,
|
||||||
|
|||||||
+6
@@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show
|
|||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
|
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
|
||||||
@@ -41,12 +42,16 @@ class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
|
|||||||
hasLocationPermission = true,
|
hasLocationPermission = true,
|
||||||
isTrackMyLocation = true,
|
isTrackMyLocation = true,
|
||||||
),
|
),
|
||||||
|
aShowLocationState(
|
||||||
|
customMapStyleUrl = AsyncData.Loading(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val APP_NAME = "ApplicationName"
|
private const val APP_NAME = "ApplicationName"
|
||||||
|
|
||||||
fun aShowLocationState(
|
fun aShowLocationState(
|
||||||
|
customMapStyleUrl: AsyncData<String?> = AsyncData.Success(null),
|
||||||
isLive: Boolean = false,
|
isLive: Boolean = false,
|
||||||
constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None,
|
constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None,
|
||||||
locationShares: List<LocationShareItem> = listOf(aLocationShareItem(isLive = isLive)),
|
locationShares: List<LocationShareItem> = listOf(aLocationShareItem(isLive = isLive)),
|
||||||
@@ -57,6 +62,7 @@ fun aShowLocationState(
|
|||||||
eventSink: (ShowLocationEvent) -> Unit = {},
|
eventSink: (ShowLocationEvent) -> Unit = {},
|
||||||
): ShowLocationState {
|
): ShowLocationState {
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
|
customMapStyleUrl = customMapStyleUrl,
|
||||||
dialogState = constraintsDialogState,
|
dialogState = constraintsDialogState,
|
||||||
locationShares = locationShares.toImmutableList(),
|
locationShares = locationShares.toImmutableList(),
|
||||||
focusedLocation = focusedLocation,
|
focusedLocation = focusedLocation,
|
||||||
|
|||||||
+1
@@ -100,6 +100,7 @@ fun ShowLocationView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
|
customMapStyleUrl = state.customMapStyleUrl,
|
||||||
sheetDragHandle = if (state.isSheetDraggable) {
|
sheetDragHandle = if (state.isSheetDraggable) {
|
||||||
{ BottomSheetDefaults.DragHandle() }
|
{ BottomSheetDefaults.DragHandle() }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+52
-44
@@ -15,6 +15,7 @@ import androidx.datastore.preferences.core.Preferences
|
|||||||
import androidx.datastore.preferences.core.emptyPreferences
|
import androidx.datastore.preferences.core.emptyPreferences
|
||||||
import app.cash.molecule.RecompositionMode
|
import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
|
import app.cash.turbine.ReceiveTurbine
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
@@ -75,7 +76,10 @@ class ShareLocationPresenterTest {
|
|||||||
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
private val fakeMessageComposerContext = FakeMessageComposerContext()
|
||||||
private val fakeLocationActions = FakeLocationActions()
|
private val fakeLocationActions = FakeLocationActions()
|
||||||
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
|
||||||
private val fakeMatrixClient = FakeMatrixClient(sessionId = A_USER_ID)
|
private val fakeMatrixClient = FakeMatrixClient(
|
||||||
|
sessionId = A_USER_ID,
|
||||||
|
getMapStyleUrlResult = { Result.success(null) },
|
||||||
|
)
|
||||||
|
|
||||||
private val durationFormatter = FakeDurationFormatter()
|
private val durationFormatter = FakeDurationFormatter()
|
||||||
|
|
||||||
@@ -85,6 +89,7 @@ class ShareLocationPresenterTest {
|
|||||||
locationActions: FakeLocationActions = fakeLocationActions,
|
locationActions: FakeLocationActions = fakeLocationActions,
|
||||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId),
|
liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId),
|
||||||
|
client: FakeMatrixClient = fakeMatrixClient,
|
||||||
): ShareLocationPresenter = ShareLocationPresenter(
|
): ShareLocationPresenter = ShareLocationPresenter(
|
||||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||||
room = joinedRoom,
|
room = joinedRoom,
|
||||||
@@ -93,7 +98,7 @@ class ShareLocationPresenterTest {
|
|||||||
messageComposerContext = fakeMessageComposerContext,
|
messageComposerContext = fakeMessageComposerContext,
|
||||||
locationActions = locationActions,
|
locationActions = locationActions,
|
||||||
buildMeta = fakeBuildMeta,
|
buildMeta = fakeBuildMeta,
|
||||||
client = fakeMatrixClient,
|
client = client,
|
||||||
durationFormatter = durationFormatter,
|
durationFormatter = durationFormatter,
|
||||||
liveLocationShareManager = liveLocationShareManager,
|
liveLocationShareManager = liveLocationShareManager,
|
||||||
liveLocationStore = liveLocationStore,
|
liveLocationStore = liveLocationStore,
|
||||||
@@ -110,14 +115,31 @@ class ShareLocationPresenterTest {
|
|||||||
|
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
assertThat(state.customMapStyleUrl.isLoading()).isFalse()
|
||||||
assertThat(state.trackUserLocation).isTrue()
|
assertThat(state.trackUserLocation).isTrue()
|
||||||
assertThat(state.hasLocationPermission).isTrue()
|
assertThat(state.hasLocationPermission).isTrue()
|
||||||
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - non-null customMapStyleUrl`() = runTest {
|
||||||
|
val shareLocationPresenter = createShareLocationPresenter(
|
||||||
|
client = FakeMatrixClient(
|
||||||
|
sessionId = A_USER_ID,
|
||||||
|
getMapStyleUrlResult = { Result.success("aUrl") },
|
||||||
|
)
|
||||||
|
)
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
val state = awaitItem()
|
||||||
|
assertThat(state.customMapStyleUrl.isLoading()).isTrue()
|
||||||
|
val finalState = awaitItem()
|
||||||
|
assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state with permissions partially granted and location enabled`() = runTest {
|
fun `initial state with permissions partially granted and location enabled`() = runTest {
|
||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
@@ -131,8 +153,7 @@ class ShareLocationPresenterTest {
|
|||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
shareLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.trackUserLocation).isTrue()
|
assertThat(initialState.trackUserLocation).isTrue()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||||
@@ -152,8 +173,7 @@ class ShareLocationPresenterTest {
|
|||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
shareLocationPresenter.present()
|
shareLocationPresenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.trackUserLocation).isFalse()
|
assertThat(initialState.trackUserLocation).isFalse()
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
assertThat(initialState.dialogState).isEqualTo(
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
@@ -173,8 +193,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.trackUserLocation).isFalse()
|
assertThat(initialState.trackUserLocation).isFalse()
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
assertThat(initialState.dialogState).isEqualTo(
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
@@ -195,8 +214,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.trackUserLocation).isFalse()
|
assertThat(initialState.trackUserLocation).isFalse()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.dialogState).isEqualTo(
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
@@ -216,8 +234,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.trackUserLocation).isTrue()
|
assertThat(initialState.trackUserLocation).isTrue()
|
||||||
|
|
||||||
initialState.eventSink(ShareLocationEvent.StopTrackingUserLocation)
|
initialState.eventSink(ShareLocationEvent.StopTrackingUserLocation)
|
||||||
@@ -237,8 +254,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.dialogState).isEqualTo(
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale)
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale)
|
||||||
)
|
)
|
||||||
@@ -260,7 +276,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(ShareLocationEvent.RequestPermissions)
|
initialState.eventSink(ShareLocationEvent.RequestPermissions)
|
||||||
|
|
||||||
// Wait for dialog to be dismissed
|
// Wait for dialog to be dismissed
|
||||||
@@ -282,8 +298,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
initialState.eventSink(ShareLocationEvent.OpenAppSettings)
|
initialState.eventSink(ShareLocationEvent.OpenAppSettings)
|
||||||
val settingsOpenedState = awaitItem()
|
val settingsOpenedState = awaitItem()
|
||||||
|
|
||||||
@@ -304,8 +319,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.dialogState).isEqualTo(
|
assertThat(initialState.dialogState).isEqualTo(
|
||||||
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled)
|
ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled)
|
||||||
)
|
)
|
||||||
@@ -337,8 +351,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
initialState.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
initialState.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
||||||
val durationDialogState = awaitItem()
|
val durationDialogState = awaitItem()
|
||||||
|
|
||||||
@@ -358,8 +371,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
|
|
||||||
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
||||||
val dialogState = awaitItem()
|
val dialogState = awaitItem()
|
||||||
@@ -385,8 +397,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
||||||
awaitItem()
|
awaitItem()
|
||||||
|
|
||||||
@@ -413,8 +424,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
||||||
val disclaimerState = awaitItem()
|
val disclaimerState = awaitItem()
|
||||||
|
|
||||||
@@ -444,8 +454,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
|
|
||||||
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
||||||
val durationState = awaitItem()
|
val durationState = awaitItem()
|
||||||
@@ -472,8 +481,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
|
|
||||||
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
state.eventSink(ShareLocationEvent.InitiateLiveLocationShare)
|
||||||
val dialogState = awaitItem()
|
val dialogState = awaitItem()
|
||||||
@@ -504,8 +512,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
// Dismiss initial dialog
|
// Dismiss initial dialog
|
||||||
initialState.eventSink(ShareLocationEvent.DismissDialog)
|
initialState.eventSink(ShareLocationEvent.DismissDialog)
|
||||||
val dismissedState = awaitItem()
|
val dismissedState = awaitItem()
|
||||||
@@ -539,8 +546,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val initialState = awaitFirstItem()
|
||||||
val initialState = awaitItem()
|
|
||||||
|
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
ShareLocationEvent.ShareStaticLocation(
|
ShareLocationEvent.ShareStaticLocation(
|
||||||
@@ -593,7 +599,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
|
|
||||||
initialState.eventSink(
|
initialState.eventSink(
|
||||||
ShareLocationEvent.ShareStaticLocation(
|
ShareLocationEvent.ShareStaticLocation(
|
||||||
@@ -641,8 +647,7 @@ class ShareLocationPresenterTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
state.eventSink(ShareLocationEvent.StartLiveLocationShare(duration = 1.hours))
|
state.eventSink(ShareLocationEvent.StartLiveLocationShare(duration = 1.hours))
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
assert(startShareLambda).isCalledOnce().with(
|
assert(startShareLambda).isCalledOnce().with(
|
||||||
@@ -659,8 +664,7 @@ class ShareLocationPresenterTest {
|
|||||||
timelineMode = Timeline.Mode.Live,
|
timelineMode = Timeline.Mode.Live,
|
||||||
)
|
)
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
assertThat(state.canShareLiveLocation).isTrue()
|
assertThat(state.canShareLiveLocation).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -671,11 +675,15 @@ class ShareLocationPresenterTest {
|
|||||||
timelineMode = Timeline.Mode.Thread(A_THREAD_ID),
|
timelineMode = Timeline.Mode.Thread(A_THREAD_ID),
|
||||||
)
|
)
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
val state = awaitFirstItem()
|
||||||
val state = awaitItem()
|
|
||||||
assertThat(state.canShareLiveLocation).isFalse()
|
assertThat(state.canShareLiveLocation).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||||
|
skipItems(2)
|
||||||
|
return awaitItem()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLiveLocationStore(
|
private fun createLiveLocationStore(
|
||||||
|
|||||||
+2
@@ -19,6 +19,7 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi
|
|||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
@@ -48,6 +49,7 @@ class DefaultShowLocationEntryPointTest {
|
|||||||
dateFormatter = FakeDateFormatter(),
|
dateFormatter = FakeDateFormatter(),
|
||||||
stringProvider = FakeStringProvider(),
|
stringProvider = FakeStringProvider(),
|
||||||
joinedRoom = joinedRoom,
|
joinedRoom = joinedRoom,
|
||||||
|
client = FakeMatrixClient(),
|
||||||
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
+28
-1
@@ -22,10 +22,13 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS
|
|||||||
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState
|
||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.room.location.LiveLocationShare
|
import io.element.android.libraries.matrix.api.room.location.LiveLocationShare
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||||
import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare
|
import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare
|
||||||
@@ -61,6 +64,7 @@ class ShowLocationPresenterTest {
|
|||||||
),
|
),
|
||||||
locationActions: FakeLocationActions = fakeLocationActions,
|
locationActions: FakeLocationActions = fakeLocationActions,
|
||||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||||
|
client: MatrixClient = FakeMatrixClient(),
|
||||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
) = ShowLocationPresenter(
|
) = ShowLocationPresenter(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
@@ -70,6 +74,7 @@ class ShowLocationPresenterTest {
|
|||||||
dateFormatter = fakeDateFormatter,
|
dateFormatter = fakeDateFormatter,
|
||||||
stringProvider = FakeStringProvider(),
|
stringProvider = FakeStringProvider(),
|
||||||
joinedRoom = joinedRoom,
|
joinedRoom = joinedRoom,
|
||||||
|
client = client,
|
||||||
liveLocationShareManager = liveLocationShareManager,
|
liveLocationShareManager = liveLocationShareManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,11 +90,28 @@ class ShowLocationPresenterTest {
|
|||||||
val presenter = createShowLocationPresenter()
|
val presenter = createShowLocationPresenter()
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
|
assertThat(initialState.customMapStyleUrl.isLoading()).isTrue()
|
||||||
assertThat(initialState.hasLocationPermission).isFalse()
|
assertThat(initialState.hasLocationPermission).isFalse()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - non-null customMapStyleUrl`() = runTest {
|
||||||
|
val shareLocationPresenter = createShowLocationPresenter(
|
||||||
|
client = FakeMatrixClient(
|
||||||
|
sessionId = A_USER_ID,
|
||||||
|
getMapStyleUrlResult = { Result.success("aUrl") },
|
||||||
|
)
|
||||||
|
)
|
||||||
|
shareLocationPresenter.test {
|
||||||
|
val state = awaitItem()
|
||||||
|
assertThat(state.customMapStyleUrl.isLoading()).isTrue()
|
||||||
|
val finalState = awaitItem()
|
||||||
|
assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `emits initial state location permission denied once`() = runTest {
|
fun `emits initial state location permission denied once`() = runTest {
|
||||||
fakePermissionsPresenter.givenState(
|
fakePermissionsPresenter.givenState(
|
||||||
@@ -146,8 +168,13 @@ class ShowLocationPresenterTest {
|
|||||||
fun `centers on user location`() = runTest {
|
fun `centers on user location`() = runTest {
|
||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
|
|
||||||
val presenter = createShowLocationPresenter()
|
val presenter = createShowLocationPresenter(
|
||||||
|
client = FakeMatrixClient(
|
||||||
|
getMapStyleUrlResult = { Result.success(null) }
|
||||||
|
)
|
||||||
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
|
skipItems(1)
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.hasLocationPermission).isTrue()
|
assertThat(initialState.hasLocationPermission).isTrue()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
|
|||||||
+5
@@ -221,6 +221,11 @@ interface MatrixClient {
|
|||||||
*/
|
*/
|
||||||
suspend fun performDatabaseVacuum(): Result<Unit>
|
suspend fun performDatabaseVacuum(): Result<Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of the map style configured on the server, if any.
|
||||||
|
*/
|
||||||
|
suspend fun getMapStyleUrl(): Result<String?>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the cached client `well-known` config by the SDK.
|
* Resets the cached client `well-known` config by the SDK.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
@@ -808,6 +808,12 @@ class RustMatrixClient(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getMapStyleUrl(): Result<String?> = withContext(sessionDispatcher) {
|
||||||
|
runCatchingExceptions {
|
||||||
|
innerClient.tileServer()?.mapStyleUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun resetWellKnownConfig(): Result<Unit> {
|
override suspend fun resetWellKnownConfig(): Result<Unit> {
|
||||||
return runCatchingExceptions {
|
return runCatchingExceptions {
|
||||||
Timber.d("Resetting well-known config for session $sessionId")
|
Timber.d("Resetting well-known config for session $sessionId")
|
||||||
|
|||||||
+5
@@ -115,6 +115,7 @@ class FakeMatrixClient(
|
|||||||
private val addRecentEmojiLambda: (String) -> Result<Unit> = { Result.success(Unit) },
|
private val addRecentEmojiLambda: (String) -> Result<Unit> = { Result.success(Unit) },
|
||||||
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
|
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||||
private val performDatabaseVacuumLambda: () -> Result<Unit> = { lambdaError() },
|
private val performDatabaseVacuumLambda: () -> Result<Unit> = { lambdaError() },
|
||||||
|
private val getMapStyleUrlResult: () -> Result<String?> = { lambdaError() },
|
||||||
private val getDatabaseSizesLambda: () -> Result<SdkStoreSizes> = { lambdaError() },
|
private val getDatabaseSizesLambda: () -> Result<SdkStoreSizes> = { lambdaError() },
|
||||||
private val resetWellKnownConfigLambda: () -> Result<Unit> = { lambdaError() },
|
private val resetWellKnownConfigLambda: () -> Result<Unit> = { lambdaError() },
|
||||||
) : MatrixClient {
|
) : MatrixClient {
|
||||||
@@ -373,6 +374,10 @@ class FakeMatrixClient(
|
|||||||
return performDatabaseVacuumLambda()
|
return performDatabaseVacuumLambda()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getMapStyleUrl(): Result<String?> = simulateLongTask {
|
||||||
|
getMapStyleUrlResult()
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun canLinkNewDevice(): Result<Boolean> = simulateLongTask {
|
override suspend fun canLinkNewDevice(): Result<Boolean> = simulateLongTask {
|
||||||
return canLinkNewDeviceResult()
|
return canLinkNewDeviceResult()
|
||||||
}
|
}
|
||||||
|
|||||||
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
Reference in New Issue
Block a user