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:
Jorge Martin Espinosa
2026-08-14 08:20:37 +02:00
committed by GitHub
parent ddbc6a2a33
commit 22195a47f2
35 changed files with 279 additions and 117 deletions
@@ -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.theme.components.BottomSheetScaffold
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.rememberCameraState
import org.maplibre.compose.map.MapOptions
@@ -66,7 +67,7 @@ import kotlin.math.roundToInt
* - Updating camera position padding based on sheet height
* - 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 scaffoldState State for the bottom sheet scaffold
* @param cameraState The camera state for the map
@@ -83,7 +84,7 @@ import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MapBottomSheetScaffold(
customMapStyleUrl: AsyncData<String?>,
customMapTilerConfig: AsyncData<MapTilerConfig?>,
modifier: Modifier = Modifier,
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = rememberBottomSheetState(
@@ -137,11 +138,11 @@ fun MapBottomSheetScaffold(
val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding)
val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions)
Box {
when (customMapStyleUrl) {
when (customMapTilerConfig) {
is AsyncData.Success -> {
MaplibreMap(
options = mapOptions,
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapStyleUrl.data)),
baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapTilerConfig.data)),
modifier = Modifier.fillMaxSize(),
cameraState = cameraState,
content = mapContent,
@@ -22,6 +22,7 @@ import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
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.impl.common.LocationConstraintsCheck
import io.element.android.features.location.impl.common.MapDefaults
@@ -72,6 +73,7 @@ class ShareLocationPresenter(
private val liveLocationShareManager: ActiveLiveLocationShareManager,
private val liveLocationStore: LiveLocationStore,
private val userLocationStateFactory: UserLocationState.Factory,
private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider,
) : Presenter<ShareLocationState> {
@AssistedFactory
fun interface Factory {
@@ -92,9 +94,9 @@ class ShareLocationPresenter(
var pendingLiveLocationShare by remember { mutableStateOf(false) }
val startLiveLocationAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
val currentUser by client.userProfile.collectAsState()
val customMapStyleUrl by produceState(AsyncData.Loading()) {
val customMapConfig by produceState(AsyncData.Loading()) {
// 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 ->
perms.sendLiveLocationPermissions()
@@ -199,7 +201,7 @@ class ShareLocationPresenter(
}
return ShareLocationState(
customMapStyleUrl = customMapStyleUrl,
customMapTilerConfig = customMapConfig,
currentUser = currentUser,
dialogState = dialogState,
trackUserLocation = trackUserPosition,
@@ -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.AsyncData
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.wellknown.api.MapTilerConfig
import kotlinx.collections.immutable.ImmutableList
data class ShareLocationState(
val customMapStyleUrl: AsyncData<String?>,
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
val currentUser: MatrixUser,
val dialogState: Dialog,
val trackUserLocation: Boolean,
@@ -15,6 +15,7 @@ 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.user.MatrixUser
import io.element.android.libraries.wellknown.api.MapTilerConfig
import kotlinx.collections.immutable.persistentListOf
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
@@ -70,13 +71,13 @@ class ShareLocationStateProvider : PreviewParameterProvider<ShareLocationState>
canShareLiveLocation = true,
),
aShareLocationState(
customMapStyleUrl = AsyncData.Loading(),
customMapTilerConfig = AsyncData.Loading(),
),
)
}
fun aShareLocationState(
customMapStyleUrl: AsyncData<String?> = AsyncData.Success(null),
customMapTilerConfig: AsyncData<MapTilerConfig?> = AsyncData.Success(null),
currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")),
dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None,
trackUserPosition: Boolean = false,
@@ -87,7 +88,7 @@ fun aShareLocationState(
eventSink: (ShareLocationEvent) -> Unit = {},
): ShareLocationState {
return ShareLocationState(
customMapStyleUrl = customMapStyleUrl,
customMapTilerConfig = customMapTilerConfig,
currentUser = currentUser,
dialogState = dialogState,
trackUserLocation = trackUserPosition,
@@ -95,6 +96,6 @@ fun aShareLocationState(
canShareLiveLocation = canShareLiveLocation,
appName = appName,
startLiveLocationAction = startLiveLocationAction,
eventSink = eventSink
eventSink = eventSink,
)
}
@@ -118,7 +118,7 @@ fun ShareLocationView(
}
MapBottomSheetScaffold(
customMapStyleUrl = state.customMapStyleUrl,
customMapTilerConfig = state.customMapTilerConfig,
cameraState = cameraState,
modifier = modifier,
scaffoldState = scaffoldState,
@@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
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.ShowLocationMode
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.designsystem.components.avatar.AvatarData
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.getBestName
import io.element.android.libraries.matrix.api.room.joinedRoomMembers
@@ -64,10 +65,11 @@ import kotlinx.coroutines.launch
private val buildMeta: BuildMeta,
private val dateFormatter: DateFormatter,
private val stringProvider: StringProvider,
private val client: MatrixClient,
private val sessionId: SessionId,
private val joinedRoom: JoinedRoom,
private val liveLocationShareManager: ActiveLiveLocationShareManager,
private val userLocationStateFactory: UserLocationState.Factory,
private val remoteEnterpriseConfigProvider: RemoteEnterpriseConfigProvider,
) : Presenter<ShowLocationState> {
@AssistedFactory fun interface Factory {
fun create(mode: ShowLocationMode): ShowLocationPresenter
@@ -85,9 +87,9 @@ import kotlinx.coroutines.launch
mutableStateOf(LocationConstraintsDialogState.None)
}
val customMapStyleUrl by produceState(AsyncData.Loading()) {
val customMapTilerConfig by produceState(AsyncData.Loading()) {
// Ignore errors
value = AsyncData.Success(client.getMapStyleUrl().getOrNull())
value = AsyncData.Success(remoteEnterpriseConfigProvider.get(sessionId).dataOrNull()?.mapTilerConfig)
}
fun checkLocationConstraints() {
@@ -220,7 +222,7 @@ import kotlinx.coroutines.launch
userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted)
}
return ShowLocationState(
customMapStyleUrl = customMapStyleUrl,
customMapTilerConfig = customMapTilerConfig,
dialogState = dialogState,
locationShares = locationShares,
focusedLocation = focusedLocation,
@@ -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.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.wellknown.api.MapTilerConfig
import kotlinx.collections.immutable.ImmutableList
data class ShowLocationState(
val customMapStyleUrl: AsyncData<String?>,
val customMapTilerConfig: AsyncData<MapTilerConfig?>,
val isLive: Boolean,
val dialogState: LocationConstraintsDialogState,
val locationShares: ImmutableList<LocationShareItem>,
@@ -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.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.wellknown.api.MapTilerConfig
import kotlinx.collections.immutable.toImmutableList
class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
@@ -36,14 +37,14 @@ class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled,
),
aShowLocationState(isTrackMyLocation = true),
aShowLocationState(customMapStyleUrl = AsyncData.Loading()),
aShowLocationState(customMapTilerConfig = AsyncData.Loading()),
)
}
private const val APP_NAME = "ApplicationName"
fun aShowLocationState(
customMapStyleUrl: AsyncData<String?> = AsyncData.Success(null),
customMapTilerConfig: AsyncData<MapTilerConfig?> = AsyncData.Success(null),
isLive: Boolean = false,
constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None,
locationShares: List<LocationShareItem> = listOf(aLocationShareItem(isLive = isLive)),
@@ -55,7 +56,7 @@ fun aShowLocationState(
eventSink: (ShowLocationEvent) -> Unit = {},
): ShowLocationState {
return ShowLocationState(
customMapStyleUrl = customMapStyleUrl,
customMapTilerConfig = customMapTilerConfig,
dialogState = constraintsDialogState,
locationShares = locationShares.toImmutableList(),
focusedLocation = focusedLocation,
@@ -102,7 +102,7 @@ fun ShowLocationView(
}
}
MapBottomSheetScaffold(
customMapStyleUrl = state.customMapStyleUrl,
customMapTilerConfig = state.customMapTilerConfig,
sheetDragHandle = if (state.isSheetDraggable) {
{ BottomSheetDefaults.DragHandle() }
} else {