Merge branch 'develop' into feature/valere/support_verification_violation_banner
This commit is contained in:
commit
36d5528904
625 changed files with 6274 additions and 2292 deletions
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="error_no_compatible_app_found">"Bu eylemi gerçekleştirecek uyumlu bir uygulama bulunamadı."</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="common_date_date_at_time">"%1$s às %2$s"</string>
|
||||
<string name="common_date_this_month">"Este mês"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="common_date_date_at_time">"%1$svid %2$s"</string>
|
||||
<string name="common_date_this_month">"Denna månad"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="common_date_date_at_time">"%1$s %2$s"</string>
|
||||
<string name="common_date_this_month">"Bu ay"</string>
|
||||
</resources>
|
||||
|
|
@ -31,6 +31,7 @@ android {
|
|||
implementation(libs.androidx.compose.material3.adaptive)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.vanniktech.blurhash)
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.preferences.api)
|
||||
implementation(projects.libraries.testtags)
|
||||
|
|
|
|||
|
|
@ -7,24 +7,24 @@
|
|||
|
||||
package io.element.android.libraries.designsystem
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
|
||||
@Composable
|
||||
fun Boolean.toEnabledColor(): Color {
|
||||
return if (this) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
ElementTheme.colors.textPrimary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.40f)
|
||||
ElementTheme.colors.textDisabled
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Boolean.toSecondaryEnabledColor(): Color {
|
||||
return if (this) {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
ElementTheme.colors.textSecondary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.secondary.copy(alpha = 0.40f)
|
||||
ElementTheme.colors.textDisabled
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ fun RedIndicatorAtom(
|
|||
Box(
|
||||
modifier = modifier
|
||||
.size(size)
|
||||
.border(borderSize, ElementTheme.materialColors.background, CircleShape)
|
||||
.border(borderSize, ElementTheme.colors.bgCanvasDefault, CircleShape)
|
||||
.padding(borderSize / 2)
|
||||
.clip(CircleShape)
|
||||
.background(color)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -45,7 +44,7 @@ fun RoundedIconAtom(
|
|||
size: RoundedIconAtomSize = RoundedIconAtomSize.Big,
|
||||
resourceId: Int? = null,
|
||||
imageVector: ImageVector? = null,
|
||||
tint: Color = MaterialTheme.colorScheme.secondary,
|
||||
tint: Color = ElementTheme.colors.iconSecondary,
|
||||
backgroundTint: Color = ElementTheme.colors.temporaryColorBgSpecial,
|
||||
) {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ fun ComposerAlertMolecule(
|
|||
)
|
||||
val startColor = if (isCritical) ElementTheme.colors.bgCriticalSubtle else ElementTheme.colors.bgInfoSubtle
|
||||
val brush = Brush.verticalGradient(
|
||||
listOf(startColor, ElementTheme.materialColors.background),
|
||||
listOf(startColor, ElementTheme.colors.bgCanvasDefault),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -51,7 +50,7 @@ fun IconTitleSubtitleMolecule(
|
|||
.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
style = ElementTheme.typography.fontHeadingMdBold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
if (subTitle != null) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
|
@ -60,7 +59,7 @@ fun IconTitleSubtitleMolecule(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.movableContentOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -46,7 +45,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
|||
fun HeaderFooterPage(
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(20.dp),
|
||||
containerColor: Color = MaterialTheme.colorScheme.background,
|
||||
containerColor: Color = ElementTheme.colors.bgCanvasDefault,
|
||||
isScrollable: Boolean = false,
|
||||
background: @Composable () -> Unit = {},
|
||||
topBar: @Composable () -> Unit = {},
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ package io.element.android.libraries.designsystem.background
|
|||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.center
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RadialGradientShader
|
||||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|||
@Composable
|
||||
fun LightGradientBackground(
|
||||
modifier: Modifier = Modifier,
|
||||
backgroundColor: Color = MaterialTheme.colorScheme.background,
|
||||
backgroundColor: Color = ElementTheme.colors.bgCanvasDefault,
|
||||
firstColor: Color = Color(0x1E0DBD8B),
|
||||
secondColor: Color = Color(0x001273EB),
|
||||
ratio: Float = 642 / 775f,
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@ internal fun BloomPreview() {
|
|||
}
|
||||
.bloom(
|
||||
hash = blurhash,
|
||||
background = ElementTheme.materialColors.background,
|
||||
background = ElementTheme.colors.bgCanvasDefault,
|
||||
blurSize = DpSize(430.dp, 430.dp),
|
||||
offset = DpOffset(24.dp, 24.dp),
|
||||
clipToSize = if (topAppBarHeight > 0) DpSize(430.dp, topAppBarHeight.toDp()) else DpSize.Zero,
|
||||
|
|
@ -554,9 +554,9 @@ internal fun BloomInitialsPreview(@PreviewParameter(InitialsColorIntProvider::cl
|
|||
// Workaround to display a very subtle bloom for avatars with very soft colors
|
||||
Color(0xFFF9F9F9)
|
||||
} else {
|
||||
ElementTheme.materialColors.background
|
||||
ElementTheme.colors.bgCanvasDefault
|
||||
},
|
||||
bottomSoftEdgeColor = ElementTheme.materialColors.background,
|
||||
bottomSoftEdgeColor = ElementTheme.colors.bgCanvasDefault,
|
||||
blurSize = DpSize(256.dp, 256.dp),
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ package io.element.android.libraries.designsystem.components
|
|||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.components.Checkbox
|
||||
|
|
@ -38,7 +38,7 @@ fun LabelledCheckbox(
|
|||
)
|
||||
Text(
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
|
|
@ -64,13 +65,13 @@ fun ProgressDialog(
|
|||
when (type) {
|
||||
is ProgressDialogType.Indeterminate -> {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.iconPrimary
|
||||
)
|
||||
}
|
||||
is ProgressDialogType.Determinate -> {
|
||||
CircularProgressIndicator(
|
||||
progress = { type.progress },
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.iconPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +94,7 @@ private fun ProgressDialogContent(
|
|||
onCancelClick: () -> Unit = {},
|
||||
progressIndicator: @Composable () -> Unit = {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.iconPrimary
|
||||
)
|
||||
}
|
||||
) {
|
||||
|
|
@ -114,7 +115,7 @@ private fun ProgressDialogContent(
|
|||
Spacer(modifier = Modifier.height(22.dp))
|
||||
Text(
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
}
|
||||
if (showCancelButton) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -58,17 +57,16 @@ fun MainActionButton(
|
|||
.widthIn(min = 76.dp, max = 96.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val tintColor = if (enabled) LocalContentColor.current else MaterialTheme.colorScheme.secondary
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = tintColor,
|
||||
tint = if (enabled) LocalContentColor.current else ElementTheme.colors.iconDisabled,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
Text(
|
||||
title,
|
||||
style = ElementTheme.typography.fontBodyMdMedium.copy(hyphens = Hyphens.Auto),
|
||||
color = tintColor,
|
||||
color = if (enabled) LocalContentColor.current else ElementTheme.colors.textDisabled,
|
||||
overflow = TextOverflow.Visible,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
package io.element.android.libraries.designsystem.theme
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.compound.annotations.CoreColorToken
|
||||
|
|
@ -24,13 +23,13 @@ import kotlinx.collections.immutable.persistentMapOf
|
|||
* Room list.
|
||||
*/
|
||||
@Composable
|
||||
fun MaterialTheme.roomListRoomName() = colorScheme.primary
|
||||
fun ElementTheme.roomListRoomName() = colors.textPrimary
|
||||
|
||||
@Composable
|
||||
fun MaterialTheme.roomListRoomMessage() = colorScheme.secondary
|
||||
fun ElementTheme.roomListRoomMessage() = colors.textSecondary
|
||||
|
||||
@Composable
|
||||
fun MaterialTheme.roomListRoomMessageDate() = colorScheme.secondary
|
||||
fun ElementTheme.roomListRoomMessageDate() = colors.textSecondary
|
||||
|
||||
val SemanticColors.unreadIndicator
|
||||
get() = iconAccentTertiary
|
||||
|
|
@ -181,9 +180,9 @@ internal fun ColorAliasesPreview() = ElementPreview {
|
|||
backgroundColor = Color.Black,
|
||||
foregroundColor = Color.White,
|
||||
colors = persistentMapOf(
|
||||
"roomListRoomName" to MaterialTheme.roomListRoomName(),
|
||||
"roomListRoomMessage" to MaterialTheme.roomListRoomMessage(),
|
||||
"roomListRoomMessageDate" to MaterialTheme.roomListRoomMessageDate(),
|
||||
"roomListRoomName" to ElementTheme.roomListRoomName(),
|
||||
"roomListRoomMessage" to ElementTheme.roomListRoomMessage(),
|
||||
"roomListRoomMessageDate" to ElementTheme.roomListRoomMessageDate(),
|
||||
"unreadIndicator" to ElementTheme.colors.unreadIndicator,
|
||||
"placeholderBackground" to ElementTheme.colors.placeholderBackground,
|
||||
"messageFromMeBackground" to ElementTheme.colors.messageFromMeBackground,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import io.element.android.compound.theme.ElementTheme
|
|||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.compound.theme.isDark
|
||||
import io.element.android.compound.theme.mapToTheme
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
|
||||
/**
|
||||
|
|
@ -29,6 +30,7 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
|||
@Composable
|
||||
fun ElementThemeApp(
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
enterpriseService: EnterpriseService,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val theme by remember {
|
||||
|
|
@ -44,8 +46,12 @@ fun ElementThemeApp(
|
|||
}
|
||||
)
|
||||
}
|
||||
val compoundLight = remember { enterpriseService.semanticColorsLight() }
|
||||
val compoundDark = remember { enterpriseService.semanticColorsDark() }
|
||||
ElementTheme(
|
||||
darkTheme = theme.isDark(),
|
||||
content = content,
|
||||
compoundLight = compoundLight,
|
||||
compoundDark = compoundDark,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ internal fun SimpleAlertDialogContent(
|
|||
// most cases, TextButtons should be used for dismiss and confirm buttons.
|
||||
// TextButtons will not consume this provided content color value, and will used their
|
||||
// own defined or default colors.
|
||||
buttonContentColor = MaterialTheme.colorScheme.primary,
|
||||
buttonContentColor = ElementTheme.colors.textPrimary,
|
||||
applyPaddingToContents = applyPaddingToContents,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ internal enum class ButtonStyle {
|
|||
contentColor = if (destructive) {
|
||||
ElementTheme.colors.textCriticalPrimary
|
||||
} else {
|
||||
if (LocalContentColor.current.isSpecified) LocalContentColor.current else ElementTheme.materialColors.primary
|
||||
if (LocalContentColor.current.isSpecified) LocalContentColor.current else ElementTheme.colors.textPrimary
|
||||
},
|
||||
disabledContainerColor = Color.Transparent,
|
||||
disabledContentColor = getDisabledContentColor(destructive),
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ fun <T> SearchBar(
|
|||
Icon(
|
||||
imageVector = CompoundIcons.Search(),
|
||||
contentDescription = stringResource(CommonStrings.action_search),
|
||||
tint = ElementTheme.materialColors.tertiary,
|
||||
tint = ElementTheme.colors.iconTertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -138,7 +138,7 @@ fun <T> SearchBar(
|
|||
Text(
|
||||
text = stringResource(CommonStrings.common_no_results),
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.materialColors.tertiary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ package io.element.android.libraries.designsystem.theme.components.previews
|
|||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
|
|
@ -48,7 +48,7 @@ private fun ContentToPreview() {
|
|||
shape = AlertDialogDefaults.shape,
|
||||
containerColor = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
buttonContentColor = MaterialTheme.colorScheme.primary,
|
||||
buttonContentColor = ElementTheme.colors.textPrimary,
|
||||
iconContentColor = AlertDialogDefaults.iconContentColor,
|
||||
titleContentColor = AlertDialogDefaults.titleContentColor,
|
||||
textContentColor = AlertDialogDefaults.textContentColor,
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ package io.element.android.libraries.designsystem.theme.components.previews
|
|||
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TimePicker
|
||||
import androidx.compose.material3.TimePickerLayoutType
|
||||
import androidx.compose.material3.rememberTimePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
|
|
@ -35,7 +35,7 @@ internal fun TimePickerHorizontalPreview() {
|
|||
shape = AlertDialogDefaults.shape,
|
||||
containerColor = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
buttonContentColor = MaterialTheme.colorScheme.primary,
|
||||
buttonContentColor = ElementTheme.colors.textPrimary,
|
||||
iconContentColor = AlertDialogDefaults.iconContentColor,
|
||||
titleContentColor = AlertDialogDefaults.titleContentColor,
|
||||
textContentColor = AlertDialogDefaults.textContentColor,
|
||||
|
|
@ -57,7 +57,7 @@ internal fun TimePickerVerticalLightPreview() {
|
|||
shape = AlertDialogDefaults.shape,
|
||||
containerColor = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
buttonContentColor = MaterialTheme.colorScheme.primary,
|
||||
buttonContentColor = ElementTheme.colors.textPrimary,
|
||||
iconContentColor = AlertDialogDefaults.iconContentColor,
|
||||
titleContentColor = AlertDialogDefaults.titleContentColor,
|
||||
textContentColor = AlertDialogDefaults.textContentColor,
|
||||
|
|
@ -83,7 +83,7 @@ internal fun TimePickerVerticalDarkPreview() {
|
|||
shape = AlertDialogDefaults.shape,
|
||||
containerColor = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
buttonContentColor = MaterialTheme.colorScheme.primary,
|
||||
buttonContentColor = ElementTheme.colors.textPrimary,
|
||||
iconContentColor = AlertDialogDefaults.iconContentColor,
|
||||
titleContentColor = AlertDialogDefaults.titleContentColor,
|
||||
textContentColor = AlertDialogDefaults.textContentColor,
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@
|
|||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
fun ForceOrientation(orientation: ScreenOrientation) {
|
||||
val activity = LocalContext.current as? ComponentActivity ?: return
|
||||
val activity = LocalActivity.current ?: return
|
||||
val orientationFlags = when (orientation) {
|
||||
ScreenOrientation.PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
ScreenOrientation.LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(Profil fotoğrafı değiştirildi)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s profil fotoğrafını değiştirdi"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Profil fotoğrafını değiştirdin"</string>
|
||||
<string name="state_event_demoted_to_member">"%1$s üyeliğe düşürüldü"</string>
|
||||
<string name="state_event_demoted_to_moderator">"%1$s moderatörlüğe düşürüldü"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s görünen adını bundan %2$s şuna %3$s değiştirdi"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Görünen adınızı bundan %1$s şuna %2$s değiştirdiniz"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s görünen adını kaldırdı (önceden %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Görünen adınızı kaldırdınız (önceden %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s görünen adlarını şuna ayarla %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Görünen adınız %1$s"</string>
|
||||
<string name="state_event_promoted_to_administrator">"%1$s yöneticiliğe terfi etti"</string>
|
||||
<string name="state_event_promoted_to_moderator">"%1$s moderatörlüğe terfi etti"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s odanın fotoğrafını değiştirdi"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Odanın fotoğrafını değiştirdiniz"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s oda fotoğrafını kaldırdı"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Oda fotoğrafını kaldırdınız"</string>
|
||||
<string name="state_event_room_ban">"%1$syasaklandı%2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"%1$s yasakladınız"</string>
|
||||
<string name="state_event_room_created">"%1$sodayı yarattı"</string>
|
||||
<string name="state_event_room_created_by_you">"Odayı sen yarattın"</string>
|
||||
<string name="state_event_room_invite">"%1$s davet edildi %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s daveti kabul etti"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Daveti kabul ettiniz"</string>
|
||||
<string name="state_event_room_invite_by_you">"%1$s davet ettiniz"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s sizi davet etti"</string>
|
||||
<string name="state_event_room_join">"%1$sodaya katıldı"</string>
|
||||
<string name="state_event_room_join_by_you">"Odaya katıldınız"</string>
|
||||
<string name="state_event_room_knock">"%1$s katılmak istiyor"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s, %2$s\'e erişim izni verdi"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s \'ın katılmasına izin verdiniz"</string>
|
||||
<string name="state_event_room_knock_by_you">"Katılmayı talep ettiniz"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s, %2$s\'ın katılma isteğini reddetti"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"%1$s kullanıcısının katılma isteğini reddettiniz"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s katılma isteğinizi reddetti"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s artık katılmakla ilgilenmiyor"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Katılma talebinizi iptal ettiniz"</string>
|
||||
<string name="state_event_room_leave">"%1$sodadan ayrıldı"</string>
|
||||
<string name="state_event_room_leave_by_you">"Odadan ayrıldın."</string>
|
||||
<string name="state_event_room_name_changed">"%1$s Oda adını değiştirdi: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Odanın adını değiştridiniz: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s Oda adını kaldırdı"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Oda adını kaldırdınız"</string>
|
||||
<string name="state_event_room_none">"%1$s hiçbir değişiklik yapmadı"</string>
|
||||
<string name="state_event_room_none_by_you">"Hiçbir değişiklik yapmadınız"</string>
|
||||
<string name="state_event_room_pinned_events_changed">"%1$s sabitlenmiş iletileri değiştirdi"</string>
|
||||
<string name="state_event_room_pinned_events_changed_by_you">"Sabitlenmiş mesajları değiştirdiniz"</string>
|
||||
<string name="state_event_room_pinned_events_pinned">"%1$s bir mesaj sabitledi"</string>
|
||||
<string name="state_event_room_pinned_events_pinned_by_you">"Bir mesaj sabitlediniz"</string>
|
||||
<string name="state_event_room_pinned_events_unpinned">"%1$s bir mesajın sabitlemesini kaldırdı"</string>
|
||||
<string name="state_event_room_pinned_events_unpinned_by_you">"Bir mesajın sabitlemesini kaldırdınız"</string>
|
||||
<string name="state_event_room_reject">"%1$sdaveti reddetti"</string>
|
||||
<string name="state_event_room_reject_by_you">"Daveti reddettiniz"</string>
|
||||
<string name="state_event_room_remove">"%1$skaldırıldı%2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"%1$s kaldırdınız"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s odaya katılması için %2$s\'a davet gönderdi"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Odaya katılması için %1$s\'a davet gönderdin"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s, %2$s\'nin odaya katılma davetini iptal etti"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"%1$s\'ın odaya katılma davetini iptal ettiniz"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s konuyu değiştirdi: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Konuyu değiştirdiniz: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s oda konusunu kaldırdı"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Oda konusunu kaldırdınız"</string>
|
||||
<string name="state_event_room_unban">"%1$syasaklanmamış%2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"%1$s yasağını kaldırdınız"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s üyeliğinde bilinmeyen bir değişiklik yaptı"</string>
|
||||
</resources>
|
||||
|
|
@ -25,10 +25,9 @@ import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
|||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.PendingRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
|
|
@ -55,7 +54,7 @@ interface MatrixClient : Closeable {
|
|||
val sessionCoroutineScope: CoroutineScope
|
||||
val ignoredUsersFlow: StateFlow<ImmutableList<UserId>>
|
||||
suspend fun getRoom(roomId: RoomId): MatrixRoom?
|
||||
suspend fun getPendingRoom(roomId: RoomId): PendingRoom?
|
||||
suspend fun getPendingRoom(roomId: RoomId): RoomPreview?
|
||||
suspend fun findDM(userId: UserId): RoomId?
|
||||
suspend fun ignoreUser(userId: UserId): Result<Unit>
|
||||
suspend fun unignoreUser(userId: UserId): Result<Unit>
|
||||
|
|
@ -146,7 +145,11 @@ interface MatrixClient : Closeable {
|
|||
* Execute generic GET requests through the SDKs internal HTTP client.
|
||||
*/
|
||||
suspend fun getUrl(url: String): Result<String>
|
||||
suspend fun getRoomPreviewInfo(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreviewInfo>
|
||||
|
||||
/**
|
||||
* Get a room preview for a given room ID or alias. This is especially useful for rooms that the user is not a member of, or hasn't joined yet.
|
||||
*/
|
||||
suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview>
|
||||
|
||||
/**
|
||||
* Returns the currently used sliding sync version.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.api.exception
|
|||
|
||||
sealed class ClientException(message: String) : Exception(message) {
|
||||
class Generic(message: String) : ClientException(message)
|
||||
class MatrixApi(val kind: ErrorKind, val code: String, message: String) : ClientException(message)
|
||||
class Other(message: String) : ClientException(message)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector 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.libraries.matrix.api.exception
|
||||
|
||||
sealed interface ErrorKind {
|
||||
/**
|
||||
* M_BAD_ALIAS
|
||||
*
|
||||
* One or more room aliases within the m.room.canonical_alias event do
|
||||
* not point to the room ID for which the state event is to be sent to.
|
||||
*
|
||||
* room aliases: https://spec.matrix.org/latest/client-server-api/#room-aliases
|
||||
*/
|
||||
data object BadAlias : ErrorKind
|
||||
|
||||
/**
|
||||
* M_BAD_JSON
|
||||
*
|
||||
* The request contained valid JSON, but it was malformed in some way, e.g.
|
||||
* missing required keys, invalid values for keys.
|
||||
*/
|
||||
data object BadJson : ErrorKind
|
||||
|
||||
/**
|
||||
* M_BAD_STATE
|
||||
*
|
||||
* The state change requested cannot be performed, such as attempting to
|
||||
* unban a user who is not banned.
|
||||
*/
|
||||
data object BadState : ErrorKind
|
||||
|
||||
/**
|
||||
* M_BAD_STATUS
|
||||
*
|
||||
* The application service returned a bad status.
|
||||
*/
|
||||
data class BadStatus(
|
||||
/**
|
||||
* The HTTP status code of the response.
|
||||
*/
|
||||
val status: Int?,
|
||||
/**
|
||||
* The body of the response.
|
||||
*/
|
||||
val body: String?
|
||||
) : ErrorKind
|
||||
|
||||
/**
|
||||
* M_CANNOT_LEAVE_SERVER_NOTICE_ROOM
|
||||
*
|
||||
* The user is unable to reject an invite to join the server notices
|
||||
* room.
|
||||
*
|
||||
* server notices: https://spec.matrix.org/latest/client-server-api/#server-notices
|
||||
*/
|
||||
data object CannotLeaveServerNoticeRoom : ErrorKind
|
||||
|
||||
/**
|
||||
* M_CANNOT_OVERWRITE_MEDIA
|
||||
*
|
||||
* The create_content_async endpoint was called with a media ID that
|
||||
* already has content.
|
||||
*
|
||||
*/
|
||||
data object CannotOverwriteMedia : ErrorKind
|
||||
|
||||
/**
|
||||
* M_CAPTCHA_INVALID
|
||||
*
|
||||
* The Captcha provided did not match what was expected.
|
||||
*/
|
||||
data object CaptchaInvalid : ErrorKind
|
||||
|
||||
/**
|
||||
* M_CAPTCHA_NEEDED
|
||||
*
|
||||
* A Captcha is required to complete the request.
|
||||
*/
|
||||
data object CaptchaNeeded : ErrorKind
|
||||
|
||||
/**
|
||||
* M_CONNECTION_FAILED
|
||||
*
|
||||
* The connection to the application service failed.
|
||||
*/
|
||||
data object ConnectionFailed : ErrorKind
|
||||
|
||||
/**
|
||||
* M_CONNECTION_TIMEOUT
|
||||
*
|
||||
* The connection to the application service timed out.
|
||||
*/
|
||||
data object ConnectionTimeout : ErrorKind
|
||||
|
||||
/**
|
||||
* M_DUPLICATE_ANNOTATION
|
||||
*
|
||||
* The request is an attempt to send a duplicate annotation.
|
||||
*
|
||||
* duplicate annotation: https://spec.matrix.org/latest/client-server-api/#avoiding-duplicate-annotations
|
||||
*/
|
||||
data object DuplicateAnnotation : ErrorKind
|
||||
|
||||
/**
|
||||
* M_EXCLUSIVE
|
||||
*
|
||||
* The resource being requested is reserved by an application service, or
|
||||
* the application service making the request has not created the
|
||||
* resource.
|
||||
*/
|
||||
data object Exclusive : ErrorKind
|
||||
|
||||
/**
|
||||
* M_FORBIDDEN
|
||||
*
|
||||
* Forbidden access, e.g. joining a room without permission, failed login.
|
||||
*/
|
||||
data object Forbidden : ErrorKind
|
||||
|
||||
/**
|
||||
* M_GUEST_ACCESS_FORBIDDEN
|
||||
*
|
||||
* The room or resource does not permit guests to access it.
|
||||
*
|
||||
* guests: https://spec.matrix.org/latest/client-server-api/#guest-access
|
||||
*/
|
||||
data object GuestAccessForbidden : ErrorKind
|
||||
|
||||
/**
|
||||
* M_INCOMPATIBLE_ROOM_VERSION
|
||||
*
|
||||
* The client attempted to join a room that has a version the server does
|
||||
* not support.
|
||||
*/
|
||||
data class IncompatibleRoomVersion(
|
||||
/**
|
||||
* The room's version.
|
||||
*/
|
||||
val roomVersion: String
|
||||
) : ErrorKind
|
||||
|
||||
/**
|
||||
* M_INVALID_PARAM
|
||||
*
|
||||
* A parameter that was specified has the wrong value. For example, the
|
||||
* server expected an integer and instead received a string.
|
||||
*/
|
||||
data object InvalidParam : ErrorKind
|
||||
|
||||
/**
|
||||
* M_INVALID_ROOM_STATE
|
||||
*
|
||||
* The initial state implied by the parameters to the create_room
|
||||
* request is invalid, e.g. the user's power_level is set below that
|
||||
* necessary to set the room name.
|
||||
*
|
||||
*/
|
||||
data object InvalidRoomState : ErrorKind
|
||||
|
||||
/**
|
||||
* M_INVALID_USERNAME
|
||||
*
|
||||
* The desired user name is not valid.
|
||||
*/
|
||||
data object InvalidUsername : ErrorKind
|
||||
|
||||
/**
|
||||
* M_LIMIT_EXCEEDED
|
||||
*
|
||||
* The request has been refused due to rate limiting: too many requests
|
||||
* have been sent in a short period of time.
|
||||
*
|
||||
* rate limiting: https://spec.matrix.org/latest/client-server-api/#rate-limiting
|
||||
*/
|
||||
data class LimitExceeded(
|
||||
/**
|
||||
* How long a client should wait before they can try again.
|
||||
*/
|
||||
val retryAfterMs: Long?
|
||||
) : ErrorKind
|
||||
|
||||
/**
|
||||
* M_MISSING_PARAM
|
||||
*
|
||||
* A required parameter was missing from the request.
|
||||
*/
|
||||
data object MissingParam : ErrorKind
|
||||
|
||||
/**
|
||||
* M_MISSING_TOKEN
|
||||
*
|
||||
* No access token was specified for the request, but one is required.
|
||||
*
|
||||
* access token: https://spec.matrix.org/latest/client-server-api/#client-authentication
|
||||
*/
|
||||
data object MissingToken : ErrorKind
|
||||
|
||||
/**
|
||||
* M_NOT_FOUND
|
||||
*
|
||||
* No resource was found for this request.
|
||||
*/
|
||||
data object NotFound : ErrorKind
|
||||
|
||||
/**
|
||||
* M_NOT_JSON
|
||||
*
|
||||
* The request did not contain valid JSON.
|
||||
*/
|
||||
data object NotJson : ErrorKind
|
||||
|
||||
/**
|
||||
* M_NOT_YET_UPLOADED
|
||||
*
|
||||
* An mxc URI generated was used and the content is not yet available.
|
||||
*
|
||||
*/
|
||||
data object NotYetUploaded : ErrorKind
|
||||
|
||||
/**
|
||||
* M_RESOURCE_LIMIT_EXCEEDED
|
||||
*
|
||||
* The request cannot be completed because the homeserver has reached a
|
||||
* resource limit imposed on it. For example, a homeserver held in a
|
||||
* shared hosting environment may reach a resource limit if it starts
|
||||
* using too much memory or disk space.
|
||||
*/
|
||||
data class ResourceLimitExceeded(
|
||||
/**
|
||||
* A URI giving a contact method for the server administrator.
|
||||
*/
|
||||
val adminContact: String
|
||||
) : ErrorKind
|
||||
|
||||
/**
|
||||
* M_ROOM_IN_USE
|
||||
*
|
||||
* The room alias specified in the request is already taken.
|
||||
*
|
||||
* room alias: https://spec.matrix.org/latest/client-server-api/#room-aliases
|
||||
*/
|
||||
data object RoomInUse : ErrorKind
|
||||
|
||||
/**
|
||||
* M_SERVER_NOT_TRUSTED
|
||||
*
|
||||
* The client's request used a third-party server, e.g. identity server,
|
||||
* that this server does not trust.
|
||||
*/
|
||||
data object ServerNotTrusted : ErrorKind
|
||||
|
||||
/**
|
||||
* M_THREEPID_AUTH_FAILED
|
||||
*
|
||||
* Authentication could not be performed on the third-party identifier.
|
||||
*
|
||||
* third-party identifier: https://spec.matrix.org/latest/client-server-api/#adding-account-administrative-contact-information
|
||||
*/
|
||||
data object ThreepidAuthFailed : ErrorKind
|
||||
|
||||
/**
|
||||
* M_THREEPID_DENIED
|
||||
*
|
||||
* The server does not permit this third-party identifier. This may
|
||||
* happen if the server only permits, for example, email addresses from
|
||||
* a particular domain.
|
||||
*
|
||||
* third-party identifier: https://spec.matrix.org/latest/client-server-api/#adding-account-administrative-contact-information
|
||||
*/
|
||||
data object ThreepidDenied : ErrorKind
|
||||
|
||||
/**
|
||||
* M_THREEPID_IN_USE
|
||||
*
|
||||
* The third-party identifier is already in use by another user.
|
||||
*
|
||||
* third-party identifier: https://spec.matrix.org/latest/client-server-api/#adding-account-administrative-contact-information
|
||||
*/
|
||||
data object ThreepidInUse : ErrorKind
|
||||
|
||||
/**
|
||||
* M_THREEPID_MEDIUM_NOT_SUPPORTED
|
||||
*
|
||||
* The homeserver does not support adding a third-party identifier of the
|
||||
* given medium.
|
||||
*
|
||||
* third-party identifier: https://spec.matrix.org/latest/client-server-api/#adding-account-administrative-contact-information
|
||||
*/
|
||||
data object ThreepidMediumNotSupported : ErrorKind
|
||||
|
||||
/**
|
||||
* M_THREEPID_NOT_FOUND
|
||||
*
|
||||
* No account matching the given third-party identifier could be found.
|
||||
*
|
||||
* third-party identifier: https://spec.matrix.org/latest/client-server-api/#adding-account-administrative-contact-information
|
||||
*/
|
||||
data object ThreepidNotFound : ErrorKind
|
||||
|
||||
/**
|
||||
* M_TOO_LARGE
|
||||
*
|
||||
* The request or entity was too large.
|
||||
*/
|
||||
data object TooLarge : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNABLE_TO_AUTHORISE_JOIN
|
||||
*
|
||||
* The room is restricted and none of the conditions can be validated by
|
||||
* the homeserver. This can happen if the homeserver does not know
|
||||
* about any of the rooms listed as conditions, for example.
|
||||
*
|
||||
* restricted: https://spec.matrix.org/latest/client-server-api/#restricted-rooms
|
||||
*/
|
||||
data object UnableToAuthorizeJoin : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNABLE_TO_GRANT_JOIN
|
||||
*
|
||||
* A different server should be attempted for the join. This is typically
|
||||
* because the resident server can see that the joining user satisfies
|
||||
* one or more conditions, such as in the case of restricted rooms,
|
||||
* but the resident server would be unable to meet the authorization
|
||||
* rules.
|
||||
*
|
||||
* restricted rooms: https://spec.matrix.org/latest/client-server-api/#restricted-rooms
|
||||
*/
|
||||
data object UnableToGrantJoin : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNAUTHORIZED
|
||||
*
|
||||
* The request was not correctly authorized. Usually due to login failures.
|
||||
*/
|
||||
data object Unauthorized : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNKNOWN
|
||||
*
|
||||
* An unknown error has occurred.
|
||||
*/
|
||||
data object Unknown : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNKNOWN_TOKEN
|
||||
*
|
||||
* The access or refresh token specified was not recognized.
|
||||
*
|
||||
* access or refresh token: https://spec.matrix.org/latest/client-server-api/#client-authentication
|
||||
*/
|
||||
data class UnknownToken(
|
||||
/**
|
||||
* If this is true, the client is in a "soft logout" state, i.e.
|
||||
* the server requires re-authentication but the session is not
|
||||
* invalidated. The client can acquire a new access token by
|
||||
* specifying the device ID it is already using to the login API.
|
||||
*
|
||||
* soft logout: https://spec.matrix.org/latest/client-server-api/#soft-logout
|
||||
*/
|
||||
val softLogout: Boolean
|
||||
) : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNRECOGNIZED
|
||||
*
|
||||
* The server did not understand the request.
|
||||
*
|
||||
* This is expected to be returned with a 404 HTTP status code if the
|
||||
* endpoint is not implemented or a 405 HTTP status code if the
|
||||
* endpoint is implemented, but the incorrect HTTP method is used.
|
||||
*/
|
||||
data object Unrecognized : ErrorKind
|
||||
|
||||
/**
|
||||
* M_UNSUPPORTED_ROOM_VERSION
|
||||
*
|
||||
* The request to create_room used a room version that the server does
|
||||
* not support.
|
||||
*
|
||||
*/
|
||||
data object UnsupportedRoomVersion : ErrorKind
|
||||
|
||||
/**
|
||||
* M_URL_NOT_SET
|
||||
*
|
||||
* The application service doesn't have a URL configured.
|
||||
*/
|
||||
data object UrlNotSet : ErrorKind
|
||||
|
||||
/**
|
||||
* M_USER_DEACTIVATED
|
||||
*
|
||||
* The user ID associated with the request has been deactivated.
|
||||
*/
|
||||
data object UserDeactivated : ErrorKind
|
||||
|
||||
/**
|
||||
* M_USER_IN_USE
|
||||
*
|
||||
* The desired user ID is already taken.
|
||||
*/
|
||||
data object UserInUse : ErrorKind
|
||||
|
||||
/**
|
||||
* M_USER_LOCKED
|
||||
*
|
||||
* The account has been locked and cannot be used at this time.
|
||||
*
|
||||
* locked: https://spec.matrix.org/latest/client-server-api/#account-locking
|
||||
*/
|
||||
data object UserLocked : ErrorKind
|
||||
|
||||
/**
|
||||
* M_USER_SUSPENDED
|
||||
*
|
||||
* The account has been suspended and can only be used for limited
|
||||
* actions at this time.
|
||||
*
|
||||
* suspended: https://spec.matrix.org/latest/client-server-api/#account-suspension
|
||||
*/
|
||||
data object UserSuspended : ErrorKind
|
||||
|
||||
/**
|
||||
* M_WEAK_PASSWORD
|
||||
*
|
||||
* The password was rejected by the server for being too weak.
|
||||
*
|
||||
* rejected: https://spec.matrix.org/latest/client-server-api/#notes-on-password-management
|
||||
*/
|
||||
data object WeakPassword : ErrorKind
|
||||
|
||||
/**
|
||||
* M_WRONG_ROOM_KEYS_VERSION
|
||||
*
|
||||
* The version of the room keys backup provided in the request does not
|
||||
* match the current backup version.
|
||||
*
|
||||
* room keys backup: https://spec.matrix.org/latest/client-server-api/#server-side-key-backups
|
||||
*/
|
||||
data class WrongRoomKeysVersion(
|
||||
/**
|
||||
* The currently active backup version.
|
||||
*/
|
||||
val currentVersion: String?
|
||||
) : ErrorKind
|
||||
|
||||
/**
|
||||
* A custom API error.
|
||||
*/
|
||||
data class Custom(val errcode: String) : ErrorKind
|
||||
}
|
||||
|
|
@ -28,7 +28,6 @@ data class MatrixRoomInfo(
|
|||
val topic: String?,
|
||||
val avatarUrl: String?,
|
||||
val isDirect: Boolean,
|
||||
val isPublic: Boolean,
|
||||
val joinRule: JoinRule?,
|
||||
val isSpace: Boolean,
|
||||
val isTombstoned: Boolean,
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
/** A reference to a room the current user has knocked to or has been invited to, with the ability to leave the room. */
|
||||
interface PendingRoom : AutoCloseable {
|
||||
val sessionId: SessionId
|
||||
val roomId: RoomId
|
||||
|
||||
/** Leave the room ie.decline invite or cancel knock. */
|
||||
suspend fun leave(): Result<Unit>
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ data class RoomMember(
|
|||
val normalizedPowerLevel: Long,
|
||||
val isIgnored: Boolean,
|
||||
val role: Role,
|
||||
val membershipChangeReason: String?,
|
||||
) {
|
||||
/**
|
||||
* Role of the RoomMember, based on its [powerLevel].
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector 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.libraries.matrix.api.room
|
||||
|
||||
/**
|
||||
* Room membership details for the current user and the sender of the membership event.
|
||||
*
|
||||
* It also includes the reason the current user's membership changed, if any.
|
||||
*/
|
||||
data class RoomMembershipDetails(
|
||||
val currentUserMember: RoomMember,
|
||||
val senderMember: RoomMember?,
|
||||
) {
|
||||
val membershipChangeReason: String? = currentUserMember.membershipChangeReason
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
|
||||
/** A reference to a room either invited, knocked or banned. */
|
||||
interface RoomPreview : AutoCloseable {
|
||||
val sessionId: SessionId
|
||||
val info: RoomPreviewInfo
|
||||
|
||||
/** Leave the room ie.decline invite or cancel knock. */
|
||||
suspend fun leave(): Result<Unit>
|
||||
|
||||
/**
|
||||
* Forget the room if we had access to it, and it was left or banned.
|
||||
*/
|
||||
suspend fun forget(): Result<Unit>
|
||||
|
||||
/**
|
||||
* Get the membership details of the user in the room, as well as from the user who sent the `m.room.member` event.
|
||||
*/
|
||||
suspend fun membershipDetails(): Result<RoomMembershipDetails?>
|
||||
}
|
||||
|
|
@ -9,7 +9,9 @@ package io.element.android.libraries.matrix.api.room.preview
|
|||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
|
||||
data class RoomPreviewInfo(
|
||||
/** The room id for this room. */
|
||||
|
|
@ -28,12 +30,8 @@ data class RoomPreviewInfo(
|
|||
val roomType: RoomType,
|
||||
/** Is the history world-readable for this room? */
|
||||
val isHistoryWorldReadable: Boolean,
|
||||
/** Is the room joined by the current user? */
|
||||
val isJoined: Boolean,
|
||||
/** Is the current user invited to this room? */
|
||||
val isInvited: Boolean,
|
||||
/** is the join rule public for this room? */
|
||||
val isPublic: Boolean,
|
||||
/** Can we knock (or restricted-knock) to this room? */
|
||||
val canKnock: Boolean,
|
||||
/** the membership of the current user. */
|
||||
val membership: CurrentUserMembership?,
|
||||
/** The room's join rule. */
|
||||
val joinRule: JoinRule,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.core.bool.orFalse
|
|||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScope
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
|
|
@ -32,12 +33,11 @@ import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
|||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.PendingRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
|
|
@ -50,6 +50,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
|||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
||||
import io.element.android.libraries.matrix.impl.encryption.RustEncryptionService
|
||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||
import io.element.android.libraries.matrix.impl.media.RustMediaLoader
|
||||
import io.element.android.libraries.matrix.impl.notification.RustNotificationService
|
||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||
|
|
@ -58,9 +59,9 @@ import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
|||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomFactory
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomPreview
|
||||
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.matrix.impl.room.join.map
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.map
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
|
||||
|
|
@ -261,8 +262,8 @@ class RustMatrixClient(
|
|||
return roomFactory.create(roomId)
|
||||
}
|
||||
|
||||
override suspend fun getPendingRoom(roomId: RoomId): PendingRoom? {
|
||||
return roomFactory.createPendingRoom(roomId)
|
||||
override suspend fun getPendingRoom(roomId: RoomId): RoomPreview? {
|
||||
return roomFactory.createRoomPreview(roomId)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -393,7 +394,7 @@ class RustMatrixClient(
|
|||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}.mapFailure { it.mapClientException() }
|
||||
|
||||
override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomSummary?> = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
|
|
@ -407,7 +408,7 @@ class RustMatrixClient(
|
|||
Timber.e(e, "Timeout waiting for the room to be available in the room list")
|
||||
null
|
||||
}
|
||||
}
|
||||
}.mapFailure { it.mapClientException() }
|
||||
}
|
||||
|
||||
override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List<String>): Result<RoomSummary?> = withContext(
|
||||
|
|
@ -421,7 +422,7 @@ class RustMatrixClient(
|
|||
Timber.e(e, "Timeout waiting for the room to be available in the room list")
|
||||
null
|
||||
}
|
||||
}
|
||||
}.mapFailure { it.mapClientException() }
|
||||
}
|
||||
|
||||
override suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result<Unit> = withContext(sessionDispatcher) {
|
||||
|
|
@ -448,15 +449,14 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getRoomPreviewInfo(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreviewInfo> = withContext(sessionDispatcher) {
|
||||
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview> = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
when (roomIdOrAlias) {
|
||||
val roomPreview = when (roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> innerClient.getRoomPreviewFromRoomAlias(roomIdOrAlias.roomAlias.value)
|
||||
is RoomIdOrAlias.Id -> innerClient.getRoomPreviewFromRoomId(roomIdOrAlias.roomId.value, serverNames)
|
||||
}.use { roomPreview ->
|
||||
RoomPreviewInfoMapper.map(roomPreview.info())
|
||||
}
|
||||
}
|
||||
RustRoomPreview(sessionId, roomPreview, roomMembershipObserver)
|
||||
}.mapFailure { it.mapClientException() }
|
||||
}
|
||||
|
||||
override fun syncService(): SyncService = rustSyncService
|
||||
|
|
@ -495,6 +495,7 @@ class RustMatrixClient(
|
|||
|
||||
override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean): String? {
|
||||
var result: String? = null
|
||||
sessionCoroutineScope.cancel()
|
||||
// Remove current delegate so we don't receive an auth error
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
clientDelegateTaskHandle = null
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ fun Throwable.mapClientException(): ClientException {
|
|||
is RustClientException -> {
|
||||
when (this) {
|
||||
is RustClientException.Generic -> ClientException.Generic(msg)
|
||||
is RustClientException.MatrixApi -> ClientException.MatrixApi(
|
||||
kind = kind.map(),
|
||||
code = code,
|
||||
message = msg
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> ClientException.Other(message ?: "Unknown error")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector 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.libraries.matrix.impl.exception
|
||||
import io.element.android.libraries.matrix.api.exception.ErrorKind
|
||||
import org.matrix.rustcomponents.sdk.ErrorKind as RustErrorKind
|
||||
|
||||
fun RustErrorKind.map(): ErrorKind {
|
||||
return when (this) {
|
||||
RustErrorKind.BadAlias -> ErrorKind.BadAlias
|
||||
RustErrorKind.BadJson -> ErrorKind.BadJson
|
||||
RustErrorKind.BadState -> ErrorKind.BadState
|
||||
is RustErrorKind.BadStatus -> ErrorKind.BadStatus(status?.toInt(), body)
|
||||
RustErrorKind.CannotLeaveServerNoticeRoom -> ErrorKind.CannotLeaveServerNoticeRoom
|
||||
RustErrorKind.CannotOverwriteMedia -> ErrorKind.CannotOverwriteMedia
|
||||
RustErrorKind.CaptchaInvalid -> ErrorKind.CaptchaInvalid
|
||||
RustErrorKind.CaptchaNeeded -> ErrorKind.CaptchaNeeded
|
||||
RustErrorKind.ConnectionFailed -> ErrorKind.ConnectionFailed
|
||||
RustErrorKind.ConnectionTimeout -> ErrorKind.ConnectionTimeout
|
||||
is RustErrorKind.Custom -> ErrorKind.Custom(errcode)
|
||||
RustErrorKind.DuplicateAnnotation -> ErrorKind.DuplicateAnnotation
|
||||
RustErrorKind.Exclusive -> ErrorKind.Exclusive
|
||||
RustErrorKind.Forbidden -> ErrorKind.Forbidden
|
||||
RustErrorKind.GuestAccessForbidden -> ErrorKind.GuestAccessForbidden
|
||||
is RustErrorKind.IncompatibleRoomVersion -> ErrorKind.IncompatibleRoomVersion(roomVersion)
|
||||
RustErrorKind.InvalidParam -> ErrorKind.InvalidParam
|
||||
RustErrorKind.InvalidRoomState -> ErrorKind.InvalidRoomState
|
||||
RustErrorKind.InvalidUsername -> ErrorKind.InvalidUsername
|
||||
is RustErrorKind.LimitExceeded -> ErrorKind.LimitExceeded(retryAfterMs?.toLong())
|
||||
RustErrorKind.MissingParam -> ErrorKind.MissingParam
|
||||
RustErrorKind.MissingToken -> ErrorKind.MissingToken
|
||||
RustErrorKind.NotFound -> ErrorKind.NotFound
|
||||
RustErrorKind.NotJson -> ErrorKind.NotJson
|
||||
RustErrorKind.NotYetUploaded -> ErrorKind.NotYetUploaded
|
||||
is RustErrorKind.ResourceLimitExceeded -> ErrorKind.ResourceLimitExceeded(adminContact)
|
||||
RustErrorKind.RoomInUse -> ErrorKind.RoomInUse
|
||||
RustErrorKind.ServerNotTrusted -> ErrorKind.ServerNotTrusted
|
||||
RustErrorKind.ThreepidAuthFailed -> ErrorKind.ThreepidAuthFailed
|
||||
RustErrorKind.ThreepidDenied -> ErrorKind.ThreepidDenied
|
||||
RustErrorKind.ThreepidInUse -> ErrorKind.ThreepidInUse
|
||||
RustErrorKind.ThreepidMediumNotSupported -> ErrorKind.ThreepidMediumNotSupported
|
||||
RustErrorKind.ThreepidNotFound -> ErrorKind.ThreepidNotFound
|
||||
RustErrorKind.TooLarge -> ErrorKind.TooLarge
|
||||
RustErrorKind.UnableToAuthorizeJoin -> ErrorKind.UnableToAuthorizeJoin
|
||||
RustErrorKind.UnableToGrantJoin -> ErrorKind.UnableToGrantJoin
|
||||
RustErrorKind.Unauthorized -> ErrorKind.Unauthorized
|
||||
RustErrorKind.Unknown -> ErrorKind.Unknown
|
||||
is RustErrorKind.UnknownToken -> ErrorKind.UnknownToken(softLogout)
|
||||
RustErrorKind.Unrecognized -> ErrorKind.Unrecognized
|
||||
RustErrorKind.UnsupportedRoomVersion -> ErrorKind.UnsupportedRoomVersion
|
||||
RustErrorKind.UrlNotSet -> ErrorKind.UrlNotSet
|
||||
RustErrorKind.UserDeactivated -> ErrorKind.UserDeactivated
|
||||
RustErrorKind.UserInUse -> ErrorKind.UserInUse
|
||||
RustErrorKind.UserLocked -> ErrorKind.UserLocked
|
||||
RustErrorKind.UserSuspended -> ErrorKind.UserSuspended
|
||||
RustErrorKind.WeakPassword -> ErrorKind.WeakPassword
|
||||
is RustErrorKind.WrongRoomKeysVersion -> ErrorKind.WrongRoomKeysVersion(currentVersion)
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@ class MatrixRoomInfoMapper {
|
|||
topic = it.topic,
|
||||
avatarUrl = it.avatarUrl,
|
||||
isDirect = it.isDirect,
|
||||
isPublic = it.isPublic,
|
||||
joinRule = it.joinRule?.map(),
|
||||
isSpace = it.isSpace,
|
||||
isTombstoned = it.isTombstoned,
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.PendingRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import org.matrix.rustcomponents.sdk.RoomPreview
|
||||
|
||||
class RustPendingRoom(
|
||||
override val sessionId: SessionId,
|
||||
override val roomId: RoomId,
|
||||
private val inner: RoomPreview,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
) : PendingRoom {
|
||||
override suspend fun leave(): Result<Unit> = runCatching {
|
||||
inner.leave()
|
||||
}.onSuccess {
|
||||
roomMembershipObserver.notifyUserLeftRoom(roomId)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
inner.destroy()
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.PendingRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
|
|
@ -28,7 +28,6 @@ import kotlinx.coroutines.NonCancellable
|
|||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListException
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
|
|
@ -36,7 +35,6 @@ import timber.log.Timber
|
|||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||
|
||||
private const val CACHE_SIZE = 16
|
||||
private val PENDING_MEMBERSHIPS = setOf(Membership.INVITED, Membership.KNOCKED)
|
||||
|
||||
class RustRoomFactory(
|
||||
private val sessionId: SessionId,
|
||||
|
|
@ -125,7 +123,7 @@ class RustRoomFactory(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun createPendingRoom(roomId: RoomId): PendingRoom? = withContext(dispatcher) {
|
||||
suspend fun createRoomPreview(roomId: RoomId): RoomPreview? = withContext(dispatcher) {
|
||||
if (isDestroyed) {
|
||||
Timber.d("Room factory is destroyed, returning null for $roomId")
|
||||
return@withContext null
|
||||
|
|
@ -135,19 +133,18 @@ class RustRoomFactory(
|
|||
Timber.d("Room not found for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
if (roomListItem.membership() !in PENDING_MEMBERSHIPS) {
|
||||
Timber.d("Room $roomId is not in pending state")
|
||||
if (roomListItem.membership() !in RustRoomPreview.ALLOWED_MEMBERSHIPS) {
|
||||
Timber.d("Room $roomId is not in allowed membership")
|
||||
return@withContext null
|
||||
}
|
||||
val innerRoom = try {
|
||||
roomListItem.previewRoom(via = emptyList())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to get pending room for $roomId")
|
||||
Timber.e(e, "Failed to get room preview for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
RustPendingRoom(
|
||||
RustRoomPreview(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
inner = innerRoom,
|
||||
roomMembershipObserver = roomMembershipObserver,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.impl.room
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.RoomPreview as InnerRoomPreview
|
||||
|
||||
@Immutable
|
||||
class RustRoomPreview(
|
||||
override val sessionId: SessionId,
|
||||
private val inner: InnerRoomPreview,
|
||||
private val roomMembershipObserver: RoomMembershipObserver?,
|
||||
) : RoomPreview {
|
||||
companion object {
|
||||
val ALLOWED_MEMBERSHIPS = setOf(Membership.INVITED, Membership.KNOCKED, Membership.BANNED)
|
||||
}
|
||||
|
||||
override val info: RoomPreviewInfo = RoomPreviewInfoMapper.map(inner.info())
|
||||
|
||||
override suspend fun leave(): Result<Unit> = runCatching {
|
||||
inner.leave()
|
||||
}.onSuccess {
|
||||
roomMembershipObserver?.notifyUserLeftRoom(info.roomId)
|
||||
}
|
||||
|
||||
override suspend fun forget(): Result<Unit> = runCatching {
|
||||
inner.forget()
|
||||
}
|
||||
|
||||
override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = runCatching {
|
||||
val details = inner.ownMembershipDetails() ?: return@runCatching null
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = RoomMemberMapper.map(details.ownRoomMember),
|
||||
senderMember = details.senderRoomMember?.let { RoomMemberMapper.map(it) },
|
||||
)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
inner.destroy()
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ object RoomMemberMapper {
|
|||
normalizedPowerLevel = roomMember.normalizedPowerLevel,
|
||||
isIgnored = roomMember.isIgnored,
|
||||
role = mapRole(roomMember.suggestedRoleForPowerLevel),
|
||||
membershipChangeReason = roomMember.membershipChangeReason
|
||||
)
|
||||
|
||||
fun mapRole(role: RoomMemberRole): RoomMember.Role =
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ import io.element.android.libraries.core.bool.orFalse
|
|||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.impl.room.join.map
|
||||
import io.element.android.libraries.matrix.impl.room.map
|
||||
import org.matrix.rustcomponents.sdk.JoinRule
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.RoomPreviewInfo as RustRoomPreviewInfo
|
||||
|
||||
object RoomPreviewInfoMapper {
|
||||
|
|
@ -27,10 +26,8 @@ object RoomPreviewInfoMapper {
|
|||
numberOfJoinedMembers = info.numJoinedMembers.toLong(),
|
||||
roomType = info.roomType.map(),
|
||||
isHistoryWorldReadable = info.isHistoryWorldReadable.orFalse(),
|
||||
isJoined = info.membership == Membership.JOINED,
|
||||
isInvited = info.membership == Membership.INVITED,
|
||||
isPublic = info.joinRule == JoinRule.Public,
|
||||
canKnock = info.joinRule == JoinRule.Knock
|
||||
membership = info.membership?.map(),
|
||||
joinRule = info.joinRule.map(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock
|
|||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -296,7 +297,7 @@ class RustTimeline(
|
|||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = withContext(dispatcher) {
|
||||
runCatching<Unit> {
|
||||
runCatching {
|
||||
val editedContent = EditedContent.RoomMessage(
|
||||
content = MessageEventContent.from(
|
||||
body = body,
|
||||
|
|
@ -324,10 +325,12 @@ class RustTimeline(
|
|||
},
|
||||
mentions = null,
|
||||
)
|
||||
inner.edit(
|
||||
newContent = editedContent,
|
||||
eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(),
|
||||
)
|
||||
withContext(Dispatchers.IO) {
|
||||
inner.edit(
|
||||
newContent = editedContent,
|
||||
eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -519,7 +522,7 @@ class RustTimeline(
|
|||
newContent = editedContent,
|
||||
eventOrTransactionId = RustEventOrTransactionId.EventId(pollStartId.value),
|
||||
)
|
||||
}.map { }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ class MatrixRoomInfoMapperTest {
|
|||
topic = "topic",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
isDirect = true,
|
||||
isPublic = false,
|
||||
isSpace = false,
|
||||
isTombstoned = false,
|
||||
isFavorite = false,
|
||||
|
|
@ -167,7 +166,6 @@ class MatrixRoomInfoMapperTest {
|
|||
topic = null,
|
||||
avatarUrl = null,
|
||||
isDirect = false,
|
||||
isPublic = true,
|
||||
joinRule = null,
|
||||
isSpace = false,
|
||||
isTombstoned = false,
|
||||
|
|
|
|||
|
|
@ -8,14 +8,16 @@
|
|||
package io.element.android.libraries.matrix.impl.room.preview
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.JoinRule
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule
|
||||
|
||||
class RoomPreviewInfoMapperTest {
|
||||
@Test
|
||||
|
|
@ -23,7 +25,7 @@ class RoomPreviewInfoMapperTest {
|
|||
assertThat(
|
||||
RoomPreviewInfoMapper.map(
|
||||
info = aRustRoomPreviewInfo(
|
||||
membership = null,
|
||||
membership = Membership.JOINED,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
|
|
@ -36,10 +38,8 @@ class RoomPreviewInfoMapperTest {
|
|||
numberOfJoinedMembers = 1L,
|
||||
roomType = RoomType.Room,
|
||||
isHistoryWorldReadable = true,
|
||||
isJoined = false,
|
||||
isInvited = false,
|
||||
isPublic = true,
|
||||
canKnock = false,
|
||||
membership = CurrentUserMembership.JOINED,
|
||||
joinRule = JoinRule.Public,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ class RoomPreviewInfoMapperTest {
|
|||
info = aRustRoomPreviewInfo(
|
||||
canonicalAlias = null,
|
||||
membership = Membership.JOINED,
|
||||
joinRule = JoinRule.Knock,
|
||||
joinRule = RustJoinRule.Knock,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
|
|
@ -64,10 +64,8 @@ class RoomPreviewInfoMapperTest {
|
|||
numberOfJoinedMembers = 1L,
|
||||
roomType = RoomType.Room,
|
||||
isHistoryWorldReadable = true,
|
||||
isJoined = true,
|
||||
isInvited = false,
|
||||
isPublic = false,
|
||||
canKnock = true,
|
||||
membership = CurrentUserMembership.JOINED,
|
||||
joinRule = JoinRule.Knock,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,9 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
|
|||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.PendingRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
|
|
@ -79,7 +78,7 @@ class FakeMatrixClient(
|
|||
Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList()))
|
||||
)
|
||||
},
|
||||
private val getRoomPreviewInfoResult: (RoomIdOrAlias, List<String>) -> Result<RoomPreviewInfo> = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
private val getRoomPreviewResult: (RoomIdOrAlias, List<String>) -> Result<RoomPreview> = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
private val clearCacheLambda: () -> Unit = { lambdaError() },
|
||||
private val userIdServerNameLambda: () -> String = { lambdaError() },
|
||||
private val getUrlLambda: (String) -> Result<String> = { lambdaError() },
|
||||
|
|
@ -105,7 +104,6 @@ class FakeMatrixClient(
|
|||
private var createDmResult: Result<RoomId> = Result.success(A_ROOM_ID)
|
||||
private var findDmResult: RoomId? = A_ROOM_ID
|
||||
private val getRoomResults = mutableMapOf<RoomId, MatrixRoom>()
|
||||
val getPendingRoomResults = mutableMapOf<RoomId, PendingRoom>()
|
||||
private val searchUserResults = mutableMapOf<String, Result<MatrixSearchUserResults>>()
|
||||
private val getProfileResults = mutableMapOf<UserId, Result<MatrixUser>>()
|
||||
private var uploadMediaResult: Result<String> = Result.success(AN_AVATAR_URL)
|
||||
|
|
@ -132,8 +130,8 @@ class FakeMatrixClient(
|
|||
return getRoomResults[roomId]
|
||||
}
|
||||
|
||||
override suspend fun getPendingRoom(roomId: RoomId): PendingRoom? {
|
||||
return getPendingRoomResults[roomId]
|
||||
override suspend fun getPendingRoom(roomId: RoomId): RoomPreview? = simulateLongTask {
|
||||
getRoomPreviewResult(RoomIdOrAlias.Id(roomId), emptyList()).getOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findDM(userId: UserId): RoomId? {
|
||||
|
|
@ -313,8 +311,8 @@ class FakeMatrixClient(
|
|||
resolveRoomAliasResult(roomAlias)
|
||||
}
|
||||
|
||||
override suspend fun getRoomPreviewInfo(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreviewInfo> = simulateLongTask {
|
||||
getRoomPreviewInfoResult(roomIdOrAlias, serverNames)
|
||||
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomPreview> = simulateLongTask {
|
||||
getRoomPreviewResult(roomIdOrAlias, serverNames)
|
||||
}
|
||||
|
||||
override suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>> {
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.PendingRoom
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakePendingRoom(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val roomId: RoomId = A_ROOM_ID,
|
||||
private val declineInviteResult: () -> Result<Unit> = { lambdaError() }
|
||||
) : PendingRoom {
|
||||
override suspend fun leave(): Result<Unit> = simulateLongTask {
|
||||
declineInviteResult()
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.test.room
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomPreview
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
@Immutable
|
||||
class FakeRoomPreview(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val info: RoomPreviewInfo = aRoomPreviewInfo(),
|
||||
private val declineInviteResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val forgetRoomResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val roomMembershipDetails: () -> Result<RoomMembershipDetails?> = { lambdaError() },
|
||||
) : RoomPreview {
|
||||
override suspend fun leave(): Result<Unit> = simulateLongTask {
|
||||
declineInviteResult()
|
||||
}
|
||||
|
||||
override suspend fun forget(): Result<Unit> = simulateLongTask {
|
||||
forgetRoomResult()
|
||||
}
|
||||
|
||||
override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = simulateLongTask {
|
||||
roomMembershipDetails()
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
|
|
@ -34,7 +34,6 @@ fun aRoomInfo(
|
|||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
joinRule: JoinRule? = JoinRule.Public,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
|
|
@ -43,8 +42,8 @@ fun aRoomInfo(
|
|||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
invitedMembersCount: Long = 0,
|
||||
activeMembersCount: Long = 2,
|
||||
invitedMembersCount: Long = 1,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
|
|
@ -67,7 +66,6 @@ fun aRoomInfo(
|
|||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
joinRule = joinRule,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ fun aRoomMember(
|
|||
normalizedPowerLevel: Long = 0L,
|
||||
isIgnored: Boolean = false,
|
||||
role: RoomMember.Role = RoomMember.Role.USER,
|
||||
membershipChangeReason: String? = null,
|
||||
) = RoomMember(
|
||||
userId = userId,
|
||||
displayName = displayName,
|
||||
|
|
@ -31,4 +32,5 @@ fun aRoomMember(
|
|||
normalizedPowerLevel = normalizedPowerLevel,
|
||||
isIgnored = isIgnored,
|
||||
role = role,
|
||||
membershipChangeReason = membershipChangeReason,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector 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.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
fun aRoomPreview(
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
info: RoomPreviewInfo = aRoomPreviewInfo(),
|
||||
declineInviteResult: () -> Result<Unit> = { lambdaError() },
|
||||
forgetRoomResult: () -> Result<Unit> = { lambdaError() },
|
||||
roomMembershipDetails: () -> Result<RoomMembershipDetails?> = { lambdaError() },
|
||||
) = FakeRoomPreview(
|
||||
sessionId = sessionId,
|
||||
info = info,
|
||||
declineInviteResult = declineInviteResult,
|
||||
forgetRoomResult = forgetRoomResult,
|
||||
roomMembershipDetails = roomMembershipDetails,
|
||||
)
|
||||
|
||||
fun aRoomPreviewInfo(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
joinRule: JoinRule = JoinRule.Public,
|
||||
isSpace: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
currentUserMembership: CurrentUserMembership? = null,
|
||||
numberOfJoinedMembers: Long = 1,
|
||||
isHistoryWorldReadable: Boolean = true,
|
||||
) = RoomPreviewInfo(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
joinRule = joinRule,
|
||||
canonicalAlias = canonicalAlias,
|
||||
numberOfJoinedMembers = numberOfJoinedMembers,
|
||||
roomType = if (isSpace) RoomType.Space else RoomType.Room,
|
||||
isHistoryWorldReadable = isHistoryWorldReadable,
|
||||
membership = currentUserMembership,
|
||||
)
|
||||
|
|
@ -47,7 +47,6 @@ fun aRoomSummary(
|
|||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = null,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
joinRule: JoinRule? = JoinRule.Public,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
|
|
@ -82,7 +81,6 @@ fun aRoomSummary(
|
|||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
joinRule = joinRule,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
|
|
@ -97,7 +96,7 @@ private fun AvatarActionBottomSheetContent(
|
|||
Text(
|
||||
text = stringResource(action.titleResId),
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
color = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
|
||||
color = if (action.destructive) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textPrimary,
|
||||
)
|
||||
},
|
||||
leadingContent = ListItemContent.Icon(IconSource.Resource(action.iconResourceId)),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -27,6 +26,7 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
|
|
@ -79,7 +79,7 @@ fun EditableAvatarView(
|
|||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.primary)
|
||||
.background(ElementTheme.colors.iconPrimary)
|
||||
.size(24.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
|
|
@ -87,7 +87,7 @@ fun EditableAvatarView(
|
|||
modifier = Modifier.size(16.dp),
|
||||
imageVector = CompoundIcons.EditSolid(),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -40,7 +39,7 @@ fun InviteSenderView(
|
|||
Text(
|
||||
text = inviteSender.annotatedString(),
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -56,8 +55,9 @@ internal fun InviteSenderViewPreview() = ElementPreview {
|
|||
id = "@bob:example.com",
|
||||
name = "Bob",
|
||||
url = null,
|
||||
size = AvatarSize.InviteSender
|
||||
)
|
||||
size = AvatarSize.InviteSender,
|
||||
),
|
||||
membershipChangeReason = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,14 +77,14 @@ private fun MatrixUserHeaderContent(
|
|||
maxLines = 1,
|
||||
style = ElementTheme.typography.fontHeadingSmMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = ElementTheme.materialColors.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
// Id
|
||||
if (matrixUser.displayName.isNullOrEmpty().not()) {
|
||||
Text(
|
||||
text = matrixUser.userId.value,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = ElementTheme.materialColors.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.avatar.CompositeAvatar
|
||||
|
|
@ -65,21 +66,21 @@ fun SelectedRoom(
|
|||
)
|
||||
}
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.iconPrimary,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onRemoveRoom(roomInfo) }
|
||||
),
|
||||
.clip(CircleShape)
|
||||
.size(20.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.clickable(
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onRemoveRoom(roomInfo) }
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Close(),
|
||||
contentDescription = stringResource(id = CommonStrings.action_remove),
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
modifier = Modifier.padding(2.dp)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -58,7 +57,7 @@ fun UnresolvedUserRow(
|
|||
text = id,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
)
|
||||
|
||||
|
|
@ -75,12 +74,11 @@ fun UnresolvedUserRow(
|
|||
.size(18.dp)
|
||||
.align(Alignment.Top)
|
||||
.padding(2.dp),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(CommonStrings.common_invite_unknown_profile),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
style = ElementTheme.typography.fontBodySmRegular.copy(lineHeight = 16.sp),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ fun UnsavedAvatar(
|
|||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(40.dp),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -51,14 +50,14 @@ internal fun UserRow(
|
|||
text = name,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
)
|
||||
// Id
|
||||
subtext?.let {
|
||||
Text(
|
||||
text = subtext,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ private fun ReplyToErrorContent(
|
|||
Text(
|
||||
text = data.message,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
color = ElementTheme.colors.textCriticalPrimary,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
|
@ -171,7 +171,7 @@ private fun ReplyToContentText(metadata: InReplyToMetadata?) {
|
|||
if (iconResourceId != null) {
|
||||
Icon(
|
||||
resourceId = iconResourceId,
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
|
@ -182,7 +182,7 @@ private fun ReplyToContentText(metadata: InReplyToMetadata?) {
|
|||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
fontStyle = fontStyle,
|
||||
textAlign = TextAlign.Start,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ package io.element.android.libraries.matrix.ui.messages.sender
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -77,7 +76,7 @@ private fun RowScope.MainText(
|
|||
val color = when (mode) {
|
||||
is SenderNameMode.Timeline -> mode.mainColor
|
||||
SenderNameMode.ActionList,
|
||||
SenderNameMode.Reply -> MaterialTheme.colorScheme.primary
|
||||
SenderNameMode.Reply -> ElementTheme.colors.textPrimary
|
||||
}
|
||||
Text(
|
||||
modifier = modifier.clipToBounds(),
|
||||
|
|
@ -108,7 +107,7 @@ private fun RowScope.SecondaryText(
|
|||
modifier = modifier.clipToBounds(),
|
||||
text = text,
|
||||
style = style,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
package io.element.android.libraries.matrix.ui.model
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
|
@ -15,6 +14,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
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.core.UserId
|
||||
|
|
@ -26,6 +26,7 @@ data class InviteSender(
|
|||
val userId: UserId,
|
||||
val displayName: String,
|
||||
val avatarData: AvatarData,
|
||||
val membershipChangeReason: String?,
|
||||
) {
|
||||
@Composable
|
||||
fun annotatedString(): AnnotatedString {
|
||||
|
|
@ -37,7 +38,7 @@ data class InviteSender(
|
|||
AnnotatedString.Range(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.textPrimary
|
||||
),
|
||||
start = senderNameStart,
|
||||
end = senderNameStart + displayName.length
|
||||
|
|
@ -52,4 +53,5 @@ fun RoomMember.toInviteSender() = InviteSender(
|
|||
userId = userId,
|
||||
displayName = displayName ?: "",
|
||||
avatarData = getAvatarData(size = AvatarSize.InviteSender),
|
||||
membershipChangeReason = membershipChangeReason
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Poslat pozvánku"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Chcete začít chatovat s %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Poslat pozvánku?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) vás pozval(a)"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Einladung senden"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Möchten Sie einen Chat mit %1$s starten?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Einladung senden?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) hat dich eingeladen"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Saada kutse"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Kas sa soovid alustada vestlust kasutajaga %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Kas saadame kutse?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) saatis sulle kutse"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Lähetä kutsu"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Lähetetäänkö kutsu?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) kutsui sinut"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Envoyer l’invitation"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Voulez-vous entamer une discussion avec %1$s ?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Envoyer l’invitation ?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) vous a invité(e)"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Meghívó küldése"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Csevegést kezd vele: %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Meghívó küldése?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) meghívta"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Invia invito"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Inviare invito?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) ti ha invitato"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Wyślij zaproszenie"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Czy chcesz rozpocząć czat z %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Wysłać zaproszenie?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) zaprosił Cię"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Enviar convite"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Gostarias de iniciar uma conversa com %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Enviar convite?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) convidou-te"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Отправить приглашение"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Хотите начать чат с %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Отправить приглашение?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил вас"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Odoslať pozvánku"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Chceli by ste začať rozhovor s používateľom %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Poslať pozvánku?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) vás pozval/a"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Davet gönder"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"%1$s ile sohbet başlatmak ister misiniz?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Davet gönder?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) sizi davet etti"</string>
|
||||
</resources>
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Надіслати запрошення"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_message">"Хочете розпочати бесіду з %1$s?"</string>
|
||||
<string name="screen_bottom_sheet_create_dm_title">"Надіслати запрошення?"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) запрошує вас"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -141,7 +140,7 @@ private fun MediaRow(
|
|||
// Info
|
||||
Text(
|
||||
text = state.mediaInfo.mimeType + " - " + state.mediaInfo.formattedFileSize,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -165,7 +164,7 @@ private fun SenderRow(
|
|||
// Id
|
||||
Text(
|
||||
text = mediaInfo.senderId?.value.orEmpty(),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.squareup.anvil.annotations.ContributesBinding
|
|||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
|
||||
import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer
|
||||
import me.saket.telephoto.zoomable.OverzoomEffect
|
||||
import me.saket.telephoto.zoomable.ZoomSpec
|
||||
import me.saket.telephoto.zoomable.rememberZoomableState
|
||||
import javax.inject.Inject
|
||||
|
|
@ -24,7 +25,7 @@ class DefaultLocalMediaRenderer @Inject constructor() : LocalMediaRenderer {
|
|||
override fun Render(localMedia: LocalMedia) {
|
||||
val localMediaViewState = rememberLocalMediaViewState(
|
||||
zoomableState = rememberZoomableState(
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 4f, preventOverOrUnderZoom = false)
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 4f, overzoomEffect = OverzoomEffect.NoLimits)
|
||||
)
|
||||
)
|
||||
LocalMediaView(
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import androidx.compose.foundation.shape.CircleShape
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.GraphicEq
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
|
|
@ -261,13 +260,13 @@ private fun ExoPlayerMediaAudioView(
|
|||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.onBackground),
|
||||
.background(ElementTheme.colors.iconPrimary),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.GraphicEq,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.background,
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
modifier = Modifier
|
||||
.size(32.dp),
|
||||
)
|
||||
|
|
@ -335,7 +334,7 @@ private fun AudioInfoView(
|
|||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.textPrimary
|
||||
)
|
||||
}
|
||||
if (info != null) {
|
||||
|
|
@ -346,7 +345,7 @@ private fun AudioInfoView(
|
|||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.textPrimary
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
|
|
@ -354,7 +353,7 @@ private fun AudioInfoView(
|
|||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.GraphicEq
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -72,13 +71,13 @@ fun MediaFileView(
|
|||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.onBackground),
|
||||
.background(ElementTheme.colors.iconPrimary),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isAudio) Icons.Outlined.GraphicEq else CompoundIcons.Attachment(),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.background,
|
||||
tint = ElementTheme.colors.iconOnSolidPrimary,
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.rotate(if (isAudio) 0f else -45f),
|
||||
|
|
@ -92,7 +91,7 @@ fun MediaFileView(
|
|||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.textPrimary
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
|
|
@ -100,7 +99,7 @@ fun MediaFileView(
|
|||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = ElementTheme.colors.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaVie
|
|||
import io.element.android.libraries.mediaviewer.impl.util.bgCanvasWithTransparency
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.delay
|
||||
import me.saket.telephoto.zoomable.OverzoomEffect
|
||||
import me.saket.telephoto.zoomable.ZoomSpec
|
||||
import me.saket.telephoto.zoomable.rememberZoomableState
|
||||
|
||||
|
|
@ -297,7 +298,7 @@ private fun MediaViewerPage(
|
|||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
val zoomableState = rememberZoomableState(
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 4f, preventOverOrUnderZoom = false)
|
||||
zoomSpec = ZoomSpec(maxZoomFactor = 4f, overzoomEffect = OverzoomEffect.NoLimits)
|
||||
)
|
||||
val localMediaViewState = rememberLocalMediaViewState(zoomableState)
|
||||
val showThumbnail = !localMediaViewState.isReady
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Název souboru"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Žádné další soubory k zobrazení"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Žádná další média k zobrazení"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Tento soubor bude odstraněn z místnosti a členové k němu nebudou mít přístup."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Smazat soubor?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Nahrál(a)"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Nahráno"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Dateiname"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Keine weiteren Dateien zum Anzeigen"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Keine weiteren Medien mehr zum Anzeigen"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Diese Datei wird aus dem Chatroom entfernt und die Mitglieder werden keinen Zugriff mehr darauf haben."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Datei löschen?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Hochgeladen von"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Hochgeladen am"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
<string name="screen_media_browser_title">"Πολυμέσα και αρχεία"</string>
|
||||
<string name="screen_media_details_file_format">"Μορφή αρχείου"</string>
|
||||
<string name="screen_media_details_filename">"Όνομα αρχείου"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Αυτό το αρχείο θα αφαιρεθεί από το δωμάτιο και τα μέλη δεν θα έχουν πρόσβαση σε αυτό."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Διαγραφή αρχείου;"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Μεταφορτώθηκε από"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Μεταφορτώθηκε στις"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Failinimi"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Pole enam kuvatavaid faile"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Pole enam kuvatavat meediat"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Järgnevaga eemaldame selle faili jututoast ja tema liikmed enam ei pääse failile ligi."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Kas kustutame faili?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Üleslaadija"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Üleslaaditud"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Tiedostonimi"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Ei enää näytettäviä tiedostoja"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Ei enää näytettävää mediaa"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Tämä tiedosto poistetaan huoneesta, eikä jäsenillä ole enää pääsyä siihen."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Poistetaanko tiedosto?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Lähettäjä"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Lähetetty"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Nom du fichier"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Il n’y a plus de fichiers à montrer"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Il n’y a plus de médias à montrer"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Ce fichier sera supprimé du salon et les membres n’y auront plus accès."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Supprimer le fichier ?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Envoyé par"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Envoyé le"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Fájlnév"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Nincs több megjeleníthető fájl"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Nincs több megjeleníthető média"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Ez a fájl el lesz távolítva a szobából, és a tagok nem férhetnek hozzá."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Törli a fájlt?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Feltöltötte:"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Feltöltve:"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Nome del file"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Nessun altro file da mostrare"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Non ci sono più contenuti multimediali da mostrare"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Questo file verrà rimosso dalla stanza e i membri non ne avranno accesso."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Eliminare il file?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Caricato da"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Caricato il"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_media_browser_delete_confirmation_subtitle">"Ten plik zostanie usunięty z pokoju, a członkowie nie będą mieli do niego dostępu."</string>
|
||||
<string name="screen_media_browser_delete_confirmation_title">"Usunąć plik?"</string>
|
||||
<string name="screen_media_browser_download_error_message">"Sprawdź połączenie internetowe i spróbuj ponownie."</string>
|
||||
<string name="screen_media_browser_files_empty_state_subtitle">"Dokumenty, pliki audio i wiadomości głosowe przesłane do tego pokoju będą wyświetlane tutaj."</string>
|
||||
<string name="screen_media_browser_files_empty_state_title">"Jeszcze nie przesłano plików"</string>
|
||||
<string name="screen_media_browser_list_loading_files">"Wczytywanie plików…"</string>
|
||||
|
|
@ -13,8 +14,8 @@
|
|||
<string name="screen_media_browser_title">"Media i pliki"</string>
|
||||
<string name="screen_media_details_file_format">"Format pliku"</string>
|
||||
<string name="screen_media_details_filename">"Nazwa pliku"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Plik zostanie usunięty z pokoju, a członkowie nie będą mieli do niego dostępu."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Usunąć plik?"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Brak plików do pokazania"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Brak mediów do pokazania"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Przesłane przez"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Przesłane w dniu"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_media_browser_delete_confirmation_subtitle">"Este ficheiro será removido da sala e os membros deixarão de o poder aceder."</string>
|
||||
<string name="screen_media_browser_delete_confirmation_title">"Eliminar ficheiro?"</string>
|
||||
<string name="screen_media_browser_download_error_message">"Verifica a tua ligação à Internet e tenta novamente."</string>
|
||||
<string name="screen_media_browser_files_empty_state_subtitle">"Documentos, ficheiros de áudio e mensagens de voz carregados para esta sala serão mostrados aqui."</string>
|
||||
<string name="screen_media_browser_files_empty_state_title">"Ainda sem qualquer ficheiro"</string>
|
||||
<string name="screen_media_browser_list_loading_files">"A carregar ficheiros…"</string>
|
||||
<string name="screen_media_browser_list_loading_media">"A carregar multimédia…"</string>
|
||||
<string name="screen_media_browser_list_mode_files">"Ficheiros"</string>
|
||||
|
|
@ -11,7 +14,8 @@
|
|||
<string name="screen_media_browser_title">"Multimédia e ficheiros"</string>
|
||||
<string name="screen_media_details_file_format">"Formato do ficheiro"</string>
|
||||
<string name="screen_media_details_filename">"Nome do ficheiro"</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Eliminar ficheiro?"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Sem mais ficheiros para mostrar"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Sem mais multimédia para mostrar"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Enviado por"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Enviado a"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Имя файла"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Больше нет файлов для показа"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Больше нет медиа для показа"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Этот файл будет удален из комнаты и у участников не будет к нему доступа."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Удалить файл?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Загружено"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Загружено на"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"Názov súboru"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Žiadne ďalšie súbory na zobrazenie"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Žiadne ďalšie médiá na zobrazenie"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Tento súbor bude odstránený z miestnosti a členovia k nemu nebudú mať prístup."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Vymazať súbor?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Nahrané používateľom"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Nahrané dňa"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_media_browser_delete_confirmation_subtitle">"Den här filen kommer att tas bort från rummet och medlemmar kommer inte att ha tillgång till den."</string>
|
||||
<string name="screen_media_browser_delete_confirmation_title">"Radera fil?"</string>
|
||||
<string name="screen_media_browser_list_loading_files">"Laddar in filer…"</string>
|
||||
<string name="screen_media_browser_list_loading_media">"Läser in media…"</string>
|
||||
<string name="screen_media_browser_list_mode_files">"Filer"</string>
|
||||
<string name="screen_media_browser_list_mode_media">"Media"</string>
|
||||
<string name="screen_media_browser_media_empty_state_subtitle">"Bilder och videor som laddas upp till detta rum kommer att visas här."</string>
|
||||
<string name="screen_media_browser_media_empty_state_title">"Ingen media uppladdad ännu"</string>
|
||||
<string name="screen_media_browser_title">"Media och filer"</string>
|
||||
<string name="screen_media_details_file_format">"Filformat"</string>
|
||||
<string name="screen_media_details_filename">"Filnamn"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Uppladdad av"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Uppladdad på"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_media_browser_delete_confirmation_subtitle">"Bu dosya odadan kaldırılır ve üyeler bu dosyaya erişemez."</string>
|
||||
<string name="screen_media_browser_delete_confirmation_title">"Dosyayı sil?"</string>
|
||||
<string name="screen_media_browser_download_error_message">"İnternet bağlantınızı kontrol edin ve tekrar deneyin."</string>
|
||||
<string name="screen_media_browser_files_empty_state_subtitle">"Bu odaya yüklenen belgeler, ses dosyaları ve sesli mesajlar burada gösterilecektir."</string>
|
||||
<string name="screen_media_browser_files_empty_state_title">"Henüz yüklenen dosya yok"</string>
|
||||
<string name="screen_media_browser_list_loading_files">"Dosyalar yükleniyor…"</string>
|
||||
<string name="screen_media_browser_list_loading_media">"Medya yükleniyor…"</string>
|
||||
<string name="screen_media_browser_list_mode_files">"Dosyalar"</string>
|
||||
<string name="screen_media_browser_list_mode_media">"Medya"</string>
|
||||
<string name="screen_media_browser_media_empty_state_subtitle">"Bu odaya yüklenen resimler ve videolar burada gösterilecektir."</string>
|
||||
<string name="screen_media_browser_media_empty_state_title">"Henüz yüklenen medya yok"</string>
|
||||
<string name="screen_media_browser_title">"Medya ve dosyalar"</string>
|
||||
<string name="screen_media_details_file_format">"Dosya biçimi"</string>
|
||||
<string name="screen_media_details_filename">"Dosya adı"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Gösterilecek daha fazla dosya yok"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Gösterilecek daha fazla medya yok"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Yükleyen:"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Yüklendi"</string>
|
||||
</resources>
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
<string name="screen_media_browser_title">"Медіа та файли"</string>
|
||||
<string name="screen_media_details_file_format">"Формат файлу"</string>
|
||||
<string name="screen_media_details_filename">"Назва файлу"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"Цей файл буде вилучено з кімнати, і учасники не матимуть доступу до нього."</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"Видалити файл?"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Більше немає файлів для показу"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Більше немає медіа для показу"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Вивантажено користувачем"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Вивантажено"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@
|
|||
<string name="screen_media_browser_title">"媒體與檔案"</string>
|
||||
<string name="screen_media_details_file_format">"檔案格式"</string>
|
||||
<string name="screen_media_details_filename">"檔案名稱"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"此檔案將會從聊天室中移除,成員將無法存取該檔案。"</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"刪除檔案?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"上傳者:"</string>
|
||||
<string name="screen_media_details_uploaded_on">"上傳於"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
<string name="screen_media_details_filename">"文件名"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"没有更多文件可显示了"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"没有更多媒体可显示了"</string>
|
||||
<string name="screen_media_details_redact_confirmation_message">"此文件将从房间中删除,并且成员将无法访问它。"</string>
|
||||
<string name="screen_media_details_redact_confirmation_title">"删除文件?"</string>
|
||||
<string name="screen_media_details_uploaded_by">"上传者:"</string>
|
||||
<string name="screen_media_details_uploaded_on">"上传于"</string>
|
||||
</resources>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue