Merge branch 'develop' into feature/fga/media_viewer_actions

This commit is contained in:
ganfra 2023-06-06 14:35:25 +02:00
commit 438fc6bb99
408 changed files with 3011 additions and 2886 deletions

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
@ -25,12 +26,14 @@ import androidx.compose.ui.unit.sp
// TODO Remove
object ElementTextStyles {
@Suppress("DEPRECATION")
val Button = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
fontWeight = FontWeight.Medium,
lineHeight = 22.sp,
fontStyle = FontStyle.Normal,
textAlign = TextAlign.Center,
platformStyle = PlatformTextStyle(includeFontPadding = false)
)
object Bold {

View file

@ -19,7 +19,8 @@ package io.element.android.libraries.designsystem.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
@ -46,29 +47,10 @@ fun ProgressDialog(
onDismissRequest = onDismiss,
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)
) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.fillMaxWidth()
.background(
color = MaterialTheme.colorScheme.surfaceVariant,
shape = RoundedCornerShape(8.dp)
)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator(
modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant
)
if (!text.isNullOrBlank()) {
Text(
text = text,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(16.dp)
)
}
}
}
ProgressDialogContent(
modifier = modifier,
text = text,
)
}
}
@ -80,22 +62,23 @@ private fun ProgressDialogContent(
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.fillMaxWidth()
.background(
color = MaterialTheme.colorScheme.surfaceVariant,
shape = RoundedCornerShape(8.dp)
)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(top = 38.dp, bottom = 32.dp, start = 40.dp, end = 40.dp)
) {
CircularProgressIndicator(
modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant
)
if (!text.isNullOrBlank()) {
Spacer(modifier = Modifier.height(22.dp))
Text(
text = text,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(16.dp)
)
}
}

View file

@ -49,7 +49,7 @@ fun Avatar(
val commonModifier = modifier
.size(avatarData.size.dp)
.clip(CircleShape)
if (avatarData.url == null) {
if (avatarData.url.isNullOrBlank()) {
InitialsAvatar(
avatarData = avatarData,
modifier = commonModifier,
@ -72,7 +72,7 @@ private fun ImageAvatar(
AsyncImage(
model = avatarData,
onError = {
Timber.e("TAG", "Error $it\n${it.result}", it.result.throwable)
Timber.e(it.result.throwable, "Error loading avatar $it\n${it.result}")
},
contentDescription = contentDescription,
contentScale = ContentScale.Crop,

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.designsystem.theme
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@ -107,3 +108,13 @@ val titleMediumDefault: TextStyle = TextStyle(
letterSpacing = 0.5.sp
)
// Temporary style for text that needs to be aligned without weird font padding issues. `includeFontPadding` will default to false in a future version of
// compose, at which point this can be removed.
//
// Ref: https://medium.com/androiddevelopers/fixing-font-padding-in-compose-text-768cd232425b
@Suppress("DEPRECATION")
val noFontPadding: TextStyle = TextStyle(
platformStyle = PlatformTextStyle(
includeFontPadding = false
)
)

View file

@ -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">"(avatar byl také změněn)"</string>
<string name="state_event_avatar_url_changed">"%1$s změnil(a) svůj profilový obrázek"</string>
<string name="state_event_avatar_url_changed_by_you">"Změnili jste svůj profilový obrázek"</string>
<string name="state_event_display_name_changed_from">"%1$s změnil(a) své zobrazované jméno z %2$s na %3$s"</string>
<string name="state_event_display_name_changed_from_by_you">"Změnili jste své zobrazované jméno z %1$s na %2$s"</string>
<string name="state_event_display_name_removed">"%1$s odstranil(a) své zobrazované jméno (%2$s)"</string>
<string name="state_event_display_name_removed_by_you">"Odstranili jste své zobrazované jméno (%1$s)"</string>
<string name="state_event_display_name_set">"%1$s nastavil(a) své zobrazované jméno na %2$s"</string>
<string name="state_event_display_name_set_by_you">"Změnili jste své zobrazované jméno na %1$s"</string>
<string name="state_event_room_avatar_changed">"%1$s změnil(a) obrázek místnosti"</string>
<string name="state_event_room_avatar_changed_by_you">"Změnili jste obrázek místnosti"</string>
<string name="state_event_room_avatar_removed">"%1$s odstranili obrázek místnosti"</string>
<string name="state_event_room_avatar_removed_by_you">"Odstranili jste obrázek místnosti"</string>
<string name="state_event_room_ban">"%1$s vykázal(a) %2$s"</string>
<string name="state_event_room_ban_by_you">"Vykázali jste %1$s"</string>
<string name="state_event_room_created">"%1$s založil(a) místnost"</string>
<string name="state_event_room_created_by_you">"Založili jste místnost"</string>
<string name="state_event_room_invite">"%1$s pozval(a) %2$s"</string>
<string name="state_event_room_invite_accepted">"%1$s přijal(a) pozvání"</string>
<string name="state_event_room_invite_accepted_by_you">"Přijali jste pozvání"</string>
<string name="state_event_room_invite_by_you">"Pozvali jste %1$s"</string>
<string name="state_event_room_invite_you">"Pozvali jste %1$s"</string>
<string name="state_event_room_join">"%1$s vstoupil(a) do místnosti"</string>
<string name="state_event_room_join_by_you">"Vstoupili jste do místnosti"</string>
<string name="state_event_room_knock">"%1$s požádal(a) o vstup"</string>
<string name="state_event_room_knock_accepted">"%1$s povolil(a) vstoupit %2$s"</string>
<string name="state_event_room_knock_accepted_by_you">"%1$s vám povolil(a) vstoupit"</string>
<string name="state_event_room_knock_by_you">"Požádali jste o vstup"</string>
<string name="state_event_room_knock_denied">"%1$s zamítl(a) žádost %2$s o vstup"</string>
<string name="state_event_room_knock_denied_by_you">"Zamítli jste žádost %1$s o vstup"</string>
<string name="state_event_room_knock_denied_you">"%1$s zamítl(a) vaši žádost o vstup"</string>
<string name="state_event_room_knock_retracted">"%1$s již nemá zájem vstoupit"</string>
<string name="state_event_room_knock_retracted_by_you">"Zrušili jste svou žádost vstoupit"</string>
<string name="state_event_room_leave">"%1$s opustil(a) místnost"</string>
<string name="state_event_room_leave_by_you">"Opustili jste místnost"</string>
<string name="state_event_room_name_changed">"%1$s změnil(a) název místnosti na: %2$s"</string>
<string name="state_event_room_name_changed_by_you">"Změnili jste název místnosti na: %1$s"</string>
<string name="state_event_room_name_removed">"%1$s odstranil(a) název místnosti"</string>
<string name="state_event_room_name_removed_by_you">"Odstranili jste název místnosti"</string>
<string name="state_event_room_reject">"%1$s pozvánku odmítl(a)"</string>
<string name="state_event_room_reject_by_you">"Odmítli jste pozvání"</string>
<string name="state_event_room_remove">"%1$s odebral(a) %2$s"</string>
<string name="state_event_room_remove_by_you">"Odebrali jste %1$s"</string>
<string name="state_event_room_third_party_invite">"%1$s do této místnosti pozval(a) %2$s"</string>
<string name="state_event_room_third_party_invite_by_you">"Poslali jste %1$s pozvání do místnosti"</string>
<string name="state_event_room_third_party_revoked_invite">"%1$s zrušil(a) pozvánku do místnosti pro %2$s"</string>
<string name="state_event_room_third_party_revoked_invite_by_you">"Zrušili jste pozvánku do místnosti pro %1$s"</string>
<string name="state_event_room_topic_changed">"%1$s změnil(a) téma na: %2$s"</string>
<string name="state_event_room_topic_changed_by_you">"Změnili jste téma na: %1$s"</string>
<string name="state_event_room_topic_removed">"%1$s odstranil(a) téma místnosti"</string>
<string name="state_event_room_topic_removed_by_you">"Odstranili jste téma místnosti"</string>
<string name="state_event_room_unban">"%1$s zrušil(a) vykázání %2$s"</string>
<string name="state_event_room_unban_by_you">"Zrušili jste vykázání pro %1$s"</string>
<string name="state_event_room_unknown_membership_change">"%1$s provedl(a) neznámou změnu svého členství"</string>
</resources>

View file

@ -43,6 +43,7 @@ interface MatrixRoom : Closeable {
val isEncrypted: Boolean
val isDirect: Boolean
val isPublic: Boolean
val joinedMemberCount: Long
/**
* The current loaded members as a StateFlow.

View file

@ -137,6 +137,9 @@ class RustMatrixRoom(
override val isDirect: Boolean
get() = innerRoom.isDirect()
override val joinedMemberCount: Long
get() = innerRoom.joinedMembersCount().toLong()
override suspend fun updateMembers(): Result<Unit> = withContext(coroutineDispatchers.io) {
val currentState = _membersStateFlow.value
val currentMembers = currentState.roomMembers()

View file

@ -51,6 +51,7 @@ class FakeMatrixRoom(
override val alternativeAliases: List<String> = emptyList(),
override val isPublic: Boolean = true,
override val isDirect: Boolean = false,
override val joinedMemberCount: Long = 123L,
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
) : MatrixRoom {

View file

@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Text
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@ -35,10 +34,12 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.ui.media.AvatarAction
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -62,6 +63,7 @@ fun AvatarActionBottomSheet(
ModalBottomSheetLayout(
modifier = modifier,
sheetState = modalBottomSheetState,
displayHandle = true,
sheetContent = {
AvatarActionBottomSheetContent(
actions = actions,
@ -91,6 +93,7 @@ private fun AvatarActionBottomSheetContent(
headlineContent = {
Text(
text = stringResource(action.titleResId),
fontSize = 16.sp,
color = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
)
},
@ -98,7 +101,7 @@ private fun AvatarActionBottomSheetContent(
Icon(
imageVector = action.icon,
contentDescription = stringResource(action.titleResId),
tint = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
tint = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary,
)
}
)

View file

@ -20,12 +20,14 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@ -79,8 +81,10 @@ fun CheckableUserRow(
)
Checkbox(
modifier = Modifier
.padding(end = 16.dp),
checked = checked,
onCheckedChange = onCheckedChange,
onCheckedChange = null,
enabled = enabled,
)
}

View file

@ -16,9 +16,11 @@
package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@ -38,6 +40,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.noFontPadding
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.model.getBestName
@ -46,7 +49,7 @@ import io.element.android.libraries.matrix.ui.model.getBestName
fun MatrixUserRow(
matrixUser: MatrixUser,
modifier: Modifier = Modifier,
avatarSize: AvatarSize = AvatarSize.MEDIUM,
avatarSize: AvatarSize = AvatarSize.Custom(36.dp),
) = UserRow(
avatarData = matrixUser.getAvatarData(avatarSize),
name = matrixUser.getBestName(),
@ -71,25 +74,29 @@ fun UserRow(
Avatar(avatarData)
Column(
modifier = Modifier
.padding(start = 12.dp),
.padding(start = 12.dp)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
// Name
Text(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
fontWeight = FontWeight.Normal,
text = name,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.primary,
style = noFontPadding,
)
// Id
subtext?.let {
Text(
text = subtext,
color = MaterialTheme.colorScheme.secondary,
fontSize = 14.sp,
fontSize = 12.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
overflow = TextOverflow.Ellipsis,
style = noFontPadding,
)
}
}

View file

@ -16,16 +16,20 @@
package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -38,7 +42,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
@ -51,7 +55,9 @@ fun SelectedUser(
modifier: Modifier = Modifier,
onUserRemoved: (MatrixUser) -> Unit = {},
) {
Box(modifier = modifier.width(56.dp)) {
Box(modifier = modifier
.width(56.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
@ -63,18 +69,23 @@ fun SelectedUser(
style = MaterialTheme.typography.bodyLarge,
)
}
IconButton(
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primary)
.size(20.dp)
.align(Alignment.TopEnd),
onClick = { onUserRemoved(matrixUser) }
.align(Alignment.TopEnd)
.clickable(
indication = rememberRipple(),
interactionSource = remember { MutableInteractionSource() },
onClick = { onUserRemoved(matrixUser) }
),
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(id = StringR.string.action_remove),
tint = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier.padding(2.dp)
)
}
}

View file

@ -16,18 +16,27 @@
package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@ -35,6 +44,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlin.math.floor
@Composable
fun SelectedUsersList(
@ -56,16 +66,64 @@ fun SelectedUsersList(
}
}
val rowWidth by remember {
derivedStateOf {
lazyListState.layoutInfo.viewportSize.width - lazyListState.layoutInfo.beforeContentPadding
}
}
// Calculate spacing to show between each user. This is at least [minimumSpacing], and will grow to ensure that if the available space is filled with
// users, the last visible user will be precisely half visible. This gives an obvious affordance that there are more entries and the list can be scrolled.
// For efficiency, we assume that all the children are the same width. If they needed to be different sizes we'd have to do this calculation each time
// they needed to be measured.
val minimumSpacing = with(LocalDensity.current) { 24.dp.toPx() }
val userWidth = with(LocalDensity.current) { 56.dp.toPx() }
val userSpacing by remember {
derivedStateOf {
if (rowWidth == 0) {
// The row hasn't yet been measured yet, so we don't know how big it is
minimumSpacing
} else {
val userWidthWithSpacing = userWidth + minimumSpacing
val maxVisibleUsers = rowWidth / userWidthWithSpacing
// Round down the number of visible users to end with a state where one is half visible
val targetFraction = (userWidth / 2) / userWidthWithSpacing
val targetUsers = floor(maxVisibleUsers - targetFraction) + targetFraction
// Work out how much extra spacing we need to reduce the number of users that much, then split it evenly amongst the visible users
val extraSpacing = (maxVisibleUsers - targetUsers) * userWidthWithSpacing
val extraSpacingPerUser = extraSpacing / floor(targetUsers)
minimumSpacing + extraSpacingPerUser
}
}
}
LazyRow(
state = lazyListState,
modifier = modifier,
modifier = modifier
.fillMaxWidth(),
contentPadding = contentPadding,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
items(selectedUsers.toList()) { matrixUser ->
SelectedUser(
matrixUser = matrixUser,
onUserRemoved = onUserRemoved,
itemsIndexed(selectedUsers.toList()) { index, matrixUser ->
Layout(
content = {
SelectedUser(
matrixUser = matrixUser,
onUserRemoved = onUserRemoved,
)
},
measurePolicy = { measurables, constraints ->
val placeable = measurables.first().measure(constraints)
val spacing = if (index == selectedUsers.lastIndex) 0f else userSpacing
layout(
width = (placeable.width + spacing).toInt(),
height = placeable.height
) {
placeable.place(0, 0)
}
}
)
}
}
@ -81,7 +139,23 @@ internal fun SelectedUsersListDarkPreview() = ElementPreviewDark { ContentToPrev
@Composable
private fun ContentToPreview() {
SelectedUsersList(
selectedUsers = aMatrixUserList().take(6).toImmutableList(),
)
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
// Two users that will be visible with no scrolling
SelectedUsersList(
selectedUsers = aMatrixUserList().take(2).toImmutableList(),
modifier = Modifier
.width(200.dp)
.border(1.dp, Color.Red)
)
// Multiple users that don't fit, so will be spaced out per the measure policy
for (i in 0..5) {
SelectedUsersList(
selectedUsers = aMatrixUserList().take(6).toImmutableList(),
modifier = Modifier
.width((200 + (i * 20)).dp)
.border(1.dp, Color.Red)
)
}
}
}

View file

@ -17,9 +17,11 @@
package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@ -39,10 +41,12 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.theme.components.Checkbox
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.noFontPadding
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.ui.strings.R
@ -62,7 +66,9 @@ fun UnresolvedUserRow(
Avatar(avatarData)
Column(
modifier = Modifier
.padding(start = 12.dp),
.padding(start = 12.dp)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
// ID
Text(
@ -72,10 +78,11 @@ fun UnresolvedUserRow(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.primary,
style = noFontPadding,
)
// Warning
Row(modifier = Modifier.fillMaxWidth()) {
Row(modifier = Modifier.fillMaxWidth().padding(top = 3.dp)) {
Icon(
imageVector = Icons.Filled.Error,
contentDescription = "",
@ -121,8 +128,9 @@ fun CheckableUnresolvedUserRow(
)
Checkbox(
modifier = Modifier.padding(end = 16.dp),
checked = checked,
onCheckedChange = onCheckedChange,
onCheckedChange = null,
enabled = enabled,
)
}
@ -142,9 +150,9 @@ internal fun CheckableUnresolvedUserRowPreview() =
ElementThemedPreview {
val matrixUser = aMatrixUser()
Column {
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(), matrixUser.userId.value)
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(), matrixUser.userId.value)
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(), matrixUser.userId.value, enabled = false)
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(), matrixUser.userId.value, enabled = false)
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.Custom(36.dp)), matrixUser.userId.value)
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.Custom(36.dp)), matrixUser.userId.value)
CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(AvatarSize.Custom(36.dp)), matrixUser.userId.value, enabled = false)
CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(AvatarSize.Custom(36.dp)), matrixUser.userId.value, enabled = false)
}
}

View file

@ -55,7 +55,7 @@ fun MatrixRoom.getDirectRoomMember(): State<RoomMember?> {
@Composable
fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): State<RoomMember?> {
val roomMembers = roomMembersState.roomMembers()
return remember(roomMembers) {
return remember(roomMembersState) {
derivedStateOf {
if (roomMembers == null) {
null

View file

@ -17,8 +17,8 @@
package io.element.android.libraries.push.api
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider
interface PushService {
// TODO Move away

View file

@ -22,8 +22,8 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import javax.inject.Inject

View file

@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
import io.element.android.libraries.push.impl.config.PushConfig
import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushproviders.api.PusherSubscriber
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.services.toolbox.api.appname.AppNameProvider

View file

@ -26,7 +26,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.push.impl.log.notificationLoggerTag
import io.element.android.services.analytics.api.AnalyticsTracker
import io.element.android.services.toolbox.api.systemclock.SystemClock
import timber.log.Timber
import javax.inject.Inject
@ -41,7 +40,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
//@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var analyticsTracker: AnalyticsTracker
//@Inject lateinit var analyticsTracker: AnalyticsTracker
@Inject lateinit var clock: SystemClock
@Inject lateinit var actionIds: NotificationActionIds

View file

@ -16,7 +16,6 @@
package io.element.android.libraries.push.impl.notifications.factories.action
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
@ -71,8 +70,6 @@ class QuickReplyActionFactory @Inject constructor(
* However, for Android devices running Marshmallow and below (API level 23 and below),
* it will be more appropriate to use an activity. Since you have to provide your own UI.
*/
//TODO remove when minSdk will be back to 23
@SuppressLint("ObsoleteSdkInt")
private fun buildQuickReplyIntent(
sessionId: SessionId,
roomId: RoomId,

View file

@ -33,8 +33,8 @@ import io.element.android.libraries.push.impl.notifications.NotifiableEventResol
import io.element.android.libraries.push.impl.notifications.NotificationActionIds
import io.element.android.libraries.push.impl.notifications.NotificationDrawerManager
import io.element.android.libraries.push.impl.store.DefaultPushDataStore
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.push.providers.api.PushHandler
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.libraries.pushproviders.api.PushHandler
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import kotlinx.coroutines.CoroutineScope

View file

@ -4,6 +4,7 @@
<string name="notification_channel_listening_for_events">"Naslouchání událostem"</string>
<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_new_messages">"Nové zprávy"</string>
@ -20,6 +21,31 @@
<item quantity="few">"%1$s: %2$d zprávy"</item>
<item quantity="other">"%1$s: %2$d zpráv"</item>
</plurals>
<plurals name="notification_compat_summary_title">
<item quantity="one">"%d oznámení"</item>
<item quantity="few">"%d oznámení"</item>
<item quantity="other">"%d oznámení"</item>
</plurals>
<plurals name="notification_invitations">
<item quantity="one">"%d pozvánka"</item>
<item quantity="few">"%d pozvánky"</item>
<item quantity="other">"%d pozvánek"</item>
</plurals>
<plurals name="notification_new_messages_for_room">
<item quantity="one">"%d nová zpráva"</item>
<item quantity="few">"%d nové zprávy"</item>
<item quantity="other">"%d nových zpráv"</item>
</plurals>
<plurals name="notification_unread_notified_messages">
<item quantity="one">"%d nepřečtená oznámená zpráva"</item>
<item quantity="few">"%d nepřečtené oznámené zprávy"</item>
<item quantity="other">"%d nepřečtených oznámených zpráv"</item>
</plurals>
<plurals name="notification_unread_notified_messages_in_room_rooms">
<item quantity="one">"%d místnost"</item>
<item quantity="few">"%d místnosti"</item>
<item quantity="other">"%d místností"</item>
</plurals>
<string name="push_choose_distributor_dialog_title_android">"Vyberte, jak chcete přijímat oznámení"</string>
<string name="push_distributor_background_sync_android">"Synchronizace na pozadí"</string>
<string name="push_distributor_firebase_android">"Služby Google"</string>

View file

@ -7,6 +7,7 @@
<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"</string>
<string name="notification_new_messages">"New Messages"</string>
<string name="notification_room_action_mark_as_read">"Mark as read"</string>
<string name="notification_sender_me">"Me"</string>

View file

@ -18,7 +18,7 @@ plugins {
}
android {
namespace = "io.element.android.libraries.push.providers.api"
namespace = "io.element.android.libraries.pushproviders.api"
}
dependencies {

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
package io.element.android.libraries.pushproviders.api
data class Distributor(
val value: String,

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
package io.element.android.libraries.pushproviders.api
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
package io.element.android.libraries.pushproviders.api
interface PushHandler {
suspend fun handle(pushData: PushData)

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
package io.element.android.libraries.pushproviders.api
import io.element.android.libraries.matrix.api.MatrixClient

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.api
package io.element.android.libraries.pushproviders.api
import io.element.android.libraries.matrix.api.MatrixClient

View file

@ -19,7 +19,7 @@ plugins {
}
android {
namespace = "io.element.android.libraries.push.providers.firebase"
namespace = "io.element.android.libraries.pushproviders.firebase"
}
anvil {

View file

@ -21,7 +21,7 @@
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
<service
android:name="io.element.android.libraries.push.providers.firebase.VectorFirebaseMessagingService"
android:name="io.element.android.libraries.pushproviders.firebase.VectorFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import javax.inject.Inject

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
object FirebaseConfig {
/**

View file

@ -14,12 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushproviders.api.PusherSubscriber
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.toUserList

View file

@ -14,9 +14,9 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.pushproviders.api.PushData
import javax.inject.Inject
class FirebasePushParser @Inject constructor() {

View file

@ -14,15 +14,15 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import com.squareup.anvil.annotations.ContributesMultibinding
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushproviders.api.PusherSubscriber
import timber.log.Timber
import javax.inject.Inject

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import android.content.SharedPreferences
import androidx.core.content.edit

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import android.content.Context
import com.google.android.gms.common.ConnectionResult

View file

@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.pushproviders.api.PushData
/**
* In this case, the format is:

View file

@ -14,13 +14,13 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.push.providers.api.PushHandler
import io.element.android.libraries.pushproviders.api.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.libraries.di.AppScope

View file

@ -14,12 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.firebase
package io.element.android.libraries.pushproviders.firebase
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.tests.testutils.assertThrowsInDebug
import org.junit.Test

View file

@ -20,7 +20,7 @@ plugins {
}
android {
namespace = "io.element.android.libraries.push.providers.unifiedpush"
namespace = "io.element.android.libraries.pushproviders.unifiedpush"
}
anvil {

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import android.content.BroadcastReceiver
import android.content.Context

View file

@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.pushproviders.api.PushData
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View file

@ -14,13 +14,13 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import android.content.Context
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PusherSubscriber
import org.unifiedpush.android.connector.UnifiedPush
import javax.inject.Inject

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
object UnifiedPushConfig {
/**

View file

@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.network.RetrofitFactory
import io.element.android.libraries.push.providers.unifiedpush.network.UnifiedPushApi
import io.element.android.libraries.pushproviders.unifiedpush.network.UnifiedPushApi
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.net.URL

View file

@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.push.providers.api.PusherSubscriber
import io.element.android.libraries.pushproviders.api.PusherSubscriber
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import timber.log.Timber

View file

@ -14,10 +14,10 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.pushproviders.api.PushData
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import javax.inject.Inject

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import android.content.Context
import com.squareup.anvil.annotations.ContributesMultibinding
@ -22,8 +22,8 @@ import io.element.android.libraries.androidutils.system.getApplicationLabel
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.providers.api.Distributor
import io.element.android.libraries.push.providers.api.PushProvider
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import org.unifiedpush.android.connector.UnifiedPush
import javax.inject.Inject

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import android.content.Context
import android.content.SharedPreferences

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import android.content.Context
import io.element.android.libraries.di.ApplicationContext

View file

@ -14,13 +14,13 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import android.content.Context
import android.content.Intent
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.push.providers.api.PushHandler
import io.element.android.libraries.pushproviders.api.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import com.squareup.anvil.annotations.ContributesTo
import io.element.android.libraries.di.AppScope

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush.network
package io.element.android.libraries.pushproviders.unifiedpush.network
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush.network
package io.element.android.libraries.pushproviders.unifiedpush.network
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush.network
package io.element.android.libraries.pushproviders.unifiedpush.network
import retrofit2.http.GET

View file

@ -14,12 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.push.providers.unifiedpush
package io.element.android.libraries.pushproviders.unifiedpush
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.tests.testutils.assertThrowsInDebug
import org.junit.Test

View file

@ -1,4 +1,17 @@
<?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">"Přepnout seznam s odrážkami"</string>
<string name="rich_text_editor_code_block">"Přepnout blok kódu"</string>
<string name="rich_text_editor_composer_placeholder">"Zpráva…"</string>
<string name="rich_text_editor_format_bold">"Použít tučný text"</string>
<string name="rich_text_editor_format_italic">"Použít kurzívu"</string>
<string name="rich_text_editor_format_strikethrough">"Použít přeškrtnutí"</string>
<string name="rich_text_editor_format_underline">"Použít podtržení"</string>
<string name="rich_text_editor_full_screen_toggle">"Přepnout režim celé obrazovky"</string>
<string name="rich_text_editor_indent">"Odsazení"</string>
<string name="rich_text_editor_inline_code">"Použít formát inline kódu"</string>
<string name="rich_text_editor_link">"Nastavit odkaz"</string>
<string name="rich_text_editor_numbered_list">"Přepnout číslovaný seznam"</string>
<string name="rich_text_editor_quote">"Přepnout citaci"</string>
<string name="rich_text_editor_unindent">"Zrušit odsazení"</string>
</resources>

View file

@ -22,7 +22,9 @@
<string name="action_done">"Hotovo"</string>
<string name="action_edit">"Upravit"</string>
<string name="action_enable">"Povolit"</string>
<string name="action_forgot_password">"Zapomněli jste heslo?"</string>
<string name="action_invite">"Pozvat"</string>
<string name="action_invite_friends">"Pozvat přátele"</string>
<string name="action_invite_friends_to_app">"Pozvat přátele do %1$s"</string>
<string name="action_invites_list">"Pozvánky"</string>
<string name="action_learn_more">"Zjistit více"</string>
@ -69,6 +71,8 @@
<string name="common_file">"Soubor"</string>
<string name="common_gif">"GIF"</string>
<string name="common_image">"Obrázek"</string>
<string name="common_invite_unknown_profile">"Nemůžeme ověřit Matrix ID tohoto uživatele. Pozvánka nemusí být přijata."</string>
<string name="common_leaving_room">"Opuštění místnosti"</string>
<string name="common_link_copied_to_clipboard">"Odkaz zkopírován do schránky"</string>
<string name="common_loading">"Načítání…"</string>
<string name="common_message">"Zpráva"</string>
@ -85,6 +89,7 @@
<string name="common_report_a_bug">"Nahlásit chybu"</string>
<string name="common_report_submitted">"Zpráva odeslána"</string>
<string name="common_room_name">"Název místnosti"</string>
<string name="common_room_name_placeholder">"např. Produktový sprint"</string>
<string name="common_search_for_someone">"Hledat někoho"</string>
<string name="common_search_results">"Výsledky hledání"</string>
<string name="common_security">"Zabezpečení"</string>
@ -93,11 +98,15 @@
<string name="common_server_not_supported">"Server není podporován"</string>
<string name="common_server_url">"URL serveru"</string>
<string name="common_settings">"Nastavení"</string>
<string name="common_starting_chat">"Zahajování chatu…"</string>
<string name="common_sticker">"Nálepka"</string>
<string name="common_success">"Úspěch"</string>
<string name="common_suggestions">"Návrhy"</string>
<string name="common_topic">"Téma"</string>
<string name="common_topic_placeholder">"O čem je tato místnost?"</string>
<string name="common_unable_to_decrypt">"Nelze dešifrovat"</string>
<string name="common_unable_to_invite_message">"Nepodařilo se nám úspěšně odeslat pozvánky jednomu nebo více uživatelům."</string>
<string name="common_unable_to_invite_title">"Nelze odeslat pozvánky"</string>
<string name="common_unsupported_event">"Nepodporovaná událost"</string>
<string name="common_username">"Uživatelské jméno"</string>
<string name="common_verification_cancelled">"Ověření zrušeno"</string>
@ -118,6 +127,7 @@
<string name="error_failed_loading_messages">"Načítání zpráv se nezdařilo"</string>
<string name="error_some_messages_have_not_been_sent">"Některé zprávy nebyly odeslány"</string>
<string name="error_unknown">"Omlouváme se, došlo k chybě"</string>
<string name="invite_friends_rich_title">"🔐️ Připojte se ke mně na %1$s"</string>
<string name="invite_friends_text">"Ahoj, ozvi se mi na %1$s: %2$s"</string>
<string name="leave_room_alert_empty_subtitle">"Opravdu chcete opustit tuto místnost? Jste tu jediná osoba. Pokud odejdete, nikdo se v budoucnu nebude moci připojit, včetně vás."</string>
<string name="leave_room_alert_private_subtitle">"Opravdu chcete opustit tuto místnost? Tato místnost není veřejná a bez pozvánky se nebudete moci znovu připojit."</string>
@ -128,16 +138,33 @@
<item quantity="few">"%1$d členové"</item>
<item quantity="other">"%1$d členů"</item>
</plurals>
<string name="preference_rageshake">"Zatřeste zařízením pro nahlášení chyby"</string>
<string name="rageshake_dialog_content">"Zdá se, že jste frustrovaně třásli telefonem. Chcete otevřít obrazovku pro nahlášení chyby?"</string>
<string name="report_content_explanation">"Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy."</string>
<string name="report_content_hint">"Důvod nahlášení tohoto obsahu"</string>
<string name="room_timeline_beginning_of_room">"Toto je začátek %1$s."</string>
<string name="room_timeline_beginning_of_room_no_name">"Toto je začátek této konverzace."</string>
<string name="room_timeline_read_marker_title">"Nové"</string>
<string name="screen_analytics_settings_share_data">"Sdílet analytická data"</string>
<string name="screen_media_picker_error_failed_selection">"Výběr média se nezdařil, zkuste to prosím znovu."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
<string name="screen_report_content_block_user_hint">"Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele"</string>
<string name="screen_server_confirmation_change_server">"Změnit poskytovatele účtu"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Soukromý server pro zaměstnance Elementu."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci."</string>
<string name="screen_server_confirmation_message_register">"Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily."</string>
<string name="screen_server_confirmation_title_login">"Chystáte se přihlásit do služby %1$s"</string>
<string name="screen_server_confirmation_title_register">"Chystáte se vytvořit účet na %1$s"</string>
<string name="settings_rageshake">"Rageshake"</string>
<string name="settings_rageshake_detection_threshold">"Práh detekce"</string>
<string name="settings_title_general">"Obecné"</string>
<string name="settings_version_number">"Verze: %1$s (%2$s)"</string>
<string name="test_language_identifier">"en"</string>
<string name="dialog_title_error">"Chyba"</string>
<string name="dialog_title_success">"Úspěch"</string>
<string name="screen_analytics_settings_help_us_improve">"Pomozte nám identifikovat problémy a vylepšit %1$s sdílením anonymních údajů o používání."</string>
<string name="screen_analytics_settings_read_terms">"Můžete si přečíst všechny naše podmínky %1$s."</string>
<string name="screen_analytics_settings_read_terms_content_link">"zde"</string>
<string name="screen_report_content_block_user">"Zablokovat uživatele"</string>
</resources>
</resources>

View file

@ -22,7 +22,9 @@
<string name="action_done">"Fertig"</string>
<string name="action_edit">"Bearbeiten"</string>
<string name="action_enable">"Aktivieren"</string>
<string name="action_forgot_password">"Passwort vergessen?"</string>
<string name="action_invite">"Einladen"</string>
<string name="action_invite_friends">"Freunde einladen"</string>
<string name="action_invite_friends_to_app">"Freunde zu %1$s einladen"</string>
<string name="action_invites_list">"Einladungen"</string>
<string name="action_learn_more">"Mehr erfahren"</string>
@ -68,6 +70,7 @@
<string name="common_file">"Datei"</string>
<string name="common_gif">"GIF"</string>
<string name="common_image">"Bild"</string>
<string name="common_leaving_room">"Raum verlassen"</string>
<string name="common_link_copied_to_clipboard">"Link in Zwischenablage kopiert"</string>
<string name="common_loading">"Wird geladen…"</string>
<string name="common_message">"Nachricht"</string>
@ -118,6 +121,7 @@
<string name="error_failed_loading_messages">"Fehler beim Laden der Nachrichten"</string>
<string name="error_some_messages_have_not_been_sent">"Einige Nachrichten wurden nicht gesendet"</string>
<string name="error_unknown">"Entschuldigung, ein Fehler ist aufgetreten."</string>
<string name="invite_friends_rich_title">"🔐️ Besuchen Sie mich auf %1$s"</string>
<string name="invite_friends_text">"Hey, sprich mit mir auf %1$s: %2$s"</string>
<string name="leave_room_alert_empty_subtitle">"Bist du sicher, dass du diesen Raum verlassen willst? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr beitreten, auch du nicht."</string>
<string name="leave_room_alert_private_subtitle">"Bist du dir sicher, dass du den Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne eine Einladung nicht mehr beitreten."</string>
@ -134,16 +138,13 @@
<string name="room_timeline_beginning_of_room">"Dies ist der Anfang von %1$s."</string>
<string name="room_timeline_beginning_of_room_no_name">"Dies ist der Beginn dieser Konversation."</string>
<string name="room_timeline_read_marker_title">"Neu"</string>
<string name="screen_analytics_prompt_data_usage">"Wir erfassen und analysieren "<b>"keine"</b>" Account-Daten"</string>
<string name="screen_analytics_prompt_help_us_improve">"Helfen Sie uns, Probleme zu identifizieren und %1$s zu verbessern, indem Sie anonyme Nutzungsdaten weitergeben."</string>
<string name="screen_analytics_prompt_read_terms">"Sie können alle unsere Nutzerbedingungen %1$s lesen."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"hier"</string>
<string name="screen_analytics_prompt_settings">"Sie können die Analyse jederzeit in den Einstellungen deaktivieren"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Wir geben "<b>"keine"</b>" Informationen an Dritte weiter"</string>
<string name="screen_analytics_prompt_title">"Helfen Sie %1$s zu verbessern"</string>
<string name="screen_analytics_settings_share_data">"Teile Analyse-Daten"</string>
<string name="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Medien hochladen fehlgeschlagen. Bitte versuchen Sie es erneut."</string>
<string name="screen_report_content_block_user_hint">"Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"</string>
<string name="screen_server_confirmation_change_server">"Kontoanbieter wechseln"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Ein privater Server für Element-Mitarbeiter."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation"</string>
<string name="settings_rageshake">"Rageshake"</string>
<string name="settings_rageshake_detection_threshold">"Erkennungsschwelle"</string>
<string name="settings_title_general">"Allgemein"</string>

View file

@ -141,13 +141,6 @@
<string name="room_timeline_beginning_of_room">"Acesta este începutul conversației %1$s."</string>
<string name="room_timeline_beginning_of_room_no_name">"Acesta este începutul acestei conversații."</string>
<string name="room_timeline_read_marker_title">"Nou"</string>
<string name="screen_analytics_prompt_data_usage"><b>"Nu"</b>" înregistrăm sau profilăm datele contului"</string>
<string name="screen_analytics_prompt_help_us_improve">"Ajutați-ne să identificăm problemele și să îmbunătățim %1$s prin partajarea datelor de utilizare anonime."</string>
<string name="screen_analytics_prompt_read_terms">"Puteți citi toate condițiile noastre %1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"aici"</string>
<string name="screen_analytics_prompt_settings">"Puteți dezactiva această opțiune oricând din setări"</string>
<string name="screen_analytics_prompt_third_party_sharing"><b>"Nu"</b>" împărtășim informații cu terți"</string>
<string name="screen_analytics_prompt_title">"Ajutați la îmbunătățirea %1$s"</string>
<string name="screen_analytics_settings_share_data">"Partajați datele analitice"</string>
<string name="screen_media_picker_error_failed_selection">"Selectarea fișierelor media a eșuat, încercați din nou."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Procesarea datelor media a eșuat, vă rugăm să încercați din nou."</string>

View file

@ -90,7 +90,7 @@
<string name="common_report_a_bug">"Report a bug"</string>
<string name="common_report_submitted">"Report submitted"</string>
<string name="common_room_name">"Room name"</string>
<string name="common_room_name_placeholder">"e.g. Product Sprint"</string>
<string name="common_room_name_placeholder">"e.g. your project name"</string>
<string name="common_search_for_someone">"Search for someone"</string>
<string name="common_search_results">"Search results"</string>
<string name="common_security">"Security"</string>
@ -145,14 +145,20 @@
<string name="room_timeline_beginning_of_room">"This is the beginning of %1$s."</string>
<string name="room_timeline_beginning_of_room_no_name">"This is the beginning of this conversation."</string>
<string name="room_timeline_read_marker_title">"New"</string>
<string name="screen_analytics_prompt_data_usage">"We "<b>"don\'t"</b>" record or profile any account data"</string>
<string name="screen_analytics_prompt_help_us_improve">"Help us identify issues and improve %1$s by sharing anonymous usage data."</string>
<string name="screen_analytics_prompt_read_terms">"You can read all our terms %1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"here"</string>
<string name="screen_analytics_prompt_settings">"You can turn this off anytime in settings"</string>
<string name="screen_analytics_prompt_third_party_sharing">"We "<b>"don\'t"</b>" share information with third parties"</string>
<string name="screen_analytics_prompt_title">"Help improve %1$s"</string>
<string name="screen_account_provider_change">"Change account provider"</string>
<string name="screen_account_provider_continue">"Continue"</string>
<string name="screen_account_provider_form_hint">"Homeserver address"</string>
<string name="screen_account_provider_form_notice">"Enter a search term or a domain address."</string>
<string name="screen_account_provider_form_subtitle">"Search for a company, community, or private server."</string>
<string name="screen_account_provider_form_title">"Find an account provider"</string>
<string name="screen_account_provider_signin_title">"Youre about to sign in to %s"</string>
<string name="screen_account_provider_signup_subtitle">"This is where you conversations will live — just like you would use an email provider to keep your emails."</string>
<string name="screen_account_provider_signup_title">"Youre about to create an account on %s"</string>
<string name="screen_analytics_settings_share_data">"Share analytics data"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org is an open network for secure, decentralized communication."</string>
<string name="screen_change_account_provider_other">"Other"</string>
<string name="screen_change_account_provider_subtitle">"Use a different account provider, such as your own private server or a work account."</string>
<string name="screen_change_account_provider_title">"Change account provider"</string>
<string name="screen_media_picker_error_failed_selection">"Failed selecting media, please try again."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Failed processing media to upload, please try again."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Failed uploading media, please try again."</string>

View file

@ -21,6 +21,6 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
interface UserListDataSource {
//TODO should probably have a flow
suspend fun search(query: String): List<MatrixUser>
suspend fun search(query: String, count: Long): List<MatrixUser>
suspend fun getProfile(userId: UserId): MatrixUser?
}

View file

@ -28,16 +28,12 @@ import javax.inject.Inject
class MatrixUserListDataSource @Inject constructor(
private val client: MatrixClient
) : UserListDataSource {
override suspend fun search(query: String): List<MatrixUser> {
val res = client.searchUsers(query, MAX_SEARCH_RESULTS)
override suspend fun search(query: String, count: Long): List<MatrixUser> {
val res = client.searchUsers(query, count)
return res.getOrNull()?.results.orEmpty()
}
override suspend fun getProfile(userId: UserId): MatrixUser? {
return client.getProfile(userId).getOrNull()
}
companion object {
private const val MAX_SEARCH_RESULTS = 5L
}
}

View file

@ -45,7 +45,7 @@ class MatrixUserRepository @Inject constructor(
// Debounce
delay(DEBOUNCE_TIME_MILLIS)
val results = dataSource.search(query).map { UserSearchResult(it) }.toMutableList()
val results = dataSource.search(query, MAXIMUM_SEARCH_RESULTS).map { UserSearchResult(it) }.toMutableList()
// If the query is a user ID and the result doesn't contain that user ID, query the profile information explicitly
if (isUserId && results.none { it.matrixUser.userId.value == query }) {
@ -61,7 +61,8 @@ class MatrixUserRepository @Inject constructor(
}
companion object {
private const val DEBOUNCE_TIME_MILLIS = 500L
private const val DEBOUNCE_TIME_MILLIS = 250L
private const val MINIMUM_SEARCH_LENGTH = 3
private const val MAXIMUM_SEARCH_RESULTS = 10L
}
}

View file

@ -47,7 +47,7 @@ internal class MatrixUserListDataSourceTest {
)
val dataSource = MatrixUserListDataSource(matrixClient)
val results = dataSource.search("test")
val results = dataSource.search("test", 2)
Truth.assertThat(results).containsExactly(
aMatrixUserProfile(),
aMatrixUserProfile(userId = A_USER_ID_2)
@ -63,7 +63,7 @@ internal class MatrixUserListDataSourceTest {
)
val dataSource = MatrixUserListDataSource(matrixClient)
val results = dataSource.search("test")
val results = dataSource.search("test", 2)
Truth.assertThat(results).isEmpty()
}

View file

@ -25,7 +25,7 @@ class FakeUserListDataSource : UserListDataSource {
private var searchResult: List<MatrixUser> = emptyList()
private var profile: MatrixUser? = null
override suspend fun search(query: String): List<MatrixUser> = searchResult
override suspend fun search(query: String, count: Long): List<MatrixUser> = searchResult.take(count.toInt())
override suspend fun getProfile(userId: UserId): MatrixUser? = profile