From 67dadb457118f0e7dd96f294f5ad24dac1659b22 Mon Sep 17 00:00:00 2001 From: Sedat Qaja Date: Tue, 20 Oct 2026 13:39:31 +0200 Subject: [PATCH] feat(healthchat): drop Element links, labs, bug reports and location sharing Bloom gradient fades the brand blue instead of Compound's green. --- appconfig/build.gradle.kts | 14 ++++------- .../android/appconfig/LearnMoreConfig.kt | 11 +++++---- .../impl/DefaultEnterpriseService.kt | 24 ++++++++++++++++++- .../impl/DefaultEnterpriseServiceTest.kt | 17 +++++++++++-- .../location/impl/DefaultLocationService.kt | 4 ++++ .../impl/DefaultLocationServiceTest.kt | 7 +++--- .../impl/root/PreferencesRootPresenter.kt | 3 ++- .../impl/root/PreferencesRootPresenterTest.kt | 3 ++- .../src/main/kotlin/config/BuildTimeConfig.kt | 6 +++-- 9 files changed, 64 insertions(+), 25 deletions(-) diff --git a/appconfig/build.gradle.kts b/appconfig/build.gradle.kts index e355499350..2f0a8802a7 100644 --- a/appconfig/build.gradle.kts +++ b/appconfig/build.gradle.kts @@ -22,19 +22,13 @@ android { defaultConfig { buildConfigFieldStr( name = "URL_POLICY", - value = if (isEnterpriseBuild) { - BuildTimeConfig.URL_POLICY ?: "" - } else { - "https://element.io/cookie-policy" - }, + // HealthChat fork: a configured value wins for every build type, not only enterprise ones. + value = BuildTimeConfig.URL_POLICY ?: if (isEnterpriseBuild) "" else "https://element.io/cookie-policy", ) buildConfigFieldStr( name = "BUG_REPORT_URL", - value = if (isEnterpriseBuild) { - BuildTimeConfig.BUG_REPORT_URL ?: "" - } else { - "https://rageshakes.element.io/api/submit" - }, + // HealthChat fork: a configured value wins for every build type, not only enterprise ones. + value = BuildTimeConfig.BUG_REPORT_URL ?: if (isEnterpriseBuild) "" else "https://rageshakes.element.io/api/submit", ) buildConfigFieldStr( name = "BUG_REPORT_APP_NAME", diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt index 855586892a..49140db63d 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/LearnMoreConfig.kt @@ -9,9 +9,10 @@ package io.element.android.appconfig object LearnMoreConfig { - const val ENCRYPTION_URL: String = "https://element.io/help#encryption" - const val DEVICE_VERIFICATION_URL: String = "https://element.io/help#encryption-device-verification" - const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5" - const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18" - const val HISTORY_VISIBLE_URL: String = "https://element.io/en/help#e2ee-history-sharing" + // HealthChat fork: every "learn more" link points at the HealthChat site instead of element.io/help. + const val ENCRYPTION_URL: String = "https://www.healthchat.ch" + const val DEVICE_VERIFICATION_URL: String = "https://www.healthchat.ch" + const val SECURE_BACKUP_URL: String = "https://www.healthchat.ch" + const val IDENTITY_CHANGE_URL: String = "https://www.healthchat.ch" + const val HISTORY_VISIBLE_URL: String = "https://www.healthchat.ch" } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 15db06e578..b43a358e40 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.graphics.Color import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.compound.colors.SemanticColorsLightDark +import io.element.android.compound.tokens.generated.SemanticColors import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.androidutils.json.JsonProvider @@ -64,7 +65,7 @@ class DefaultEnterpriseService( } override fun semanticColorsFlow(sessionId: SessionId?): Flow { - return flowOf(SemanticColorsLightDark.default) + return flowOf(healthChatSemanticColors) } override fun firebasePushGateway(): String? = null @@ -77,6 +78,27 @@ class DefaultEnterpriseService( override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null } +/** HealthChat brand blue, the launcher background colour. */ +private val healthChatBrandBlue = Color(0xFF12325C) + +/** + * The subtle gradient (home screen bloom, highlighted timeline item) fades the brand blue out at the + * opacities Compound uses for its green stops; stop 6 stays transparent. Every other token, including the + * accent colours, is Compound's default: only the bloom changes. + */ +private fun SemanticColors.withHealthChatBloom() = copy( + gradientSubtleStop1 = healthChatBrandBlue.copy(alpha = 0.41f), + gradientSubtleStop2 = healthChatBrandBlue.copy(alpha = 0.23f), + gradientSubtleStop3 = healthChatBrandBlue.copy(alpha = 0.11f), + gradientSubtleStop4 = healthChatBrandBlue.copy(alpha = 0.06f), + gradientSubtleStop5 = healthChatBrandBlue.copy(alpha = 0.03f), +) + +internal val healthChatSemanticColors = SemanticColorsLightDark( + light = SemanticColorsLightDark.default.light.withHealthChatBloom(), + dark = SemanticColorsLightDark.default.dark.withHealthChatBloom(), +) + /** * HealthChat is locked to its own homeserver: this is the only account provider users can sign in to. */ diff --git a/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt b/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt index 02e8de0ce5..60268a7b4b 100644 --- a/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt +++ b/features/enterprise/impl-foss/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt @@ -8,6 +8,7 @@ package io.element.android.features.enterprise.impl +import androidx.compose.ui.graphics.Color import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.compound.colors.SemanticColorsLightDark @@ -23,6 +24,7 @@ import io.element.android.libraries.matrix.test.accountprovider.anAccountProvide import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json @@ -61,11 +63,22 @@ class DefaultEnterpriseServiceTest { val defaultEnterpriseService = createDefaultEnterpriseService() defaultEnterpriseService.semanticColorsFlow(null).test { val initialState = awaitItem() - assertThat(initialState).isEqualTo(SemanticColorsLightDark.default) + assertThat(initialState).isEqualTo(healthChatSemanticColors) awaitComplete() } } + @Test + fun `semanticColorsFlow only changes the subtle gradient to the HealthChat blue`() = runTest { + val colors = createDefaultEnterpriseService().semanticColorsFlow(null).first() + val default = SemanticColorsLightDark.default + assertThat(colors.light.gradientSubtleStop1).isEqualTo(Color(0xFF12325C).copy(alpha = 0.41f)) + assertThat(colors.dark.gradientSubtleStop5).isEqualTo(Color(0xFF12325C).copy(alpha = 0.03f)) + assertThat(colors.light.gradientSubtleStop6).isEqualTo(default.light.gradientSubtleStop6) + assertThat(colors.light.bgAccentRest).isEqualTo(default.light.bgAccentRest) + assertThat(colors.dark.bgAccentRest).isEqualTo(default.dark.bgAccentRest) + } + @Test fun `brandColorsFlow always emits null`() = runTest { val defaultEnterpriseService = createDefaultEnterpriseService() @@ -81,7 +94,7 @@ class DefaultEnterpriseServiceTest { val defaultEnterpriseService = createDefaultEnterpriseService() defaultEnterpriseService.semanticColorsFlow(A_SESSION_ID).test { val initialState = awaitItem() - assertThat(initialState).isEqualTo(SemanticColorsLightDark.default) + assertThat(initialState).isEqualTo(healthChatSemanticColors) awaitComplete() } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/DefaultLocationService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/DefaultLocationService.kt index aa14e6505b..7ad887ea61 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/DefaultLocationService.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/DefaultLocationService.kt @@ -17,6 +17,7 @@ import io.element.android.features.location.api.buildTimeMapTilerConfig import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flow @@ -35,4 +36,7 @@ class DefaultLocationService( started = SharingStarted.Eagerly, initialValue = buildTimeMapTilerConfig(), ) + + // HealthChat fork: location sharing is not offered, even if the homeserver advertises a map provider. + override fun isServiceAvailable(): StateFlow = MutableStateFlow(false) } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/DefaultLocationServiceTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/DefaultLocationServiceTest.kt index 8a81895e1f..850eccdab4 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/DefaultLocationServiceTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/DefaultLocationServiceTest.kt @@ -29,7 +29,7 @@ class DefaultLocationServiceTest { ) runCurrent() assertThat(locationService.mapTilerConfig.value).isEqualTo(buildTimeMapTilerConfig()) - assertThat(locationService.isServiceAvailable().value).isEqualTo(buildTimeMapTilerConfig() != null) + assertThat(locationService.isServiceAvailable().value).isFalse() } @Test @@ -39,7 +39,7 @@ class DefaultLocationServiceTest { ) runCurrent() assertThat(locationService.mapTilerConfig.value).isEqualTo(buildTimeMapTilerConfig()) - assertThat(locationService.isServiceAvailable().value).isEqualTo(buildTimeMapTilerConfig() != null) + assertThat(locationService.isServiceAvailable().value).isFalse() } @Test @@ -50,7 +50,8 @@ class DefaultLocationServiceTest { ) runCurrent() assertThat(locationService.mapTilerConfig.value).isEqualTo(config) - assertThat(locationService.isServiceAvailable().value).isTrue() + // HealthChat fork: the service is never available, even with a homeserver config. + assertThat(locationService.isServiceAvailable().value).isFalse() } private fun TestScope.createDefaultLocationService( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index f3223b3d37..c953d33ab0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -131,7 +131,8 @@ class PreferencesRootPresenter( val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() } - val showLabsItem = remember { featureFlagService.getAvailableFeatures(isInLabs = true).isNotEmpty() } + // HealthChat fork: upstream experiments are not offered to HealthChat users. + val showLabsItem = false val showDeveloperSettings by showDeveloperSettingsProvider.showDeveloperSettings.collectAsState() diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 57b85af096..923b0504dd 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -198,7 +198,8 @@ class PreferencesRootPresenterTest { } ), ).test { - assertThat(awaitItem().showLabsItem).isTrue() + // HealthChat fork: labs are never offered, whatever the feature flags say. + assertThat(awaitItem().showLabsItem).isFalse() cancelAndIgnoreRemainingEvents() } } diff --git a/plugins/src/main/kotlin/config/BuildTimeConfig.kt b/plugins/src/main/kotlin/config/BuildTimeConfig.kt index 51dae48e88..395f048248 100644 --- a/plugins/src/main/kotlin/config/BuildTimeConfig.kt +++ b/plugins/src/main/kotlin/config/BuildTimeConfig.kt @@ -23,7 +23,7 @@ object BuildTimeConfig { // or a subdomain of it. val URL_WEBSITE: String? = "https://healthchat.ch" val URL_LOGO: String? = "https://www.healthchat.ch/favicon.ico" - val URL_COPYRIGHT: String? = null + val URL_COPYRIGHT: String? = "https://www.healthchat.ch" val URL_ACCEPTABLE_USE: String? = "https://www.healthchat.ch/datenschutz" val URL_PRIVACY: String? = "https://www.healthchat.ch/datenschutz" val URL_POLICY: String? = "https://www.healthchat.ch/datenschutz" @@ -35,7 +35,9 @@ object BuildTimeConfig { val SERVICES_POSTHOG_APIKEY: String? = null val SERVICES_SENTRY_DSN: String? = null val SERVICES_SENTRY_DSN_RUST: String? = null - val BUG_REPORT_URL: String? = null + // Empty on purpose: HealthChat runs no rageshake server, and an empty URL hides every + // "report a problem" entry (settings, home menu, crash prompt). + val BUG_REPORT_URL: String? = "" val BUG_REPORT_APP_NAME: String? = null const val PUSH_CONFIG_INCLUDE_FIREBASE: Boolean = true const val PUSH_CONFIG_INCLUDE_UNIFIED_PUSH: Boolean = true