Merge branch 'develop' into feature/fga/mark_room_as_favorite
This commit is contained in:
commit
a8bc0cb4ca
538 changed files with 4465 additions and 1639 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">"Не знойдзена сумяшчальная праграма для выканання гэтага дзеяння."</string>
|
||||
</resources>
|
||||
|
|
@ -18,6 +18,8 @@ package io.element.android.libraries.dateformatter.test
|
|||
|
||||
import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter
|
||||
|
||||
const val A_FORMATTED_DATE = "formatted_date"
|
||||
|
||||
class FakeLastMessageTimestampFormatter : LastMessageTimestampFormatter {
|
||||
private var format = ""
|
||||
fun givenFormat(format: String) {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,9 @@ fun HeaderFooterPage(
|
|||
content()
|
||||
}
|
||||
// Footer
|
||||
footer()
|
||||
Box(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
footer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,13 +67,12 @@ fun OnBoardingPage(
|
|||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.padding(vertical = 16.dp),
|
||||
.padding(all = 20.dp),
|
||||
) {
|
||||
// Content
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = contentAlignment,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -37,8 +37,10 @@ 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.compound.annotations.CoreColorToken
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.theme.ForcedDarkElementTheme
|
||||
import io.element.android.compound.tokens.generated.internal.DarkColorTokens
|
||||
import io.element.android.compound.tokens.generated.internal.LightColorTokens
|
||||
import io.element.android.libraries.designsystem.R
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
|
@ -54,7 +56,11 @@ fun SunsetPage(
|
|||
modifier: Modifier = Modifier,
|
||||
overallContent: @Composable () -> Unit,
|
||||
) {
|
||||
ForcedDarkElementTheme(lightStatusBar = true) {
|
||||
ElementTheme(
|
||||
// Always use the opposite value of the current theme
|
||||
darkTheme = ElementTheme.isLightTheme,
|
||||
applySystemBarsUpdate = false,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize()
|
||||
) {
|
||||
|
|
@ -107,21 +113,32 @@ fun SunsetPage(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(CoreColorToken::class)
|
||||
@Composable
|
||||
private fun SunsetBackground(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier.fillMaxSize()) {
|
||||
private fun SunsetBackground() {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
// The top background colors are the opposite of the current theme ones
|
||||
val topBackgroundColor = if (ElementTheme.isLightTheme) {
|
||||
DarkColorTokens.colorThemeBg
|
||||
} else {
|
||||
LightColorTokens.colorThemeBg
|
||||
}
|
||||
// The bottom background colors follow the current theme
|
||||
val bottomBackgroundColor = if (ElementTheme.isLightTheme) {
|
||||
LightColorTokens.colorThemeBg
|
||||
} else {
|
||||
// The dark background color doesn't 100% match the image, so we use a custom color
|
||||
Color(0xFF121418)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.3f)
|
||||
.background(Color.White)
|
||||
.background(topBackgroundColor)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(id = R.drawable.light_dark),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
painter = painterResource(id = R.drawable.bg_migration),
|
||||
contentScale = ContentScale.Crop,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
|
@ -129,7 +146,7 @@ private fun SunsetBackground(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.7f)
|
||||
.background(Color(0xFF121418))
|
||||
.background(bottomBackgroundColor)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import android.os.Build
|
|||
import android.text.TextPaint
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
|
|
@ -136,13 +135,13 @@ object BloomDefaults {
|
|||
@Composable
|
||||
fun defaultLayers() = persistentListOf(
|
||||
// Bottom layer
|
||||
if (isSystemInDarkTheme()) {
|
||||
BloomLayer(0.5f, BlendMode.Exclusion)
|
||||
} else {
|
||||
if (ElementTheme.isLightTheme) {
|
||||
BloomLayer(0.2f, BlendMode.Hardlight)
|
||||
} else {
|
||||
BloomLayer(0.5f, BlendMode.Exclusion)
|
||||
},
|
||||
// Top layer
|
||||
BloomLayer(if (isSystemInDarkTheme()) 0.2f else 0.8f, BlendMode.Color),
|
||||
BloomLayer(if (ElementTheme.isLightTheme) 0.8f else 0.2f, BlendMode.Color),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ fun BlurHashAsyncImage(
|
|||
@Composable
|
||||
private fun BlurHashImage(
|
||||
blurHash: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
contentDescription: String? = null,
|
||||
contentScale: ContentScale = ContentScale.Fit,
|
||||
) {
|
||||
|
|
@ -91,7 +90,7 @@ private fun BlurHashImage(
|
|||
}
|
||||
bitmapState.value?.let { bitmap ->
|
||||
Image(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bitmap = bitmap.asImageBitmap(),
|
||||
contentScale = contentScale,
|
||||
contentDescription = contentDescription
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ private fun ConfirmationDialogContent(
|
|||
cancelText: String,
|
||||
onSubmitClicked: () -> Unit,
|
||||
onCancelClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
thirdButtonText: String? = null,
|
||||
onThirdButtonClicked: () -> Unit = {},
|
||||
|
|
@ -73,7 +72,6 @@ private fun ConfirmationDialogContent(
|
|||
icon: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
SimpleAlertDialogContent(
|
||||
modifier = modifier,
|
||||
title = title,
|
||||
content = content,
|
||||
submitText = submitText,
|
||||
|
|
|
|||
|
|
@ -51,12 +51,10 @@ fun ErrorDialog(
|
|||
private fun ErrorDialogContent(
|
||||
content: String,
|
||||
onSubmitClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String = ErrorDialogDefaults.title,
|
||||
submitText: String = ErrorDialogDefaults.submitText,
|
||||
) {
|
||||
SimpleAlertDialogContent(
|
||||
modifier = modifier,
|
||||
title = title,
|
||||
content = content,
|
||||
submitText = submitText,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ private fun ListDialogContent(
|
|||
onSubmitClicked: () -> Unit,
|
||||
cancelText: String,
|
||||
submitText: String,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
enabled: Boolean = true,
|
||||
subtitle: @Composable (() -> Unit)? = null,
|
||||
|
|
@ -88,7 +87,6 @@ private fun ListDialogContent(
|
|||
SimpleAlertDialogContent(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
modifier = modifier,
|
||||
cancelText = cancelText,
|
||||
submitText = submitText,
|
||||
onCancelClicked = onDismissRequest,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ private fun MultipleSelectionDialogContent(
|
|||
onConfirmClicked: (List<Int>) -> Unit,
|
||||
dismissButtonTitle: String,
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
initialSelected: ImmutableList<Int> = persistentListOf(),
|
||||
subtitle: @Composable (() -> Unit)? = null,
|
||||
|
|
@ -96,7 +95,6 @@ private fun MultipleSelectionDialogContent(
|
|||
SimpleAlertDialogContent(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
modifier = modifier,
|
||||
submitText = confirmButtonTitle,
|
||||
onSubmitClicked = {
|
||||
onConfirmClicked(selectedOptionIndexes.toList())
|
||||
|
|
|
|||
|
|
@ -56,13 +56,11 @@ private fun RetryDialogContent(
|
|||
content: String,
|
||||
onRetry: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String = RetryDialogDefaults.title,
|
||||
retryText: String = RetryDialogDefaults.retryText,
|
||||
dismissText: String = RetryDialogDefaults.dismissText,
|
||||
) {
|
||||
SimpleAlertDialogContent(
|
||||
modifier = modifier,
|
||||
title = title,
|
||||
content = content,
|
||||
submitText = retryText,
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ private fun SingleSelectionDialogContent(
|
|||
onOptionSelected: (Int) -> Unit,
|
||||
dismissButtonTitle: String,
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
initialSelection: Int? = null,
|
||||
subtitle: @Composable (() -> Unit)? = null,
|
||||
|
|
@ -87,7 +86,6 @@ private fun SingleSelectionDialogContent(
|
|||
SimpleAlertDialogContent(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
modifier = modifier,
|
||||
submitText = dismissButtonTitle,
|
||||
onSubmitClicked = onDismissRequest,
|
||||
applyPaddingToContents = false,
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ fun PreferenceCategory(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceCategoryTitle(title: String, modifier: Modifier = Modifier) {
|
||||
private fun PreferenceCategoryTitle(title: String) {
|
||||
Text(
|
||||
modifier = modifier.padding(
|
||||
modifier = Modifier.padding(
|
||||
top = 20.dp,
|
||||
bottom = 8.dp,
|
||||
start = preferencePaddingHorizontal,
|
||||
|
|
|
|||
|
|
@ -80,10 +80,8 @@ fun PreferencePage(
|
|||
private fun PreferenceTopAppBar(
|
||||
title: String,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TopAppBar(
|
||||
modifier = modifier,
|
||||
navigationIcon = {
|
||||
BackButton(onClick = onBackPressed)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ private fun TextFieldDialog(
|
|||
onDismissRequest: () -> Unit,
|
||||
value: String?,
|
||||
placeholder: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
validation: (String?) -> Boolean = { true },
|
||||
onValidationErrorMessage: String? = null,
|
||||
autoSelectOnDisplay: Boolean = true,
|
||||
|
|
@ -110,7 +109,6 @@ private fun TextFieldDialog(
|
|||
onSubmit = { onSubmit(textFieldContents.text) },
|
||||
onDismissRequest = onDismissRequest,
|
||||
enabled = canSubmit,
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
TextFieldListItem(
|
||||
|
|
|
|||
|
|
@ -98,11 +98,9 @@ private fun IconsPreview(
|
|||
title: String,
|
||||
iconsList: ImmutableList<Int>,
|
||||
iconNameTransform: (String) -> String,
|
||||
modifier: Modifier = Modifier,
|
||||
) = ElementPreview {
|
||||
val context = LocalContext.current
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.modifiers
|
||||
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
|
||||
fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = then(
|
||||
pointerInput(Unit) {
|
||||
detectTapGestures(onTap = {
|
||||
focusManager.clearFocus()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
@ -55,7 +55,6 @@ internal fun SimpleAlertDialogContent(
|
|||
content: String,
|
||||
submitText: String,
|
||||
onSubmitClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
subtitle: @Composable (() -> Unit)? = null,
|
||||
destructiveSubmit: Boolean = false,
|
||||
|
|
@ -67,7 +66,6 @@ internal fun SimpleAlertDialogContent(
|
|||
icon: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
SimpleAlertDialogContent(
|
||||
modifier = modifier,
|
||||
icon = icon,
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
|
|
@ -92,7 +90,6 @@ internal fun SimpleAlertDialogContent(
|
|||
internal fun SimpleAlertDialogContent(
|
||||
submitText: String,
|
||||
onSubmitClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
subtitle: @Composable (() -> Unit)? = null,
|
||||
destructiveSubmit: Boolean = false,
|
||||
|
|
@ -148,7 +145,6 @@ internal fun SimpleAlertDialogContent(
|
|||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
title = title?.let { titleText ->
|
||||
@Composable {
|
||||
Text(
|
||||
|
|
@ -192,11 +188,9 @@ internal fun AlertDialogContent(
|
|||
iconContentColor: Color,
|
||||
titleContentColor: Color,
|
||||
textContentColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
applyPaddingToContents: Boolean = true,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
shape = shape,
|
||||
color = containerColor,
|
||||
tonalElevation = tonalElevation,
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* 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 androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import io.element.android.libraries.designsystem.BuildConfig
|
||||
import timber.log.Timber
|
||||
|
||||
// Note the inline function below which ensures that this function is essentially
|
||||
// copied at the call site to ensure that its logging only recompositions from the
|
||||
// original call site.
|
||||
@Composable
|
||||
fun LogCompositions(tag: String, msg: String) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
val ref = remember { Ref() }
|
||||
SideEffect { ref.value++ }
|
||||
Timber.tag(tag).d("Compositions: $msg ${ref.value}")
|
||||
}
|
||||
}
|
||||
|
||||
private class Ref {
|
||||
var value: Int = 0
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 538 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
|
@ -0,0 +1,57 @@
|
|||
<?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">"(аватар таксама быў зменены)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s змяніў аватар"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Вы змянілі свой аватар"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s змяніў сваё адлюстраванае імя з %2$s на %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Вы змянілі сваё адлюстраванае імя з %1$s на %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s выдаліў сваё адлюстраванае імя (яно было %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Вы выдалілі сваё адлюстраванае імя (яно было %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s усталявалі сваё адлюстраванае імя на %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Вы ўстанавілі адлюстраванае імя на %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s змяніў аватар пакоя"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Вы змянілі аватар пакоя"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s выдаліў(-ла) аватар пакоя"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Вы выдалілі аватар пакоя"</string>
|
||||
<string name="state_event_room_ban">"%1$s заблакіраваў %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Вы заблакіравалі %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s стварыў пакой"</string>
|
||||
<string name="state_event_room_created_by_you">"Вы стварылі пакой"</string>
|
||||
<string name="state_event_room_invite">"%1$s запрасіў %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s прыняў(-ла) запрашэнне"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Вы прынялі запрашэнне"</string>
|
||||
<string name="state_event_room_invite_by_you">"Вы запрасілі %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s запрасіў вас"</string>
|
||||
<string name="state_event_room_join">"%1$s далучыўся да пакоя"</string>
|
||||
<string name="state_event_room_join_by_you">"Вы далучыліся да пакоя"</string>
|
||||
<string name="state_event_room_knock">"%1$s прасіў(-ла) далучыцца"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s дазволіў(-ла) %2$s далучыцца"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s дазволіў(-ла) вам далучыцца"</string>
|
||||
<string name="state_event_room_knock_by_you">"Вы прасілі далучыцца"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s адхіліў(-ла) %2$s запыт на далучэнне"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Вы адхілілі %1$s запыт на далучэнне"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s адхіліў(-ла) ваш запыт на далучэнне"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s больш не зацікаўлены(-на) у далучэнні"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Вы адмянілі запыт на далучэнне"</string>
|
||||
<string name="state_event_room_leave">"%1$s выйшаў з пакоя"</string>
|
||||
<string name="state_event_room_leave_by_you">"Вы выйшлі з пакоя"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s змяніў назву пакоя на: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Вы змянілі назву пакоя на: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s выдаліў(-ла) назву пакоя"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Вы выдалілі назву пакоя"</string>
|
||||
<string name="state_event_room_reject">"%1$s адхіліў запрашэнне"</string>
|
||||
<string name="state_event_room_reject_by_you">"Вы адхілілі запрашэнне"</string>
|
||||
<string name="state_event_room_remove">"%1$s выдаліў %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Вы выдалілі %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s адправіў(-ла) запрашэнне %2$s далучыцца да пакоя"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Вы адправілі запрашэнне %1$s далучыцца да пакоя"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s адклікаў(-ла) запрашэнне для %2$s далучыцца да пакоя"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Вы адклікалі запрашэнне для %1$s далучыцца да пакоя"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s змяніў тэму на: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Вы змянілі тэму на: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s выдаліў(-ла) тэму пакоя"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Вы выдалілі тэму пакоя"</string>
|
||||
<string name="state_event_room_unban">"%1$s разблакіраваў %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Вы разблакіравалі %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s унеслі невядомую змену ў сяброўства"</string>
|
||||
</resources>
|
||||
|
|
@ -39,6 +39,8 @@
|
|||
<string name="state_event_room_name_changed_by_you">"Du hast den Raumnamen geändert in: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s hat den Raumnamen entfernt"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Du hast den Raumnamen entfernt"</string>
|
||||
<string name="state_event_room_none">"%1$shat keine Änderungen vorgenommen"</string>
|
||||
<string name="state_event_room_none_by_you">"Du hast keine Änderungen vorgenommen"</string>
|
||||
<string name="state_event_room_reject">"%1$s hat die Einladung abgelehnt"</string>
|
||||
<string name="state_event_room_reject_by_you">"Du hast die Einladung abgelehnt"</string>
|
||||
<string name="state_event_room_remove">"%1$s hat %2$s entfernt"</string>
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
<string name="state_event_room_name_changed_by_you">"Megváltoztatta a szoba nevét: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s eltávolította a szoba nevét"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Eltávolította a szoba nevét"</string>
|
||||
<string name="state_event_room_none">"%1$s nem változtatott semmin"</string>
|
||||
<string name="state_event_room_none_by_you">"Nem változtatott semmin"</string>
|
||||
<string name="state_event_room_reject">"%1$s elutasította a meghívást"</string>
|
||||
<string name="state_event_room_reject_by_you">"Elutasította a meghívást"</string>
|
||||
<string name="state_event_room_remove">"%1$s eltávolította: %2$s"</string>
|
||||
|
|
|
|||
|
|
@ -75,4 +75,11 @@ enum class FeatureFlags(
|
|||
defaultValue = true,
|
||||
isFinished = false,
|
||||
),
|
||||
MarkAsUnread(
|
||||
key = "feature.markAsUnread",
|
||||
title = "Mark as unread",
|
||||
description = "Allow user to mark a room as unread",
|
||||
defaultValue = true,
|
||||
isFinished = false,
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
|
|||
FeatureFlags.PinUnlock -> true
|
||||
FeatureFlags.Mentions -> true
|
||||
FeatureFlags.SecureStorage -> true
|
||||
FeatureFlags.MarkAsUnread -> false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -49,13 +49,11 @@ fun FeatureListView(
|
|||
private fun FeaturePreferenceView(
|
||||
feature: FeatureUiModel,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
PreferenceCheckbox(
|
||||
title = feature.title,
|
||||
supportingText = feature.description,
|
||||
isChecked = feature.isEnabled,
|
||||
modifier = modifier,
|
||||
onCheckedChange = onCheckedChange
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.poll.PollKind
|
|||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -57,6 +58,7 @@ interface MatrixRoom : Closeable {
|
|||
val isDm: Boolean get() = isDirect && isOneToOne
|
||||
|
||||
val roomInfoFlow: Flow<MatrixRoomInfo>
|
||||
val roomTypingMembersFlow: Flow<List<UserId>>
|
||||
|
||||
/**
|
||||
* The current notable tags as a Flow.
|
||||
|
|
@ -158,6 +160,17 @@ interface MatrixRoom : Closeable {
|
|||
|
||||
suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Reverts a previously set unread flag, and eventually send a Read Receipt.
|
||||
* @param receiptType The type of receipt to send. If null, no Read Receipt will be sent.
|
||||
*/
|
||||
suspend fun markAsRead(receiptType: ReceiptType?): Result<Unit>
|
||||
|
||||
/**
|
||||
* Sets a flag on the room to indicate that the user has explicitly marked it as unread.
|
||||
*/
|
||||
suspend fun markAsUnread(): Result<Unit>
|
||||
|
||||
/**
|
||||
* Share a location message in the room.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -28,9 +28,15 @@ enum class MessageEventType {
|
|||
KEY_VERIFICATION_KEY,
|
||||
KEY_VERIFICATION_MAC,
|
||||
KEY_VERIFICATION_DONE,
|
||||
REACTION_SENT,
|
||||
REACTION,
|
||||
ROOM_ENCRYPTED,
|
||||
ROOM_MESSAGE,
|
||||
ROOM_REDACTION,
|
||||
STICKER
|
||||
STICKER,
|
||||
POLL_END,
|
||||
POLL_RESPONSE,
|
||||
POLL_START,
|
||||
UNSTABLE_POLL_END,
|
||||
UNSTABLE_POLL_RESPONSE,
|
||||
UNSTABLE_POLL_START,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,19 @@ data class RoomMember(
|
|||
val powerLevel: Long,
|
||||
val normalizedPowerLevel: Long,
|
||||
val isIgnored: Boolean,
|
||||
)
|
||||
) {
|
||||
/**
|
||||
* Disambiguated display name for the RoomMember.
|
||||
* If the display name is null, the user ID is returned.
|
||||
* If the display name is ambiguous, the user ID is appended in parentheses.
|
||||
* Otherwise, the display name is returned.
|
||||
*/
|
||||
val disambiguatedDisplayName: String = when {
|
||||
displayName == null -> userId.value
|
||||
isNameAmbiguous -> "$displayName ($userId)"
|
||||
else -> displayName
|
||||
}
|
||||
}
|
||||
|
||||
enum class RoomMembershipState {
|
||||
BAN,
|
||||
|
|
|
|||
|
|
@ -28,29 +28,7 @@ import kotlinx.coroutines.flow.onEach
|
|||
* It lets load rooms on demand and filter them.
|
||||
*/
|
||||
interface DynamicRoomList : RoomList {
|
||||
sealed interface Filter {
|
||||
/**
|
||||
* No filter applied.
|
||||
*/
|
||||
data object All : Filter
|
||||
|
||||
/**
|
||||
* Filter only the left rooms.
|
||||
*/
|
||||
data object AllNonLeft : Filter
|
||||
|
||||
/**
|
||||
* Filter all rooms.
|
||||
*/
|
||||
data object None : Filter
|
||||
|
||||
/**
|
||||
* Filter rooms by normalized room name.
|
||||
*/
|
||||
data class NormalizedMatchRoomName(val pattern: String) : Filter
|
||||
}
|
||||
|
||||
val currentFilter: StateFlow<Filter>
|
||||
val currentFilter: StateFlow<RoomListFilter>
|
||||
val loadedPages: StateFlow<Int>
|
||||
val pageSize: Int
|
||||
|
||||
|
|
@ -68,7 +46,7 @@ interface DynamicRoomList : RoomList {
|
|||
* Update the filter to apply to the list.
|
||||
* @param filter the filter to apply.
|
||||
*/
|
||||
suspend fun updateFilter(filter: Filter)
|
||||
suspend fun updateFilter(filter: RoomListFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.matrix.api.roomlist
|
||||
|
||||
sealed interface RoomListFilter {
|
||||
companion object {
|
||||
fun all(vararg filters: RoomListFilter): RoomListFilter {
|
||||
return All(filters.toList())
|
||||
}
|
||||
|
||||
fun any(vararg filters: RoomListFilter): RoomListFilter {
|
||||
return Any(filters.toList())
|
||||
}
|
||||
}
|
||||
|
||||
data class All(
|
||||
val filters: List<RoomListFilter>
|
||||
) : RoomListFilter
|
||||
|
||||
data class Any(
|
||||
val filters: List<RoomListFilter>
|
||||
) : RoomListFilter
|
||||
|
||||
data object NonLeft : RoomListFilter
|
||||
|
||||
data object Unread : RoomListFilter
|
||||
|
||||
sealed interface Category : RoomListFilter {
|
||||
data object Group : Category
|
||||
data object People : Category
|
||||
}
|
||||
|
||||
data object None : RoomListFilter
|
||||
|
||||
data class NormalizedMatchRoomName(
|
||||
val pattern: String
|
||||
) : RoomListFilter
|
||||
|
||||
data class FuzzyMatchRoomName(
|
||||
val pattern: String
|
||||
) : RoomListFilter
|
||||
}
|
||||
|
|
@ -43,6 +43,7 @@ data class RoomSummaryDetails(
|
|||
val numUnreadMessages: Int,
|
||||
val numUnreadMentions: Int,
|
||||
val numUnreadNotifications: Int,
|
||||
val isMarkedUnread: Boolean,
|
||||
val inviter: RoomMember?,
|
||||
val userDefinedNotificationMode: RoomNotificationMode?,
|
||||
val hasRoomCall: Boolean,
|
||||
|
|
|
|||
|
|
@ -85,11 +85,7 @@ data class ProfileChangeContent(
|
|||
data class StateContent(
|
||||
val stateKey: String,
|
||||
val content: OtherState
|
||||
) : EventContent {
|
||||
fun isVisibleInTimeline(): Boolean {
|
||||
return content.isVisibleInTimeline()
|
||||
}
|
||||
}
|
||||
) : EventContent
|
||||
|
||||
data class FailedToParseMessageLikeContent(
|
||||
val eventType: String,
|
||||
|
|
|
|||
|
|
@ -41,30 +41,4 @@ sealed interface OtherState {
|
|||
data object SpaceChild : OtherState
|
||||
data object SpaceParent : OtherState
|
||||
data class Custom(val eventType: String) : OtherState
|
||||
|
||||
fun isVisibleInTimeline() = when (this) {
|
||||
// Visible
|
||||
is RoomAvatar,
|
||||
is RoomName,
|
||||
is RoomTopic,
|
||||
is RoomThirdPartyInvite,
|
||||
is RoomCreate,
|
||||
is RoomEncryption,
|
||||
is Custom -> true
|
||||
// Hidden
|
||||
is RoomAliases,
|
||||
is RoomCanonicalAlias,
|
||||
is RoomGuestAccess,
|
||||
is RoomHistoryVisibility,
|
||||
is RoomJoinRules,
|
||||
is RoomPinnedEvents,
|
||||
is RoomPowerLevels,
|
||||
is RoomServerAcl,
|
||||
is RoomTombstone,
|
||||
is SpaceChild,
|
||||
is SpaceParent,
|
||||
is PolicyRuleRoom,
|
||||
is PolicyRuleServer,
|
||||
is PolicyRuleUser -> false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ data class TracingFilterConfiguration(
|
|||
private val targetsToLogLevel: Map<Target, LogLevel> = mapOf(
|
||||
Target.HYPER to LogLevel.WARN,
|
||||
Target.MATRIX_SDK_CRYPTO to LogLevel.DEBUG,
|
||||
Target.MATRIX_SDK_CRYPTO_ACCOUNT to LogLevel.TRACE,
|
||||
Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.DEBUG,
|
||||
Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.TRACE,
|
||||
Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.TRACE,
|
||||
|
|
@ -58,6 +59,7 @@ enum class Target(open val filter: String) {
|
|||
MATRIX_SDK_FFI("matrix_sdk_ffi"),
|
||||
MATRIX_SDK_UNIFFI_API("matrix_sdk_ffi::uniffi_api"),
|
||||
MATRIX_SDK_CRYPTO("matrix_sdk_crypto"),
|
||||
MATRIX_SDK_CRYPTO_ACCOUNT("matrix_sdk_crypto::olm::account"),
|
||||
MATRIX_SDK("matrix_sdk"),
|
||||
MATRIX_SDK_HTTP_CLIENT("matrix_sdk::http_client"),
|
||||
MATRIX_SDK_CLIENT("matrix_sdk::client"),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.util.UUID
|
|||
interface CallWidgetSettingsProvider {
|
||||
fun provide(
|
||||
baseUrl: String,
|
||||
widgetId: String = UUID.randomUUID().toString()
|
||||
widgetId: String = UUID.randomUUID().toString(),
|
||||
encrypted: Boolean,
|
||||
): MatrixWidgetSettings
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,10 +55,12 @@ import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
|||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
|
||||
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
||||
import io.element.android.libraries.matrix.impl.util.SessionDirectoryNameProvider
|
||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
|
|
@ -76,12 +78,12 @@ import kotlinx.coroutines.withTimeout
|
|||
import org.matrix.rustcomponents.sdk.BackupState
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientDelegate
|
||||
import org.matrix.rustcomponents.sdk.FilterStateEventType
|
||||
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
|
||||
import org.matrix.rustcomponents.sdk.PowerLevels
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.StateEventType
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
|
|
@ -133,6 +135,7 @@ class RustMatrixClient(
|
|||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
dispatchers = dispatchers,
|
||||
).apply { start() }
|
||||
private val sessionDirectoryNameProvider = SessionDirectoryNameProvider()
|
||||
|
||||
private val isLoggingOut = AtomicBoolean(false)
|
||||
|
||||
|
|
@ -203,20 +206,20 @@ class RustMatrixClient(
|
|||
|
||||
private val eventFilters = TimelineEventTypeFilter.exclude(
|
||||
listOf(
|
||||
FilterStateEventType.ROOM_ALIASES,
|
||||
FilterStateEventType.ROOM_CANONICAL_ALIAS,
|
||||
FilterStateEventType.ROOM_GUEST_ACCESS,
|
||||
FilterStateEventType.ROOM_HISTORY_VISIBILITY,
|
||||
FilterStateEventType.ROOM_JOIN_RULES,
|
||||
FilterStateEventType.ROOM_PINNED_EVENTS,
|
||||
FilterStateEventType.ROOM_POWER_LEVELS,
|
||||
FilterStateEventType.ROOM_SERVER_ACL,
|
||||
FilterStateEventType.ROOM_TOMBSTONE,
|
||||
FilterStateEventType.SPACE_CHILD,
|
||||
FilterStateEventType.SPACE_PARENT,
|
||||
FilterStateEventType.POLICY_RULE_ROOM,
|
||||
FilterStateEventType.POLICY_RULE_SERVER,
|
||||
FilterStateEventType.POLICY_RULE_USER,
|
||||
StateEventType.ROOM_ALIASES,
|
||||
StateEventType.ROOM_CANONICAL_ALIAS,
|
||||
StateEventType.ROOM_GUEST_ACCESS,
|
||||
StateEventType.ROOM_HISTORY_VISIBILITY,
|
||||
StateEventType.ROOM_JOIN_RULES,
|
||||
StateEventType.ROOM_PINNED_EVENTS,
|
||||
StateEventType.ROOM_POWER_LEVELS,
|
||||
StateEventType.ROOM_SERVER_ACL,
|
||||
StateEventType.ROOM_TOMBSTONE,
|
||||
StateEventType.SPACE_CHILD,
|
||||
StateEventType.SPACE_PARENT,
|
||||
StateEventType.POLICY_RULE_ROOM,
|
||||
StateEventType.POLICY_RULE_SERVER,
|
||||
StateEventType.POLICY_RULE_USER,
|
||||
).map(FilterTimelineEventType::State)
|
||||
)
|
||||
|
||||
|
|
@ -270,12 +273,7 @@ class RustMatrixClient(
|
|||
|
||||
private suspend fun pairOfRoom(roomId: RoomId): Pair<RoomListItem, Room>? {
|
||||
val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
val fullRoom = cachedRoomListItem?.let { roomListItem ->
|
||||
if (!roomListItem.isTimelineInitialized()) {
|
||||
roomListItem.initTimeline(eventFilters)
|
||||
}
|
||||
roomListItem.fullRoom()
|
||||
}
|
||||
val fullRoom = cachedRoomListItem?.fullRoomWithTimeline(filter = eventFilters)
|
||||
return if (cachedRoomListItem == null || fullRoom == null) {
|
||||
Timber.d("No room cached for $roomId")
|
||||
null
|
||||
|
|
@ -401,13 +399,12 @@ class RustMatrixClient(
|
|||
}
|
||||
|
||||
override suspend fun getCacheSize(): Long {
|
||||
// Do not use client.userId since it can throw if client has been closed (during clear cache)
|
||||
return baseDirectory.getCacheSize(userID = sessionId.value)
|
||||
return baseDirectory.getCacheSize()
|
||||
}
|
||||
|
||||
override suspend fun clearCache() {
|
||||
close()
|
||||
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false)
|
||||
baseDirectory.deleteSessionDirectory(deleteCryptoDb = false)
|
||||
}
|
||||
|
||||
override suspend fun logout(ignoreSdkError: Boolean): String? = doLogout(
|
||||
|
|
@ -436,7 +433,7 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
close()
|
||||
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = true)
|
||||
baseDirectory.deleteSessionDirectory(deleteCryptoDb = true)
|
||||
if (removeSession) {
|
||||
sessionStore.removeSession(sessionId.value)
|
||||
}
|
||||
|
|
@ -482,12 +479,10 @@ class RustMatrixClient(
|
|||
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
|
||||
|
||||
private suspend fun File.getCacheSize(
|
||||
userID: String,
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(sessionDispatcher) {
|
||||
// Rust sanitises the user ID replacing invalid characters with an _
|
||||
val sanitisedUserID = userID.replace(":", "_")
|
||||
val sessionDirectory = File(this@getCacheSize, sanitisedUserID)
|
||||
val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId)
|
||||
val sessionDirectory = File(this@getCacheSize, sessionDirectoryName)
|
||||
if (includeCryptoDb) {
|
||||
sessionDirectory.getSizeOfFiles()
|
||||
} else {
|
||||
|
|
@ -504,12 +499,10 @@ class RustMatrixClient(
|
|||
}
|
||||
|
||||
private suspend fun File.deleteSessionDirectory(
|
||||
userID: String,
|
||||
deleteCryptoDb: Boolean = false,
|
||||
): Boolean = withContext(sessionDispatcher) {
|
||||
// Rust sanitises the user ID replacing invalid characters with an _
|
||||
val sanitisedUserID = userID.replace(":", "_")
|
||||
val sessionDirectory = File(this@deleteSessionDirectory, sanitisedUserID)
|
||||
val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId)
|
||||
val sessionDirectory = File(this@deleteSessionDirectory, sessionDirectoryName)
|
||||
if (deleteCryptoDb) {
|
||||
// Delete the folder and all its content
|
||||
sessionDirectory.deleteRecursively()
|
||||
|
|
|
|||
|
|
@ -31,11 +31,17 @@ fun MessageEventType.map(): MessageLikeEventType = when (this) {
|
|||
MessageEventType.KEY_VERIFICATION_KEY -> MessageLikeEventType.KEY_VERIFICATION_KEY
|
||||
MessageEventType.KEY_VERIFICATION_MAC -> MessageLikeEventType.KEY_VERIFICATION_MAC
|
||||
MessageEventType.KEY_VERIFICATION_DONE -> MessageLikeEventType.KEY_VERIFICATION_DONE
|
||||
MessageEventType.REACTION_SENT -> MessageLikeEventType.REACTION_SENT
|
||||
MessageEventType.REACTION -> MessageLikeEventType.REACTION
|
||||
MessageEventType.ROOM_ENCRYPTED -> MessageLikeEventType.ROOM_ENCRYPTED
|
||||
MessageEventType.ROOM_MESSAGE -> MessageLikeEventType.ROOM_MESSAGE
|
||||
MessageEventType.ROOM_REDACTION -> MessageLikeEventType.ROOM_REDACTION
|
||||
MessageEventType.STICKER -> MessageLikeEventType.STICKER
|
||||
MessageEventType.POLL_END -> MessageLikeEventType.POLL_END
|
||||
MessageEventType.POLL_RESPONSE -> MessageLikeEventType.POLL_RESPONSE
|
||||
MessageEventType.POLL_START -> MessageLikeEventType.POLL_START
|
||||
MessageEventType.UNSTABLE_POLL_END -> MessageLikeEventType.UNSTABLE_POLL_END
|
||||
MessageEventType.UNSTABLE_POLL_RESPONSE -> MessageLikeEventType.UNSTABLE_POLL_RESPONSE
|
||||
MessageEventType.UNSTABLE_POLL_START -> MessageLikeEventType.UNSTABLE_POLL_START
|
||||
}
|
||||
|
||||
fun MessageLikeEventType.map(): MessageEventType = when (this) {
|
||||
|
|
@ -50,9 +56,15 @@ fun MessageLikeEventType.map(): MessageEventType = when (this) {
|
|||
MessageLikeEventType.KEY_VERIFICATION_KEY -> MessageEventType.KEY_VERIFICATION_KEY
|
||||
MessageLikeEventType.KEY_VERIFICATION_MAC -> MessageEventType.KEY_VERIFICATION_MAC
|
||||
MessageLikeEventType.KEY_VERIFICATION_DONE -> MessageEventType.KEY_VERIFICATION_DONE
|
||||
MessageLikeEventType.REACTION_SENT -> MessageEventType.REACTION_SENT
|
||||
MessageLikeEventType.REACTION -> MessageEventType.REACTION
|
||||
MessageLikeEventType.ROOM_ENCRYPTED -> MessageEventType.ROOM_ENCRYPTED
|
||||
MessageLikeEventType.ROOM_MESSAGE -> MessageEventType.ROOM_MESSAGE
|
||||
MessageLikeEventType.ROOM_REDACTION -> MessageEventType.ROOM_REDACTION
|
||||
MessageLikeEventType.STICKER -> MessageEventType.STICKER
|
||||
MessageLikeEventType.POLL_END -> MessageEventType.POLL_END
|
||||
MessageLikeEventType.POLL_RESPONSE -> MessageEventType.POLL_RESPONSE
|
||||
MessageLikeEventType.POLL_START -> MessageEventType.POLL_START
|
||||
MessageLikeEventType.UNSTABLE_POLL_END -> MessageEventType.UNSTABLE_POLL_END
|
||||
MessageLikeEventType.UNSTABLE_POLL_RESPONSE -> MessageEventType.UNSTABLE_POLL_RESPONSE
|
||||
MessageLikeEventType.UNSTABLE_POLL_START -> MessageEventType.UNSTABLE_POLL_START
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.parallelMap
|
|||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.ForwardEventException
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
|
@ -50,7 +51,9 @@ class RoomContentForwarder(
|
|||
) {
|
||||
val content = fromTimeline.getTimelineEventContentByEventId(eventId.value)
|
||||
val targetSlidingSyncRooms = toRoomIds.mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) }
|
||||
val targetRooms = targetSlidingSyncRooms.mapNotNull { slidingSyncRoom -> slidingSyncRoom.use { it.fullRoom() } }
|
||||
val targetRooms = targetSlidingSyncRooms.map { slidingSyncRoom ->
|
||||
slidingSyncRoom.use { it.fullRoomWithTimeline(null) }
|
||||
}
|
||||
val failedForwardingTo = mutableSetOf<RoomId>()
|
||||
targetRooms.parallelMap { room ->
|
||||
room.use { targetRoom ->
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType
|
|||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
||||
|
|
@ -53,6 +54,7 @@ import io.element.android.libraries.matrix.impl.room.location.toInner
|
|||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
||||
import io.element.android.libraries.matrix.impl.room.tags.map
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
|
||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
|
||||
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
|
||||
|
|
@ -74,6 +76,7 @@ import org.matrix.rustcomponents.sdk.RoomListItem
|
|||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||
import org.matrix.rustcomponents.sdk.RoomNotableTagsListener
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.TypingNotificationsListener
|
||||
import org.matrix.rustcomponents.sdk.WidgetCapabilities
|
||||
import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||
|
|
@ -115,6 +118,7 @@ class RustMatrixRoom(
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
override val notableTagsFlow: Flow<RoomNotableTags> = mxCallbackFlow {
|
||||
innerRoom.subscribeToNotableTags(object : RoomNotableTagsListener {
|
||||
override fun call(notableTags: RustRoomNotableTags) {
|
||||
|
|
@ -123,6 +127,22 @@ class RustMatrixRoom(
|
|||
})
|
||||
}
|
||||
|
||||
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
|
||||
launch {
|
||||
val initial = emptyList<UserId>()
|
||||
channel.trySend(initial)
|
||||
}
|
||||
innerRoom.subscribeToTypingNotifications(object : TypingNotificationsListener {
|
||||
override fun call(typingUserIds: List<String>) {
|
||||
channel.trySend(
|
||||
typingUserIds
|
||||
.filter { it != sessionData.userId }
|
||||
.map(::UserId)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create a dispatcher for all room methods...
|
||||
private val roomDispatcher = coroutineDispatchers.io.limitedParallelism(32)
|
||||
|
||||
|
|
@ -441,6 +461,22 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun markAsRead(receiptType: ReceiptType?): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
if (receiptType != null) {
|
||||
innerRoom.markAsReadAndSendReadReceipt(receiptType.toRustReceiptType())
|
||||
} else {
|
||||
innerRoom.markAsRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun markAsUnread(): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.markAsUnread()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist
|
|||
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -28,7 +29,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||
|
|
@ -44,7 +44,7 @@ internal class RoomListFactory(
|
|||
*/
|
||||
fun createRoomList(
|
||||
pageSize: Int,
|
||||
initialFilter: DynamicRoomList.Filter = DynamicRoomList.Filter.All,
|
||||
initialFilter: RoomListFilter = RoomListFilter.all(),
|
||||
innerProvider: suspend () -> InnerRoomList
|
||||
): DynamicRoomList {
|
||||
val loadingStateFlow: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
|
||||
|
|
@ -91,7 +91,7 @@ internal class RoomListFactory(
|
|||
private class RustDynamicRoomList(
|
||||
override val summaries: MutableStateFlow<List<RoomSummary>>,
|
||||
override val loadingState: MutableStateFlow<RoomList.LoadingState>,
|
||||
override val currentFilter: MutableStateFlow<DynamicRoomList.Filter>,
|
||||
override val currentFilter: MutableStateFlow<RoomListFilter>,
|
||||
override val loadedPages: MutableStateFlow<Int>,
|
||||
private val dynamicEvents: MutableSharedFlow<RoomListDynamicEvents>,
|
||||
private val processor: RoomSummaryListProcessor,
|
||||
|
|
@ -101,7 +101,7 @@ private class RustDynamicRoomList(
|
|||
processor.rebuildRoomSummaries()
|
||||
}
|
||||
|
||||
override suspend fun updateFilter(filter: DynamicRoomList.Filter) {
|
||||
override suspend fun updateFilter(filter: RoomListFilter) {
|
||||
currentFilter.emit(filter)
|
||||
val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter())
|
||||
dynamicEvents.emit(filterEvent)
|
||||
|
|
@ -124,12 +124,3 @@ private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState {
|
|||
RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded
|
||||
}
|
||||
}
|
||||
|
||||
private fun DynamicRoomList.Filter.toRustFilter(): RoomListEntriesDynamicFilterKind {
|
||||
return when (this) {
|
||||
DynamicRoomList.Filter.All -> RoomListEntriesDynamicFilterKind.All
|
||||
is DynamicRoomList.Filter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(this.pattern)
|
||||
DynamicRoomList.Filter.None -> RoomListEntriesDynamicFilterKind.None
|
||||
DynamicRoomList.Filter.AllNonLeft -> RoomListEntriesDynamicFilterKind.AllNonLeft
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||
import org.matrix.rustcomponents.sdk.RoomListFilterCategory
|
||||
|
||||
fun RoomListFilter.toRustFilter(): RoomListEntriesDynamicFilterKind {
|
||||
return when (this) {
|
||||
is RoomListFilter.All -> RoomListEntriesDynamicFilterKind.All(filters.map { it.toRustFilter() })
|
||||
is RoomListFilter.Any -> RoomListEntriesDynamicFilterKind.Any(filters.map { it.toRustFilter() })
|
||||
RoomListFilter.Category.Group -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.GROUP)
|
||||
RoomListFilter.Category.People -> RoomListEntriesDynamicFilterKind.Category(RoomListFilterCategory.PEOPLE)
|
||||
is RoomListFilter.FuzzyMatchRoomName -> RoomListEntriesDynamicFilterKind.FuzzyMatchRoomName(pattern)
|
||||
RoomListFilter.NonLeft -> RoomListEntriesDynamicFilterKind.NonLeft
|
||||
RoomListFilter.None -> RoomListEntriesDynamicFilterKind.None
|
||||
is RoomListFilter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(pattern)
|
||||
RoomListFilter.Unread -> RoomListEntriesDynamicFilterKind.Unread
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.matrix.impl.roomlist
|
||||
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
|
||||
/** Returns a `Room` with an initialized timeline using the given [filter]. */
|
||||
suspend fun RoomListItem.fullRoomWithTimeline(filter: TimelineEventTypeFilter? = null): Room {
|
||||
if (!isTimelineInitialized()) {
|
||||
initTimeline(filter)
|
||||
}
|
||||
return fullRoom()
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
|
|||
numUnreadMentions = roomInfo.numUnreadMentions.toInt(),
|
||||
numUnreadMessages = roomInfo.numUnreadMessages.toInt(),
|
||||
numUnreadNotifications = roomInfo.numUnreadNotifications.toInt(),
|
||||
isMarkedUnread = roomInfo.isMarkedUnread,
|
||||
lastMessage = latestRoomMessage,
|
||||
inviter = roomInfo.inviter?.let(RoomMemberMapper::map),
|
||||
userDefinedNotificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist
|
|||
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -45,7 +46,7 @@ internal class RustRoomListService(
|
|||
) : RoomListService {
|
||||
override val allRooms: DynamicRoomList = roomListFactory.createRoomList(
|
||||
pageSize = DEFAULT_PAGE_SIZE,
|
||||
initialFilter = DynamicRoomList.Filter.AllNonLeft,
|
||||
initialFilter = RoomListFilter.all(RoomListFilter.NonLeft),
|
||||
) {
|
||||
innerRoomListService.allRooms()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelin
|
|||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.DmBeginningTimelineProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterHiddenStateEventsProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
|
@ -84,8 +83,6 @@ class RustMatrixTimeline(
|
|||
dispatcher = dispatcher,
|
||||
)
|
||||
|
||||
private val filterHiddenStateEventsProcessor = FilterHiddenStateEventsProcessor()
|
||||
|
||||
private val dmBeginningTimelineProcessor = DmBeginningTimelineProcessor()
|
||||
|
||||
private val timelineItemFactory = MatrixTimelineItemMapper(
|
||||
|
|
@ -109,7 +106,6 @@ class RustMatrixTimeline(
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems
|
||||
.mapLatest { items -> encryptedHistoryPostProcessor.process(items) }
|
||||
.mapLatest { items -> filterHiddenStateEventsProcessor.process(items) }
|
||||
.mapLatest { items ->
|
||||
dmBeginningTimelineProcessor.process(
|
||||
items = items,
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.matrix.impl.timeline.postprocessor
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
|
||||
/**
|
||||
* This class is used to filter out 'hidden' state events from the timeline.
|
||||
*/
|
||||
class FilterHiddenStateEventsProcessor {
|
||||
fun process(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> {
|
||||
return items.filter { item ->
|
||||
when (item) {
|
||||
is MatrixTimelineItem.Event -> {
|
||||
when (val content = item.event.content) {
|
||||
// If it's a state event, make sure it's visible
|
||||
is StateContent -> content.isVisibleInTimeline()
|
||||
// We can display any other event
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
is MatrixTimelineItem.Virtual -> true
|
||||
is MatrixTimelineItem.Other -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.matrix.impl.util
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
class SessionDirectoryNameProvider {
|
||||
// Rust sanitises the user ID replacing invalid characters with an _
|
||||
fun provides(sessionId: SessionId): String {
|
||||
return sessionId.value.replace(":", "_")
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ import javax.inject.Inject
|
|||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultCallWidgetSettingsProvider @Inject constructor() : CallWidgetSettingsProvider {
|
||||
override fun provide(baseUrl: String, widgetId: String): MatrixWidgetSettings {
|
||||
override fun provide(baseUrl: String, widgetId: String, encrypted: Boolean): MatrixWidgetSettings {
|
||||
val options = VirtualElementCallWidgetOptions(
|
||||
elementCallUrl = baseUrl,
|
||||
widgetId = widgetId,
|
||||
|
|
@ -40,7 +40,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor() : CallWidgetSettin
|
|||
confineToRoom = true,
|
||||
font = null,
|
||||
analyticsId = null,
|
||||
encryption = EncryptionSystem.PerParticipantKeys,
|
||||
encryption = if (encrypted) EncryptionSystem.PerParticipantKeys else EncryptionSystem.Unencrypted,
|
||||
)
|
||||
val rustWidgetSettings = newVirtualElementCallWidget(options)
|
||||
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.matrix.impl.timeline.postprocessor
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||
import org.junit.Test
|
||||
|
||||
class FilterHiddenStateEventsProcessorTest {
|
||||
@Test
|
||||
fun test() {
|
||||
val items = listOf(
|
||||
// These are visible because they're not state events
|
||||
MatrixTimelineItem.Other,
|
||||
MatrixTimelineItem.Virtual("virtual", VirtualTimelineItem.ReadMarker),
|
||||
MatrixTimelineItem.Event("event", anEventTimelineItem()),
|
||||
// These are visible state events
|
||||
MatrixTimelineItem.Event("m.room.avatar", anEventTimelineItem(content = StateContent("", OtherState.RoomAvatar("")))),
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.encrypted", anEventTimelineItem(content = StateContent("", OtherState.RoomEncryption))),
|
||||
MatrixTimelineItem.Event("m.room.name", anEventTimelineItem(content = StateContent("", OtherState.RoomName("")))),
|
||||
MatrixTimelineItem.Event("m.room.third_party_invite", anEventTimelineItem(content = StateContent("", OtherState.RoomThirdPartyInvite("")))),
|
||||
MatrixTimelineItem.Event("m.room.topic", anEventTimelineItem(content = StateContent("", OtherState.RoomTopic("")))),
|
||||
MatrixTimelineItem.Event("m.room.custom", anEventTimelineItem(content = StateContent("", OtherState.Custom("")))),
|
||||
// These ones are hidden
|
||||
MatrixTimelineItem.Event("m.room.aliases", anEventTimelineItem(content = StateContent("", OtherState.RoomAliases))),
|
||||
MatrixTimelineItem.Event("m.room.canonical_alias", anEventTimelineItem(content = StateContent("", OtherState.RoomCanonicalAlias))),
|
||||
MatrixTimelineItem.Event("m.room.guest_access", anEventTimelineItem(content = StateContent("", OtherState.RoomGuestAccess))),
|
||||
MatrixTimelineItem.Event("m.room.history_visibility", anEventTimelineItem(content = StateContent("", OtherState.RoomHistoryVisibility))),
|
||||
MatrixTimelineItem.Event("m.room.join_rules", anEventTimelineItem(content = StateContent("", OtherState.RoomJoinRules))),
|
||||
MatrixTimelineItem.Event("m.room.pinned_events", anEventTimelineItem(content = StateContent("", OtherState.RoomPinnedEvents))),
|
||||
MatrixTimelineItem.Event("m.room.power_levels", anEventTimelineItem(content = StateContent("", OtherState.RoomPowerLevels))),
|
||||
MatrixTimelineItem.Event("m.room.server_acl", anEventTimelineItem(content = StateContent("", OtherState.RoomServerAcl))),
|
||||
MatrixTimelineItem.Event("m.room.tombstone", anEventTimelineItem(content = StateContent("", OtherState.RoomTombstone))),
|
||||
MatrixTimelineItem.Event("m.space.child", anEventTimelineItem(content = StateContent("", OtherState.SpaceChild))),
|
||||
MatrixTimelineItem.Event("m.space.parent", anEventTimelineItem(content = StateContent("", OtherState.SpaceParent))),
|
||||
MatrixTimelineItem.Event("m.room.policy.rule.room", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleRoom))),
|
||||
MatrixTimelineItem.Event("m.room.policy.rule.server", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleServer))),
|
||||
MatrixTimelineItem.Event("m.room.policy.rule.user", anEventTimelineItem(content = StateContent("", OtherState.PolicyRuleUser))),
|
||||
)
|
||||
|
||||
val expected = listOf(
|
||||
MatrixTimelineItem.Other,
|
||||
MatrixTimelineItem.Virtual("virtual", VirtualTimelineItem.ReadMarker),
|
||||
MatrixTimelineItem.Event("event", anEventTimelineItem()),
|
||||
MatrixTimelineItem.Event("m.room.avatar", anEventTimelineItem(content = StateContent("", OtherState.RoomAvatar("")))),
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.encrypted", anEventTimelineItem(content = StateContent("", OtherState.RoomEncryption))),
|
||||
MatrixTimelineItem.Event("m.room.name", anEventTimelineItem(content = StateContent("", OtherState.RoomName("")))),
|
||||
MatrixTimelineItem.Event("m.room.third_party_invite", anEventTimelineItem(content = StateContent("", OtherState.RoomThirdPartyInvite("")))),
|
||||
MatrixTimelineItem.Event("m.room.topic", anEventTimelineItem(content = StateContent("", OtherState.RoomTopic("")))),
|
||||
MatrixTimelineItem.Event("m.room.custom", anEventTimelineItem(content = StateContent("", OtherState.Custom("")))),
|
||||
)
|
||||
|
||||
val processor = FilterHiddenStateEventsProcessor()
|
||||
|
||||
assertThat(processor.process(items)).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,7 @@ import io.element.android.libraries.matrix.api.room.StateEventType
|
|||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.tags.RoomNotableTags
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
|
|
@ -174,6 +175,9 @@ class FakeMatrixRoom(
|
|||
private val _notableTagsFlow: MutableStateFlow<RoomNotableTags> = MutableStateFlow(aRoomNotableTags())
|
||||
override val notableTagsFlow: Flow<RoomNotableTags> = _notableTagsFlow
|
||||
|
||||
private val _roomTypingMembersFlow: MutableSharedFlow<List<UserId>> = MutableSharedFlow(replay = 1)
|
||||
override val roomTypingMembersFlow: Flow<List<UserId>> = _roomTypingMembersFlow
|
||||
|
||||
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
|
||||
|
||||
override val roomNotificationSettingsStateFlow: MutableStateFlow<MatrixRoomNotificationSettingsState> =
|
||||
|
|
@ -393,6 +397,22 @@ class FakeMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
val markAsReadCalls = mutableListOf<ReceiptType?>()
|
||||
|
||||
override suspend fun markAsRead(receiptType: ReceiptType?): Result<Unit> {
|
||||
markAsReadCalls.add(receiptType)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
var markAsUnreadReadCallCount = 0
|
||||
private set
|
||||
|
||||
override suspend fun markAsUnread(): Result<Unit> {
|
||||
markAsUnreadReadCallCount++
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
|
|
@ -597,6 +617,10 @@ class FakeMatrixRoom(
|
|||
fun givenRoomInfo(roomInfo: MatrixRoomInfo) {
|
||||
_roomInfoFlow.tryEmit(roomInfo)
|
||||
}
|
||||
|
||||
fun givenRoomTypingMembers(typingMembers: List<UserId>) {
|
||||
_roomTypingMembersFlow.tryEmit(typingMembers)
|
||||
}
|
||||
}
|
||||
|
||||
data class SendLocationInvocation(
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ fun aRoomSummaryFilled(
|
|||
isDirect: Boolean = false,
|
||||
avatarUrl: String? = null,
|
||||
lastMessage: RoomMessage? = aRoomMessage(),
|
||||
numUnreadMentions: Int = 1,
|
||||
numUnreadMessages: Int = 2,
|
||||
numUnreadMentions: Int = 0,
|
||||
numUnreadMessages: Int = 0,
|
||||
notificationMode: RoomNotificationMode? = null,
|
||||
) = RoomSummary.Filled(
|
||||
aRoomSummaryDetails(
|
||||
|
|
@ -62,6 +62,7 @@ fun aRoomSummaryDetails(
|
|||
numUnreadMentions: Int = 0,
|
||||
numUnreadMessages: Int = 0,
|
||||
numUnreadNotifications: Int = 0,
|
||||
isMarkedUnread: Boolean = false,
|
||||
notificationMode: RoomNotificationMode? = null,
|
||||
inviter: RoomMember? = null,
|
||||
canonicalAlias: String? = null,
|
||||
|
|
@ -76,6 +77,7 @@ fun aRoomSummaryDetails(
|
|||
numUnreadMentions = numUnreadMentions,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
userDefinedNotificationMode = notificationMode,
|
||||
inviter = inviter,
|
||||
canonicalAlias = canonicalAlias,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test.roomlist
|
|||
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -61,13 +62,13 @@ class FakeRoomListService : RoomListService {
|
|||
override val allRooms: DynamicRoomList = SimplePagedRoomList(
|
||||
allRoomSummariesFlow,
|
||||
allRoomsLoadingStateFlow,
|
||||
MutableStateFlow(DynamicRoomList.Filter.None)
|
||||
MutableStateFlow(RoomListFilter.all())
|
||||
)
|
||||
|
||||
override val invites: RoomList = SimplePagedRoomList(
|
||||
inviteRoomSummariesFlow,
|
||||
inviteRoomsLoadingStateFlow,
|
||||
MutableStateFlow(DynamicRoomList.Filter.None)
|
||||
MutableStateFlow(RoomListFilter.all())
|
||||
)
|
||||
|
||||
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.test.roomlist
|
|||
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -26,7 +27,7 @@ import kotlinx.coroutines.flow.getAndUpdate
|
|||
data class SimplePagedRoomList(
|
||||
override val summaries: StateFlow<List<RoomSummary>>,
|
||||
override val loadingState: StateFlow<RoomList.LoadingState>,
|
||||
override val currentFilter: MutableStateFlow<DynamicRoomList.Filter>
|
||||
override val currentFilter: MutableStateFlow<RoomListFilter>
|
||||
) : DynamicRoomList {
|
||||
override val pageSize: Int = Int.MAX_VALUE
|
||||
override val loadedPages = MutableStateFlow(1)
|
||||
|
|
@ -40,7 +41,7 @@ data class SimplePagedRoomList(
|
|||
loadedPages.emit(1)
|
||||
}
|
||||
|
||||
override suspend fun updateFilter(filter: DynamicRoomList.Filter) {
|
||||
override suspend fun updateFilter(filter: RoomListFilter) {
|
||||
currentFilter.emit(filter)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class FakeCallWidgetSettingsProvider(
|
|||
) : CallWidgetSettingsProvider {
|
||||
val providedBaseUrls = mutableListOf<String>()
|
||||
|
||||
override fun provide(baseUrl: String, widgetId: String): MatrixWidgetSettings {
|
||||
override fun provide(baseUrl: String, widgetId: String, encrypted: Boolean): MatrixWidgetSettings {
|
||||
providedBaseUrls += baseUrl
|
||||
return provideFn(baseUrl, widgetId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ fun aRoomSummaryDetails(
|
|||
numUnreadMentions: Int = 0,
|
||||
numUnreadMessages: Int = 0,
|
||||
numUnreadNotifications: Int = 0,
|
||||
isMarkedUnread: Boolean = false,
|
||||
) = RoomSummaryDetails(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
|
|
@ -130,4 +131,5 @@ fun aRoomSummaryDetails(
|
|||
numUnreadMentions = numUnreadMentions,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ private fun PdfPagesView(
|
|||
@Composable
|
||||
private fun PdfPageView(
|
||||
pdfPage: PdfPage,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val pdfPageState by pdfPage.stateFlow.collectAsState()
|
||||
DisposableEffect(pdfPage) {
|
||||
|
|
@ -101,12 +100,12 @@ private fun PdfPageView(
|
|||
bitmap = state.bitmap.asImageBitmap(),
|
||||
contentDescription = stringResource(id = CommonStrings.a11y_page_n, pdfPage.pageIndex),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
is PdfPage.State.Loading -> {
|
||||
Box(
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(state.height.toDp())
|
||||
.background(color = Color.White)
|
||||
|
|
|
|||
|
|
@ -259,10 +259,8 @@ private fun ErrorView(
|
|||
errorMessage: String,
|
||||
onRetry: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
RetryDialog(
|
||||
modifier = modifier,
|
||||
content = errorMessage,
|
||||
onRetry = onRetry,
|
||||
onDismiss = onDismiss
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="dialog_permission_camera">"Каб дазволіць праграме выкарыстоўваць камеру, дайце дазвол у наладах сістэмы."</string>
|
||||
<string name="dialog_permission_generic">"Калі ласка, дайце дазвол у наладах сістэмы."</string>
|
||||
<string name="dialog_permission_microphone">"Каб дазволіць праграме выкарыстоўваць мікрафон, дайце дазвол у наладах сістэмы."</string>
|
||||
<string name="dialog_permission_notification">"Каб дазволіць праграме паказваць апавяшчэнні, дайце дазвол у наладах сістэмы."</string>
|
||||
</resources>
|
||||
|
|
@ -19,8 +19,20 @@ package io.element.android.features.preferences.api.store
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SessionPreferencesStore {
|
||||
suspend fun setSharePresence(enabled: Boolean)
|
||||
fun isSharePresenceEnabled(): Flow<Boolean>
|
||||
|
||||
suspend fun setSendPublicReadReceipts(enabled: Boolean)
|
||||
fun isSendPublicReadReceiptsEnabled(): Flow<Boolean>
|
||||
|
||||
suspend fun setRenderReadReceipts(enabled: Boolean)
|
||||
fun isRenderReadReceiptsEnabled(): Flow<Boolean>
|
||||
|
||||
suspend fun setSendTypingNotifications(enabled: Boolean)
|
||||
fun isSendTypingNotificationsEnabled(): Flow<Boolean>
|
||||
|
||||
suspend fun setRenderTypingNotifications(enabled: Boolean)
|
||||
fun isRenderTypingNotificationsEnabled(): Flow<Boolean>
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
|
||||
class DefaultSessionPreferencesStore(
|
||||
|
|
@ -43,13 +45,41 @@ class DefaultSessionPreferencesStore(
|
|||
return context.preferencesDataStoreFile("session_${hashedUserId}_preferences")
|
||||
}
|
||||
}
|
||||
|
||||
private val sharePresenceKey = booleanPreferencesKey("sharePresence")
|
||||
private val sendPublicReadReceiptsKey = booleanPreferencesKey("sendPublicReadReceipts")
|
||||
private val renderReadReceiptsKey = booleanPreferencesKey("renderReadReceipts")
|
||||
private val sendTypingNotificationsKey = booleanPreferencesKey("sendTypingNotifications")
|
||||
private val renderTypingNotificationsKey = booleanPreferencesKey("renderTypingNotifications")
|
||||
|
||||
private val dataStoreFile = storeFile(context, sessionId)
|
||||
private val store = PreferenceDataStoreFactory.create(scope = sessionCoroutineScope) { dataStoreFile }
|
||||
|
||||
override suspend fun setSharePresence(enabled: Boolean) {
|
||||
update(sharePresenceKey, enabled)
|
||||
// Also update all the other settings
|
||||
setSendPublicReadReceipts(enabled)
|
||||
setRenderReadReceipts(enabled)
|
||||
setSendTypingNotifications(enabled)
|
||||
setRenderTypingNotifications(enabled)
|
||||
}
|
||||
|
||||
override fun isSharePresenceEnabled(): Flow<Boolean> {
|
||||
// Migration, if sendPublicReadReceiptsKey was false, consider that sharing presence is false.
|
||||
return get(sharePresenceKey) { runBlocking { isSendPublicReadReceiptsEnabled().first() } }
|
||||
}
|
||||
|
||||
override suspend fun setSendPublicReadReceipts(enabled: Boolean) = update(sendPublicReadReceiptsKey, enabled)
|
||||
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> = get(sendPublicReadReceiptsKey, true)
|
||||
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> = get(sendPublicReadReceiptsKey) { true }
|
||||
|
||||
override suspend fun setRenderReadReceipts(enabled: Boolean) = update(renderReadReceiptsKey, enabled)
|
||||
override fun isRenderReadReceiptsEnabled(): Flow<Boolean> = get(renderReadReceiptsKey) { true }
|
||||
|
||||
override suspend fun setSendTypingNotifications(enabled: Boolean) = update(sendTypingNotificationsKey, enabled)
|
||||
override fun isSendTypingNotificationsEnabled(): Flow<Boolean> = get(sendTypingNotificationsKey) { true }
|
||||
|
||||
override suspend fun setRenderTypingNotifications(enabled: Boolean) = update(renderTypingNotificationsKey, enabled)
|
||||
override fun isRenderTypingNotificationsEnabled(): Flow<Boolean> = get(renderTypingNotificationsKey) { true }
|
||||
|
||||
override suspend fun clear() {
|
||||
dataStoreFile.safeDelete()
|
||||
|
|
@ -59,7 +89,7 @@ class DefaultSessionPreferencesStore(
|
|||
store.edit { prefs -> prefs[key] = value }
|
||||
}
|
||||
|
||||
private fun <T> get(key: Preferences.Key<T>, default: T): Flow<T> {
|
||||
return store.data.map { prefs -> prefs[key] ?: default }
|
||||
private fun <T> get(key: Preferences.Key<T>, default: () -> T): Flow<T> {
|
||||
return store.data.map { prefs -> prefs[key] ?: default() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import io.element.android.libraries.di.AppScope
|
|||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
|
@ -28,9 +30,20 @@ import javax.inject.Inject
|
|||
@SingleIn(AppScope::class)
|
||||
class DefaultSessionPreferencesStoreFactory @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
sessionObserver: SessionObserver,
|
||||
) {
|
||||
private val cache = ConcurrentHashMap<SessionId, DefaultSessionPreferencesStore>()
|
||||
|
||||
init {
|
||||
sessionObserver.addListener(object : SessionListener {
|
||||
override suspend fun onSessionCreated(userId: String) = Unit
|
||||
override suspend fun onSessionDeleted(userId: String) {
|
||||
val sessionPreferences = cache.remove(SessionId(userId))
|
||||
sessionPreferences?.clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun get(sessionId: SessionId, sessionCoroutineScope: CoroutineScope): DefaultSessionPreferencesStore = cache.getOrPut(sessionId) {
|
||||
DefaultSessionPreferencesStore(context, sessionId, sessionCoroutineScope)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,19 +21,50 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class InMemorySessionPreferencesStore(
|
||||
isSharePresenceEnabled: Boolean = true,
|
||||
isSendPublicReadReceiptsEnabled: Boolean = true,
|
||||
isRenderReadReceiptsEnabled: Boolean = true,
|
||||
isSendTypingNotificationsEnabled: Boolean = true,
|
||||
isRenderTypingNotificationsEnabled: Boolean = true,
|
||||
) : SessionPreferencesStore {
|
||||
private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled)
|
||||
private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled)
|
||||
private val isRenderReadReceiptsEnabled = MutableStateFlow(isRenderReadReceiptsEnabled)
|
||||
private val isSendTypingNotificationsEnabled = MutableStateFlow(isSendTypingNotificationsEnabled)
|
||||
private val isRenderTypingNotificationsEnabled = MutableStateFlow(isRenderTypingNotificationsEnabled)
|
||||
var clearCallCount = 0
|
||||
private set
|
||||
|
||||
override suspend fun setSharePresence(enabled: Boolean) {
|
||||
isSharePresenceEnabled.tryEmit(enabled)
|
||||
}
|
||||
|
||||
override fun isSharePresenceEnabled(): Flow<Boolean> = isSharePresenceEnabled
|
||||
|
||||
override suspend fun setSendPublicReadReceipts(enabled: Boolean) {
|
||||
isSendPublicReadReceiptsEnabled.tryEmit(enabled)
|
||||
}
|
||||
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> {
|
||||
return isSendPublicReadReceiptsEnabled
|
||||
|
||||
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> = isSendPublicReadReceiptsEnabled
|
||||
|
||||
override suspend fun setRenderReadReceipts(enabled: Boolean) {
|
||||
isRenderReadReceiptsEnabled.tryEmit(enabled)
|
||||
}
|
||||
|
||||
override fun isRenderReadReceiptsEnabled(): Flow<Boolean> = isRenderReadReceiptsEnabled
|
||||
|
||||
override suspend fun setSendTypingNotifications(enabled: Boolean) {
|
||||
isSendTypingNotificationsEnabled.tryEmit(enabled)
|
||||
}
|
||||
|
||||
override fun isSendTypingNotificationsEnabled(): Flow<Boolean> = isSendTypingNotificationsEnabled
|
||||
|
||||
override suspend fun setRenderTypingNotifications(enabled: Boolean) {
|
||||
isRenderTypingNotificationsEnabled.tryEmit(enabled)
|
||||
}
|
||||
|
||||
override fun isRenderTypingNotificationsEnabled(): Flow<Boolean> = isRenderTypingNotificationsEnabled
|
||||
|
||||
override suspend fun clear() {
|
||||
clearCallCount++
|
||||
isSendPublicReadReceiptsEnabled.tryEmit(true)
|
||||
|
|
|
|||
58
libraries/push/impl/src/main/res/values-be/translations.xml
Normal file
58
libraries/push/impl/src/main/res/values-be/translations.xml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="notification_channel_call">"Выклік"</string>
|
||||
<string name="notification_channel_listening_for_events">"Праслухоўванне падзей"</string>
|
||||
<string name="notification_channel_noisy">"Шумныя апавяшчэнні"</string>
|
||||
<string name="notification_channel_silent">"Ціхія апавяшчэнні"</string>
|
||||
<string name="notification_inline_reply_failed">"** Не атрымалася даслаць - калі ласка, адкрыйце пакой"</string>
|
||||
<string name="notification_invitation_action_reject">"Адхіліць"</string>
|
||||
<string name="notification_invite_body">"Запрасіў вас у чат"</string>
|
||||
<string name="notification_mentioned_you_body">"Згадаў вас: %1$s"</string>
|
||||
<string name="notification_new_messages">"Новыя паведамленні"</string>
|
||||
<string name="notification_reaction_body">"Адрэагаваў на %1$s"</string>
|
||||
<string name="notification_room_invite_body">"Запрасіў вас далучыцца да пакоя"</string>
|
||||
<string name="notification_sender_me">"Я"</string>
|
||||
<string name="notification_test_push_notification_content">"Вы праглядаеце апавяшчэнне! Націсніце мяне!"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
<string name="notification_ticker_text_group">"%1$s: %2$s %3$s"</string>
|
||||
<string name="notification_unread_notified_messages_and_invitation">"%1$s і %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room">"%1$s у %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room_and_invitation">"%1$s у %2$s і %3$s"</string>
|
||||
<plurals name="notification_compat_summary_line_for_room">
|
||||
<item quantity="one">"%1$s: %2$d паведамленне"</item>
|
||||
<item quantity="few">"%1$s: %2$d паведамленняў"</item>
|
||||
<item quantity="many">"%1$s: %2$d паведамленняў"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_compat_summary_title">
|
||||
<item quantity="one">"%d апавяшчэнне"</item>
|
||||
<item quantity="few">"%d апавяшчэнняў"</item>
|
||||
<item quantity="many">"%d апавяшчэнняў"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_invitations">
|
||||
<item quantity="one">"%d запрашэнне"</item>
|
||||
<item quantity="few">"%d запрашэнняў"</item>
|
||||
<item quantity="many">"%d запрашэнняў"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_new_messages_for_room">
|
||||
<item quantity="one">"%d новае паведамленне"</item>
|
||||
<item quantity="few">"%d новых паведамленняў"</item>
|
||||
<item quantity="many">"%d новых паведамленняў"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_unread_notified_messages">
|
||||
<item quantity="one">"%d непрачытанае апавяшчэнне"</item>
|
||||
<item quantity="few">"%d непрачытаных апавяшчэнняў"</item>
|
||||
<item quantity="many">"%d непрачытаных апавяшчэнняў"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_unread_notified_messages_in_room_rooms">
|
||||
<item quantity="one">"%d пакой"</item>
|
||||
<item quantity="few">"%d пакояў"</item>
|
||||
<item quantity="many">"%d пакояў"</item>
|
||||
</plurals>
|
||||
<string name="push_choose_distributor_dialog_title_android">"Выберыце спосаб атрымання апавяшчэнняў"</string>
|
||||
<string name="push_distributor_background_sync_android">"Фонавая сінхранізацыя"</string>
|
||||
<string name="push_distributor_firebase_android">"Сэрвісы Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Службы Google Play не знойдзены. Апавяшчэнні могуць не працаваць належным чынам."</string>
|
||||
<string name="notification_fallback_content">"Апавяшчэнне"</string>
|
||||
<string name="notification_invitation_action_join">"Далучыцца"</string>
|
||||
<string name="notification_room_action_quick_reply">"Хуткі адказ"</string>
|
||||
</resources>
|
||||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Hlasitá oznámení"</string>
|
||||
<string name="notification_channel_silent">"Tichá oznámení"</string>
|
||||
<string name="notification_inline_reply_failed">"** Nepodařilo se odeslat - otevřete prosím místnost"</string>
|
||||
<string name="notification_invitation_action_join">"Vstoupit"</string>
|
||||
<string name="notification_invitation_action_reject">"Odmítnout"</string>
|
||||
<string name="notification_invite_body">"Vás pozval(a) do chatu"</string>
|
||||
<string name="notification_mentioned_you_body">"Zmínili vás: %1$s"</string>
|
||||
<string name="notification_new_messages">"Nové zprávy"</string>
|
||||
<string name="notification_reaction_body">"Reagoval(a) s %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Označit jako přečtené"</string>
|
||||
<string name="notification_room_invite_body">"Vás pozval(a) do místnosti"</string>
|
||||
<string name="notification_sender_me">"Já"</string>
|
||||
<string name="notification_test_push_notification_content">"Prohlížíte si oznámení! Klikněte na mě!"</string>
|
||||
|
|
@ -55,5 +53,7 @@
|
|||
<string name="push_distributor_firebase_android">"Služby Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Nebyly nalezeny žádné funkční služby Google Play. Oznámení nemusí fungovat správně."</string>
|
||||
<string name="notification_fallback_content">"Oznámení"</string>
|
||||
<string name="notification_invitation_action_join">"Vstoupit"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Označit jako přečtené"</string>
|
||||
<string name="notification_room_action_quick_reply">"Rychlá odpověď"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Laute Benachrichtigungen"</string>
|
||||
<string name="notification_channel_silent">"Stumme Benachrichtigungen"</string>
|
||||
<string name="notification_inline_reply_failed">"** Fehler beim Senden - 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">"Du wurdest zu einem Chat eingeladen"</string>
|
||||
<string name="notification_mentioned_you_body">"Hat Dich erwähnt: %1$s"</string>
|
||||
<string name="notification_new_messages">"Neue Nachrichten"</string>
|
||||
<string name="notification_reaction_body">"Reagiert mit %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Als gelesen markieren"</string>
|
||||
<string name="notification_room_invite_body">"Du wurdest eingeladen, den Raum zu betreten"</string>
|
||||
<string name="notification_sender_me">"Ich"</string>
|
||||
<string name="notification_test_push_notification_content">"Du siehst dir die Benachrichtigung an! Klicke hier!"</string>
|
||||
|
|
@ -49,5 +47,7 @@
|
|||
<string name="push_distributor_firebase_android">"Google-Dienste"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig."</string>
|
||||
<string name="notification_fallback_content">"Mitteilung"</string>
|
||||
<string name="notification_invitation_action_join">"Beitreten"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Als gelesen markieren"</string>
|
||||
<string name="notification_room_action_quick_reply">"Schnelle Antwort"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Notificaciones ruidosas"</string>
|
||||
<string name="notification_channel_silent">"Notificaciones silenciosas"</string>
|
||||
<string name="notification_inline_reply_failed">"** No se ha podido enviar - por favor, abre la sala"</string>
|
||||
<string name="notification_invitation_action_join">"Unirse"</string>
|
||||
<string name="notification_invitation_action_reject">"Rechazar"</string>
|
||||
<string name="notification_invite_body">"Te invitó a chatear"</string>
|
||||
<string name="notification_mentioned_you_body">"Te mencionó: %1$s"</string>
|
||||
<string name="notification_new_messages">"Mensajes nuevos"</string>
|
||||
<string name="notification_reaction_body">"Reaccionó con %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Marcar como leído"</string>
|
||||
<string name="notification_room_invite_body">"Te invitó a unirte a la sala"</string>
|
||||
<string name="notification_sender_me">"Yo"</string>
|
||||
<string name="notification_test_push_notification_content">"¡Estás viendo la notificación! ¡Haz clic en mí!"</string>
|
||||
|
|
@ -49,5 +47,6 @@
|
|||
<string name="push_distributor_firebase_android">"Servicios de Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"No se han encontrado Servicios de Google Play válidos. Es posible que las notificaciones no funcionen correctamente."</string>
|
||||
<string name="notification_fallback_content">"Notificación"</string>
|
||||
<string name="notification_invitation_action_join">"Unirse"</string>
|
||||
<string name="notification_room_action_quick_reply">"Respuesta rápida"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Notifications bruyantes"</string>
|
||||
<string name="notification_channel_silent">"Notifications silencieuses"</string>
|
||||
<string name="notification_inline_reply_failed">"** Échec de l’envoi - veuillez ouvrir le salon"</string>
|
||||
<string name="notification_invitation_action_join">"Rejoindre"</string>
|
||||
<string name="notification_invitation_action_reject">"Rejeter"</string>
|
||||
<string name="notification_invite_body">"Vous a invité(e) à discuter"</string>
|
||||
<string name="notification_mentioned_you_body">"Mentionné(e): %1$s"</string>
|
||||
<string name="notification_new_messages">"Nouveaux messages"</string>
|
||||
<string name="notification_reaction_body">"A réagi avec %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Marquer comme lu"</string>
|
||||
<string name="notification_room_invite_body">"Vous a invité(e) à rejoindre le salon"</string>
|
||||
<string name="notification_sender_me">"Moi"</string>
|
||||
<string name="notification_test_push_notification_content">"Vous êtes en train de voir la notification ! Cliquez-moi !"</string>
|
||||
|
|
@ -49,5 +47,7 @@
|
|||
<string name="push_distributor_firebase_android">"Services Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Aucun service Google Play valide n’a été trouvé. Les notifications peuvent ne pas fonctionner correctement."</string>
|
||||
<string name="notification_fallback_content">"Notification"</string>
|
||||
<string name="notification_invitation_action_join">"Rejoindre"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Marquer comme lu"</string>
|
||||
<string name="notification_room_action_quick_reply">"Réponse rapide"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,14 +5,12 @@
|
|||
<string name="notification_channel_noisy">"Zajos értesítések"</string>
|
||||
<string name="notification_channel_silent">"Csendes értesítések"</string>
|
||||
<string name="notification_inline_reply_failed">"** Nem sikerült elküldeni – kérlek nyisd meg a szobát"</string>
|
||||
<string name="notification_invitation_action_join">"Csatlakozás"</string>
|
||||
<string name="notification_invitation_action_reject">"Elutasítás"</string>
|
||||
<string name="notification_invite_body">"Meghívta, hogy csevegjen"</string>
|
||||
<string name="notification_mentioned_you_body">"Megemlítette Önt: %1$s"</string>
|
||||
<string name="notification_new_messages">"Új üzenetek"</string>
|
||||
<string name="notification_reaction_body">"Ezzel reagált: %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Megjelölés olvasottként"</string>
|
||||
<string name="notification_room_invite_body">"Meghívta, hogy csatlakozzon a szobához"</string>
|
||||
<string name="notification_room_invite_body">"Meghívott, hogy csatlakozz a szobához"</string>
|
||||
<string name="notification_sender_me">"Én"</string>
|
||||
<string name="notification_test_push_notification_content">"Az értesítést nézed! Kattints ide!"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
|
|
@ -49,5 +47,7 @@
|
|||
<string name="push_distributor_firebase_android">"Google szolgáltatások"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"A Google Play szolgáltatások nem találhatók. Előfordulhat, hogy az értesítések nem működnek megfelelően."</string>
|
||||
<string name="notification_fallback_content">"Értesítés"</string>
|
||||
<string name="notification_invitation_action_join">"Csatlakozás"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Megjelölés olvasottként"</string>
|
||||
<string name="notification_room_action_quick_reply">"Gyors válasz"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Pemberitahuan berisik"</string>
|
||||
<string name="notification_channel_silent">"Pemberitahuan diam"</string>
|
||||
<string name="notification_inline_reply_failed">"** Gagal mengirim — silakan buka ruangan"</string>
|
||||
<string name="notification_invitation_action_join">"Bergabung"</string>
|
||||
<string name="notification_invitation_action_reject">"Tolak"</string>
|
||||
<string name="notification_invite_body">"Mengundang Anda untuk mengobrol"</string>
|
||||
<string name="notification_mentioned_you_body">"Menyebutkan Anda: %1$s"</string>
|
||||
<string name="notification_new_messages">"Pesan Baru"</string>
|
||||
<string name="notification_reaction_body">"Menghapus dengan %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Tandai sebagai dibaca"</string>
|
||||
<string name="notification_room_invite_body">"Mengundang Anda untuk bergabung ke ruangan"</string>
|
||||
<string name="notification_sender_me">"Saya"</string>
|
||||
<string name="notification_test_push_notification_content">"Anda sedang melihat pemberitahuan ini! Klik saya!"</string>
|
||||
|
|
@ -43,5 +41,6 @@
|
|||
<string name="push_distributor_firebase_android">"Layanan Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Tidak ditemukan Layanan Google Play yang valid. Pemberitahuan mungkin tidak berfungsi dengan baik."</string>
|
||||
<string name="notification_fallback_content">"Notifikasi"</string>
|
||||
<string name="notification_invitation_action_join">"Gabung"</string>
|
||||
<string name="notification_room_action_quick_reply">"Balas cepat"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="notification_channel_call">"Chiamata"</string>
|
||||
<string name="notification_channel_listening_for_events">"Ascolto degli eventi"</string>
|
||||
<string name="notification_channel_noisy">"Notifiche con suono"</string>
|
||||
<string name="notification_channel_silent">"Notifiche silenziose"</string>
|
||||
<string name="notification_inline_reply_failed">"** Invio fallito - si prega di aprire la stanza"</string>
|
||||
<string name="notification_invitation_action_join">"Entra"</string>
|
||||
<string name="notification_invitation_action_reject">"Rifiuta"</string>
|
||||
<string name="notification_invite_body">"Ti ha invitato a chattare"</string>
|
||||
<string name="notification_mentioned_you_body">"Ti ha menzionato: %1$s"</string>
|
||||
<string name="notification_new_messages">"Nuovi messaggi"</string>
|
||||
<string name="notification_reaction_body">"Ha reagito con %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Segna come letto"</string>
|
||||
<string name="notification_room_invite_body">"Ti ha invitato ad entrare nella stanza"</string>
|
||||
<string name="notification_sender_me">"Io"</string>
|
||||
<string name="notification_test_push_notification_content">"Stai visualizzando la notifica! Cliccami!"</string>
|
||||
|
|
@ -47,5 +47,7 @@
|
|||
<string name="push_distributor_firebase_android">"Servizi Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Google Play Services non trovato. Le notifiche non funzioneranno bene."</string>
|
||||
<string name="notification_fallback_content">"Notifica"</string>
|
||||
<string name="notification_invitation_action_join">"Entra"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Segna come letto"</string>
|
||||
<string name="notification_room_action_quick_reply">"Risposta rapida"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@
|
|||
<string name="notification_channel_noisy">"Notificări zgomotoase"</string>
|
||||
<string name="notification_channel_silent">"Notificări silențioase"</string>
|
||||
<string name="notification_inline_reply_failed">"** Trimiterea eșuată - vă rugăm să deschideți camera"</string>
|
||||
<string name="notification_invitation_action_join">"Alăturați-vă"</string>
|
||||
<string name="notification_invitation_action_reject">"Respingeți"</string>
|
||||
<string name="notification_invite_body">"V-a invitat la o discuție"</string>
|
||||
<string name="notification_new_messages">"Mesaje noi"</string>
|
||||
<string name="notification_reaction_body">"A reacționat cu %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Marcați ca citit"</string>
|
||||
<string name="notification_room_invite_body">"V-a invitat să vă alăturați camerei"</string>
|
||||
<string name="notification_sender_me">"Eu"</string>
|
||||
<string name="notification_test_push_notification_content">"Vizualizați o notificare! Faceți clic pe mine!"</string>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Шумные уведомления"</string>
|
||||
<string name="notification_channel_silent">"Бесшумные уведомления"</string>
|
||||
<string name="notification_inline_reply_failed">"** Не удалось отправить - пожалуйста, откройте комнату"</string>
|
||||
<string name="notification_invitation_action_join">"Присоединиться"</string>
|
||||
<string name="notification_invitation_action_reject">"Отклонить"</string>
|
||||
<string name="notification_invite_body">"Пригласил вас в чат"</string>
|
||||
<string name="notification_mentioned_you_body">"Упомянул вас: %1$s"</string>
|
||||
<string name="notification_new_messages">"Новые сообщения"</string>
|
||||
<string name="notification_reaction_body">"Отреагировал на %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Отметить как прочитанное"</string>
|
||||
<string name="notification_room_invite_body">"Пригласил вас в комнату"</string>
|
||||
<string name="notification_sender_me">"Я"</string>
|
||||
<string name="notification_test_push_notification_content">"Вы просматриваете уведомление! Нажмите на меня!"</string>
|
||||
|
|
@ -55,5 +53,7 @@
|
|||
<string name="push_distributor_firebase_android">"Сервисы Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Не найдены действующие службы Google Play. Уведомления могут работать некорректно."</string>
|
||||
<string name="notification_fallback_content">"Уведомление"</string>
|
||||
<string name="notification_invitation_action_join">"Присоединиться"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Пометить как прочитанное"</string>
|
||||
<string name="notification_room_action_quick_reply">"Быстрый ответ"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Hlasité oznámenia"</string>
|
||||
<string name="notification_channel_silent">"Tiché oznámenia"</string>
|
||||
<string name="notification_inline_reply_failed">"** Nepodarilo sa odoslať - prosím otvorte miestnosť"</string>
|
||||
<string name="notification_invitation_action_join">"Pripojiť sa"</string>
|
||||
<string name="notification_invitation_action_reject">"Zamietnuť"</string>
|
||||
<string name="notification_invite_body">"Vás pozval/a na konverzáciu"</string>
|
||||
<string name="notification_mentioned_you_body">"Spomenul/a vás: %1$s"</string>
|
||||
<string name="notification_new_messages">"Nové správy"</string>
|
||||
<string name="notification_reaction_body">"Reagoval/a s %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Označiť ako prečítané"</string>
|
||||
<string name="notification_room_invite_body">"Vás pozval do miestnosti"</string>
|
||||
<string name="notification_sender_me">"Ja"</string>
|
||||
<string name="notification_test_push_notification_content">"Prezeráte si oznámenie! Kliknite na mňa!"</string>
|
||||
|
|
@ -55,5 +53,7 @@
|
|||
<string name="push_distributor_firebase_android">"Služby Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Nenašli sa žiadne platné služby Google Play. Oznámenia nemusia fungovať správne."</string>
|
||||
<string name="notification_fallback_content">"Oznámenie"</string>
|
||||
<string name="notification_invitation_action_join">"Pripojiť sa"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Označiť ako prečítané"</string>
|
||||
<string name="notification_room_action_quick_reply">"Rýchla odpoveď"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@
|
|||
<string name="notification_channel_call">"通話"</string>
|
||||
<string name="notification_channel_silent">"無聲通知"</string>
|
||||
<string name="notification_inline_reply_failed">"** 無法傳送,請開啟聊天室"</string>
|
||||
<string name="notification_invitation_action_join">"加入"</string>
|
||||
<string name="notification_invitation_action_reject">"拒絕"</string>
|
||||
<string name="notification_invite_body">"邀請您聊天"</string>
|
||||
<string name="notification_mentioned_you_body">"提及您:%1$s"</string>
|
||||
<string name="notification_new_messages">"新訊息"</string>
|
||||
<string name="notification_reaction_body">"回應 %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"標示為已讀"</string>
|
||||
<string name="notification_room_invite_body">"邀請您加入聊天室"</string>
|
||||
<string name="notification_sender_me">"我"</string>
|
||||
<string name="notification_test_push_notification_content">"您正在查看通知!點我!"</string>
|
||||
|
|
@ -34,5 +32,6 @@
|
|||
<string name="push_distributor_background_sync_android">"背景同步"</string>
|
||||
<string name="push_distributor_firebase_android">"Google 服務"</string>
|
||||
<string name="notification_fallback_content">"通知"</string>
|
||||
<string name="notification_invitation_action_join">"加入"</string>
|
||||
<string name="notification_room_action_quick_reply">"快速回覆"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Noisy notifications"</string>
|
||||
<string name="notification_channel_silent">"Silent notifications"</string>
|
||||
<string name="notification_inline_reply_failed">"** Failed to send - please open room"</string>
|
||||
<string name="notification_invitation_action_join">"Join"</string>
|
||||
<string name="notification_invitation_action_reject">"Reject"</string>
|
||||
<string name="notification_invite_body">"Invited you to chat"</string>
|
||||
<string name="notification_mentioned_you_body">"Mentioned you: %1$s"</string>
|
||||
<string name="notification_new_messages">"New Messages"</string>
|
||||
<string name="notification_reaction_body">"Reacted with %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Mark as read"</string>
|
||||
<string name="notification_room_invite_body">"Invited you to join the room"</string>
|
||||
<string name="notification_sender_me">"Me"</string>
|
||||
<string name="notification_test_push_notification_content">"You are viewing the notification! Click me!"</string>
|
||||
|
|
@ -49,5 +47,7 @@
|
|||
<string name="push_distributor_firebase_android">"Google Services"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"No valid Google Play Services found. Notifications may not work properly."</string>
|
||||
<string name="notification_fallback_content">"Notification"</string>
|
||||
<string name="notification_invitation_action_join">"Join"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Mark as read"</string>
|
||||
<string name="notification_room_action_quick_reply">"Quick reply"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
|
|||
*/
|
||||
override fun onUnregistered(context: Context, instance: String) {
|
||||
Timber.tag(loggerTag.value).d("Unifiedpush: Unregistered")
|
||||
TODO()
|
||||
/*
|
||||
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME
|
||||
pushDataStore.setFdroidSyncBackgroundMode(mode)
|
||||
|
|
|
|||
|
|
@ -209,10 +209,9 @@ private fun RoomSummaryView(
|
|||
summary: RoomSummaryDetails,
|
||||
isSelected: Boolean,
|
||||
onSelection: (RoomSummaryDetails) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.clickable { onSelection(summary) }
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 4.dp)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ object TestTags {
|
|||
*/
|
||||
val richTextEditor = TestTag("rich_text_editor")
|
||||
|
||||
/**
|
||||
* Message bubble.
|
||||
*/
|
||||
val messageBubble = TestTag("message_bubble")
|
||||
|
||||
/**
|
||||
* Dialogs.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -405,14 +405,13 @@ private fun TextInput(
|
|||
onError: (Throwable) -> Unit,
|
||||
onTyping: (Boolean) -> Unit,
|
||||
onRichContentSelected: ((Uri) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val bgColor = ElementTheme.colors.bgSubtleSecondary
|
||||
val borderColor = ElementTheme.colors.borderDisabled
|
||||
val roundedCorners = textInputRoundedCornerShape(composerMode = composerMode)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.clip(roundedCorners)
|
||||
.border(0.5.dp, borderColor, roundedCorners)
|
||||
.background(color = bgColor)
|
||||
|
|
@ -464,15 +463,14 @@ private fun TextInput(
|
|||
private fun ComposerModeView(
|
||||
composerMode: MessageComposerMode,
|
||||
onResetComposerMode: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (composerMode) {
|
||||
is MessageComposerMode.Edit -> {
|
||||
EditingModeView(onResetComposerMode = onResetComposerMode, modifier = modifier)
|
||||
EditingModeView(onResetComposerMode = onResetComposerMode)
|
||||
}
|
||||
is MessageComposerMode.Reply -> {
|
||||
ReplyToModeView(
|
||||
modifier = modifier.padding(8.dp),
|
||||
modifier = Modifier.padding(8.dp),
|
||||
senderName = composerMode.senderName,
|
||||
text = composerMode.defaultContent,
|
||||
attachmentThumbnailInfo = composerMode.attachmentThumbnailInfo,
|
||||
|
|
@ -486,12 +484,11 @@ private fun ComposerModeView(
|
|||
@Composable
|
||||
private fun EditingModeView(
|
||||
onResetComposerMode: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp)
|
||||
) {
|
||||
|
|
@ -881,11 +878,8 @@ internal fun TextComposerVoicePreview() = ElementPreview {
|
|||
@Composable
|
||||
private fun PreviewColumn(
|
||||
items: ImmutableList<@Composable () -> Unit>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
) {
|
||||
Column {
|
||||
items.forEach { item ->
|
||||
Box(
|
||||
modifier = Modifier.height(IntrinsicSize.Min)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import io.element.android.wysiwyg.view.models.LinkAction
|
|||
import kotlinx.coroutines.launch
|
||||
import uniffi.wysiwyg_composer.ActionState
|
||||
import uniffi.wysiwyg_composer.ComposerAction
|
||||
|
||||
@Composable
|
||||
internal fun TextFormatting(
|
||||
state: RichTextEditorState,
|
||||
|
|
|
|||
|
|
@ -124,11 +124,10 @@ private fun PlayerButton(
|
|||
type: PlayerButtonType,
|
||||
enabled: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.background(color = ElementTheme.colors.bgCanvasDefault, shape = CircleShape)
|
||||
.size(30.dp),
|
||||
enabled = enabled,
|
||||
|
|
|
|||
|
|
@ -88,9 +88,7 @@ internal fun VoiceMessageRecording(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun RedRecordingDot(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
private fun RedRecordingDot() {
|
||||
val infiniteTransition = rememberInfiniteTransition("RedRecordingDot")
|
||||
val alpha by infiniteTransition.animateFloat(
|
||||
initialValue = 1f,
|
||||
|
|
@ -102,7 +100,7 @@ private fun RedRecordingDot(
|
|||
label = "RedRecordingDotAlpha",
|
||||
)
|
||||
Box(
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.size(8.dp)
|
||||
.alpha(alpha)
|
||||
.background(color = ElementTheme.colors.textCriticalPrimary, shape = CircleShape)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="rich_text_editor_bullet_list">"Пераключыць маркіраваны спіс"</string>
|
||||
<string name="rich_text_editor_close_formatting_options">"Закрыць параметры фарматавання"</string>
|
||||
<string name="rich_text_editor_code_block">"Пераключыць блок кода"</string>
|
||||
<string name="rich_text_editor_composer_placeholder">"Паведамленне…"</string>
|
||||
<string name="rich_text_editor_create_link">"Стварыце спасылку"</string>
|
||||
<string name="rich_text_editor_edit_link">"Рэдагаваць спасылку"</string>
|
||||
<string name="rich_text_editor_format_bold">"Ужыць тоўсты шрыфт"</string>
|
||||
<string name="rich_text_editor_format_italic">"Ужыць курсіўны фармат"</string>
|
||||
<string name="rich_text_editor_format_strikethrough">"Ужыць фармат закрэслівання"</string>
|
||||
<string name="rich_text_editor_format_underline">"Ужыць фармат падкрэслення"</string>
|
||||
<string name="rich_text_editor_full_screen_toggle">"Пераключэнне поўнаэкраннага рэжыму"</string>
|
||||
<string name="rich_text_editor_indent">"Водступ"</string>
|
||||
<string name="rich_text_editor_inline_code">"Ужыць убудаваны фармат кода"</string>
|
||||
<string name="rich_text_editor_link">"Усталяваць спасылку"</string>
|
||||
<string name="rich_text_editor_numbered_list">"Пераключыць нумараваны спіс"</string>
|
||||
<string name="rich_text_editor_open_compose_options">"Адкрыйце параметры кампазіцыі"</string>
|
||||
<string name="rich_text_editor_quote">"Пераключыць цытату"</string>
|
||||
<string name="rich_text_editor_remove_link">"Выдаліць спасылку"</string>
|
||||
<string name="rich_text_editor_unindent">"Без водступу"</string>
|
||||
<string name="rich_text_editor_url_placeholder">"Спасылка"</string>
|
||||
<string name="rich_text_editor_a11y_add_attachment">"Дадаць далучэнне"</string>
|
||||
<string name="screen_room_voice_message_tooltip">"Утрымлівайце для запісу"</string>
|
||||
</resources>
|
||||
249
libraries/ui-strings/src/main/res/values-be/translations.xml
Normal file
249
libraries/ui-strings/src/main/res/values-be/translations.xml
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_delete">"Выдаліць"</string>
|
||||
<string name="a11y_hide_password">"Схаваць пароль"</string>
|
||||
<string name="a11y_jump_to_bottom">"Перайсці ўніз"</string>
|
||||
<string name="a11y_notifications_mentions_only">"Толькі згадкі"</string>
|
||||
<string name="a11y_notifications_muted">"Гук адключаны"</string>
|
||||
<string name="a11y_page_n">"Старонка %1$d"</string>
|
||||
<string name="a11y_pause">"Паўза"</string>
|
||||
<string name="a11y_pin_field">"Поле PIN-кода"</string>
|
||||
<string name="a11y_play">"Прайграць"</string>
|
||||
<string name="a11y_poll">"Апытанне"</string>
|
||||
<string name="a11y_poll_end">"Апытанне скончана"</string>
|
||||
<string name="a11y_react_with">"Рэагаваць з %1$s"</string>
|
||||
<string name="a11y_react_with_other_emojis">"Рэагаваць з іншымі эмодзі"</string>
|
||||
<string name="a11y_read_receipts_multiple">"Прачытана %1$s і %2$s"</string>
|
||||
<string name="a11y_read_receipts_single">"Прачытана %1$s"</string>
|
||||
<string name="a11y_read_receipts_tap_to_show_all">"Націсніце, каб паказаць усе"</string>
|
||||
<string name="a11y_remove_reaction_with">"Выдаліць рэакцыю з %1$s"</string>
|
||||
<string name="a11y_send_files">"Адправіць файлы"</string>
|
||||
<string name="a11y_show_password">"Паказаць пароль"</string>
|
||||
<string name="a11y_start_call">"Пазваніць"</string>
|
||||
<string name="a11y_user_menu">"Меню карыстальніка"</string>
|
||||
<string name="a11y_voice_message_record">"Запісаць галасавое паведамленне."</string>
|
||||
<string name="a11y_voice_message_stop_recording">"Спыніць запіс"</string>
|
||||
<string name="action_accept">"Прыняць"</string>
|
||||
<string name="action_add_to_timeline">"Дадаць на часовую шкалу"</string>
|
||||
<string name="action_back">"Назад"</string>
|
||||
<string name="action_cancel">"Скасаваць"</string>
|
||||
<string name="action_choose_photo">"Выбраць фота"</string>
|
||||
<string name="action_clear">"Ачысціць"</string>
|
||||
<string name="action_close">"Закрыць"</string>
|
||||
<string name="action_complete_verification">"Поўная праверка"</string>
|
||||
<string name="action_confirm">"Пацвердзіць"</string>
|
||||
<string name="action_continue">"Працягнуць"</string>
|
||||
<string name="action_copy">"Капіраваць"</string>
|
||||
<string name="action_copy_link">"Скапіраваць спасылку"</string>
|
||||
<string name="action_copy_link_to_message">"Скапіраваць спасылку на паведамленне"</string>
|
||||
<string name="action_create">"Стварыць"</string>
|
||||
<string name="action_create_a_room">"Стварыце пакой"</string>
|
||||
<string name="action_decline">"Адхіліць"</string>
|
||||
<string name="action_delete_poll">"Выдаліць апытанне"</string>
|
||||
<string name="action_disable">"Адключыць"</string>
|
||||
<string name="action_done">"Гатова"</string>
|
||||
<string name="action_edit">"Рэдагаваць"</string>
|
||||
<string name="action_edit_poll">"Рэдагаваць апытанне"</string>
|
||||
<string name="action_enable">"Уключыць"</string>
|
||||
<string name="action_end_poll">"Скончыць апытанне"</string>
|
||||
<string name="action_enter_pin">"Увядзіце PIN-код"</string>
|
||||
<string name="action_forgot_password">"Забылі пароль?"</string>
|
||||
<string name="action_forward">"Пераслаць"</string>
|
||||
<string name="action_invite">"Запрасіць"</string>
|
||||
<string name="action_invite_friends">"Запрасіць карыстальникаў"</string>
|
||||
<string name="action_invite_friends_to_app">"Запрасіць карыстальнікаў у %1$s"</string>
|
||||
<string name="action_invite_people_to_app">"Запрасіць карыстальнікаў у %1$s"</string>
|
||||
<string name="action_invites_list">"Запрашэнні"</string>
|
||||
<string name="action_join">"Далучыцца"</string>
|
||||
<string name="action_learn_more">"Падрабязней"</string>
|
||||
<string name="action_leave">"Пакінуць"</string>
|
||||
<string name="action_leave_conversation">"Пакінуць размову"</string>
|
||||
<string name="action_leave_room">"Пакінуць пакой"</string>
|
||||
<string name="action_manage_account">"Кіраванне ўліковым запісам"</string>
|
||||
<string name="action_manage_devices">"Кіраванне прыладамі"</string>
|
||||
<string name="action_next">"Далей"</string>
|
||||
<string name="action_no">"Не"</string>
|
||||
<string name="action_not_now">"Не зараз"</string>
|
||||
<string name="action_ok">"Добра"</string>
|
||||
<string name="action_open_settings">"Налады"</string>
|
||||
<string name="action_open_with">"Адкрыць з дапамогай"</string>
|
||||
<string name="action_quick_reply">"Хуткі адказ"</string>
|
||||
<string name="action_quote">"Цытата"</string>
|
||||
<string name="action_react">"Рэакцыя"</string>
|
||||
<string name="action_remove">"Выдаліць"</string>
|
||||
<string name="action_reply">"Адказаць"</string>
|
||||
<string name="action_reply_in_thread">"Адказаць у гутаркі"</string>
|
||||
<string name="action_report_bug">"Паведаміць пра памылку"</string>
|
||||
<string name="action_report_content">"Паскардзіцца на змест"</string>
|
||||
<string name="action_retry">"Паўтарыць"</string>
|
||||
<string name="action_retry_decryption">"Паўтарыце расшыфроўку"</string>
|
||||
<string name="action_save">"Захаваць"</string>
|
||||
<string name="action_search">"Пошук"</string>
|
||||
<string name="action_send">"Адправіць"</string>
|
||||
<string name="action_send_message">"Адправіць паведамленне"</string>
|
||||
<string name="action_share">"Падзяліцца"</string>
|
||||
<string name="action_share_link">"Абагуліць спасылку"</string>
|
||||
<string name="action_sign_in_again">"Увайдзіце яшчэ раз"</string>
|
||||
<string name="action_signout">"Выйсці"</string>
|
||||
<string name="action_signout_anyway">"Усё роўна выйсці"</string>
|
||||
<string name="action_skip">"Прапусціць"</string>
|
||||
<string name="action_start">"Пачаць"</string>
|
||||
<string name="action_start_chat">"Пачаць чат"</string>
|
||||
<string name="action_start_verification">"Пачаць праверку"</string>
|
||||
<string name="action_static_map_load">"Націсніце, каб загрузіць карту"</string>
|
||||
<string name="action_take_photo">"Зрабіць фота"</string>
|
||||
<string name="action_tap_for_options">"Дакраніцеся, каб убачыць параметры"</string>
|
||||
<string name="action_try_again">"Паўтарыць спробу"</string>
|
||||
<string name="action_view_source">"Паказаць крыніцу"</string>
|
||||
<string name="action_yes">"Так"</string>
|
||||
<string name="action_load_more">"Загрузіць больш"</string>
|
||||
<string name="common_about">"Аб праграме"</string>
|
||||
<string name="common_acceptable_use_policy">"Палітыка дапушчальнага выкарыстання"</string>
|
||||
<string name="common_advanced_settings">"Пашыраныя налады"</string>
|
||||
<string name="common_analytics">"Аналітыка"</string>
|
||||
<string name="common_appearance">"Знешні выгляд"</string>
|
||||
<string name="common_audio">"Аўдыё"</string>
|
||||
<string name="common_bubbles">"Бурбалкі"</string>
|
||||
<string name="common_chat_backup">"Рэзервовае капіраванне чата"</string>
|
||||
<string name="common_copyright">"Аўтарскае права"</string>
|
||||
<string name="common_creating_room">"Стварэнне пакоя…"</string>
|
||||
<string name="common_current_user_left_room">"Выйшаў з пакоя"</string>
|
||||
<string name="common_dark">"Цёмная"</string>
|
||||
<string name="common_decryption_error">"Памылка расшыфроўкі"</string>
|
||||
<string name="common_developer_options">"Параметры распрацоўшчыка"</string>
|
||||
<string name="common_direct_chat">"Прамы чат"</string>
|
||||
<string name="common_edited_suffix">"(Адрэдагавана)"</string>
|
||||
<string name="common_editing">"Рэдагаванне"</string>
|
||||
<string name="common_emote">"* %1$s %2$s"</string>
|
||||
<string name="common_encryption_enabled">"Шыфраванне ўключана"</string>
|
||||
<string name="common_enter_your_pin">"Увядзіце свой PIN-код"</string>
|
||||
<string name="common_error">"Памылка"</string>
|
||||
<string name="common_everyone">"Усе"</string>
|
||||
<string name="common_file">"Файл"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Файл захаваны ў папку Спампоўкі"</string>
|
||||
<string name="common_forward_message">"Перасылка паведамлення"</string>
|
||||
<string name="common_gif">"GIF"</string>
|
||||
<string name="common_image">"Выява"</string>
|
||||
<string name="common_in_reply_to">"У адказ на %1$s"</string>
|
||||
<string name="common_install_apk_android">"Усталяваць APK"</string>
|
||||
<string name="common_invite_unknown_profile">"Гэты Matrix ID не знойдзены, таму запрашэнне можа быць не атрымана."</string>
|
||||
<string name="common_leaving_room">"Пакінуць пакой"</string>
|
||||
<string name="common_light">"Светлая"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Спасылка скапіравана ў буфер абмену"</string>
|
||||
<string name="common_loading">"Загрузка…"</string>
|
||||
<string name="common_message">"Паведамленне"</string>
|
||||
<string name="common_message_actions">"Дзеянні з паведамленням"</string>
|
||||
<string name="common_message_removed">"Паведамленне выдалена"</string>
|
||||
<string name="common_modern">"Сучасны"</string>
|
||||
<string name="common_mute">"Адключыць гук"</string>
|
||||
<string name="common_no_results">"Вынікаў няма"</string>
|
||||
<string name="common_offline">"Па-за сеткай"</string>
|
||||
<string name="common_password">"Пароль"</string>
|
||||
<string name="common_people">"Людзі"</string>
|
||||
<string name="common_permalink">"Пастаянная спасылка"</string>
|
||||
<string name="common_permission">"Дазвол"</string>
|
||||
<string name="common_poll_total_votes">"Усяго галасоў: %1$s"</string>
|
||||
<string name="common_poll_undisclosed_text">"Вынікі будуць паказаны пасля завяршэння апытання"</string>
|
||||
<string name="common_privacy_policy">"Палітыка прыватнасці"</string>
|
||||
<string name="common_reaction">"Рэакцыя"</string>
|
||||
<string name="common_reactions">"Рэакцыі"</string>
|
||||
<string name="common_recovery_key">"Ключ аднаўлення"</string>
|
||||
<string name="common_refreshing">"Абнаўленне…"</string>
|
||||
<string name="common_replying_to">"Адказвае на %1$s"</string>
|
||||
<string name="common_report_a_bug">"Паведаміць пра памылку"</string>
|
||||
<string name="common_report_a_problem">"Паведаміць аб праблеме"</string>
|
||||
<string name="common_report_submitted">"Скарга прынята"</string>
|
||||
<string name="common_rich_text_editor">"Рэдактар фарматаванага тэксту"</string>
|
||||
<string name="common_room">"Пакой"</string>
|
||||
<string name="common_room_name">"Назва пакоя"</string>
|
||||
<string name="common_room_name_placeholder">"напрыклад, назва вашага праекта"</string>
|
||||
<string name="common_screen_lock">"Блакіроўка экрана"</string>
|
||||
<string name="common_search_for_someone">"Шукаць карыстальніка"</string>
|
||||
<string name="common_search_results">"Вынікі пошуку"</string>
|
||||
<string name="common_security">"Бяспека"</string>
|
||||
<string name="common_seen_by">"Прагледжана"</string>
|
||||
<string name="common_sending">"Адпраўка…"</string>
|
||||
<string name="common_sending_failed">"Памылка адпраўкі"</string>
|
||||
<string name="common_sent">"Адпраўлена"</string>
|
||||
<string name="common_server_not_supported">"Сервер не падтрымліваецца"</string>
|
||||
<string name="common_server_url">"URL-адрас сервера"</string>
|
||||
<string name="common_settings">"Налады"</string>
|
||||
<string name="common_signing_out">"Выхад"</string>
|
||||
<string name="common_starting_chat">"Пачатак чата…"</string>
|
||||
<string name="common_sticker">"Стыкер"</string>
|
||||
<string name="common_success">"Поспех"</string>
|
||||
<string name="common_suggestions">"Прапановы"</string>
|
||||
<string name="common_syncing">"Сінхранізацыя"</string>
|
||||
<string name="common_system">"Сістэма"</string>
|
||||
<string name="common_text">"Тэкст"</string>
|
||||
<string name="common_third_party_notices">"Паведамленні трэціх асоб"</string>
|
||||
<string name="common_thread">"Гутарка"</string>
|
||||
<string name="common_topic">"Тэма"</string>
|
||||
<string name="common_topic_placeholder">"Пра што гэты пакой?"</string>
|
||||
<string name="common_unable_to_decrypt">"Немагчыма расшыфраваць"</string>
|
||||
<string name="common_unable_to_invite_message">"Не ўдалося адправіць запрашэнні аднаму або некалькім карыстальнікам."</string>
|
||||
<string name="common_unable_to_invite_title">"Немагчыма адправіць запрашэнне(я)"</string>
|
||||
<string name="common_unlock">"Разблакіраваць"</string>
|
||||
<string name="common_unmute">"Уключыць гук"</string>
|
||||
<string name="common_unsupported_event">"Падзея не падтрымліваецца"</string>
|
||||
<string name="common_username">"Імя карыстальніка"</string>
|
||||
<string name="common_verification_cancelled">"Праверка адменена"</string>
|
||||
<string name="common_verification_complete">"Праверка завершана"</string>
|
||||
<string name="common_video">"Відэа"</string>
|
||||
<string name="common_voice_message">"Галасавое паведамленне"</string>
|
||||
<string name="common_waiting">"Чакаем…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"Чакаю гэтага паведамлення"</string>
|
||||
<string name="common_poll_end_confirmation">"Вы ўпэўнены, што хочаце скончыць гэтае апытанне?"</string>
|
||||
<string name="common_poll_summary">"Апытанне: %1$s"</string>
|
||||
<string name="common_verify_device">"Праверце прыладу"</string>
|
||||
<string name="dialog_title_confirmation">"Пацвярджэнне"</string>
|
||||
<string name="dialog_title_warning">"Папярэджанне"</string>
|
||||
<string name="error_failed_creating_the_permalink">"Не атрымалася стварыць пастаянную спасылку"</string>
|
||||
<string name="error_failed_loading_map">"%1$s не атрымалася загрузіць карту. Калі ласка паспрабуйце зноў пазней."</string>
|
||||
<string name="error_failed_loading_messages">"Не ўдалося загрузіць паведамленні"</string>
|
||||
<string name="error_failed_locating_user">"%1$s не магчыма атрымаць доступ да вашага месцазнаходжання. Калі ласка паспрабуйце зноў пазней."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Не ўдалося загрузіць ваша галасавое паведамленне."</string>
|
||||
<string name="error_missing_location_auth_android">"У %1$s няма дазволу на доступ да вашага месцазнаходжання. Вы можаце даць доступ у Наладах."</string>
|
||||
<string name="error_missing_location_rationale_android">"У %1$s няма дазволу на доступ да вашага месцазнаходжання. Дазвольце доступ ніжэй."</string>
|
||||
<string name="error_missing_microphone_voice_rationale_android">"%1$s не мае дазволу на доступ да вашага мікрафона. Дазвольце доступ да запісу галасавога паведамлення."</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Некаторыя паведамленні не былі адпраўлены"</string>
|
||||
<string name="error_unknown">"На жаль, адбылася памылка"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Далучайцеся да мяне %1$s"</string>
|
||||
<string name="invite_friends_text">"Гэй, пагавары са мной у %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<plurals name="a11y_digits_entered">
|
||||
<item quantity="one">"Уведзеная лічба %1$d"</item>
|
||||
<item quantity="few">"Уведзена %1$d лічбы"</item>
|
||||
<item quantity="many">"Уведзена %1$d лічб"</item>
|
||||
</plurals>
|
||||
<plurals name="a11y_read_receipts_multiple_with_others">
|
||||
<item quantity="one">"Прачытана %1$s і %2$d іншым"</item>
|
||||
<item quantity="few">"Прачытана %1$s і %2$d іншымі"</item>
|
||||
<item quantity="many">"Прачытана %1$s і %2$d іншымі"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d карыстальнік"</item>
|
||||
<item quantity="few">"%1$d карыстальнікаў"</item>
|
||||
<item quantity="many">"%1$d карыстальнікаў"</item>
|
||||
</plurals>
|
||||
<plurals name="common_poll_votes_count">
|
||||
<item quantity="one">"%d голас"</item>
|
||||
<item quantity="few">"%d галасоў"</item>
|
||||
<item quantity="many">"%d галасоў"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Rageshake паведаміць пра памылку"</string>
|
||||
<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_share_location_title">"Падзяліцца месцазнаходжаннем"</string>
|
||||
<string name="screen_share_my_location_action">"Падзяліцца маім месцазнаходжаннем"</string>
|
||||
<string name="screen_share_open_apple_maps">"Адкрыць у Apple Maps"</string>
|
||||
<string name="screen_share_open_google_maps">"Адкрыць у Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Адкрыць у OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Падзяліцеся гэтым месцазнаходжаннем"</string>
|
||||
<string name="screen_view_location_title">"Месцазнаходжанне"</string>
|
||||
<string name="settings_version_number">"Версія: %1$s (%2$s)"</string>
|
||||
<string name="test_language_identifier">"be"</string>
|
||||
<string name="dialog_title_error">"Памылка"</string>
|
||||
<string name="dialog_title_success">"Поспех"</string>
|
||||
</resources>
|
||||
|
|
@ -119,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Zadejte svůj PIN"</string>
|
||||
<string name="common_error">"Chyba"</string>
|
||||
<string name="common_everyone">"Všichni"</string>
|
||||
<string name="common_favourite">"Oblíbené"</string>
|
||||
<string name="common_file">"Soubor"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Soubor byl uložen do složky Stažené soubory"</string>
|
||||
<string name="common_forward_message">"Přeposlat zprávu"</string>
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
<string name="action_join">"Beitreten"</string>
|
||||
<string name="action_learn_more">"Mehr erfahren"</string>
|
||||
<string name="action_leave">"Verlassen"</string>
|
||||
<string name="action_leave_conversation">"Unterhaltung verlassen"</string>
|
||||
<string name="action_leave_room">"Raum verlassen"</string>
|
||||
<string name="action_manage_account">"Konto verwalten"</string>
|
||||
<string name="action_manage_devices">"Geräte verwalten"</string>
|
||||
|
|
@ -118,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"PIN eingeben"</string>
|
||||
<string name="common_error">"Fehler"</string>
|
||||
<string name="common_everyone">"Alle"</string>
|
||||
<string name="common_favourite">"Favorit"</string>
|
||||
<string name="common_file">"Datei"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Datei wurde unter Downloads gespeichert"</string>
|
||||
<string name="common_forward_message">"Nachricht weiterleiten"</string>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Saisissez votre code PIN"</string>
|
||||
<string name="common_error">"Erreur"</string>
|
||||
<string name="common_everyone">"Tout le monde"</string>
|
||||
<string name="common_favourite">"Favori"</string>
|
||||
<string name="common_file">"Fichier"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Fichier enregistré dans Téléchargements"</string>
|
||||
<string name="common_forward_message">"Transférer le message"</string>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
<string name="action_edit_poll">"Szavazás szerkesztése"</string>
|
||||
<string name="action_enable">"Engedélyezés"</string>
|
||||
<string name="action_end_poll">"Szavazás lezárása"</string>
|
||||
<string name="action_enter_pin">"Adja meg a PIN-kódot"</string>
|
||||
<string name="action_enter_pin">"Add meg a PIN-kódot"</string>
|
||||
<string name="action_forgot_password">"Elfelejtetted a jelszavadat?"</string>
|
||||
<string name="action_forward">"Tovább"</string>
|
||||
<string name="action_invite">"Meghívás"</string>
|
||||
|
|
@ -57,6 +57,7 @@
|
|||
<string name="action_join">"Csatlakozás"</string>
|
||||
<string name="action_learn_more">"További tudnivalók"</string>
|
||||
<string name="action_leave">"Elhagyás"</string>
|
||||
<string name="action_leave_conversation">"Beszélgetés elhagyása"</string>
|
||||
<string name="action_leave_room">"Szoba elhagyása"</string>
|
||||
<string name="action_manage_account">"Fiók kezelése"</string>
|
||||
<string name="action_manage_devices">"Eszközök kezelése"</string>
|
||||
|
|
@ -91,7 +92,7 @@
|
|||
<string name="action_start_verification">"Ellenőrzés elindítása"</string>
|
||||
<string name="action_static_map_load">"Koppints a térkép betöltéséhez"</string>
|
||||
<string name="action_take_photo">"Fénykép készítése"</string>
|
||||
<string name="action_tap_for_options">"Koppintson a lehetőségekért"</string>
|
||||
<string name="action_tap_for_options">"Koppints a beállításokért"</string>
|
||||
<string name="action_try_again">"Próbáld újra"</string>
|
||||
<string name="action_view_source">"Forrás megtekintése"</string>
|
||||
<string name="action_yes">"Igen"</string>
|
||||
|
|
@ -118,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Add meg a PIN-kódodat"</string>
|
||||
<string name="common_error">"Hiba"</string>
|
||||
<string name="common_everyone">"Mindenki"</string>
|
||||
<string name="common_favourite">"Kedvenc"</string>
|
||||
<string name="common_file">"Fájl"</string>
|
||||
<string name="common_file_saved_on_disk_android">"A fájl a Letöltések mappába mentve"</string>
|
||||
<string name="common_forward_message">"Üzenet továbbítása"</string>
|
||||
|
|
@ -200,13 +202,13 @@
|
|||
<string name="dialog_title_confirmation">"Megerősítés"</string>
|
||||
<string name="dialog_title_warning">"Figyelmeztetés"</string>
|
||||
<string name="error_failed_creating_the_permalink">"Nem sikerült létrehozni az állandó hivatkozást"</string>
|
||||
<string name="error_failed_loading_map">"Az %1$s nem tudta betölteni a térképet. Próbálja meg újra később."</string>
|
||||
<string name="error_failed_loading_map">"A(z) %1$s nem tudta betölteni a térképet. Próbáld meg újra később."</string>
|
||||
<string name="error_failed_loading_messages">"Nem sikerült betölteni az üzeneteket"</string>
|
||||
<string name="error_failed_locating_user">"A(z) %1$s nem tudta elérni a tartózkodási helyét. Próbáld meg újra később."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Nem sikerült feltölteni a hangüzenetét."</string>
|
||||
<string name="error_missing_location_auth_android">"Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyedhez. Ezt a beállításokban engedélyezheted."</string>
|
||||
<string name="error_missing_location_rationale_android">"Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyéhez. Engedélyezze alább az elérését."</string>
|
||||
<string name="error_missing_microphone_voice_rationale_android">"Az %1$snek nincs engedélye, hogy hozzáférjen a mikrofonjához. Engedélyezze, hogy tudjon hangüzenetet felvenni."</string>
|
||||
<string name="error_missing_microphone_voice_rationale_android">"Az %1$snek nincs engedélye, hogy hozzáférjen a mikrofonhoz. Engedélyezd, hogy tudjon hangüzenetet felvenni."</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Néhány üzenet nem került elküldésre"</string>
|
||||
<string name="error_unknown">"Elnézést, hiba történt"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Csatlakozz hozzám itt: %1$s"</string>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Inserisci il PIN"</string>
|
||||
<string name="common_error">"Errore"</string>
|
||||
<string name="common_everyone">"Tutti"</string>
|
||||
<string name="common_favourite">"Preferiti"</string>
|
||||
<string name="common_file">"File"</string>
|
||||
<string name="common_file_saved_on_disk_android">"File salvato in Download"</string>
|
||||
<string name="common_forward_message">"Inoltra messaggio"</string>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Введите свой PIN-код"</string>
|
||||
<string name="common_error">"Ошибка"</string>
|
||||
<string name="common_everyone">"Для всех"</string>
|
||||
<string name="common_favourite">"Избранное"</string>
|
||||
<string name="common_file">"Файл"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Файл сохранен в «Загрузки»"</string>
|
||||
<string name="common_forward_message">"Переслать сообщение"</string>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Zadajte svoj PIN"</string>
|
||||
<string name="common_error">"Chyba"</string>
|
||||
<string name="common_everyone">"Všetci"</string>
|
||||
<string name="common_favourite">"Obľúbené"</string>
|
||||
<string name="common_file">"Súbor"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Súbor bol uložený do priečinka Stiahnuté súbory"</string>
|
||||
<string name="common_forward_message">"Preposlať správu"</string>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@
|
|||
<string name="common_enter_your_pin">"Enter your PIN"</string>
|
||||
<string name="common_error">"Error"</string>
|
||||
<string name="common_everyone">"Everyone"</string>
|
||||
<string name="common_favourite">"Favourite"</string>
|
||||
<string name="common_file">"File"</string>
|
||||
<string name="common_file_saved_on_disk_android">"File saved to Downloads"</string>
|
||||
<string name="common_forward_message">"Forward message"</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue