Merge branch 'develop' of https://github.com/vector-im/element-x-android into yostyle/notifications_global_settings
This commit is contained in:
commit
5e2ec8b504
315 changed files with 3724 additions and 1216 deletions
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.hash
|
||||
|
||||
import java.security.MessageDigest
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Compute a Hash of a String, using SHA-512 algorithm.
|
||||
*/
|
||||
fun String.hash() = try {
|
||||
val digest = MessageDigest.getInstance("SHA-512")
|
||||
digest.update(toByteArray())
|
||||
digest.digest()
|
||||
.joinToString("") { String.format(Locale.ROOT, "%02X", it) }
|
||||
.lowercase(Locale.ROOT)
|
||||
} catch (exc: Exception) {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ class FirstThrottler(private val minimumInterval: Long = 800) {
|
|||
private var lastDate = 0L
|
||||
|
||||
sealed class CanHandleResult {
|
||||
object Yes : CanHandleResult()
|
||||
data object Yes : CanHandleResult()
|
||||
data class No(val shouldWaitMillis: Long) : CanHandleResult()
|
||||
|
||||
fun waitMillis(): Long {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ sealed interface Async<out T> {
|
|||
/**
|
||||
* Represents an uninitialized operation (i.e. yet to be run).
|
||||
*/
|
||||
object Uninitialized : Async<Nothing>
|
||||
data object Uninitialized : Async<Nothing>
|
||||
|
||||
/**
|
||||
* Returns the data returned by the operation, or null otherwise.
|
||||
|
|
|
|||
|
|
@ -43,5 +43,11 @@ android {
|
|||
|
||||
ksp(libs.showkase.processor)
|
||||
kspTest(libs.showkase.processor)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.molecule.runtime)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ sealed class ElementLogoAtomSize(
|
|||
val shadowColorLight: Color,
|
||||
val shadowRadius: Dp,
|
||||
) {
|
||||
object Medium : ElementLogoAtomSize(
|
||||
data object Medium : ElementLogoAtomSize(
|
||||
outerSize = 120.dp,
|
||||
logoSize = 83.5.dp,
|
||||
cornerRadius = 33.dp,
|
||||
|
|
@ -115,7 +115,7 @@ sealed class ElementLogoAtomSize(
|
|||
shadowRadius = 32.dp,
|
||||
)
|
||||
|
||||
object Large : ElementLogoAtomSize(
|
||||
data object Large : ElementLogoAtomSize(
|
||||
outerSize = 158.dp,
|
||||
logoSize = 110.dp,
|
||||
cornerRadius = 44.dp,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.atomic.pages
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.BiasAbsoluteAlignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.R
|
||||
import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.text.withColoredPeriod
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
@Composable
|
||||
fun SunsetPage(
|
||||
isLoading: Boolean,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
modifier: Modifier = Modifier,
|
||||
overallContent: @Composable () -> Unit,
|
||||
) {
|
||||
ElementTheme(
|
||||
darkTheme = true
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize()
|
||||
) {
|
||||
SunsetBackground()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = BiasAbsoluteAlignment(
|
||||
horizontalBias = 0f,
|
||||
verticalBias = -0.05f
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(24.dp),
|
||||
strokeWidth = 2.dp,
|
||||
color = ElementTheme.colors.iconPrimary
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
Text(
|
||||
text = withColoredPeriod(title),
|
||||
style = ElementTheme.typography.fontHeadingXlBold,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
modifier = Modifier.widthIn(max = 360.dp),
|
||||
text = subtitle,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
overallContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SunsetBackground(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.3f)
|
||||
.background(Color.White)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(id = R.drawable.light_dark),
|
||||
contentScale = ContentScale.Crop,
|
||||
contentDescription = null,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.7f)
|
||||
.background(Color(0xFF121418))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DayNightPreviews
|
||||
@Composable
|
||||
internal fun SunsetPagePreview() = ElementPreview {
|
||||
SunsetPage(
|
||||
isLoading = true,
|
||||
title = "Title with a green period.",
|
||||
subtitle = "Subtitle",
|
||||
overallContent = {}
|
||||
)
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -79,8 +78,8 @@ fun ClickableLinkText(
|
|||
@Composable
|
||||
fun ClickableLinkText(
|
||||
annotatedString: AnnotatedString,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
linkify: Boolean = true,
|
||||
linkAnnotationTag: String = LINK_TAG,
|
||||
onClick: () -> Unit = {},
|
||||
|
|
@ -136,7 +135,6 @@ fun ClickableLinkText(
|
|||
layoutResult.value = it
|
||||
},
|
||||
inlineContent = inlineContent,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ fun ProgressDialog(
|
|||
@Immutable
|
||||
sealed interface ProgressDialogType {
|
||||
data class Determinate(val progress: Float) : ProgressDialogType
|
||||
object Indeterminate : ProgressDialogType
|
||||
data object Indeterminate : ProgressDialogType
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ fun String.toAnnotatedString(): AnnotatedString = buildAnnotatedString {
|
|||
* @param color the color to apply to the string
|
||||
* @param underline whether to underline the string
|
||||
* @param bold whether to bold the string
|
||||
* @param tagAndLink an optional pair of tag and link to add to the styled part of the string, as StringAnnotation
|
||||
*/
|
||||
@Composable
|
||||
fun buildAnnotatedStringWithStyledPart(
|
||||
|
|
@ -67,6 +68,7 @@ fun buildAnnotatedStringWithStyledPart(
|
|||
color: Color = LinkColor,
|
||||
underline: Boolean = true,
|
||||
bold: Boolean = false,
|
||||
tagAndLink: Pair<String, String>? = null,
|
||||
) = buildAnnotatedString {
|
||||
val coloredPart = stringResource(coloredTextRes)
|
||||
val fullText = stringResource(fullTextRes, coloredPart)
|
||||
|
|
@ -81,4 +83,31 @@ fun buildAnnotatedStringWithStyledPart(
|
|||
start = startIndex,
|
||||
end = startIndex + coloredPart.length,
|
||||
)
|
||||
if (tagAndLink != null) {
|
||||
addStringAnnotation(
|
||||
tag = tagAndLink.first,
|
||||
annotation = tagAndLink.second,
|
||||
start = startIndex,
|
||||
end = startIndex + coloredPart.length
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to an [AnnotatedString] with colored end period if present.
|
||||
*/
|
||||
fun withColoredPeriod(
|
||||
text: String,
|
||||
) = buildAnnotatedString {
|
||||
append(text)
|
||||
if (text.endsWith(".")) {
|
||||
addStyle(
|
||||
style = SpanStyle(
|
||||
// Light.colorGreen700
|
||||
color = Color(0xff0bc491),
|
||||
),
|
||||
start = text.length - 1,
|
||||
end = text.length,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.RadioButtonUnchecked
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.IconToggleButtonColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
|
||||
@Composable
|
||||
fun IconToggleButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
colors: IconToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
androidx.compose.material3.IconToggleButton(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = colors,
|
||||
interactionSource = interactionSource,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Toggles)
|
||||
@Composable
|
||||
internal fun IconToggleButtonPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() }
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
Column {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
val icon: @Composable () -> Unit = {
|
||||
Icon(
|
||||
imageVector = if (checked) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked,
|
||||
contentDescription = "IconToggleButton"
|
||||
)
|
||||
}
|
||||
IconToggleButton(checked = checked, enabled = true, onCheckedChange = { checked = !checked }, content = icon)
|
||||
IconToggleButton(checked = checked, enabled = false, onCheckedChange = { checked = !checked }, content = icon)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
val icon: @Composable () -> Unit = {
|
||||
Icon(
|
||||
imageVector = if (!checked) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked,
|
||||
contentDescription = "IconToggleButton"
|
||||
)
|
||||
}
|
||||
IconToggleButton(checked = !checked, enabled = true, onCheckedChange = { checked = !checked }, content = icon)
|
||||
IconToggleButton(checked = !checked, enabled = false, onCheckedChange = { checked = !checked }, content = icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ fun ListItem(
|
|||
|
||||
androidx.compose.material3.ListItem(
|
||||
headlineContent = decoratedHeadlineContent,
|
||||
modifier = Modifier.clickable(enabled = enabled && onClick != null, onClick = onClick ?: {}).then(modifier),
|
||||
modifier = if (onClick != null) Modifier.clickable(enabled = enabled, onClick = onClick).then(modifier) else modifier,
|
||||
overlineContent = null,
|
||||
supportingContent = decoratedSupportingContent,
|
||||
leadingContent = decoratedLeadingContent,
|
||||
|
|
@ -134,9 +134,9 @@ fun ListItem(
|
|||
* The style to use for a [ListItem].
|
||||
*/
|
||||
sealed interface ListItemStyle {
|
||||
object Default : ListItemStyle
|
||||
object Primary: ListItemStyle
|
||||
object Destructive : ListItemStyle
|
||||
data object Default : ListItemStyle
|
||||
data object Primary: ListItemStyle
|
||||
data object Destructive : ListItemStyle
|
||||
|
||||
@Composable fun headlineColor() = when (this) {
|
||||
Default, Primary -> ListItemDefaultColors.headline
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
|
|
@ -30,9 +29,11 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.components.ClickableLinkText
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
|
|
@ -103,17 +104,21 @@ fun ListSupportingText(
|
|||
* @param modifier The modifier to be applied to the text.
|
||||
* @param contentPadding The padding to apply to the text. Default is [ListSupportingTextDefaults.Padding.Default].
|
||||
*/
|
||||
@OptIn(ExperimentalTextApi::class)
|
||||
@Composable
|
||||
fun ListSupportingText(
|
||||
annotatedString: AnnotatedString,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: ListSupportingTextDefaults.Padding = ListSupportingTextDefaults.Padding.Default,
|
||||
) {
|
||||
Text(
|
||||
text = annotatedString,
|
||||
modifier = modifier.padding(contentPadding.paddingValues()),
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
val style = ElementTheme.typography.fontBodySmRegular
|
||||
.copy(color = ElementTheme.colors.textSecondary)
|
||||
val paddedModifier = modifier.padding(contentPadding.paddingValues())
|
||||
ClickableLinkText(
|
||||
annotatedString = annotatedString,
|
||||
modifier = paddedModifier,
|
||||
style = style,
|
||||
linkify = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -122,13 +127,13 @@ object ListSupportingTextDefaults {
|
|||
/** Specifies the padding to use for the supporting text. */
|
||||
sealed interface Padding {
|
||||
/** No padding. */
|
||||
object None : Padding
|
||||
data object None : Padding
|
||||
/** Default padding, it will align fine with a [ListItem] with no leading content. */
|
||||
object Default : Padding
|
||||
data object Default : Padding
|
||||
/** It will align to a [ListItem] with an [Icon] or [Checkbox] as leading content. */
|
||||
object SmallLeadingContent : Padding
|
||||
data object SmallLeadingContent : Padding
|
||||
/** It will align to with a [ListItem] with a [Switch] as leading content. */
|
||||
object LargeLeadingContent : Padding
|
||||
data object LargeLeadingContent : Padding
|
||||
/** It will align to with a [ListItem] with a custom start [padding]. */
|
||||
data class Custom(val padding: Dp) : Padding
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.material3.RadioButtonColors
|
||||
import androidx.compose.material3.RadioButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -67,14 +70,15 @@ internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { Con
|
|||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
Column {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
RadioButton(selected = false, onClick = {})
|
||||
RadioButton(selected = false, enabled = false, onClick = {})
|
||||
RadioButton(selected = checked, enabled = true, onClick = { checked = !checked })
|
||||
RadioButton(selected = checked, enabled = false, onClick = { checked = !checked })
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
RadioButton(selected = true, onClick = {})
|
||||
RadioButton(selected = true, enabled = false, onClick = {})
|
||||
RadioButton(selected = !checked, enabled = true, onClick = { checked = !checked })
|
||||
RadioButton(selected = !checked, enabled = false, onClick = { checked = !checked })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,33 +34,40 @@ import androidx.compose.ui.unit.dp
|
|||
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
import io.element.android.libraries.designsystem.theme.components.Snackbar
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* A global dispatcher of [SnackbarMessage] to be displayed in [Snackbar] via a [SnackbarHostState].
|
||||
*/
|
||||
class SnackbarDispatcher {
|
||||
private val mutex = Mutex()
|
||||
|
||||
private val _snackbarMessage = MutableStateFlow<SnackbarMessage?>(null)
|
||||
val snackbarMessage: Flow<SnackbarMessage?> = _snackbarMessage.asStateFlow()
|
||||
|
||||
suspend fun post(message: SnackbarMessage) {
|
||||
mutex.withLock {
|
||||
_snackbarMessage.update { message }
|
||||
private val queueMutex = Mutex()
|
||||
private val snackBarMessageQueue = ArrayDeque<SnackbarMessage>()
|
||||
val snackbarMessage: Flow<SnackbarMessage?> = flow {
|
||||
while (currentCoroutineContext().isActive) {
|
||||
queueMutex.lock()
|
||||
emit(snackBarMessageQueue.firstOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clear() {
|
||||
mutex.withLock {
|
||||
_snackbarMessage.update { null }
|
||||
suspend fun post(message: SnackbarMessage) {
|
||||
if (snackBarMessageQueue.isEmpty()) {
|
||||
snackBarMessageQueue.add(message)
|
||||
if (queueMutex.isLocked) queueMutex.unlock()
|
||||
} else {
|
||||
snackBarMessageQueue.add(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
if (snackBarMessageQueue.isNotEmpty()) {
|
||||
snackBarMessageQueue.removeFirstOrNull()
|
||||
if (queueMutex.isLocked) queueMutex.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,31 +94,51 @@ fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to display a [SnackbarMessage] in a [SnackbarHostState] handling cancellations.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostState {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val snackbarMessageText = snackbarMessage?.let {
|
||||
stringResource(id = snackbarMessage.messageResId)
|
||||
}
|
||||
} ?: return snackbarHostState
|
||||
|
||||
val dispatcher = LocalSnackbarDispatcher.current
|
||||
LaunchedEffect(snackbarMessage) {
|
||||
if (snackbarMessageText == null) return@LaunchedEffect
|
||||
launch {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = snackbarMessageText,
|
||||
duration = snackbarMessage.duration,
|
||||
)
|
||||
if (isActive) {
|
||||
LaunchedEffect(snackbarMessageText) {
|
||||
// If the message wasn't already displayed, do it now, and mark it as displayed
|
||||
// This will prevent the message from appearing in any other active SnackbarHosts
|
||||
if (snackbarMessage.isDisplayed.getAndSet(true) == false) {
|
||||
try {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = snackbarMessageText,
|
||||
duration = snackbarMessage.duration,
|
||||
)
|
||||
// The snackbar item was displayed and dismissed, clear its message
|
||||
dispatcher.clear()
|
||||
} catch (e: CancellationException) {
|
||||
// The snackbar was being displayed when the coroutine was cancelled,
|
||||
// so we need to clear its message
|
||||
dispatcher.clear()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
return snackbarHostState
|
||||
}
|
||||
|
||||
/**
|
||||
* A message to be displayed in a [Snackbar].
|
||||
* @param messageResId The message to be displayed.
|
||||
* @param duration The duration of the message. The default value is [SnackbarDuration.Short].
|
||||
* @param actionResId The action text to be displayed. The default value is `null`.
|
||||
* @param isDisplayed Used to track if the current message is already displayed or not.
|
||||
* @param action The action to be performed when the action is clicked.
|
||||
*/
|
||||
data class SnackbarMessage(
|
||||
@StringRes val messageResId: Int,
|
||||
val duration: SnackbarDuration = SnackbarDuration.Short,
|
||||
@StringRes val actionResId: Int? = null,
|
||||
val isDisplayed: AtomicBoolean = AtomicBoolean(false),
|
||||
val action: () -> Unit = {},
|
||||
)
|
||||
|
|
|
|||
BIN
libraries/designsystem/src/main/res/drawable/light_dark.png
Normal file
BIN
libraries/designsystem/src/main/res/drawable/light_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class SnackbarDispatcherTests {
|
||||
|
||||
@Test
|
||||
fun `given an empty queue the flow emits a null item`() = runTest {
|
||||
val snackbarDispatcher = SnackbarDispatcher()
|
||||
snackbarDispatcher.snackbarMessage.test {
|
||||
assertThat(awaitItem()).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given an empty queue calling clear does nothing`() = runTest {
|
||||
val snackbarDispatcher = SnackbarDispatcher()
|
||||
snackbarDispatcher.snackbarMessage.test {
|
||||
assertThat(awaitItem()).isNull()
|
||||
snackbarDispatcher.clear()
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a non-empty queue the flow emits an item`() = runTest {
|
||||
val snackbarDispatcher = SnackbarDispatcher()
|
||||
snackbarDispatcher.snackbarMessage.test {
|
||||
snackbarDispatcher.post(SnackbarMessage(0))
|
||||
val result = expectMostRecentItem()
|
||||
assertThat(result).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a call to clear, the current message is cleared`() = runTest {
|
||||
val snackbarDispatcher = SnackbarDispatcher()
|
||||
snackbarDispatcher.snackbarMessage.test {
|
||||
snackbarDispatcher.post(SnackbarMessage(0))
|
||||
val item = expectMostRecentItem()
|
||||
assertThat(item).isNotNull()
|
||||
snackbarDispatcher.clear()
|
||||
assertThat(awaitItem()).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given 2 message emissions, the next message is displayed only after a call to clear`() = runTest {
|
||||
val snackbarDispatcher = SnackbarDispatcher()
|
||||
snackbarDispatcher.snackbarMessage.test {
|
||||
val messageA = SnackbarMessage(0)
|
||||
val messageB = SnackbarMessage(1)
|
||||
|
||||
// Send message A - it is the most recent item
|
||||
snackbarDispatcher.post(messageA)
|
||||
assertThat(expectMostRecentItem()).isEqualTo(messageA)
|
||||
|
||||
// Send message B - message A is still the most recent item
|
||||
snackbarDispatcher.post(messageB)
|
||||
expectNoEvents()
|
||||
|
||||
// Clear the last message - message B is now the most recent item
|
||||
snackbarDispatcher.clear()
|
||||
assertThat(expectMostRecentItem()).isEqualTo(messageB)
|
||||
|
||||
// Clear again - the queue is empty
|
||||
snackbarDispatcher.clear()
|
||||
assertThat(awaitItem()).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
|||
is StateContent -> {
|
||||
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
|
||||
}
|
||||
is PollContent,
|
||||
is PollContent, // TODO Polls: handle last message
|
||||
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
|
||||
prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ enum class FeatureFlags(
|
|||
Polls(
|
||||
key = "feature.polls",
|
||||
title = "Polls",
|
||||
description = "Render poll events in the timeline",
|
||||
description = "Create poll and render poll events in the timeline",
|
||||
defaultValue = false,
|
||||
),
|
||||
NotificationSettings(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ plugins {
|
|||
id("io.element.android-library")
|
||||
id("kotlin-parcelize")
|
||||
alias(libs.plugins.anvil)
|
||||
kotlin("plugin.serialization") version "1.9.0"
|
||||
kotlin("plugin.serialization") version "1.9.10"
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ interface MatrixClient : Closeable {
|
|||
|
||||
/**
|
||||
* Logout the user.
|
||||
* Returns an optional URL. When the URL is there, it should be presented to the user after logout for RP initiated logout on their account page.
|
||||
* Returns an optional URL. When the URL is there, it should be presented to the user after logout for
|
||||
* Relying Party (RP) initiated logout on their account page.
|
||||
*/
|
||||
suspend fun logout(): String?
|
||||
suspend fun loadUserDisplayName(): Result<String>
|
||||
|
|
|
|||
|
|
@ -40,54 +40,53 @@ data class NotificationData(
|
|||
|
||||
sealed interface NotificationContent {
|
||||
sealed interface MessageLike : NotificationContent {
|
||||
object CallAnswer : MessageLike
|
||||
object CallInvite : MessageLike
|
||||
object CallHangup : MessageLike
|
||||
object CallCandidates : MessageLike
|
||||
object KeyVerificationReady : MessageLike
|
||||
object KeyVerificationStart : MessageLike
|
||||
object KeyVerificationCancel : MessageLike
|
||||
object KeyVerificationAccept : MessageLike
|
||||
object KeyVerificationKey : MessageLike
|
||||
object KeyVerificationMac : MessageLike
|
||||
object KeyVerificationDone : MessageLike
|
||||
data object CallAnswer : MessageLike
|
||||
data object CallInvite : MessageLike
|
||||
data object CallHangup : MessageLike
|
||||
data object CallCandidates : MessageLike
|
||||
data object KeyVerificationReady : MessageLike
|
||||
data object KeyVerificationStart : MessageLike
|
||||
data object KeyVerificationCancel : MessageLike
|
||||
data object KeyVerificationAccept : MessageLike
|
||||
data object KeyVerificationKey : MessageLike
|
||||
data object KeyVerificationMac : MessageLike
|
||||
data object KeyVerificationDone : MessageLike
|
||||
data class ReactionContent(
|
||||
val relatedEventId: String
|
||||
) : MessageLike
|
||||
object RoomEncrypted : MessageLike
|
||||
data object RoomEncrypted : MessageLike
|
||||
data class RoomMessage(
|
||||
val senderId: UserId,
|
||||
val messageType: MessageType
|
||||
) : MessageLike
|
||||
object RoomRedaction : MessageLike
|
||||
object Sticker : MessageLike
|
||||
data object RoomRedaction : MessageLike
|
||||
data object Sticker : MessageLike
|
||||
}
|
||||
|
||||
sealed interface StateEvent : NotificationContent {
|
||||
object PolicyRuleRoom : StateEvent
|
||||
object PolicyRuleServer : StateEvent
|
||||
object PolicyRuleUser : StateEvent
|
||||
object RoomAliases : StateEvent
|
||||
object RoomAvatar : StateEvent
|
||||
object RoomCanonicalAlias : StateEvent
|
||||
object RoomCreate : StateEvent
|
||||
object RoomEncryption : StateEvent
|
||||
object RoomGuestAccess : StateEvent
|
||||
object RoomHistoryVisibility : StateEvent
|
||||
object RoomJoinRules : StateEvent
|
||||
data object PolicyRuleRoom : StateEvent
|
||||
data object PolicyRuleServer : StateEvent
|
||||
data object PolicyRuleUser : StateEvent
|
||||
data object RoomAliases : StateEvent
|
||||
data object RoomAvatar : StateEvent
|
||||
data object RoomCanonicalAlias : StateEvent
|
||||
data object RoomCreate : StateEvent
|
||||
data object RoomEncryption : StateEvent
|
||||
data object RoomGuestAccess : StateEvent
|
||||
data object RoomHistoryVisibility : StateEvent
|
||||
data object RoomJoinRules : StateEvent
|
||||
data class RoomMemberContent(
|
||||
val userId: String,
|
||||
val membershipState: RoomMembershipState
|
||||
) : StateEvent
|
||||
object RoomName : StateEvent
|
||||
object RoomPinnedEvents : StateEvent
|
||||
object RoomPowerLevels : StateEvent
|
||||
object RoomServerAcl : StateEvent
|
||||
object RoomThirdPartyInvite : StateEvent
|
||||
object RoomTombstone : StateEvent
|
||||
object RoomTopic : StateEvent
|
||||
object SpaceChild : StateEvent
|
||||
object SpaceParent : StateEvent
|
||||
data object RoomName : StateEvent
|
||||
data object RoomPinnedEvents : StateEvent
|
||||
data object RoomPowerLevels : StateEvent
|
||||
data object RoomServerAcl : StateEvent
|
||||
data object RoomThirdPartyInvite : StateEvent
|
||||
data object RoomTombstone : StateEvent
|
||||
data object RoomTopic : StateEvent
|
||||
data object SpaceChild : StateEvent
|
||||
data object SpaceParent : StateEvent
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ object PermalinkBuilder {
|
|||
}
|
||||
|
||||
sealed class PermalinkBuilderError : Throwable() {
|
||||
object InvalidRoomAlias : PermalinkBuilderError()
|
||||
object InvalidRoomId : PermalinkBuilderError()
|
||||
object InvalidUserId : PermalinkBuilderError()
|
||||
data object InvalidRoomAlias : PermalinkBuilderError()
|
||||
data object InvalidRoomId : PermalinkBuilderError()
|
||||
data object InvalidUserId : PermalinkBuilderError()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,5 +20,8 @@ enum class PollKind {
|
|||
Disclosed,
|
||||
|
||||
/** Results should be only revealed when the poll is ended. */
|
||||
Undisclosed
|
||||
Undisclosed,
|
||||
}
|
||||
|
||||
val PollKind.isDisclosed: Boolean
|
||||
get() = this == PollKind.Disclosed
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
package io.element.android.libraries.matrix.api.room
|
||||
|
||||
sealed interface MatrixRoomMembersState {
|
||||
object Unknown : MatrixRoomMembersState
|
||||
data object Unknown : MatrixRoomMembersState
|
||||
data class Pending(val prevRoomMembers: List<RoomMember>? = null) : MatrixRoomMembersState
|
||||
data class Error(val failure: Throwable, val prevRoomMembers: List<RoomMember>? = null) : MatrixRoomMembersState
|
||||
data class Ready(val roomMembers: List<RoomMember>) : MatrixRoomMembersState
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import kotlin.time.Duration
|
|||
*/
|
||||
interface RoomList {
|
||||
sealed class LoadingState {
|
||||
object NotLoaded : LoadingState()
|
||||
data object NotLoaded : LoadingState()
|
||||
data class Loaded(val numberOfRooms: Int) : LoadingState()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
interface RoomListService {
|
||||
|
||||
sealed class State {
|
||||
object Idle : State()
|
||||
object Running : State()
|
||||
object Error : State()
|
||||
object Terminated : State()
|
||||
data object Idle : State()
|
||||
data object Running : State()
|
||||
data object Error : State()
|
||||
data object Terminated : State()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,6 +28,6 @@ sealed interface MatrixTimelineItem {
|
|||
}
|
||||
|
||||
data class Virtual(val uniqueId: Long, val virtual: VirtualTimelineItem) : MatrixTimelineItem
|
||||
object Other : MatrixTimelineItem
|
||||
data object Other : MatrixTimelineItem
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,5 +17,5 @@
|
|||
package io.element.android.libraries.matrix.api.timeline
|
||||
|
||||
sealed class TimelineException : Exception() {
|
||||
object CannotPaginate : TimelineException()
|
||||
data object CannotPaginate : TimelineException()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,13 +35,12 @@ data class MessageContent(
|
|||
val type: MessageType?
|
||||
) : EventContent
|
||||
|
||||
|
||||
sealed interface InReplyTo {
|
||||
/** The event details are not loaded yet. We can fetch them. */
|
||||
data class NotLoaded(val eventId: EventId) : InReplyTo
|
||||
|
||||
/** The event details are pending to be fetched. We should **not** fetch them again. */
|
||||
object Pending : InReplyTo
|
||||
data object Pending : InReplyTo
|
||||
|
||||
/** The event details are available. */
|
||||
data class Ready(
|
||||
|
|
@ -60,7 +59,7 @@ sealed interface InReplyTo {
|
|||
* If the reason for the failure is consistent on the server, we'd enter a loop
|
||||
* where we keep trying to fetch the same event.
|
||||
* */
|
||||
object Error : InReplyTo
|
||||
data object Error : InReplyTo
|
||||
}
|
||||
|
||||
object RedactedContent : EventContent
|
||||
|
|
@ -92,7 +91,7 @@ data class UnableToDecryptContent(
|
|||
val sessionId: String
|
||||
) : Data
|
||||
|
||||
object Unknown : Data
|
||||
data object Unknown : Data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,55 +204,25 @@ enum class MembershipChange {
|
|||
}
|
||||
|
||||
sealed interface OtherState {
|
||||
object PolicyRuleRoom : OtherState
|
||||
|
||||
object PolicyRuleServer : OtherState
|
||||
|
||||
object PolicyRuleUser : OtherState
|
||||
|
||||
object RoomAliases : OtherState
|
||||
|
||||
data class RoomAvatar(
|
||||
val url: String?
|
||||
) : OtherState
|
||||
|
||||
object RoomCanonicalAlias : OtherState
|
||||
|
||||
object RoomCreate : OtherState
|
||||
|
||||
object RoomEncryption : OtherState
|
||||
|
||||
object RoomGuestAccess : OtherState
|
||||
|
||||
object RoomHistoryVisibility : OtherState
|
||||
|
||||
object RoomJoinRules : OtherState
|
||||
|
||||
data class RoomName(
|
||||
val name: String?
|
||||
) : OtherState
|
||||
|
||||
object RoomPinnedEvents : OtherState
|
||||
|
||||
object RoomPowerLevels : OtherState
|
||||
|
||||
object RoomServerAcl : OtherState
|
||||
|
||||
data class RoomThirdPartyInvite(
|
||||
val displayName: String?
|
||||
) : OtherState
|
||||
|
||||
object RoomTombstone : OtherState
|
||||
|
||||
data class RoomTopic(
|
||||
val topic: String?
|
||||
) : OtherState
|
||||
|
||||
object SpaceChild : OtherState
|
||||
|
||||
object SpaceParent : OtherState
|
||||
|
||||
data class Custom(
|
||||
val eventType: String
|
||||
) : OtherState
|
||||
data object PolicyRuleRoom : OtherState
|
||||
data object PolicyRuleServer : OtherState
|
||||
data object PolicyRuleUser : OtherState
|
||||
data object RoomAliases : OtherState
|
||||
data class RoomAvatar(val url: String?) : OtherState
|
||||
data object RoomCanonicalAlias : OtherState
|
||||
data object RoomCreate : OtherState
|
||||
data object RoomEncryption : OtherState
|
||||
data object RoomGuestAccess : OtherState
|
||||
data object RoomHistoryVisibility : OtherState
|
||||
data object RoomJoinRules : OtherState
|
||||
data class RoomName(val name: String?) : OtherState
|
||||
data object RoomPinnedEvents : OtherState
|
||||
data object RoomPowerLevels : OtherState
|
||||
data object RoomServerAcl : OtherState
|
||||
data class RoomThirdPartyInvite(val displayName: String?) : OtherState
|
||||
data object RoomTombstone : OtherState
|
||||
data class RoomTopic(val topic: String?) : OtherState
|
||||
data object SpaceChild : OtherState
|
||||
data object SpaceParent : OtherState
|
||||
data class Custom(val eventType: String) : OtherState
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ package io.element.android.libraries.matrix.api.timeline.item.event
|
|||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
|
||||
sealed interface LocalEventSendState {
|
||||
object NotSentYet : LocalEventSendState
|
||||
object Canceled : LocalEventSendState
|
||||
data object NotSentYet : LocalEventSendState
|
||||
data object Canceled : LocalEventSendState
|
||||
|
||||
data class SendingFailed(
|
||||
val error: String
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
package io.element.android.libraries.matrix.api.timeline.item.event
|
||||
|
||||
sealed interface ProfileTimelineDetails {
|
||||
object Unavailable : ProfileTimelineDetails
|
||||
data object Unavailable : ProfileTimelineDetails
|
||||
|
||||
object Pending : ProfileTimelineDetails
|
||||
data object Pending : ProfileTimelineDetails
|
||||
|
||||
data class Ready(
|
||||
val displayName: String?,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ sealed interface VirtualTimelineItem {
|
|||
val timestamp: Long
|
||||
) : VirtualTimelineItem
|
||||
|
||||
object ReadMarker : VirtualTimelineItem
|
||||
data object ReadMarker : VirtualTimelineItem
|
||||
|
||||
object EncryptedHistoryBanner : VirtualTimelineItem
|
||||
data object EncryptedHistoryBanner : VirtualTimelineItem
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,11 +60,11 @@ enum class Target(open val filter: String) {
|
|||
}
|
||||
|
||||
sealed class LogLevel(val filter: String) {
|
||||
object Warn : LogLevel("warn")
|
||||
object Trace : LogLevel("trace")
|
||||
object Info : LogLevel("info")
|
||||
object Debug : LogLevel("debug")
|
||||
object Error : LogLevel("error")
|
||||
data object Warn : LogLevel("warn")
|
||||
data object Trace : LogLevel("trace")
|
||||
data object Info : LogLevel("info")
|
||||
data object Debug : LogLevel("debug")
|
||||
data object Error : LogLevel("error")
|
||||
}
|
||||
|
||||
object TracingFilterConfigurations {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@
|
|||
package io.element.android.libraries.matrix.api.tracing
|
||||
|
||||
sealed class WriteToFilesConfiguration {
|
||||
object Disabled : WriteToFilesConfiguration()
|
||||
data object Disabled : WriteToFilesConfiguration()
|
||||
data class Enabled(val directory: String, val filenamePrefix: String) : WriteToFilesConfiguration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,35 +77,35 @@ interface SessionVerificationService {
|
|||
/** Verification status of the current session. */
|
||||
sealed interface SessionVerifiedStatus {
|
||||
/** Unknown status, we couldn't read the actual value from the SDK. */
|
||||
object Unknown : SessionVerifiedStatus
|
||||
data object Unknown : SessionVerifiedStatus
|
||||
|
||||
/** Not verified session status. */
|
||||
object NotVerified : SessionVerifiedStatus
|
||||
data object NotVerified : SessionVerifiedStatus
|
||||
|
||||
/** Verified session status. */
|
||||
object Verified : SessionVerifiedStatus
|
||||
data object Verified : SessionVerifiedStatus
|
||||
}
|
||||
|
||||
/** States produced by the [SessionVerificationService]. */
|
||||
sealed interface VerificationFlowState {
|
||||
/** Initial state. */
|
||||
object Initial : VerificationFlowState
|
||||
data object Initial : VerificationFlowState
|
||||
|
||||
/** Session verification request was accepted by another device. */
|
||||
object AcceptedVerificationRequest : VerificationFlowState
|
||||
data object AcceptedVerificationRequest : VerificationFlowState
|
||||
|
||||
/** Short Authentication String (SAS) verification started between the 2 devices. */
|
||||
object StartedSasVerification : VerificationFlowState
|
||||
data object StartedSasVerification : VerificationFlowState
|
||||
|
||||
/** Verification data for the SAS verification (emojis) received. */
|
||||
data class ReceivedVerificationData(val emoji: List<VerificationEmoji>) : VerificationFlowState
|
||||
|
||||
/** Verification completed successfully. */
|
||||
object Finished : VerificationFlowState
|
||||
data object Finished : VerificationFlowState
|
||||
|
||||
/** Verification was cancelled by either device. */
|
||||
object Canceled : VerificationFlowState
|
||||
data object Canceled : VerificationFlowState
|
||||
|
||||
/** Verification failed with an error. */
|
||||
object Failed : VerificationFlowState
|
||||
data object Failed : VerificationFlowState
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
plugins {
|
||||
id("io.element.android-library")
|
||||
alias(libs.plugins.anvil)
|
||||
kotlin("plugin.serialization") version "1.9.0"
|
||||
kotlin("plugin.serialization") version "1.9.10"
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ const val A_PASSWORD = "password"
|
|||
|
||||
val A_USER_ID = UserId("@alice:server.org")
|
||||
val A_USER_ID_2 = UserId("@bob:server.org")
|
||||
val A_USER_ID_3 = UserId("@carol:server.org")
|
||||
val A_USER_ID_4 = UserId("@david:server.org")
|
||||
val A_USER_ID_5 = UserId("@eve:server.org")
|
||||
val A_USER_ID_6 = UserId("@justin:server.org")
|
||||
val A_USER_ID_7 = UserId("@mallory:server.org")
|
||||
val A_USER_ID_8 = UserId("@susie:server.org")
|
||||
val A_USER_ID_9 = UserId("@victor:server.org")
|
||||
val A_USER_ID_10 = UserId("@walter:server.org")
|
||||
val A_SESSION_ID: SessionId = A_USER_ID
|
||||
val A_SESSION_ID_2: SessionId = A_USER_ID_2
|
||||
val A_SPACE_ID = SpaceId("!aSpaceId:domain")
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ sealed class AvatarAction(
|
|||
val icon: ImageVector,
|
||||
val destructive: Boolean = false,
|
||||
) {
|
||||
object TakePhoto : AvatarAction(titleResId = CommonStrings.action_take_photo, icon = Icons.Outlined.PhotoCamera)
|
||||
object ChoosePhoto : AvatarAction(titleResId = CommonStrings.action_choose_photo, icon = Icons.Outlined.PhotoLibrary)
|
||||
object Remove : AvatarAction(titleResId = CommonStrings.action_remove, icon = Icons.Outlined.Delete, destructive = true)
|
||||
data object TakePhoto : AvatarAction(titleResId = CommonStrings.action_take_photo, icon = Icons.Outlined.PhotoCamera)
|
||||
data object ChoosePhoto : AvatarAction(titleResId = CommonStrings.action_choose_photo, icon = Icons.Outlined.PhotoLibrary)
|
||||
data object Remove : AvatarAction(titleResId = CommonStrings.action_remove, icon = Icons.Outlined.Delete, destructive = true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ data class MediaRequestData(
|
|||
) {
|
||||
|
||||
sealed interface Kind {
|
||||
object Content : Kind
|
||||
data object Content : Kind
|
||||
data class File(val body: String?, val mimeType: String) : Kind
|
||||
data class Thumbnail(val width: Long, val height: Long) : Kind {
|
||||
constructor(size: Long) : this(size, size)
|
||||
|
|
|
|||
|
|
@ -26,14 +26,14 @@ sealed interface PickerType<Input, Output> {
|
|||
fun getContract(): ActivityResultContract<Input, Output>
|
||||
fun getDefaultRequest(): Input
|
||||
|
||||
object Image : PickerType<PickVisualMediaRequest, Uri?> {
|
||||
data object Image : PickerType<PickVisualMediaRequest, Uri?> {
|
||||
override fun getContract() = ActivityResultContracts.PickVisualMedia()
|
||||
override fun getDefaultRequest(): PickVisualMediaRequest {
|
||||
return PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
|
||||
}
|
||||
}
|
||||
|
||||
object ImageAndVideo : PickerType<PickVisualMediaRequest, Uri?> {
|
||||
data object ImageAndVideo : PickerType<PickVisualMediaRequest, Uri?> {
|
||||
override fun getContract() = ActivityResultContracts.PickVisualMedia()
|
||||
override fun getDefaultRequest(): PickVisualMediaRequest {
|
||||
return PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ data class ImageCompressionResult(
|
|||
)
|
||||
|
||||
sealed interface ResizeMode {
|
||||
object None : ResizeMode
|
||||
data object None : ResizeMode
|
||||
data class Approximate(val desiredWidth: Int, val desiredHeight: Int) : ResizeMode
|
||||
data class Strict(val maxWidth: Int, val maxHeight: Int) : ResizeMode
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@
|
|||
package io.element.android.libraries.permissions.api
|
||||
|
||||
sealed interface PermissionsEvents {
|
||||
object OpenSystemDialog : PermissionsEvents
|
||||
object CloseDialog : PermissionsEvents
|
||||
data object OpenSystemDialog : PermissionsEvents
|
||||
data object CloseDialog : PermissionsEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,5 +17,5 @@
|
|||
package io.element.android.libraries.push.api.gateway
|
||||
|
||||
sealed class PushGatewayFailure : Throwable(cause = null) {
|
||||
object PusherRejected : PushGatewayFailure()
|
||||
data object PusherRejected : PushGatewayFailure()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
plugins {
|
||||
id("io.element.android-library")
|
||||
alias(libs.plugins.anvil)
|
||||
kotlin("plugin.serialization") version "1.9.0"
|
||||
kotlin("plugin.serialization") version "1.9.10"
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -166,6 +166,6 @@ sealed interface OneShotNotification {
|
|||
}
|
||||
|
||||
sealed interface SummaryNotification {
|
||||
object Removed : SummaryNotification
|
||||
data object Removed : SummaryNotification
|
||||
data class Update(val notification: Notification) : SummaryNotification
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="notification_inline_reply_failed">"** Senden fehlgeschlagen - bitte Raum öffnen"</string>
|
||||
<string name="notification_invitation_action_join">"Beitreten"</string>
|
||||
<string name="notification_invitation_action_reject">"Ablehnen"</string>
|
||||
<string name="notification_invite_body">"Hat dich eingeladen"</string>
|
||||
<string name="notification_invite_body">"Hat dich zum Chatten eingeladen"</string>
|
||||
<string name="notification_new_messages">"Neue Nachrichten"</string>
|
||||
<string name="notification_reaction_body">"Reagierte mit %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Als gelesen markieren"</string>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
plugins {
|
||||
id("io.element.android-library")
|
||||
alias(libs.plugins.anvil)
|
||||
kotlin("plugin.serialization") version "1.9.0"
|
||||
kotlin("plugin.serialization") version "1.9.10"
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ class RegisterUnifiedPushUseCase @Inject constructor(
|
|||
) {
|
||||
|
||||
sealed interface RegisterUnifiedPushResult {
|
||||
object Success : RegisterUnifiedPushResult
|
||||
object NeedToAskUserForDistributor : RegisterUnifiedPushResult
|
||||
object Error : RegisterUnifiedPushResult
|
||||
data object Success : RegisterUnifiedPushResult
|
||||
data object NeedToAskUserForDistributor : RegisterUnifiedPushResult
|
||||
data object Error : RegisterUnifiedPushResult
|
||||
}
|
||||
|
||||
suspend fun execute(matrixClient: MatrixClient, distributor: Distributor, clientSecret: String): RegisterUnifiedPushResult {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="rich_text_editor_a11y_add_attachment">"Přidat přílohu"</string>
|
||||
<string name="rich_text_editor_bullet_list">"Přepnout seznam s odrážkami"</string>
|
||||
<string name="rich_text_editor_code_block">"Přepnout blok kódu"</string>
|
||||
<string name="rich_text_editor_composer_placeholder">"Zpráva…"</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="rich_text_editor_a11y_add_attachment">"Anhang hinzufügen"</string>
|
||||
<string name="rich_text_editor_bullet_list">"Aufzählungsliste ein-/ausschalten"</string>
|
||||
<string name="rich_text_editor_code_block">"Codeblock umschalten"</string>
|
||||
<string name="rich_text_editor_composer_placeholder">"Nachricht…"</string>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<string name="action_edit">"Upravit"</string>
|
||||
<string name="action_enable">"Povolit"</string>
|
||||
<string name="action_forgot_password">"Zapomněli jste heslo?"</string>
|
||||
<string name="action_forward">"Vpřed"</string>
|
||||
<string name="action_forward">"Přeposlat"</string>
|
||||
<string name="action_invite">"Pozvat"</string>
|
||||
<string name="action_invite_friends">"Pozvat přátele"</string>
|
||||
<string name="action_invite_friends_to_app">"Pozvat přátele do %1$s"</string>
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
<string name="action_open_with">"Otevřít v aplikaci"</string>
|
||||
<string name="action_quick_reply">"Rychlá odpověď"</string>
|
||||
<string name="action_quote">"Citovat"</string>
|
||||
<string name="action_react">"Reagovat"</string>
|
||||
<string name="action_remove">"Odstranit"</string>
|
||||
<string name="action_reply">"Odpovědět"</string>
|
||||
<string name="action_report_bug">"Nahlásit chybu"</string>
|
||||
|
|
@ -94,6 +95,9 @@
|
|||
<string name="common_password">"Heslo"</string>
|
||||
<string name="common_people">"Lidé"</string>
|
||||
<string name="common_permalink">"Trvalý odkaz"</string>
|
||||
<string name="common_poll_final_votes">"Konečné hlasy: %1$s"</string>
|
||||
<string name="common_poll_total_votes">"Celkový počet hlasů: %1$s"</string>
|
||||
<string name="common_poll_undisclosed_text">"Výsledky se zobrazí po skončení hlasování"</string>
|
||||
<string name="common_privacy_policy">"Zásady ochrany osobních údajů"</string>
|
||||
<string name="common_reactions">"Reakce"</string>
|
||||
<string name="common_refreshing">"Obnovování…"</string>
|
||||
|
|
@ -140,7 +144,11 @@
|
|||
<string name="emoji_picker_category_places">"Cestování a místa"</string>
|
||||
<string name="emoji_picker_category_symbols">"Symboly"</string>
|
||||
<string name="error_failed_creating_the_permalink">"Vytvoření trvalého odkazu se nezdařilo"</string>
|
||||
<string name="error_failed_loading_map">"%1$s nemohl načíst mapu. Zkuste to prosím později."</string>
|
||||
<string name="error_failed_loading_messages">"Načítání zpráv se nezdařilo"</string>
|
||||
<string name="error_failed_locating_user">"%1$s nemá přístup k vaší poloze. Zkuste to prosím později."</string>
|
||||
<string name="error_missing_location_auth_android">"%1$s nemá oprávnění k přístupu k vaší poloze. Přístup můžete povolit v Nastavení."</string>
|
||||
<string name="error_missing_location_rationale_android">"%1$s nemá oprávnění k přístupu k vaší poloze. Povolit přístup níže."</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Některé zprávy nebyly odeslány"</string>
|
||||
<string name="error_unknown">"Omlouváme se, došlo k chybě"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Připojte se ke mně na %1$s"</string>
|
||||
|
|
@ -154,6 +162,11 @@
|
|||
<item quantity="few">"%1$d členové"</item>
|
||||
<item quantity="other">"%1$d členů"</item>
|
||||
</plurals>
|
||||
<plurals name="common_poll_votes_count">
|
||||
<item quantity="one">"%d hlas"</item>
|
||||
<item quantity="few">"%d hlasy"</item>
|
||||
<item quantity="other">"%d hlasů"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Zatřeste zařízením pro nahlášení chyby"</string>
|
||||
<string name="rageshake_dialog_content">"Zdá se, že jste frustrovaně třásli telefonem. Chcete otevřít obrazovku pro nahlášení chyby?"</string>
|
||||
<string name="report_content_explanation">"Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy."</string>
|
||||
|
|
@ -165,9 +178,35 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Výběr média se nezdařil, zkuste to prosím znovu."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_migration_message">"Toto je jednorázový proces, děkujeme za čekání."</string>
|
||||
<string name="screen_migration_title">"Nastavení vašeho účtu"</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"Další nastavení"</string>
|
||||
<string name="screen_notification_settings_calls_label">"Halsové a video hovory"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch">"Neshoda konfigurace"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch_description">"Zjednodušili jsme nastavení oznámení, abychom usnadnili hledání možností.
|
||||
|
||||
Některá vlastní nastavení, která jste si vybrali v minulosti, se zde nezobrazují, ale jsou stále aktivní.
|
||||
|
||||
Pokud budete pokračovat, některá nastavení se mohou změnit."</string>
|
||||
<string name="screen_notification_settings_direct_chats">"Přímé zprávy"</string>
|
||||
<string name="screen_notification_settings_edit_custom_settings_section_title">"Vlastní nastavení pro chat"</string>
|
||||
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Při aktualizaci nastavení oznámení došlo k chybě."</string>
|
||||
<string name="screen_notification_settings_edit_mode_all_messages">"Všechny zprávy"</string>
|
||||
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Pouze zmínky a klíčová slova"</string>
|
||||
<string name="screen_notification_settings_edit_screen_direct_section_header">"V přímých zprávách mě upozornit na"</string>
|
||||
<string name="screen_notification_settings_edit_screen_group_section_header">"Ve skupinových chatech mě upozornit na"</string>
|
||||
<string name="screen_notification_settings_enable_notifications">"Povolit oznámení na tomto zařízení"</string>
|
||||
<string name="screen_notification_settings_failed_fixing_configuration">"Konfigurace nebyla opravena, zkuste to prosím znovu."</string>
|
||||
<string name="screen_notification_settings_group_chats">"Skupinové chaty"</string>
|
||||
<string name="screen_notification_settings_mentions_section_title">"Zmínky"</string>
|
||||
<string name="screen_notification_settings_mode_all">"Vše"</string>
|
||||
<string name="screen_notification_settings_mode_mentions">"Zmínky"</string>
|
||||
<string name="screen_notification_settings_notification_section_title">"Upozornit mě na"</string>
|
||||
<string name="screen_notification_settings_room_mention_label">"Upozornit mě na @room"</string>
|
||||
<string name="screen_notification_settings_system_notifications_action_required">"Chcete-li dostávat oznámení, změňte prosím svůj %1$s."</string>
|
||||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"systémová nastavení"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Systémová oznámení byla vypnuta"</string>
|
||||
<string name="screen_notification_settings_title">"Oznámení"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele"</string>
|
||||
<string name="screen_settings_oidc_account">"Účet a zařízení"</string>
|
||||
<string name="screen_share_location_title">"Sdílet polohu"</string>
|
||||
<string name="screen_share_my_location_action">"Sdílet moji polohu"</string>
|
||||
<string name="screen_share_open_apple_maps">"Otevřít v Mapách Apple"</string>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<string name="action_back">"Zurück"</string>
|
||||
<string name="action_cancel">"Abbrechen"</string>
|
||||
<string name="action_choose_photo">"Foto auswählen"</string>
|
||||
<string name="action_clear">"Löschen"</string>
|
||||
<string name="action_clear">"Zurücksetzen"</string>
|
||||
<string name="action_close">"Schließen"</string>
|
||||
<string name="action_complete_verification">"Verifizierung abschließen"</string>
|
||||
<string name="action_confirm">"Bestätigen"</string>
|
||||
|
|
@ -37,9 +37,10 @@
|
|||
<string name="action_no">"Nein"</string>
|
||||
<string name="action_not_now">"Nicht jetzt"</string>
|
||||
<string name="action_ok">"OK"</string>
|
||||
<string name="action_open_with">"Öffne mit"</string>
|
||||
<string name="action_open_with">"Öffnen mit"</string>
|
||||
<string name="action_quick_reply">"Schnellantwort"</string>
|
||||
<string name="action_quote">"Zitieren"</string>
|
||||
<string name="action_react">"Reagieren"</string>
|
||||
<string name="action_remove">"Entfernen"</string>
|
||||
<string name="action_reply">"Antworten"</string>
|
||||
<string name="action_report_bug">"Fehler melden"</string>
|
||||
|
|
@ -56,7 +57,7 @@
|
|||
<string name="action_start">"Starten"</string>
|
||||
<string name="action_start_chat">"Chat starten"</string>
|
||||
<string name="action_start_verification">"Verifizierung starten"</string>
|
||||
<string name="action_static_map_load">"Tippe, um die Karte zu laden"</string>
|
||||
<string name="action_static_map_load">"Zum Karte laden tippen"</string>
|
||||
<string name="action_take_photo">"Foto aufnehmen"</string>
|
||||
<string name="action_view_source">"Quelltext anzeigen"</string>
|
||||
<string name="action_yes">"Ja"</string>
|
||||
|
|
@ -80,25 +81,28 @@
|
|||
<string name="common_forward_message">"Nachricht weiterleiten"</string>
|
||||
<string name="common_gif">"GIF"</string>
|
||||
<string name="common_image">"Bild"</string>
|
||||
<string name="common_invite_unknown_profile">"Wir können die Matrix-ID dieses Benutzers nicht validieren. Die Einladung wurde möglicherweise nicht empfangen."</string>
|
||||
<string name="common_leaving_room">"Raum verlassen"</string>
|
||||
<string name="common_invite_unknown_profile">"Diese Matrix-ID kann nicht gefunden werden, daher wird die Einladung möglicherweise nicht empfangen."</string>
|
||||
<string name="common_leaving_room">"Verlasse Raum"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Link in Zwischenablage kopiert"</string>
|
||||
<string name="common_loading">"Wird geladen…"</string>
|
||||
<string name="common_loading">"Lädt…"</string>
|
||||
<string name="common_message">"Nachricht"</string>
|
||||
<string name="common_message_layout">"Nachrichtenlayout"</string>
|
||||
<string name="common_message_removed">"Nachricht wurde entfernt"</string>
|
||||
<string name="common_message_removed">"Nachricht entfernt"</string>
|
||||
<string name="common_modern">"Modern"</string>
|
||||
<string name="common_mute">"Stummschalten"</string>
|
||||
<string name="common_no_results">"Keine Ergebnisse"</string>
|
||||
<string name="common_offline">"Offline"</string>
|
||||
<string name="common_password">"Passwort"</string>
|
||||
<string name="common_people">"Personen"</string>
|
||||
<string name="common_permalink">"Permalink"</string>
|
||||
<string name="common_permalink">"Dauerlink"</string>
|
||||
<string name="common_poll_final_votes">"Endgültige Stimmen: %1$s"</string>
|
||||
<string name="common_poll_total_votes">"Stimmen insgesamt: %1$s"</string>
|
||||
<string name="common_poll_undisclosed_text">"Ergebnisse werden nach Ende der Umfrage angezeigt"</string>
|
||||
<string name="common_privacy_policy">"Datenschutzerklärung"</string>
|
||||
<string name="common_reactions">"Reaktionen"</string>
|
||||
<string name="common_refreshing">"Aktualisiere…"</string>
|
||||
<string name="common_replying_to">"Auf %1$s antworten"</string>
|
||||
<string name="common_report_a_bug">"Melde einen Fehler"</string>
|
||||
<string name="common_report_a_bug">"Einen Fehler melden"</string>
|
||||
<string name="common_report_submitted">"Bericht gesendet"</string>
|
||||
<string name="common_room_name">"Raumname"</string>
|
||||
<string name="common_room_name_placeholder">"z.B. dein Projektname"</string>
|
||||
|
|
@ -106,12 +110,12 @@
|
|||
<string name="common_search_results">"Suchergebnisse"</string>
|
||||
<string name="common_security">"Sicherheit"</string>
|
||||
<string name="common_select_your_server">"Wählen deinen Server"</string>
|
||||
<string name="common_sending">"Senden…"</string>
|
||||
<string name="common_sending">"Sendet…"</string>
|
||||
<string name="common_server_not_supported">"Server wird nicht unterstützt"</string>
|
||||
<string name="common_server_url">"Server-URL"</string>
|
||||
<string name="common_settings">"Einstellungen"</string>
|
||||
<string name="common_shared_location">"Geteilter Standort"</string>
|
||||
<string name="common_starting_chat">"Chat wird gestartet…"</string>
|
||||
<string name="common_starting_chat">"Starte Chat…"</string>
|
||||
<string name="common_sticker">"Sticker"</string>
|
||||
<string name="common_success">"Erfolg"</string>
|
||||
<string name="common_suggestions">"Vorschläge"</string>
|
||||
|
|
@ -120,7 +124,7 @@
|
|||
<string name="common_topic">"Thema"</string>
|
||||
<string name="common_topic_placeholder">"Worum geht es in diesem Raum?"</string>
|
||||
<string name="common_unable_to_decrypt">"Entschlüsselung nicht möglich"</string>
|
||||
<string name="common_unable_to_invite_message">"Wir konnten Einladungen nicht erfolgreich an einen oder mehrere Benutzer senden."</string>
|
||||
<string name="common_unable_to_invite_message">"Einladungen konnten nicht an einen oder mehrere Benutzer gesendet werden."</string>
|
||||
<string name="common_unable_to_invite_title">"Einladung(en) können nicht gesendet werden"</string>
|
||||
<string name="common_unmute">"Stummschaltung aufheben"</string>
|
||||
<string name="common_unsupported_event">"Nicht unterstütztes Ereignis"</string>
|
||||
|
|
@ -128,7 +132,7 @@
|
|||
<string name="common_verification_cancelled">"Verifizierung abgebrochen"</string>
|
||||
<string name="common_verification_complete">"Verifizierung abgeschlossen"</string>
|
||||
<string name="common_video">"Video"</string>
|
||||
<string name="common_waiting">"Warten…"</string>
|
||||
<string name="common_waiting">"Warte…"</string>
|
||||
<string name="dialog_title_confirmation">"Bestätigung"</string>
|
||||
<string name="dialog_title_warning">"Warnung"</string>
|
||||
<string name="emoji_picker_category_activity">"Aktivitäten"</string>
|
||||
|
|
@ -139,10 +143,12 @@
|
|||
<string name="emoji_picker_category_people">"Smileys & Personen"</string>
|
||||
<string name="emoji_picker_category_places">"Reisen & Orte"</string>
|
||||
<string name="emoji_picker_category_symbols">"Symbole"</string>
|
||||
<string name="error_failed_creating_the_permalink">"Fehler beim Erstellen des Permalinks"</string>
|
||||
<string name="error_failed_creating_the_permalink">"Fehler beim Erstellen des Dauerlinks"</string>
|
||||
<string name="error_failed_loading_map">"%1$s konnte die Karte nicht laden. Bitte versuche es später erneut."</string>
|
||||
<string name="error_failed_loading_messages">"Fehler beim Laden der Nachrichten"</string>
|
||||
<string name="error_failed_locating_user">"%1$s konnte nicht auf deinen Standort zugreifen. Bitte versuche es später erneut."</string>
|
||||
<string name="error_missing_location_auth_android">"%1$s hat keine Berechtigung, auf deinen Standort zuzugreifen. Du kannst den Zugriff in den Einstellungen aktivieren."</string>
|
||||
<string name="error_missing_location_rationale_android">"%1$s hat keine Berechtigung, auf deinen Standort zuzugreifen. Aktiviere den Zugriff unten."</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Einige Nachrichten wurden nicht gesendet"</string>
|
||||
<string name="error_unknown">"Entschuldigung, ein Fehler ist aufgetreten."</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Besuche mich auf %1$s"</string>
|
||||
|
|
@ -155,9 +161,13 @@
|
|||
<item quantity="one">"%1$d Mitglied"</item>
|
||||
<item quantity="other">"%1$d Mitglieder"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Rageshake zum Melden von Fehlern"</string>
|
||||
<plurals name="common_poll_votes_count">
|
||||
<item quantity="one">"%d Stimme"</item>
|
||||
<item quantity="other">"%d Stimmen"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Schütteln zum Melden von Fehlern"</string>
|
||||
<string name="rageshake_dialog_content">"Du scheinst frustriert das Telefon zu schütteln. Möchtest du den Fehlerberichtsbildschirm öffnen?"</string>
|
||||
<string name="report_content_explanation">"Diese Nachricht wird an deinen Heimserver-Admin gemeldet werden. Er wird nicht in der Lage sein, verschlüsselte Nachrichten zu lesen."</string>
|
||||
<string name="report_content_explanation">"Diese Nachricht wird an deinen Heimserver-Admin gemeldet. Er wird nicht in der Lage sein, verschlüsselte Nachrichten zu lesen."</string>
|
||||
<string name="report_content_hint">"Grund für die Meldung dieses Inhalts"</string>
|
||||
<string name="room_timeline_beginning_of_room">"Dies ist der Anfang von %1$s."</string>
|
||||
<string name="room_timeline_beginning_of_room_no_name">"Dies ist der Beginn dieser Konversation."</string>
|
||||
|
|
@ -166,9 +176,35 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Hochladen von Medien fehlgeschlagen, bitte versuchen Sie es erneut."</string>
|
||||
<string name="screen_migration_message">"Dies ist ein einmaliger Vorgang, danke fürs Warten."</string>
|
||||
<string name="screen_migration_title">"Dein Konto einrichten"</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"Zusätzliche Einstellungen"</string>
|
||||
<string name="screen_notification_settings_calls_label">"Audio- und Videoanrufe"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch">"Konfigurationskonflikt"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch_description">"Wir haben die Benachrichtigungseinstellungen vereinfacht, damit Optionen leichter zu finden sind.
|
||||
|
||||
Einige benutzerdefinierte Einstellungen, die du in der Vergangenheit ausgewählt hast, werden hier nicht angezeigt, sind aber immer noch aktiv.
|
||||
|
||||
Wenn du fortfährst, ändern sich möglicherweise einige deine Einstellungen."</string>
|
||||
<string name="screen_notification_settings_direct_chats">"Direkte Chats"</string>
|
||||
<string name="screen_notification_settings_edit_custom_settings_section_title">"Benutzerdefinierte Einstellung pro Chat"</string>
|
||||
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Beim Aktualisieren der Benachrichtigungseinstellung ist ein Fehler aufgetreten."</string>
|
||||
<string name="screen_notification_settings_edit_mode_all_messages">"Alle Nachrichten"</string>
|
||||
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Nur Erwähnungen und Schlüsselwörter"</string>
|
||||
<string name="screen_notification_settings_edit_screen_direct_section_header">"Bei direkten Chats, benachrichtigen mich für"</string>
|
||||
<string name="screen_notification_settings_edit_screen_group_section_header">"Bei Gruppenchats, benachrichtigte mich für"</string>
|
||||
<string name="screen_notification_settings_enable_notifications">"Benachrichtigungen auf diesem Gerät aktivieren"</string>
|
||||
<string name="screen_notification_settings_failed_fixing_configuration">"Die Konfiguration wurde nicht korrigiert. Bitte versuche es erneut."</string>
|
||||
<string name="screen_notification_settings_group_chats">"Gruppenchats"</string>
|
||||
<string name="screen_notification_settings_mentions_section_title">"Erwähnungen"</string>
|
||||
<string name="screen_notification_settings_mode_all">"Alle"</string>
|
||||
<string name="screen_notification_settings_mode_mentions">"Erwähnungen"</string>
|
||||
<string name="screen_notification_settings_notification_section_title">"Benachrichtige mich für"</string>
|
||||
<string name="screen_notification_settings_room_mention_label">"Benachrichtige mich bei @room"</string>
|
||||
<string name="screen_notification_settings_system_notifications_action_required">"Um Benachrichtigungen zu erhalten, ändern bitte deine %1$s."</string>
|
||||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"Systemeinstellungen"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Systembenachrichtigungen deaktiviert"</string>
|
||||
<string name="screen_notification_settings_title">"Benachrichtigungen"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"</string>
|
||||
<string name="screen_settings_oidc_account">"Konto und Geräte"</string>
|
||||
<string name="screen_share_location_title">"Standort teilen"</string>
|
||||
<string name="screen_share_my_location_action">"Meinen Standort teilen"</string>
|
||||
<string name="screen_share_open_apple_maps">"In Apple Maps öffnen"</string>
|
||||
|
|
|
|||
|
|
@ -165,8 +165,6 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Impossible de sélectionner un média, veuillez réessayer."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Échec du traitement du média avant son envoi, veuillez réessayer."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Impossible d’envoyer le média, veuillez réessayer."</string>
|
||||
<string name="screen_migration_message">"Ce processus n’a besoin d’être fait qu’une seule fois, merci de patienter."</string>
|
||||
<string name="screen_migration_title">"Configuration de votre compte."</string>
|
||||
<string name="screen_notification_settings_enable_notifications">"Activer les notifications sur cet appareil"</string>
|
||||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"paramètres système"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Notifications système désactivées"</string>
|
||||
|
|
|
|||
|
|
@ -178,15 +178,23 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Не удалось выбрать носитель, попробуйте еще раз."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Не удалось загрузить медиафайлы, попробуйте еще раз."</string>
|
||||
<string name="screen_migration_message">"Это одноразовый процесс, спасибо, что подождали."</string>
|
||||
<string name="screen_migration_title">"Настройка учетной записи."</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"Дополнительные параметры"</string>
|
||||
<string name="screen_notification_settings_calls_label">"Аудио и видео звонки"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch">"Несоответствие конфигурации"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch_description">"Мы упростили настройки уведомлений, чтобы упростить поиск опций.
|
||||
|
||||
Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны.
|
||||
|
||||
Если вы продолжите, некоторые настройки могут быть изменены."</string>
|
||||
<string name="screen_notification_settings_direct_chats">"Прямые чаты"</string>
|
||||
<string name="screen_notification_settings_edit_custom_settings_section_title">"Индивидуальные настройки для каждого чата"</string>
|
||||
<string name="screen_notification_settings_edit_failed_updating_default_mode">"При обновлении настроек уведомления произошла ошибка."</string>
|
||||
<string name="screen_notification_settings_edit_mode_all_messages">"Все сообщения"</string>
|
||||
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Только упоминания и ключевые слова"</string>
|
||||
<string name="screen_notification_settings_edit_screen_direct_section_header">"Уведомлять меня в личных чатах"</string>
|
||||
<string name="screen_notification_settings_edit_screen_group_section_header">"Уведомлять меня в групповых чатах"</string>
|
||||
<string name="screen_notification_settings_enable_notifications">"Включить уведомления на данном устройстве"</string>
|
||||
<string name="screen_notification_settings_failed_fixing_configuration">"Конфигурация не была исправлена, попробуйте еще раз."</string>
|
||||
<string name="screen_notification_settings_group_chats">"Групповые чаты"</string>
|
||||
<string name="screen_notification_settings_mentions_section_title">"Упоминания"</string>
|
||||
<string name="screen_notification_settings_mode_all">"Все"</string>
|
||||
|
|
@ -198,6 +206,7 @@
|
|||
<string name="screen_notification_settings_system_notifications_turned_off">"Системные уведомления выключены"</string>
|
||||
<string name="screen_notification_settings_title">"Уведомления"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя"</string>
|
||||
<string name="screen_settings_oidc_account">"Учетная запись и устройства"</string>
|
||||
<string name="screen_share_location_title">"Поделиться местоположением"</string>
|
||||
<string name="screen_share_my_location_action">"Поделиться моим местоположением"</string>
|
||||
<string name="screen_share_open_apple_maps">"Открыть в Apple Maps"</string>
|
||||
|
|
|
|||
|
|
@ -178,14 +178,23 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Nepodarilo sa vybrať médium, skúste to prosím znova."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Nepodarilo sa nahrať médiá, skúste to prosím znova."</string>
|
||||
<string name="screen_migration_message">"Ide o jednorazový proces, ďakujeme za trpezlivosť."</string>
|
||||
<string name="screen_migration_title">"Nastavenie vášho účtu."</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"Ďalšie nastavenia"</string>
|
||||
<string name="screen_notification_settings_calls_label">"Audio a video hovory"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch">"Nezhoda konfigurácie"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch_description">"Zjednodušili sme Nastavenia oznámení, aby ste ľahšie našli možnosti.
|
||||
|
||||
Niektoré vlastné nastavenia, ktoré ste si nastavili v minulosti, sa tu nezobrazujú, ale sú stále aktívne.
|
||||
|
||||
Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť."</string>
|
||||
<string name="screen_notification_settings_direct_chats">"Priame konverzácie"</string>
|
||||
<string name="screen_notification_settings_edit_custom_settings_section_title">"Vlastné nastavenie pre konverzácie"</string>
|
||||
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Pri aktualizácii nastavenia oznámenia došlo k chybe."</string>
|
||||
<string name="screen_notification_settings_edit_mode_all_messages">"Všetky správy"</string>
|
||||
<string name="screen_notification_settings_edit_mode_mentions_and_keywords">"Iba zmienky a kľúčové slová"</string>
|
||||
<string name="screen_notification_settings_edit_screen_direct_section_header">"Pri priamych rozhovoroch ma upozorniť na"</string>
|
||||
<string name="screen_notification_settings_edit_screen_group_section_header">"Pri skupinových rozhovoroch ma upozorniť na"</string>
|
||||
<string name="screen_notification_settings_enable_notifications">"Povoliť oznámenia na tomto zariadení"</string>
|
||||
<string name="screen_notification_settings_failed_fixing_configuration">"Konfigurácia nebola opravená, skúste to prosím znova."</string>
|
||||
<string name="screen_notification_settings_group_chats">"Skupinové rozhovory"</string>
|
||||
<string name="screen_notification_settings_mentions_section_title">"Zmienky"</string>
|
||||
<string name="screen_notification_settings_mode_all">"Všetky"</string>
|
||||
|
|
@ -197,6 +206,7 @@
|
|||
<string name="screen_notification_settings_system_notifications_turned_off">"Systémové oznámenia sú vypnuté"</string>
|
||||
<string name="screen_notification_settings_title">"Oznámenia"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa"</string>
|
||||
<string name="screen_settings_oidc_account">"Účet a zariadenia"</string>
|
||||
<string name="screen_share_location_title">"Zdieľať polohu"</string>
|
||||
<string name="screen_share_my_location_action">"Zdieľať moju polohu"</string>
|
||||
<string name="screen_share_open_apple_maps">"Otvoriť v Apple Maps"</string>
|
||||
|
|
|
|||
|
|
@ -144,7 +144,6 @@
|
|||
<string name="room_timeline_read_marker_title">"新訊息"</string>
|
||||
<string name="screen_analytics_settings_share_data">"分享分析數據"</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"無法上傳媒體檔案,請稍後再試。"</string>
|
||||
<string name="screen_migration_title">"設定您的帳號"</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"其他設定"</string>
|
||||
<string name="screen_notification_settings_direct_chats">"私訊"</string>
|
||||
<string name="screen_notification_settings_enable_notifications">"在這個裝置上開啟通知"</string>
|
||||
|
|
|
|||
|
|
@ -181,14 +181,12 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Failed selecting media, please try again."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Failed processing media to upload, please try again."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Failed uploading media, please try again."</string>
|
||||
<string name="screen_migration_message">"This is a one time process, thanks for waiting."</string>
|
||||
<string name="screen_migration_title">"Setting up your account."</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"Additional settings"</string>
|
||||
<string name="screen_notification_settings_calls_label">"Audio and video calls"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch">"Configuration mismatch"</string>
|
||||
<string name="screen_notification_settings_configuration_mismatch_description">"We’ve simplified Notifications Settings to make options easier to find.
|
||||
<string name="screen_notification_settings_configuration_mismatch_description">"We’ve simplified Notifications Settings to make options easier to find.
|
||||
|
||||
Some custom settings you’ve chosen in the past are not shown here, but they’re still active.
|
||||
Some custom settings you’ve chosen in the past are not shown here, but they’re still active.
|
||||
|
||||
If you proceed, some of your settings may change."</string>
|
||||
<string name="screen_notification_settings_direct_chats">"Direct chats"</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue