mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 20:38:40 +00:00
Move the custom tile server URL config to the Element wellknown content (#7416)
* Move the custom tile server URL config to the Element wellknown content * Iteration: use whole `MapTilerConfig` in the remote config
This commit is contained in:
committed by
GitHub
parent
ddbc6a2a33
commit
22195a47f2
@@ -44,6 +44,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.sessionStorage.api)
|
implementation(projects.libraries.sessionStorage.api)
|
||||||
implementation(projects.libraries.uiCommon)
|
implementation(projects.libraries.uiCommon)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
|
implementation(projects.libraries.wellknown.api)
|
||||||
implementation(projects.features.login.api)
|
implementation(projects.features.login.api)
|
||||||
|
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ package io.element.android.appnav
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@@ -49,11 +51,13 @@ import io.element.android.compound.colors.SemanticColorsLightDark
|
|||||||
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
import io.element.android.features.createroom.api.CreateRoomEntryPoint
|
||||||
import io.element.android.features.enterprise.api.EnterpriseService
|
import io.element.android.features.enterprise.api.EnterpriseService
|
||||||
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.ftue.api.FtueEntryPoint
|
import io.element.android.features.ftue.api.FtueEntryPoint
|
||||||
import io.element.android.features.ftue.api.state.FtueService
|
import io.element.android.features.ftue.api.state.FtueService
|
||||||
import io.element.android.features.ftue.api.state.FtueState
|
import io.element.android.features.ftue.api.state.FtueState
|
||||||
import io.element.android.features.home.api.HomeEntryPoint
|
import io.element.android.features.home.api.HomeEntryPoint
|
||||||
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
|
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
|
||||||
|
import io.element.android.features.location.api.LocalMapTilerConfig
|
||||||
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
||||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
@@ -153,6 +157,7 @@ class LoggedInFlowNode(
|
|||||||
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
|
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
|
||||||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||||
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
|
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
|
private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider,
|
||||||
) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
|
) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Placeholder,
|
initialElement = NavTarget.Placeholder,
|
||||||
@@ -677,6 +682,12 @@ class LoggedInFlowNode(
|
|||||||
val colors by remember {
|
val colors by remember {
|
||||||
enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
|
enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
|
||||||
}.collectAsState(SemanticColorsLightDark.default)
|
}.collectAsState(SemanticColorsLightDark.default)
|
||||||
|
|
||||||
|
val currentMapTilerConfig = LocalMapTilerConfig.current
|
||||||
|
val updatedMapTilerConfig by produceState(currentMapTilerConfig) {
|
||||||
|
value = remoteEnterpriseConfigProvider.get(matrixClient.sessionId).dataOrNull()?.mapTilerConfig ?: currentMapTilerConfig
|
||||||
|
}
|
||||||
|
|
||||||
ElementThemeApp(
|
ElementThemeApp(
|
||||||
appPreferencesStore = appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
featureFlagService = featureFlagService,
|
featureFlagService = featureFlagService,
|
||||||
@@ -684,16 +695,18 @@ class LoggedInFlowNode(
|
|||||||
compoundDark = colors.dark,
|
compoundDark = colors.dark,
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
) {
|
) {
|
||||||
val isOnline by syncService.isOnline.collectAsState()
|
CompositionLocalProvider(LocalMapTilerConfig provides updatedMapTilerConfig) {
|
||||||
ConnectivityIndicatorContainer(
|
val isOnline by syncService.isOnline.collectAsState()
|
||||||
isOnline = isOnline,
|
ConnectivityIndicatorContainer(
|
||||||
modifier = modifier,
|
isOnline = isOnline,
|
||||||
) { contentModifier ->
|
modifier = modifier,
|
||||||
Box(modifier = contentModifier) {
|
) { contentModifier ->
|
||||||
val ftueState by ftueService.state.collectAsState()
|
Box(modifier = contentModifier) {
|
||||||
BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack))
|
val ftueState by ftueService.state.collectAsState()
|
||||||
if (ftueState is FtueState.Complete) {
|
BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack))
|
||||||
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
|
if (ftueState is FtueState.Complete) {
|
||||||
|
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.matrixui)
|
implementation(projects.libraries.matrixui)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
|
implementation(projects.libraries.wellknown.api)
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
implementation(libs.datetime)
|
implementation(libs.datetime)
|
||||||
|
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.location.api
|
||||||
|
|
||||||
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a [MapTilerConfig] for the MapLibre maps.
|
||||||
|
*/
|
||||||
|
val LocalMapTilerConfig = staticCompositionLocalOf {
|
||||||
|
MapTilerConfig(
|
||||||
|
apiKey = BuildConfig.MAPTILER_API_KEY,
|
||||||
|
lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||||
|
darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||||
|
baseUrl = BuildConfig.MAPTILER_BASE_URL
|
||||||
|
)
|
||||||
|
}
|
||||||
+7
-2
@@ -38,13 +38,14 @@ import coil3.request.ImageRequest
|
|||||||
import coil3.request.SuccessResult
|
import coil3.request.SuccessResult
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.location.api.internal.StaticMapPlaceholder
|
import io.element.android.features.location.api.internal.StaticMapPlaceholder
|
||||||
import io.element.android.features.location.api.internal.StaticMapUrlBuilder
|
|
||||||
import io.element.android.features.location.api.internal.centerBottomEdge
|
import io.element.android.features.location.api.internal.centerBottomEdge
|
||||||
|
import io.element.android.features.location.api.internal.rememberStaticMapBuilder
|
||||||
import io.element.android.libraries.designsystem.components.LocationPin
|
import io.element.android.libraries.designsystem.components.LocationPin
|
||||||
import io.element.android.libraries.designsystem.components.PinVariant
|
import io.element.android.libraries.designsystem.components.PinVariant
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a static map image downloaded via a third party service's static maps API.
|
* Shows a static map image downloaded via a third party service's static maps API.
|
||||||
@@ -57,6 +58,7 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun StaticMapView(
|
fun StaticMapView(
|
||||||
|
mapTilerConfig: MapTilerConfig?,
|
||||||
location: Location?,
|
location: Location?,
|
||||||
zoom: Double,
|
zoom: Double,
|
||||||
pinVariant: PinVariant,
|
pinVariant: PinVariant,
|
||||||
@@ -94,6 +96,7 @@ fun StaticMapView(
|
|||||||
}
|
}
|
||||||
// Cases 3 & 4: Non-null location - fetch map
|
// Cases 3 & 4: Non-null location - fetch map
|
||||||
else -> LoadableMapContent(
|
else -> LoadableMapContent(
|
||||||
|
mapTilerConfig = mapTilerConfig,
|
||||||
location = location,
|
location = location,
|
||||||
zoom = zoom,
|
zoom = zoom,
|
||||||
pinVariant = pinVariant,
|
pinVariant = pinVariant,
|
||||||
@@ -124,6 +127,7 @@ private fun BoxWithConstraintsScope.StaleMapContent(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun BoxWithConstraintsScope.LoadableMapContent(
|
private fun BoxWithConstraintsScope.LoadableMapContent(
|
||||||
|
mapTilerConfig: MapTilerConfig?,
|
||||||
location: Location,
|
location: Location,
|
||||||
zoom: Double,
|
zoom: Double,
|
||||||
pinVariant: PinVariant,
|
pinVariant: PinVariant,
|
||||||
@@ -132,7 +136,7 @@ private fun BoxWithConstraintsScope.LoadableMapContent(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var retryHash by remember { mutableIntStateOf(0) }
|
var retryHash by remember { mutableIntStateOf(0) }
|
||||||
val builder = remember { StaticMapUrlBuilder() }
|
val builder = rememberStaticMapBuilder(mapTilerConfig)
|
||||||
|
|
||||||
val (painter, state, contentScale) = if (LocalInspectionMode.current) {
|
val (painter, state, contentScale) = if (LocalInspectionMode.current) {
|
||||||
val painter = painterResource(CommonDrawables.sample_map)
|
val painter = painterResource(CommonDrawables.sample_map)
|
||||||
@@ -203,6 +207,7 @@ private fun BoxWithConstraintsScope.LoadableMapContent(
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun StaticMapViewPreview() = ElementPreview {
|
internal fun StaticMapViewPreview() = ElementPreview {
|
||||||
StaticMapView(
|
StaticMapView(
|
||||||
|
mapTilerConfig = null,
|
||||||
location = Location(0.0, 0.0),
|
location = Location(0.0, 0.0),
|
||||||
zoom = 0.0,
|
zoom = 0.0,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|||||||
+8
@@ -9,6 +9,7 @@
|
|||||||
package io.element.android.features.location.api.internal
|
package io.element.android.features.location.api.internal
|
||||||
|
|
||||||
import io.element.android.features.location.api.BuildConfig
|
import io.element.android.features.location.api.BuildConfig
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +30,13 @@ internal class MapTilerStaticMapUrlBuilder(
|
|||||||
darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
constructor(mapTilerConfig: MapTilerConfig) : this(
|
||||||
|
baseUrl = (mapTilerConfig.baseUrl ?: BuildConfig.MAPTILER_BASE_URL).removeSuffix("/"),
|
||||||
|
apiKey = mapTilerConfig.apiKey.takeIf { it.isNotBlank() } ?: BuildConfig.MAPTILER_API_KEY,
|
||||||
|
lightMapId = mapTilerConfig.lightStyleId ?: BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||||
|
darkMapId = mapTilerConfig.darkStyleId ?: BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||||
|
)
|
||||||
|
|
||||||
override fun build(
|
override fun build(
|
||||||
lat: Double,
|
lat: Double,
|
||||||
lon: Double,
|
lon: Double,
|
||||||
|
|||||||
+9
-5
@@ -11,6 +11,7 @@
|
|||||||
package io.element.android.features.location.api.internal
|
package io.element.android.features.location.api.internal
|
||||||
|
|
||||||
import io.element.android.features.location.api.BuildConfig
|
import io.element.android.features.location.api.BuildConfig
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
|
|
||||||
internal class MapTilerTileServerStyleUriBuilder(
|
internal class MapTilerTileServerStyleUriBuilder(
|
||||||
private val baseUrl: String,
|
private val baseUrl: String,
|
||||||
@@ -26,14 +27,17 @@ internal class MapTilerTileServerStyleUriBuilder(
|
|||||||
)
|
)
|
||||||
|
|
||||||
override fun build(
|
override fun build(
|
||||||
customMapStyleUrl: String?,
|
customMapTilerConfig: MapTilerConfig?,
|
||||||
darkMode: Boolean,
|
darkMode: Boolean,
|
||||||
): String {
|
): String {
|
||||||
return if (customMapStyleUrl.isNullOrBlank()) {
|
val baseUrl = customMapTilerConfig?.baseUrl.takeIf { !it.isNullOrBlank() } ?: baseUrl
|
||||||
val mapId = if (darkMode) darkMapId else lightMapId
|
val apiKey = customMapTilerConfig?.apiKey ?: apiKey
|
||||||
"$baseUrl/$mapId/style.json?key=$apiKey"
|
val mapId = if (darkMode) {
|
||||||
|
customMapTilerConfig?.darkStyleId ?: darkMapId
|
||||||
} else {
|
} else {
|
||||||
customMapStyleUrl
|
customMapTilerConfig?.lightStyleId ?: lightMapId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "$baseUrl/$mapId/style.json?key=$apiKey"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-1
@@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
package io.element.android.features.location.api.internal
|
package io.element.android.features.location.api.internal
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an URL for a 3rd party service provider static maps API.
|
* Builds an URL for a 3rd party service provider static maps API.
|
||||||
*/
|
*/
|
||||||
@@ -25,4 +29,13 @@ interface StaticMapUrlBuilder {
|
|||||||
fun isServiceAvailable(): Boolean
|
fun isServiceAvailable(): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
fun StaticMapUrlBuilder(): StaticMapUrlBuilder = MapTilerStaticMapUrlBuilder()
|
@Composable
|
||||||
|
fun rememberStaticMapBuilder(mapTilerConfig: MapTilerConfig?): StaticMapUrlBuilder {
|
||||||
|
return remember(mapTilerConfig) {
|
||||||
|
if (mapTilerConfig != null) {
|
||||||
|
MapTilerStaticMapUrlBuilder(mapTilerConfig)
|
||||||
|
} else {
|
||||||
|
MapTilerStaticMapUrlBuilder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+5
-4
@@ -11,6 +11,7 @@ package io.element.android.features.location.api.internal
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a style URI for a MapLibre compatible tile server.
|
* Builds a style URI for a MapLibre compatible tile server.
|
||||||
@@ -19,7 +20,7 @@ import io.element.android.compound.theme.ElementTheme
|
|||||||
*/
|
*/
|
||||||
interface TileServerStyleUriBuilder {
|
interface TileServerStyleUriBuilder {
|
||||||
fun build(
|
fun build(
|
||||||
customMapStyleUrl: String?,
|
customMapTilerConfig: MapTilerConfig?,
|
||||||
darkMode: Boolean,
|
darkMode: Boolean,
|
||||||
): String
|
): String
|
||||||
}
|
}
|
||||||
@@ -31,12 +32,12 @@ interface TileServerStyleUriBuilder {
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberTileStyleUrl(
|
fun rememberTileStyleUrl(
|
||||||
customMapStyleUrl: String?,
|
customMapTileConfig: MapTilerConfig?,
|
||||||
): String {
|
): String {
|
||||||
val darkMode = !ElementTheme.isLightTheme
|
val darkMode = !ElementTheme.isLightTheme
|
||||||
return remember(darkMode, customMapStyleUrl) {
|
return remember(darkMode, customMapTileConfig) {
|
||||||
MapTilerTileServerStyleUriBuilder().build(
|
MapTilerTileServerStyleUriBuilder().build(
|
||||||
customMapStyleUrl = customMapStyleUrl,
|
customMapTilerConfig = customMapTileConfig,
|
||||||
darkMode = darkMode,
|
darkMode = darkMode,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-28
@@ -9,6 +9,7 @@
|
|||||||
package io.element.android.features.location.api.internal
|
package io.element.android.features.location.api.internal
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class MapTilerTileServerStyleUriBuilderTest {
|
class MapTilerTileServerStyleUriBuilderTest {
|
||||||
@@ -23,7 +24,7 @@ class MapTilerTileServerStyleUriBuilderTest {
|
|||||||
fun `light map uri`() {
|
fun `light map uri`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(
|
builder.build(
|
||||||
customMapStyleUrl = null,
|
customMapTilerConfig = null,
|
||||||
darkMode = false,
|
darkMode = false,
|
||||||
)
|
)
|
||||||
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
||||||
@@ -33,7 +34,7 @@ class MapTilerTileServerStyleUriBuilderTest {
|
|||||||
fun `dark map uri`() {
|
fun `dark map uri`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(
|
builder.build(
|
||||||
customMapStyleUrl = null,
|
customMapTilerConfig = null,
|
||||||
darkMode = true,
|
darkMode = true,
|
||||||
)
|
)
|
||||||
).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey")
|
).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey")
|
||||||
@@ -43,49 +44,44 @@ class MapTilerTileServerStyleUriBuilderTest {
|
|||||||
fun `custom map uri light`() {
|
fun `custom map uri light`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(
|
builder.build(
|
||||||
customMapStyleUrl = "https://custom.url/style.json",
|
customMapTilerConfig = MapTilerConfig(
|
||||||
|
apiKey = "API_KEY",
|
||||||
|
lightStyleId = "light",
|
||||||
|
darkStyleId = "dark",
|
||||||
|
baseUrl = "https://custom.url/style.json"
|
||||||
|
),
|
||||||
darkMode = false,
|
darkMode = false,
|
||||||
)
|
)
|
||||||
).isEqualTo("https://custom.url/style.json")
|
).isEqualTo("https://custom.url/style.json/light/style.json?key=API_KEY")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `custom map uri dark`() {
|
fun `custom map uri dark`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(
|
builder.build(
|
||||||
customMapStyleUrl = "https://custom.url/style.json",
|
customMapTilerConfig = MapTilerConfig(
|
||||||
|
apiKey = "API_KEY",
|
||||||
|
lightStyleId = "light",
|
||||||
|
darkStyleId = "dark",
|
||||||
|
baseUrl = "https://custom.url/style.json"
|
||||||
|
),
|
||||||
darkMode = true,
|
darkMode = true,
|
||||||
)
|
)
|
||||||
).isEqualTo("https://custom.url/style.json")
|
).isEqualTo("https://custom.url/style.json/dark/style.json?key=API_KEY")
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `custom map uri with its own api key is not altered`() {
|
|
||||||
assertThat(
|
|
||||||
builder.build(
|
|
||||||
customMapStyleUrl = "https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey",
|
|
||||||
darkMode = false,
|
|
||||||
)
|
|
||||||
).isEqualTo("https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `custom map uri with an existing query is not altered`() {
|
|
||||||
assertThat(
|
|
||||||
builder.build(
|
|
||||||
customMapStyleUrl = "https://self.hosted/style.json?foo=bar",
|
|
||||||
darkMode = true,
|
|
||||||
)
|
|
||||||
).isEqualTo("https://self.hosted/style.json?foo=bar")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `blank custom map uri falls back to the built-in style`() {
|
fun `blank custom map uri falls back to the built-in style`() {
|
||||||
assertThat(
|
assertThat(
|
||||||
builder.build(
|
builder.build(
|
||||||
customMapStyleUrl = " ",
|
customMapTilerConfig = MapTilerConfig(
|
||||||
|
apiKey = "API_KEY",
|
||||||
|
lightStyleId = "light",
|
||||||
|
darkStyleId = "dark",
|
||||||
|
baseUrl = " "
|
||||||
|
),
|
||||||
darkMode = false,
|
darkMode = false,
|
||||||
)
|
)
|
||||||
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
).isEqualTo("https://base.url/light/style.json?key=API_KEY")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ setupDependencyInjection()
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.features.location.api)
|
api(projects.features.location.api)
|
||||||
|
implementation(projects.features.enterprise.api)
|
||||||
implementation(projects.features.messages.api)
|
implementation(projects.features.messages.api)
|
||||||
implementation(libs.maplibre.compose)
|
implementation(libs.maplibre.compose)
|
||||||
implementation(libs.coil)
|
implementation(libs.coil)
|
||||||
@@ -45,6 +46,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
implementation(projects.libraries.push.api)
|
implementation(projects.libraries.push.api)
|
||||||
implementation(projects.libraries.sessionStorage.api)
|
implementation(projects.libraries.sessionStorage.api)
|
||||||
|
implementation(projects.libraries.wellknown.api)
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
implementation(libs.datetime)
|
implementation(libs.datetime)
|
||||||
|
|
||||||
@@ -58,5 +60,7 @@ dependencies {
|
|||||||
testImplementation(projects.libraries.featureflag.test)
|
testImplementation(projects.libraries.featureflag.test)
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
testImplementation(projects.libraries.sessionStorage.test)
|
testImplementation(projects.libraries.sessionStorage.test)
|
||||||
|
testImplementation(projects.libraries.wellknown.test)
|
||||||
testImplementation(projects.features.location.test)
|
testImplementation(projects.features.location.test)
|
||||||
|
testImplementation(projects.features.enterprise.test)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -50,6 +50,7 @@ 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 io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
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
|
||||||
@@ -66,7 +67,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 customMapTilerConfig 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
|
||||||
@@ -83,7 +84,7 @@ import kotlin.math.roundToInt
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MapBottomSheetScaffold(
|
fun MapBottomSheetScaffold(
|
||||||
customMapStyleUrl: AsyncData<String?>,
|
customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
||||||
bottomSheetState = rememberBottomSheetState(
|
bottomSheetState = rememberBottomSheetState(
|
||||||
@@ -137,11 +138,11 @@ 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 {
|
||||||
when (customMapStyleUrl) {
|
when (customMapTilerConfig) {
|
||||||
is AsyncData.Success -> {
|
is AsyncData.Success -> {
|
||||||
MaplibreMap(
|
MaplibreMap(
|
||||||
options = mapOptions,
|
options = mapOptions,
|
||||||
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapStyleUrl.data)),
|
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapTilerConfig.data)),
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
cameraState = cameraState,
|
cameraState = cameraState,
|
||||||
content = mapContent,
|
content = mapContent,
|
||||||
|
|||||||
+5
-3
@@ -22,6 +22,7 @@ import dev.zacsweers.metro.Assisted
|
|||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
||||||
import io.element.android.features.location.impl.common.LocationConstraintsCheck
|
import io.element.android.features.location.impl.common.LocationConstraintsCheck
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
@@ -72,6 +73,7 @@ class ShareLocationPresenter(
|
|||||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
private val liveLocationStore: LiveLocationStore,
|
private val liveLocationStore: LiveLocationStore,
|
||||||
private val userLocationStateFactory: UserLocationState.Factory,
|
private val userLocationStateFactory: UserLocationState.Factory,
|
||||||
|
private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider,
|
||||||
) : Presenter<ShareLocationState> {
|
) : Presenter<ShareLocationState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
fun interface Factory {
|
fun interface Factory {
|
||||||
@@ -92,9 +94,9 @@ class ShareLocationPresenter(
|
|||||||
var pendingLiveLocationShare by remember { mutableStateOf(false) }
|
var pendingLiveLocationShare by remember { mutableStateOf(false) }
|
||||||
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()) {
|
val customMapConfig by produceState(AsyncData.Loading()) {
|
||||||
// Ignore errors
|
// Ignore errors
|
||||||
value = AsyncData.Success(client.getMapStyleUrl().getOrNull())
|
value = AsyncData.Success(remoteEnterpriseConfigProvider.get(client.sessionId).dataOrNull()?.mapTilerConfig)
|
||||||
}
|
}
|
||||||
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms ->
|
||||||
perms.sendLiveLocationPermissions()
|
perms.sendLiveLocationPermissions()
|
||||||
@@ -199,7 +201,7 @@ class ShareLocationPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
customMapStyleUrl = customMapStyleUrl,
|
customMapTilerConfig = customMapConfig,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
|
|||||||
+2
-1
@@ -13,10 +13,11 @@ import io.element.android.features.location.impl.common.userlocation.UserLocatio
|
|||||||
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.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ShareLocationState(
|
data class ShareLocationState(
|
||||||
val customMapStyleUrl: AsyncData<String?>,
|
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
||||||
val currentUser: MatrixUser,
|
val currentUser: MatrixUser,
|
||||||
val dialogState: Dialog,
|
val dialogState: Dialog,
|
||||||
val trackUserLocation: Boolean,
|
val trackUserLocation: Boolean,
|
||||||
|
|||||||
+5
-4
@@ -15,6 +15,7 @@ import io.element.android.libraries.architecture.AsyncAction
|
|||||||
import io.element.android.libraries.architecture.AsyncData
|
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 io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlin.time.Duration.Companion.hours
|
import kotlin.time.Duration.Companion.hours
|
||||||
import kotlin.time.Duration.Companion.minutes
|
import kotlin.time.Duration.Companion.minutes
|
||||||
@@ -70,13 +71,13 @@ class ShareLocationStateProvider : PreviewParameterProvider<ShareLocationState>
|
|||||||
canShareLiveLocation = true,
|
canShareLiveLocation = true,
|
||||||
),
|
),
|
||||||
aShareLocationState(
|
aShareLocationState(
|
||||||
customMapStyleUrl = AsyncData.Loading(),
|
customMapTilerConfig = AsyncData.Loading(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aShareLocationState(
|
fun aShareLocationState(
|
||||||
customMapStyleUrl: AsyncData<String?> = AsyncData.Success(null),
|
customMapTilerConfig: AsyncData<MapTilerConfig?> = 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,
|
||||||
@@ -87,7 +88,7 @@ fun aShareLocationState(
|
|||||||
eventSink: (ShareLocationEvent) -> Unit = {},
|
eventSink: (ShareLocationEvent) -> Unit = {},
|
||||||
): ShareLocationState {
|
): ShareLocationState {
|
||||||
return ShareLocationState(
|
return ShareLocationState(
|
||||||
customMapStyleUrl = customMapStyleUrl,
|
customMapTilerConfig = customMapTilerConfig,
|
||||||
currentUser = currentUser,
|
currentUser = currentUser,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
trackUserLocation = trackUserPosition,
|
trackUserLocation = trackUserPosition,
|
||||||
@@ -95,6 +96,6 @@ fun aShareLocationState(
|
|||||||
canShareLiveLocation = canShareLiveLocation,
|
canShareLiveLocation = canShareLiveLocation,
|
||||||
appName = appName,
|
appName = appName,
|
||||||
startLiveLocationAction = startLiveLocationAction,
|
startLiveLocationAction = startLiveLocationAction,
|
||||||
eventSink = eventSink
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ fun ShareLocationView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
customMapStyleUrl = state.customMapStyleUrl,
|
customMapTilerConfig = state.customMapTilerConfig,
|
||||||
cameraState = cameraState,
|
cameraState = cameraState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
scaffoldState = scaffoldState,
|
scaffoldState = scaffoldState,
|
||||||
|
|||||||
+7
-5
@@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.api.ShowLocationMode
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
|
||||||
@@ -46,7 +47,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.core.SessionId
|
||||||
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
|
||||||
@@ -64,10 +65,11 @@ import kotlinx.coroutines.launch
|
|||||||
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 sessionId: SessionId,
|
||||||
private val joinedRoom: JoinedRoom,
|
private val joinedRoom: JoinedRoom,
|
||||||
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
private val liveLocationShareManager: ActiveLiveLocationShareManager,
|
||||||
private val userLocationStateFactory: UserLocationState.Factory,
|
private val userLocationStateFactory: UserLocationState.Factory,
|
||||||
|
private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider,
|
||||||
) : Presenter<ShowLocationState> {
|
) : Presenter<ShowLocationState> {
|
||||||
@AssistedFactory fun interface Factory {
|
@AssistedFactory fun interface Factory {
|
||||||
fun create(mode: ShowLocationMode): ShowLocationPresenter
|
fun create(mode: ShowLocationMode): ShowLocationPresenter
|
||||||
@@ -85,9 +87,9 @@ import kotlinx.coroutines.launch
|
|||||||
mutableStateOf(LocationConstraintsDialogState.None)
|
mutableStateOf(LocationConstraintsDialogState.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
val customMapStyleUrl by produceState(AsyncData.Loading()) {
|
val customMapTilerConfig by produceState(AsyncData.Loading()) {
|
||||||
// Ignore errors
|
// Ignore errors
|
||||||
value = AsyncData.Success(client.getMapStyleUrl().getOrNull())
|
value = AsyncData.Success(remoteEnterpriseConfigProvider.get(sessionId).dataOrNull()?.mapTilerConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkLocationConstraints() {
|
fun checkLocationConstraints() {
|
||||||
@@ -220,7 +222,7 @@ import kotlinx.coroutines.launch
|
|||||||
userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted)
|
userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted)
|
||||||
}
|
}
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
customMapStyleUrl = customMapStyleUrl,
|
customMapTilerConfig = customMapTilerConfig,
|
||||||
dialogState = dialogState,
|
dialogState = dialogState,
|
||||||
locationShares = locationShares,
|
locationShares = locationShares,
|
||||||
focusedLocation = focusedLocation,
|
focusedLocation = focusedLocation,
|
||||||
|
|||||||
+2
-1
@@ -17,10 +17,11 @@ 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
|
||||||
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.wellknown.api.MapTilerConfig
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class ShowLocationState(
|
data class ShowLocationState(
|
||||||
val customMapStyleUrl: AsyncData<String?>,
|
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
|
||||||
val isLive: Boolean,
|
val isLive: Boolean,
|
||||||
val dialogState: LocationConstraintsDialogState,
|
val dialogState: LocationConstraintsDialogState,
|
||||||
val locationShares: ImmutableList<LocationShareItem>,
|
val locationShares: ImmutableList<LocationShareItem>,
|
||||||
|
|||||||
+4
-3
@@ -18,6 +18,7 @@ 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
|
||||||
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.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
|
class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
|
||||||
@@ -36,14 +37,14 @@ class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
|
|||||||
constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled,
|
constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled,
|
||||||
),
|
),
|
||||||
aShowLocationState(isTrackMyLocation = true),
|
aShowLocationState(isTrackMyLocation = true),
|
||||||
aShowLocationState(customMapStyleUrl = AsyncData.Loading()),
|
aShowLocationState(customMapTilerConfig = AsyncData.Loading()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val APP_NAME = "ApplicationName"
|
private const val APP_NAME = "ApplicationName"
|
||||||
|
|
||||||
fun aShowLocationState(
|
fun aShowLocationState(
|
||||||
customMapStyleUrl: AsyncData<String?> = AsyncData.Success(null),
|
customMapTilerConfig: AsyncData<MapTilerConfig?> = 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)),
|
||||||
@@ -55,7 +56,7 @@ fun aShowLocationState(
|
|||||||
eventSink: (ShowLocationEvent) -> Unit = {},
|
eventSink: (ShowLocationEvent) -> Unit = {},
|
||||||
): ShowLocationState {
|
): ShowLocationState {
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
customMapStyleUrl = customMapStyleUrl,
|
customMapTilerConfig = customMapTilerConfig,
|
||||||
dialogState = constraintsDialogState,
|
dialogState = constraintsDialogState,
|
||||||
locationShares = locationShares.toImmutableList(),
|
locationShares = locationShares.toImmutableList(),
|
||||||
focusedLocation = focusedLocation,
|
focusedLocation = focusedLocation,
|
||||||
|
|||||||
+1
-1
@@ -102,7 +102,7 @@ fun ShowLocationView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
customMapStyleUrl = state.customMapStyleUrl,
|
customMapTilerConfig = state.customMapTilerConfig,
|
||||||
sheetDragHandle = if (state.isSheetDraggable) {
|
sheetDragHandle = if (state.isSheetDraggable) {
|
||||||
{ BottomSheetDefaults.DragHandle() }
|
{ BottomSheetDefaults.DragHandle() }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+2
@@ -11,6 +11,7 @@ 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
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
||||||
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
import io.element.android.features.location.impl.common.actions.FakeLocationActions
|
||||||
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter
|
||||||
@@ -58,6 +59,7 @@ class DefaultShareLocationEntryPointTest {
|
|||||||
sessionId = room.sessionId,
|
sessionId = room.sessionId,
|
||||||
),
|
),
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
|
remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
|
|||||||
+17
-12
@@ -19,6 +19,8 @@ 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
|
||||||
|
import io.element.android.features.enterprise.api.remoteconfig.RemoteEnterpriseConfigProvider
|
||||||
|
import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.impl.aPermissionsState
|
import io.element.android.features.location.impl.aPermissionsState
|
||||||
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
import io.element.android.features.location.impl.common.FakeUserLocationStateFactory
|
||||||
@@ -30,6 +32,8 @@ import io.element.android.features.location.impl.common.ui.LocationConstraintsDi
|
|||||||
import io.element.android.features.location.impl.live.LiveLocationStore
|
import io.element.android.features.location.impl.live.LiveLocationStore
|
||||||
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
import io.element.android.features.location.test.FakeActiveLiveLocationShareManager
|
||||||
import io.element.android.features.messages.test.FakeMessageComposerContext
|
import io.element.android.features.messages.test.FakeMessageComposerContext
|
||||||
|
import io.element.android.features.wellknown.test.aMapTilerConfig
|
||||||
|
import io.element.android.features.wellknown.test.anElementWellKnown
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDurationFormatter
|
import io.element.android.libraries.dateformatter.test.FakeDurationFormatter
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
@@ -51,6 +55,7 @@ import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermiss
|
|||||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||||
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
|
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
|
||||||
import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory
|
import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory
|
||||||
|
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.lambda.assert
|
import io.element.android.tests.testutils.lambda.assert
|
||||||
@@ -60,7 +65,6 @@ import io.element.android.tests.testutils.test
|
|||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.test.TestScope
|
|
||||||
import kotlinx.coroutines.test.advanceUntilIdle
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@@ -77,20 +81,18 @@ 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(
|
private val fakeMatrixClient = FakeMatrixClient(sessionId = A_USER_ID)
|
||||||
sessionId = A_USER_ID,
|
|
||||||
getMapStyleUrlResult = { Result.success(null) },
|
|
||||||
)
|
|
||||||
|
|
||||||
private val durationFormatter = FakeDurationFormatter()
|
private val durationFormatter = FakeDurationFormatter()
|
||||||
|
|
||||||
private fun TestScope.createShareLocationPresenter(
|
private fun createShareLocationPresenter(
|
||||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||||
timelineMode: Timeline.Mode = Timeline.Mode.Live,
|
timelineMode: Timeline.Mode = Timeline.Mode.Live,
|
||||||
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,
|
client: FakeMatrixClient = fakeMatrixClient,
|
||||||
|
remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(),
|
||||||
): ShareLocationPresenter = ShareLocationPresenter(
|
): ShareLocationPresenter = ShareLocationPresenter(
|
||||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||||
room = joinedRoom,
|
room = joinedRoom,
|
||||||
@@ -104,6 +106,7 @@ class ShareLocationPresenterTest {
|
|||||||
liveLocationShareManager = liveLocationShareManager,
|
liveLocationShareManager = liveLocationShareManager,
|
||||||
liveLocationStore = liveLocationStore,
|
liveLocationStore = liveLocationStore,
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
|
remoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -118,7 +121,7 @@ class ShareLocationPresenterTest {
|
|||||||
val shareLocationPresenter = createShareLocationPresenter()
|
val shareLocationPresenter = createShareLocationPresenter()
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
val state = awaitFirstItem()
|
val state = awaitFirstItem()
|
||||||
assertThat(state.customMapStyleUrl.isLoading()).isFalse()
|
assertThat(state.customMapTilerConfig.isLoading()).isFalse()
|
||||||
assertThat(state.trackUserLocation).isTrue()
|
assertThat(state.trackUserLocation).isTrue()
|
||||||
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None))
|
||||||
}
|
}
|
||||||
@@ -126,18 +129,20 @@ class ShareLocationPresenterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - non-null customMapStyleUrl`() = runTest {
|
fun `present - non-null customMapStyleUrl`() = runTest {
|
||||||
|
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
||||||
val shareLocationPresenter = createShareLocationPresenter(
|
val shareLocationPresenter = createShareLocationPresenter(
|
||||||
client = FakeMatrixClient(
|
client = FakeMatrixClient(
|
||||||
sessionId = A_USER_ID,
|
sessionId = A_USER_ID,
|
||||||
getMapStyleUrlResult = { Result.success("aUrl") },
|
),
|
||||||
|
remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(
|
||||||
|
getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = mapTilerConfig)) }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
skipItems(1)
|
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.customMapStyleUrl.isLoading()).isTrue()
|
assertThat(state.customMapTilerConfig.isLoading()).isTrue()
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl")
|
assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +714,7 @@ class ShareLocationPresenterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||||
skipItems(2)
|
skipItems(1)
|
||||||
return awaitItem()
|
return awaitItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show
|
|||||||
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
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.api.ShowLocationEntryPoint
|
import io.element.android.features.location.api.ShowLocationEntryPoint
|
||||||
import io.element.android.features.location.api.ShowLocationMode
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
@@ -20,7 +21,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.A_SESSION_ID
|
||||||
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
|
||||||
@@ -41,8 +42,8 @@ class DefaultShowLocationEntryPointTest {
|
|||||||
ShowLocationNode(
|
ShowLocationNode(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
presenterFactory = object : ShowLocationPresenter.Factory {
|
presenterFactory = { mode ->
|
||||||
override fun create(mode: ShowLocationMode) = ShowLocationPresenter(
|
ShowLocationPresenter(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
permissionsPresenterFactory = { FakePermissionsPresenter() },
|
permissionsPresenterFactory = { FakePermissionsPresenter() },
|
||||||
locationActions = FakeLocationActions(),
|
locationActions = FakeLocationActions(),
|
||||||
@@ -50,9 +51,10 @@ class DefaultShowLocationEntryPointTest {
|
|||||||
dateFormatter = FakeDateFormatter(),
|
dateFormatter = FakeDateFormatter(),
|
||||||
stringProvider = FakeStringProvider(),
|
stringProvider = FakeStringProvider(),
|
||||||
joinedRoom = joinedRoom,
|
joinedRoom = joinedRoom,
|
||||||
client = FakeMatrixClient(),
|
sessionId = A_SESSION_ID,
|
||||||
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
|
remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
analyticsService = FakeAnalyticsService(),
|
analyticsService = FakeAnalyticsService(),
|
||||||
|
|||||||
+36
-11
@@ -12,6 +12,7 @@ import app.cash.molecule.RecompositionMode
|
|||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
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 io.element.android.features.enterprise.test.remoteconfig.FakeRemoteEnterpriseConfigProvider
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
import io.element.android.features.location.api.ShowLocationMode
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.impl.aPermissionsState
|
import io.element.android.features.location.impl.aPermissionsState
|
||||||
@@ -22,17 +23,20 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE
|
|||||||
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.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.features.wellknown.test.aMapTilerConfig
|
||||||
|
import io.element.android.features.wellknown.test.anElementWellKnown
|
||||||
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.SessionId
|
||||||
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_SESSION_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
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
|
||||||
|
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
|
||||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
@@ -65,8 +69,9 @@ class ShowLocationPresenterTest {
|
|||||||
),
|
),
|
||||||
locationActions: FakeLocationActions = fakeLocationActions,
|
locationActions: FakeLocationActions = fakeLocationActions,
|
||||||
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
joinedRoom: JoinedRoom = FakeJoinedRoom(),
|
||||||
client: MatrixClient = FakeMatrixClient(),
|
sessionId: SessionId = A_SESSION_ID,
|
||||||
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(),
|
||||||
|
remoteEnterpriseConfigProvider: FakeRemoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(),
|
||||||
) = ShowLocationPresenter(
|
) = ShowLocationPresenter(
|
||||||
mode = mode,
|
mode = mode,
|
||||||
permissionsPresenterFactory = { fakePermissionsPresenter },
|
permissionsPresenterFactory = { fakePermissionsPresenter },
|
||||||
@@ -75,24 +80,26 @@ class ShowLocationPresenterTest {
|
|||||||
dateFormatter = fakeDateFormatter,
|
dateFormatter = fakeDateFormatter,
|
||||||
stringProvider = FakeStringProvider(),
|
stringProvider = FakeStringProvider(),
|
||||||
joinedRoom = joinedRoom,
|
joinedRoom = joinedRoom,
|
||||||
client = client,
|
sessionId = sessionId,
|
||||||
liveLocationShareManager = liveLocationShareManager,
|
liveLocationShareManager = liveLocationShareManager,
|
||||||
userLocationStateFactory = FakeUserLocationStateFactory(),
|
userLocationStateFactory = FakeUserLocationStateFactory(),
|
||||||
|
remoteEnterpriseConfigProvider = remoteEnterpriseConfigProvider,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - non-null customMapStyleUrl`() = runTest {
|
fun `present - non-null customMapStyleUrl`() = runTest {
|
||||||
|
val mapTilerConfig = aMapTilerConfig(apiKey = "A KEY")
|
||||||
val shareLocationPresenter = createShowLocationPresenter(
|
val shareLocationPresenter = createShowLocationPresenter(
|
||||||
client = FakeMatrixClient(
|
sessionId = A_SESSION_ID,
|
||||||
sessionId = A_USER_ID,
|
remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(
|
||||||
getMapStyleUrlResult = { Result.success("aUrl") },
|
getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = mapTilerConfig)) }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
shareLocationPresenter.test {
|
shareLocationPresenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.customMapStyleUrl.isLoading()).isTrue()
|
assertThat(state.customMapTilerConfig.isLoading()).isTrue()
|
||||||
val finalState = awaitItem()
|
val finalState = awaitItem()
|
||||||
assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl")
|
assertThat(finalState.customMapTilerConfig.dataOrNull()).isEqualTo(mapTilerConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +122,8 @@ class ShowLocationPresenterTest {
|
|||||||
initialState.eventSink(ShowLocationEvent.Share(location))
|
initialState.eventSink(ShowLocationEvent.Share(location))
|
||||||
|
|
||||||
assertThat(fakeLocationActions.sharedLocation).isEqualTo(location)
|
assertThat(fakeLocationActions.sharedLocation).isEqualTo(location)
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +132,12 @@ class ShowLocationPresenterTest {
|
|||||||
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
|
||||||
|
|
||||||
val presenter = createShowLocationPresenter(
|
val presenter = createShowLocationPresenter(
|
||||||
client = FakeMatrixClient(
|
sessionId = A_SESSION_ID,
|
||||||
getMapStyleUrlResult = { Result.success(null) }
|
remoteEnterpriseConfigProvider = FakeRemoteEnterpriseConfigProvider(
|
||||||
|
getResult = { WellknownRetrieverResult.Success(anElementWellKnown(mapTilerConfig = aMapTilerConfig())) }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isTrackMyLocation).isFalse()
|
assertThat(initialState.isTrackMyLocation).isFalse()
|
||||||
@@ -281,6 +292,8 @@ class ShowLocationPresenterTest {
|
|||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.appName).isEqualTo("app name")
|
assertThat(initialState.appName).isEqualTo("app name")
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,6 +347,8 @@ class ShowLocationPresenterTest {
|
|||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.locationShares).isEmpty()
|
assertThat(initialState.locationShares).isEmpty()
|
||||||
assertThat(initialState.isSheetDraggable).isFalse()
|
assertThat(initialState.isSheetDraggable).isFalse()
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,6 +423,9 @@ class ShowLocationPresenterTest {
|
|||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.locationShares).isEmpty()
|
assertThat(initialState.locationShares).isEmpty()
|
||||||
|
|
||||||
|
// Wait until we receive the custom map style URL result
|
||||||
|
skipItems(1)
|
||||||
|
|
||||||
// Emit a new live share
|
// Emit a new live share
|
||||||
liveSharesFlow.value = listOf(
|
liveSharesFlow.value = listOf(
|
||||||
aLiveLocationShare(userId = userId)
|
aLiveLocationShare(userId = userId)
|
||||||
@@ -447,6 +465,8 @@ class ShowLocationPresenterTest {
|
|||||||
assertThat(item.avatarData.id).isEqualTo(senderId.value)
|
assertThat(item.avatarData.id).isEqualTo(senderId.value)
|
||||||
assertThat(item.avatarData.name).isEqualTo(senderName)
|
assertThat(item.avatarData.name).isEqualTo(senderName)
|
||||||
assertThat(item.avatarData.url).isEqualTo(avatarUrl)
|
assertThat(item.avatarData.url).isEqualTo(avatarUrl)
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,6 +476,7 @@ class ShowLocationPresenterTest {
|
|||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.isSheetDraggable).isFalse()
|
assertThat(state.isSheetDraggable).isFalse()
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,6 +486,8 @@ class ShowLocationPresenterTest {
|
|||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.hideUserLocationPuck).isFalse()
|
assertThat(state.hideUserLocationPuck).isFalse()
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,6 +499,8 @@ class ShowLocationPresenterTest {
|
|||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.hideUserLocationPuck).isFalse()
|
assertThat(state.hideUserLocationPuck).isFalse()
|
||||||
|
|
||||||
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.push.api)
|
implementation(projects.libraries.push.api)
|
||||||
implementation(projects.libraries.uiUtils)
|
implementation(projects.libraries.uiUtils)
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
|
implementation(projects.libraries.wellknown.api)
|
||||||
implementation(projects.features.networkmonitor.api)
|
implementation(projects.features.networkmonitor.api)
|
||||||
implementation(projects.features.contentscanner.api)
|
implementation(projects.features.contentscanner.api)
|
||||||
implementation(projects.services.analytics.compose)
|
implementation(projects.services.analytics.compose)
|
||||||
|
|||||||
+2
@@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
|
import io.element.android.features.location.api.LocalMapTilerConfig
|
||||||
import io.element.android.features.location.api.StaticMapView
|
import io.element.android.features.location.api.StaticMapView
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider
|
||||||
@@ -50,6 +51,7 @@ fun TimelineItemLocationView(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.heightIn(max = 188.dp),
|
.heightIn(max = 188.dp),
|
||||||
|
mapTilerConfig = LocalMapTilerConfig.current,
|
||||||
pinVariant = content.pinVariant,
|
pinVariant = content.pinVariant,
|
||||||
location = content.location,
|
location = content.location,
|
||||||
zoom = 15.0,
|
zoom = 15.0,
|
||||||
|
|||||||
-5
@@ -255,11 +255,6 @@ interface MatrixClient : UrlContentFetcher {
|
|||||||
*/
|
*/
|
||||||
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
@@ -886,12 +886,6 @@ 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
@@ -129,7 +129,6 @@ class FakeMatrixClient(
|
|||||||
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
|
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||||
private val markAllRoomsAsReadResult: () -> Result<Unit> = { Result.success(Unit) },
|
private val markAllRoomsAsReadResult: () -> Result<Unit> = { Result.success(Unit) },
|
||||||
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() },
|
||||||
private val enableAutomaticCallStatusLambda: (Boolean) -> Unit = { },
|
private val enableAutomaticCallStatusLambda: (Boolean) -> Unit = { },
|
||||||
@@ -435,10 +434,6 @@ 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()
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -18,4 +18,19 @@ data class ElementWellKnown(
|
|||||||
val customRecoveryPassphrase: CustomRecoveryPassphrase?,
|
val customRecoveryPassphrase: CustomRecoveryPassphrase?,
|
||||||
val contentScannerUrl: String?,
|
val contentScannerUrl: String?,
|
||||||
val forceDisableE2EE: Boolean?,
|
val forceDisableE2EE: Boolean?,
|
||||||
|
val mapTilerConfig: MapTilerConfig?,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `MapTiler` configuration received from the server. This configuration is used to display maps in the app.
|
||||||
|
*/
|
||||||
|
data class MapTilerConfig(
|
||||||
|
/** The API key to use with MapTiler. This is always not null, but can be empty/blank. */
|
||||||
|
val apiKey: String,
|
||||||
|
/** The style ID to use for light mode. This is optional and can be null. */
|
||||||
|
val lightStyleId: String?,
|
||||||
|
/** The style ID to use for light mode. This is optional and can be null. */
|
||||||
|
val darkStyleId: String?,
|
||||||
|
/** The base URL to use for MapTiler. This is optional and can be null. */
|
||||||
|
val baseUrl: String?,
|
||||||
)
|
)
|
||||||
|
|||||||
+14
@@ -48,6 +48,8 @@ data class InternalElementWellKnown(
|
|||||||
val contentScannerUrl: String? = null,
|
val contentScannerUrl: String? = null,
|
||||||
@SerialName("force_disable_e2ee")
|
@SerialName("force_disable_e2ee")
|
||||||
val forceDisableE2EE: Boolean? = null,
|
val forceDisableE2EE: Boolean? = null,
|
||||||
|
@SerialName("map_tiler")
|
||||||
|
val mapTilerConfig: InternalMapTilerConfig? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -55,3 +57,15 @@ data class InternalCustomRecoveryPassphrase(
|
|||||||
@SerialName("min_character_count")
|
@SerialName("min_character_count")
|
||||||
val minCharacterCount: Int? = null,
|
val minCharacterCount: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class InternalMapTilerConfig(
|
||||||
|
@SerialName("api_key")
|
||||||
|
val apiKey: String,
|
||||||
|
@SerialName("light_style_id")
|
||||||
|
val lightStyleId: String? = null,
|
||||||
|
@SerialName("dark_style_id")
|
||||||
|
val darkStyleId: String? = null,
|
||||||
|
@SerialName("base_url")
|
||||||
|
val baseUrl: String? = null,
|
||||||
|
)
|
||||||
|
|||||||
+9
@@ -11,6 +11,7 @@ package io.element.android.libraries.wellknown.impl
|
|||||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||||
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
|
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
|
||||||
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
private val loggerTag = LoggerTag("Wellknown")
|
private val loggerTag = LoggerTag("Wellknown")
|
||||||
@@ -32,6 +33,14 @@ fun InternalElementWellKnown.map() = ElementWellKnown(
|
|||||||
customRecoveryPassphrase = customRecoveryPassphrase?.toPublic(),
|
customRecoveryPassphrase = customRecoveryPassphrase?.toPublic(),
|
||||||
contentScannerUrl = contentScannerUrl,
|
contentScannerUrl = contentScannerUrl,
|
||||||
forceDisableE2EE = forceDisableE2EE,
|
forceDisableE2EE = forceDisableE2EE,
|
||||||
|
mapTilerConfig = mapTilerConfig?.let {
|
||||||
|
MapTilerConfig(
|
||||||
|
apiKey = it.apiKey,
|
||||||
|
lightStyleId = it.lightStyleId,
|
||||||
|
darkStyleId = it.darkStyleId,
|
||||||
|
baseUrl = it.baseUrl,
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun InternalCustomRecoveryPassphrase.toPublic(): CustomRecoveryPassphrase {
|
private fun InternalCustomRecoveryPassphrase.toPublic(): CustomRecoveryPassphrase {
|
||||||
|
|||||||
+10
-1
@@ -13,6 +13,7 @@ package io.element.android.libraries.wellknown.impl
|
|||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
import io.element.android.features.enterprise.test.FakeEnterpriseService
|
||||||
import io.element.android.features.wellknown.test.FakeElementWellknownStore
|
import io.element.android.features.wellknown.test.FakeElementWellknownStore
|
||||||
|
import io.element.android.features.wellknown.test.aMapTilerConfig
|
||||||
import io.element.android.features.wellknown.test.anElementWellKnown
|
import io.element.android.features.wellknown.test.anElementWellKnown
|
||||||
import io.element.android.libraries.core.uri.ensureProtocol
|
import io.element.android.libraries.core.uri.ensureProtocol
|
||||||
import io.element.android.libraries.matrix.api.UrlContentFetcher
|
import io.element.android.libraries.matrix.api.UrlContentFetcher
|
||||||
@@ -204,7 +205,11 @@ class DefaultWellknownRetrieverTest {
|
|||||||
"notification_sound": "a_notification_sound.flac",
|
"notification_sound": "a_notification_sound.flac",
|
||||||
"idp_app_scheme": "an_app_scheme",
|
"idp_app_scheme": "an_app_scheme",
|
||||||
"content_scanner_url": "https://content-scanner.example.com",
|
"content_scanner_url": "https://content-scanner.example.com",
|
||||||
"force_disable_e2ee": false
|
"force_disable_e2ee": false,
|
||||||
|
"map_tiler": {
|
||||||
|
"api_key": "test_api_key",
|
||||||
|
"base_url": "https://tile-server.example.com"
|
||||||
|
}
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
private val expectedElementWellKnown = ElementWellKnown(
|
private val expectedElementWellKnown = ElementWellKnown(
|
||||||
@@ -217,6 +222,10 @@ class DefaultWellknownRetrieverTest {
|
|||||||
contentScannerUrl = "https://content-scanner.example.com",
|
contentScannerUrl = "https://content-scanner.example.com",
|
||||||
customRecoveryPassphrase = null,
|
customRecoveryPassphrase = null,
|
||||||
forceDisableE2EE = false,
|
forceDisableE2EE = false,
|
||||||
|
mapTilerConfig = aMapTilerConfig(
|
||||||
|
apiKey = "test_api_key",
|
||||||
|
baseUrl = "https://tile-server.example.com",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -10,6 +10,7 @@ package io.element.android.features.wellknown.test
|
|||||||
|
|
||||||
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
|
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
|
||||||
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
||||||
|
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||||
|
|
||||||
fun anElementWellKnown(
|
fun anElementWellKnown(
|
||||||
registrationHelperUrl: String? = null,
|
registrationHelperUrl: String? = null,
|
||||||
@@ -21,6 +22,7 @@ fun anElementWellKnown(
|
|||||||
customRecoveryPassphrase: CustomRecoveryPassphrase? = null,
|
customRecoveryPassphrase: CustomRecoveryPassphrase? = null,
|
||||||
contentScannerUrl: String? = null,
|
contentScannerUrl: String? = null,
|
||||||
forceDisableE2EE: Boolean? = null,
|
forceDisableE2EE: Boolean? = null,
|
||||||
|
mapTilerConfig: MapTilerConfig? = null,
|
||||||
) = ElementWellKnown(
|
) = ElementWellKnown(
|
||||||
registrationHelperUrl = registrationHelperUrl,
|
registrationHelperUrl = registrationHelperUrl,
|
||||||
enforceElementPro = enforceElementPro,
|
enforceElementPro = enforceElementPro,
|
||||||
@@ -31,6 +33,7 @@ fun anElementWellKnown(
|
|||||||
customRecoveryPassphrase = customRecoveryPassphrase,
|
customRecoveryPassphrase = customRecoveryPassphrase,
|
||||||
contentScannerUrl = contentScannerUrl,
|
contentScannerUrl = contentScannerUrl,
|
||||||
forceDisableE2EE = forceDisableE2EE,
|
forceDisableE2EE = forceDisableE2EE,
|
||||||
|
mapTilerConfig = mapTilerConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun aCustomRecoveryPassphrase(
|
fun aCustomRecoveryPassphrase(
|
||||||
@@ -38,3 +41,15 @@ fun aCustomRecoveryPassphrase(
|
|||||||
) = CustomRecoveryPassphrase(
|
) = CustomRecoveryPassphrase(
|
||||||
minCharacterCount = minCharacterCount,
|
minCharacterCount = minCharacterCount,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun aMapTilerConfig(
|
||||||
|
apiKey: String = "test_api_key",
|
||||||
|
baseUrl: String? = null,
|
||||||
|
lightStyleId: String? = null,
|
||||||
|
darkStyleId: String? = null,
|
||||||
|
) = MapTilerConfig(
|
||||||
|
apiKey = apiKey,
|
||||||
|
baseUrl = baseUrl,
|
||||||
|
lightStyleId = lightStyleId,
|
||||||
|
darkStyleId = darkStyleId,
|
||||||
|
)
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ Compose:
|
|||||||
- LocalEventContentValidationState
|
- LocalEventContentValidationState
|
||||||
- LocalSnackbarDispatcher
|
- LocalSnackbarDispatcher
|
||||||
- LocalCameraPositionState
|
- LocalCameraPositionState
|
||||||
|
- LocalMapTilerConfig
|
||||||
- LocalMediaItemPresenterFactories
|
- LocalMediaItemPresenterFactories
|
||||||
- LocalTimelineItemPresenterFactories
|
- LocalTimelineItemPresenterFactories
|
||||||
- LocalRoomMemberProfilesCache
|
- LocalRoomMemberProfilesCache
|
||||||
|
|||||||
Reference in New Issue
Block a user