mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Merge branch 'develop' into feature/valere/timeline_active_call_rendering
This commit is contained in:
+14
-6
@@ -10,6 +10,7 @@ package io.element.android.features.contentscanner.impl
|
||||
import io.element.android.features.contentscanner.api.ContentScannerService
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.scanner.ContentScanner
|
||||
import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationState
|
||||
@@ -42,12 +43,19 @@ class DefaultContentScannerService(
|
||||
contentValidationState.update(url, contentValidationValue)
|
||||
}
|
||||
.onFailure { exception ->
|
||||
if (exception is IOException) {
|
||||
// If it's an IO-related exception, we can retry later, so we don't store the error
|
||||
contentValidationState.update(url, ContentValidationValue.Unknown)
|
||||
} else {
|
||||
// For other exceptions, we cache the failure
|
||||
contentValidationState.update(url, ContentValidationValue.UnrecoverableError(exception))
|
||||
when (exception) {
|
||||
is IOException -> {
|
||||
// If it's an IO-related exception, we can retry later, so we don't store the error
|
||||
contentValidationState.update(url, ContentValidationValue.Unknown)
|
||||
}
|
||||
is ClientException.ContentScanner if exception.reason.isDangerous() -> {
|
||||
// It's a dangerous content (banned mime-type?), we mark it as invalid
|
||||
contentValidationState.update(url, ContentValidationValue.Invalid)
|
||||
}
|
||||
else -> {
|
||||
// For other exceptions, we cache the failure
|
||||
contentValidationState.update(url, ContentValidationValue.UnrecoverableError(exception))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ dependencies {
|
||||
implementation(projects.libraries.compound)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.wellknown.api)
|
||||
}
|
||||
|
||||
+6
@@ -11,6 +11,7 @@ package io.element.android.features.enterprise.api
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface EnterpriseService {
|
||||
@@ -41,6 +42,11 @@ interface EnterpriseService {
|
||||
*/
|
||||
fun getNoisyNotificationChannelId(sessionId: SessionId): String?
|
||||
|
||||
/**
|
||||
* Gets the overridden Element Well-Known data if it has been set, or null if not set.
|
||||
*/
|
||||
fun overriddenElementWellKnown(): ElementWellKnown?
|
||||
|
||||
companion object {
|
||||
const val ANY_ACCOUNT_PROVIDER = "*"
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
api(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.wellknown.api)
|
||||
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
|
||||
+3
@@ -15,6 +15,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
@@ -45,4 +46,6 @@ class DefaultEnterpriseService : EnterpriseService {
|
||||
}
|
||||
|
||||
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null
|
||||
|
||||
override fun overriddenElementWellKnown(): ElementWellKnown? = null
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.compound)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.wellknown.api)
|
||||
implementation(projects.tests.testutils)
|
||||
}
|
||||
|
||||
+6
@@ -13,6 +13,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.wellknown.api.ElementWellKnown
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -31,6 +32,7 @@ class FakeEnterpriseService(
|
||||
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
|
||||
private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() },
|
||||
private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() },
|
||||
private val overrideWellKnownResult: () -> ElementWellKnown? = { lambdaError() },
|
||||
) : EnterpriseService {
|
||||
private val brandColorState = MutableStateFlow(initialBrandColor)
|
||||
private val semanticColorsState = MutableStateFlow(initialSemanticColors)
|
||||
@@ -79,4 +81,8 @@ class FakeEnterpriseService(
|
||||
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? {
|
||||
return getNoisyNotificationChannelIdResult(sessionId)
|
||||
}
|
||||
|
||||
override fun overriddenElementWellKnown(): ElementWellKnown? {
|
||||
return overrideWellKnownResult()
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -147,6 +147,22 @@ internal fun TimelineItemScanningContentFailedPreview() = ElementPreview {
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TimelineItemScanningContentNotFoundPreview() = ElementPreview {
|
||||
val cache = remember {
|
||||
InMemoryEventContentValidationCache(initial = mapOf(
|
||||
AN_EVENT_ID to NoopContentValidationState(ContentValidationValue.UnrecoverableError(IllegalStateException("Not found")))
|
||||
))
|
||||
}
|
||||
CompositionLocalProvider(LocalEventContentValidationState provides cache) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
ATimelineItemEventRow(event = aTimelineItemEvent(eventId = AN_EVENT_ID, content = aTimelineItemImageContent()))
|
||||
ATimelineItemEventRow(event = aTimelineItemEvent(eventId = AN_EVENT_ID, content = aTimelineItemImageContent(caption = "A caption")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TimelineItemScanningContentWithInvalidRepliesPreview() = ElementPreview {
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ fun TimelineItemEventContentView(
|
||||
}
|
||||
|
||||
if (showCaption) {
|
||||
val padding = if (displayInvalidContent) {
|
||||
val padding = if (displayInvalidContent || displayContentNotFound) {
|
||||
PaddingValues(start = 12.dp, end = 12.dp, top = 4.dp, bottom = 0.dp)
|
||||
} else {
|
||||
when (content) {
|
||||
|
||||
+6
@@ -26,9 +26,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.stateDescription
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
@@ -37,6 +39,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
private val CORNER_RADIUS = 8.dp
|
||||
|
||||
@@ -52,6 +55,8 @@ fun GroupHeaderView(
|
||||
val backgroundColor = Color.Transparent
|
||||
val shape = RoundedCornerShape(CORNER_RADIUS)
|
||||
|
||||
val expandedStateDescription = stringResource(CommonStrings.a11y_state_expanded)
|
||||
val collapsedStateDescription = stringResource(CommonStrings.a11y_state_collapsed)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
@@ -62,6 +67,7 @@ fun GroupHeaderView(
|
||||
)
|
||||
.clearAndSetSemantics {
|
||||
contentDescription = text
|
||||
stateDescription = if (isExpanded) expandedStateDescription else collapsedStateDescription
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
id("io.element.android-compose-library")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.preferences.api
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
/**
|
||||
* Component for rendering extra developer options in the preferences screen.
|
||||
*/
|
||||
interface ExtraDeveloperOptionsRenderer {
|
||||
@Composable
|
||||
fun Render(modifier: Modifier)
|
||||
}
|
||||
+3
@@ -18,6 +18,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.designsystem.showkase.getBrowserIntent
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
@@ -28,6 +29,7 @@ class DeveloperSettingsNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: DeveloperSettingsPresenter,
|
||||
private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
interface Callback : Plugin {
|
||||
fun navigateToPushHistory()
|
||||
@@ -51,6 +53,7 @@ class DeveloperSettingsNode(
|
||||
onOpenShowkase = ::openShowkase,
|
||||
onPushHistoryClick = callback::navigateToPushHistory,
|
||||
onBackClick = callback::onDone,
|
||||
extraOptions = { extraDeveloperOptionsRenderer.Render(Modifier) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -43,6 +43,7 @@ fun DeveloperSettingsView(
|
||||
onPushHistoryClick: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
extraOptions: @Composable () -> Unit = {},
|
||||
) {
|
||||
if (state.showLoader) {
|
||||
ProgressDialog()
|
||||
@@ -97,6 +98,9 @@ fun DeveloperSettingsView(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extraOptions()
|
||||
|
||||
val cache = state.cacheSize
|
||||
PreferenceCategory(title = "Cache") {
|
||||
ListItem(
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.preferences.impl.developer
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class NoopExtraDeveloperOptionsRenderer : ExtraDeveloperOptionsRenderer {
|
||||
@Composable
|
||||
override fun Render(modifier: Modifier) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
+3
@@ -18,6 +18,7 @@ import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer
|
||||
import io.element.android.features.preferences.api.PreferencesEntryPoint
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.designsystem.showkase.getBrowserIntent
|
||||
@@ -28,6 +29,7 @@ class AppDeveloperSettingsNode(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: AppDeveloperSettingsPresenter,
|
||||
private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
private val callback: PreferencesEntryPoint.DeveloperSettingsCallback = callback()
|
||||
|
||||
@@ -45,6 +47,7 @@ class AppDeveloperSettingsNode(
|
||||
modifier = modifier,
|
||||
onOpenShowkase = ::openShowkase,
|
||||
onBackClick = callback::onDone,
|
||||
extraOptions = { extraDeveloperOptionsRenderer.Render(Modifier) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -24,6 +24,7 @@ fun AppDeveloperSettingsPage(
|
||||
onOpenShowkase: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
extraOptions: @Composable () -> Unit = {},
|
||||
) {
|
||||
BackHandler(
|
||||
onBack = onBackClick,
|
||||
@@ -40,6 +41,7 @@ fun AppDeveloperSettingsPage(
|
||||
onOpenShowkase = onOpenShowkase,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
extraOptions()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user