Improve API and fix theme glitch when switching between accounts.

This commit is contained in:
Benoit Marty 2025-10-22 09:32:15 +02:00
parent ea2fc290b7
commit c2c77aad2a
13 changed files with 120 additions and 61 deletions

View file

@ -16,6 +16,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
@ -26,6 +29,7 @@ import com.bumble.appyx.core.integration.NodeHost
import com.bumble.appyx.core.integrationpoint.NodeActivity import com.bumble.appyx.core.integrationpoint.NodeActivity
import com.bumble.appyx.core.plugin.NodeReadyObserver import com.bumble.appyx.core.plugin.NodeReadyObserver
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.features.lockscreen.api.LockScreenLockState import io.element.android.features.lockscreen.api.LockScreenLockState
import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.api.LockScreenService
@ -61,9 +65,13 @@ class MainActivity : NodeActivity() {
@Composable @Composable
private fun MainContent(appBindings: AppBindings) { private fun MainContent(appBindings: AppBindings) {
val migrationState = appBindings.migrationEntryPoint().present() val migrationState = appBindings.migrationEntryPoint().present()
val colors by remember {
appBindings.enterpriseService().semanticColorsFlow(sessionId = null)
}.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appBindings.preferencesStore(), appPreferencesStore = appBindings.preferencesStore(),
enterpriseService = appBindings.enterpriseService(), compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = appBindings.buildMeta() buildMeta = appBindings.buildMeta()
) { ) {
CompositionLocalProvider( CompositionLocalProvider(

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.PermanentChild import com.bumble.appyx.core.composable.PermanentChild
@ -46,6 +47,8 @@ import io.element.android.appnav.loggedin.SendQueues
import io.element.android.appnav.room.RoomFlowNode import io.element.android.appnav.room.RoomFlowNode
import io.element.android.appnav.room.RoomNavigationTarget import io.element.android.appnav.room.RoomNavigationTarget
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.features.enterprise.api.SessionEnterpriseService import io.element.android.features.enterprise.api.SessionEnterpriseService
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
@ -66,6 +69,8 @@ import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.waitForNavTargetAttached import io.element.android.libraries.architecture.waitForNavTargetAttached
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
@ -79,6 +84,7 @@ import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener
import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.api.verification.VerificationRequest
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService
import io.element.android.libraries.ui.common.nodes.emptyNode import io.element.android.libraries.ui.common.nodes.emptyNode
import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.AppNavigationStateService
@ -122,6 +128,9 @@ class LoggedInFlowNode(
private val sessionEnterpriseService: SessionEnterpriseService, private val sessionEnterpriseService: SessionEnterpriseService,
private val networkMonitor: NetworkMonitor, private val networkMonitor: NetworkMonitor,
private val notificationConversationService: NotificationConversationService, private val notificationConversationService: NotificationConversationService,
private val enterpriseService: EnterpriseService,
private val appPreferencesStore: AppPreferencesStore,
private val buildMeta: BuildMeta,
snackbarDispatcher: SnackbarDispatcher, snackbarDispatcher: SnackbarDispatcher,
) : BaseFlowNode<LoggedInFlowNode.NavTarget>( ) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
@ -538,11 +547,21 @@ class LoggedInFlowNode(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
Box(modifier = modifier) { val colors by remember {
val ftueState by ftueService.state.collectAsState() enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
BackstackView() }.collectAsState(SemanticColorsLightDark.default)
if (ftueState is FtueState.Complete) { ElementThemeApp(
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) appPreferencesStore = appPreferencesStore,
compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = buildMeta,
) {
Box(modifier = modifier) {
val ftueState by ftueService.state.collectAsState()
BackstackView()
if (ftueState is FtueState.Complete) {
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
}
} }
} }
} }

@ -1 +1 @@
Subproject commit f662f079f911b728e5769d10268e2c2775d7287a Subproject commit b5ab0a6aa01aa03e89fa7593ea144a043b8f7487

View file

@ -31,3 +31,10 @@ sealed interface CallType : NodeInputs, Parcelable {
} }
} }
} }
fun CallType.getSessionId(): SessionId? {
return when (this) {
is CallType.ExternalUrl -> null
is CallType.RoomCall -> sessionId
}
}

View file

@ -23,8 +23,10 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.core.app.PictureInPictureModeChangedInfo import androidx.core.app.PictureInPictureModeChangedInfo
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
@ -33,6 +35,7 @@ import androidx.lifecycle.Lifecycle
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.features.call.api.CallType import io.element.android.features.call.api.CallType
import io.element.android.features.call.api.CallType.ExternalUrl import io.element.android.features.call.api.CallType.ExternalUrl
import io.element.android.features.call.api.getSessionId
import io.element.android.features.call.impl.DefaultElementCallEntryPoint import io.element.android.features.call.impl.DefaultElementCallEntryPoint
import io.element.android.features.call.impl.di.CallBindings import io.element.android.features.call.impl.di.CallBindings
import io.element.android.features.call.impl.pip.PictureInPictureEvents import io.element.android.features.call.impl.pip.PictureInPictureEvents
@ -42,6 +45,7 @@ import io.element.android.features.call.impl.pip.PipView
import io.element.android.features.call.impl.services.CallForegroundService import io.element.android.features.call.impl.services.CallForegroundService
import io.element.android.features.call.impl.utils.CallIntentDataParser import io.element.android.features.call.impl.utils.CallIntentDataParser
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.libraries.androidutils.browser.ConsoleMessageLogger import io.element.android.libraries.androidutils.browser.ConsoleMessageLogger
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
@ -105,9 +109,13 @@ class ElementCallActivity :
setContent { setContent {
val pipState = pictureInPicturePresenter.present() val pipState = pictureInPicturePresenter.present()
ListenToAndroidEvents(pipState) ListenToAndroidEvents(pipState)
val colors by remember(webViewTarget.value?.getSessionId()) {
enterpriseService.semanticColorsFlow(sessionId = webViewTarget.value?.getSessionId())
}.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
enterpriseService = enterpriseService, compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = buildMeta, buildMeta = buildMeta,
) { ) {
val state = presenter.present() val state = presenter.present()

View file

@ -11,6 +11,9 @@ import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
@ -21,6 +24,7 @@ import io.element.android.features.call.impl.notifications.CallNotificationData
import io.element.android.features.call.impl.utils.ActiveCallManager import io.element.android.features.call.impl.utils.ActiveCallManager
import io.element.android.features.call.impl.utils.CallState import io.element.android.features.call.impl.utils.CallState
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.designsystem.theme.ElementThemeApp
@ -78,9 +82,13 @@ class IncomingCallActivity : AppCompatActivity() {
val notificationData = intent?.let { IntentCompat.getParcelableExtra(it, EXTRA_NOTIFICATION_DATA, CallNotificationData::class.java) } val notificationData = intent?.let { IntentCompat.getParcelableExtra(it, EXTRA_NOTIFICATION_DATA, CallNotificationData::class.java) }
if (notificationData != null) { if (notificationData != null) {
setContent { setContent {
val colors by remember {
enterpriseService.semanticColorsFlow(sessionId = notificationData.sessionId)
}.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
enterpriseService = enterpriseService, compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = buildMeta, buildMeta = buildMeta,
) { ) {
IncomingCallScreen( IncomingCallScreen(

View file

@ -7,9 +7,9 @@
package io.element.android.features.enterprise.api package io.element.android.features.enterprise.api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import io.element.android.compound.tokens.generated.SemanticColors import io.element.android.compound.tokens.generated.SemanticColors
import io.element.android.compound.tokens.generated.compoundColorsDark
import io.element.android.compound.tokens.generated.compoundColorsLight
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -26,16 +26,12 @@ interface EnterpriseService {
*/ */
suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?)
@Composable fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark>
fun semanticColorsLight(): State<SemanticColors>
@Composable
fun semanticColorsDark(): State<SemanticColors>
fun firebasePushGateway(): String? fun firebasePushGateway(): String?
fun unifiedPushDefaultPushGateway(): String? fun unifiedPushDefaultPushGateway(): String?
val bugReportUrlFlow: Flow<BugReportUrl> fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl>
companion object { companion object {
const val ANY_ACCOUNT_PROVIDER = "*" const val ANY_ACCOUNT_PROVIDER = "*"
@ -47,3 +43,15 @@ fun EnterpriseService.canConnectToAnyHomeserver(): Boolean {
it.isEmpty() || it.contains(EnterpriseService.ANY_ACCOUNT_PROVIDER) it.isEmpty() || it.contains(EnterpriseService.ANY_ACCOUNT_PROVIDER)
} }
} }
data class SemanticColorsLightDark(
val light: SemanticColors,
val dark: SemanticColors,
) {
companion object {
val default = SemanticColorsLightDark(
light = compoundColorsLight,
dark = compoundColorsDark,
)
}
}

View file

@ -7,19 +7,14 @@
package io.element.android.features.enterprise.impl package io.element.android.features.enterprise.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.compound.tokens.generated.SemanticColors
import io.element.android.compound.tokens.generated.compoundColorsDark
import io.element.android.compound.tokens.generated.compoundColorsLight
import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.BugReportUrl
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
@ -34,18 +29,14 @@ class DefaultEnterpriseService : EnterpriseService {
override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit
@Composable override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
override fun semanticColorsLight(): State<SemanticColors> { return flowOf(SemanticColorsLightDark.default)
return remember { derivedStateOf { compoundColorsLight } }
}
@Composable
override fun semanticColorsDark(): State<SemanticColors> {
return remember { derivedStateOf { compoundColorsDark } }
} }
override fun firebasePushGateway(): String? = null override fun firebasePushGateway(): String? = null
override fun unifiedPushDefaultPushGateway(): String? = null override fun unifiedPushDefaultPushGateway(): String? = null
override val bugReportUrlFlow = flowOf(BugReportUrl.UseDefault) override fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> {
return flowOf(BugReportUrl.UseDefault)
}
} }

View file

@ -7,11 +7,9 @@
package io.element.android.features.enterprise.test package io.element.android.features.enterprise.test
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import io.element.android.compound.tokens.generated.SemanticColors
import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.BugReportUrl
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask import io.element.android.tests.testutils.simulateLongTask
@ -24,12 +22,13 @@ class FakeEnterpriseService(
private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() }, private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() },
private val defaultHomeserverListResult: () -> List<String> = { emptyList() }, private val defaultHomeserverListResult: () -> List<String> = { emptyList() },
private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() }, private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() },
private val semanticColorsLightResult: () -> State<SemanticColors> = { lambdaError() }, initialSemanticColors: SemanticColorsLightDark = SemanticColorsLightDark.default,
private val semanticColorsDarkResult: () -> State<SemanticColors> = { lambdaError() },
private val overrideBrandColorResult: (SessionId?, String?) -> Unit = { _, _ -> lambdaError() }, private val overrideBrandColorResult: (SessionId?, String?) -> Unit = { _, _ -> lambdaError() },
private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val firebasePushGatewayResult: () -> String? = { lambdaError() },
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
) : EnterpriseService { ) : EnterpriseService {
private val semanticColorsState = MutableStateFlow(initialSemanticColors)
override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask { override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask {
isEnterpriseUserResult(sessionId) isEnterpriseUserResult(sessionId)
} }
@ -46,14 +45,8 @@ class FakeEnterpriseService(
overrideBrandColorResult(sessionId, brandColor) overrideBrandColorResult(sessionId, brandColor)
} }
@Composable override fun semanticColorsFlow(sessionId: SessionId?): Flow<SemanticColorsLightDark> {
override fun semanticColorsLight(): State<SemanticColors> { return semanticColorsState.asStateFlow()
return semanticColorsLightResult()
}
@Composable
override fun semanticColorsDark(): State<SemanticColors> {
return semanticColorsDarkResult()
} }
override fun firebasePushGateway(): String? { override fun firebasePushGateway(): String? {
@ -65,5 +58,7 @@ class FakeEnterpriseService(
} }
val bugReportUrlMutableFlow = MutableStateFlow<BugReportUrl>(BugReportUrl.UseDefault) val bugReportUrlMutableFlow = MutableStateFlow<BugReportUrl>(BugReportUrl.UseDefault)
override val bugReportUrlFlow: Flow<BugReportUrl> = bugReportUrlMutableFlow.asStateFlow() override fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> {
return bugReportUrlMutableFlow.asStateFlow()
}
} }

View file

@ -14,9 +14,13 @@ import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dev.zacsweers.metro.Inject import dev.zacsweers.metro.Inject
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SemanticColorsLightDark
import io.element.android.features.lockscreen.api.LockScreenLockState import io.element.android.features.lockscreen.api.LockScreenLockState
import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter
@ -46,9 +50,13 @@ class PinUnlockActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindings<PinUnlockBindings>().inject(this) bindings<PinUnlockBindings>().inject(this)
setContent { setContent {
val colors by remember {
enterpriseService.semanticColorsFlow(sessionId = null)
}.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
enterpriseService = enterpriseService, compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = buildMeta, buildMeta = buildMeta,
) { ) {
val state = presenter.present() val state = presenter.present()

View file

@ -13,7 +13,12 @@ import dev.zacsweers.metro.Inject
import io.element.android.appconfig.RageshakeConfig import io.element.android.appconfig.RageshakeConfig
import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.BugReportUrl
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.sessionIdFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import okhttp3.HttpUrl import okhttp3.HttpUrl
@ -24,17 +29,21 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
class DefaultBugReporterUrlProvider( class DefaultBugReporterUrlProvider(
private val bugReportAppNameProvider: BugReportAppNameProvider, private val bugReportAppNameProvider: BugReportAppNameProvider,
private val enterpriseService: EnterpriseService, private val enterpriseService: EnterpriseService,
private val sessionStore: SessionStore,
) : BugReporterUrlProvider { ) : BugReporterUrlProvider {
@OptIn(ExperimentalCoroutinesApi::class)
override fun provide(): Flow<HttpUrl?> { override fun provide(): Flow<HttpUrl?> {
if (bugReportAppNameProvider.provide().isEmpty()) return flowOf(null) if (bugReportAppNameProvider.provide().isEmpty()) return flowOf(null)
return enterpriseService.bugReportUrlFlow return sessionStore.sessionIdFlow().flatMapLatest { sessionId ->
.map { bugReportUrl -> enterpriseService.bugReportUrlFlow(sessionId?.let(::SessionId))
when (bugReportUrl) { .map { bugReportUrl ->
is BugReportUrl.Custom -> bugReportUrl.url when (bugReportUrl) {
BugReportUrl.Disabled -> null is BugReportUrl.Custom -> bugReportUrl.url
BugReportUrl.UseDefault -> RageshakeConfig.BUG_REPORT_URL.takeIf { it.isNotEmpty() } BugReportUrl.Disabled -> null
BugReportUrl.UseDefault -> RageshakeConfig.BUG_REPORT_URL.takeIf { it.isNotEmpty() }
}
} }
} .map { it?.toHttpUrl() }
.map { it?.toHttpUrl() } }
} }
} }

View file

@ -33,7 +33,6 @@ android {
implementation(libs.androidx.compose.material3.adaptive) implementation(libs.androidx.compose.material3.adaptive)
implementation(libs.coil.compose) implementation(libs.coil.compose)
implementation(libs.vanniktech.blurhash) implementation(libs.vanniktech.blurhash)
implementation(projects.features.enterprise.api)
implementation(projects.libraries.androidutils) implementation(projects.libraries.androidutils)
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.core) implementation(projects.libraries.core)

View file

@ -19,7 +19,7 @@ import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.theme.Theme import io.element.android.compound.theme.Theme
import io.element.android.compound.theme.isDark import io.element.android.compound.theme.isDark
import io.element.android.compound.theme.mapToTheme import io.element.android.compound.theme.mapToTheme
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.compound.tokens.generated.SemanticColors
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
@ -53,7 +53,8 @@ val LocalBuildMeta = staticCompositionLocalOf {
@Composable @Composable
fun ElementThemeApp( fun ElementThemeApp(
appPreferencesStore: AppPreferencesStore, appPreferencesStore: AppPreferencesStore,
enterpriseService: EnterpriseService, compoundLight: SemanticColors,
compoundDark: SemanticColors,
buildMeta: BuildMeta, buildMeta: BuildMeta,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
@ -70,8 +71,6 @@ fun ElementThemeApp(
} }
) )
} }
val compoundLight by enterpriseService.semanticColorsLight()
val compoundDark by enterpriseService.semanticColorsDark()
CompositionLocalProvider( CompositionLocalProvider(
LocalBuildMeta provides buildMeta, LocalBuildMeta provides buildMeta,
) { ) {