Merge pull request #2752 from element-hq/feature/bma/blockOpeningSpace
Block opening space
This commit is contained in:
commit
5579119f2e
199 changed files with 465 additions and 179 deletions
|
|
@ -128,7 +128,15 @@ class RoomFlowNode @AssistedInject constructor(
|
||||||
Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}")
|
Timber.d("Room membership: ${roomInfo.map { it.currentUserMembership }}")
|
||||||
val info = roomInfo.getOrNull()
|
val info = roomInfo.getOrNull()
|
||||||
if (info?.currentUserMembership == CurrentUserMembership.JOINED) {
|
if (info?.currentUserMembership == CurrentUserMembership.JOINED) {
|
||||||
backstack.newRoot(NavTarget.JoinedRoom(roomId))
|
if (info.isSpace) {
|
||||||
|
// It should not happen, but probably due to an issue in the sliding sync,
|
||||||
|
// we can have a space here in case the space has just been joined.
|
||||||
|
// So navigate to the JoinRoom target for now, which will
|
||||||
|
// handle the space not supported screen
|
||||||
|
backstack.newRoot(NavTarget.JoinRoom(roomId))
|
||||||
|
} else {
|
||||||
|
backstack.newRoot(NavTarget.JoinedRoom(roomId))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
backstack.newRoot(NavTarget.JoinRoom(roomId))
|
backstack.newRoot(NavTarget.JoinRoom(roomId))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,14 @@ import io.element.android.features.roomdirectory.api.RoomDescription
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
@ -56,6 +58,7 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||||
private val matrixClient: MatrixClient,
|
private val matrixClient: MatrixClient,
|
||||||
private val knockRoom: KnockRoom,
|
private val knockRoom: KnockRoom,
|
||||||
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||||
|
private val buildMeta: BuildMeta,
|
||||||
) : Presenter<JoinRoomState> {
|
) : Presenter<JoinRoomState> {
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(
|
fun create(
|
||||||
|
|
@ -134,6 +137,7 @@ class JoinRoomPresenter @AssistedInject constructor(
|
||||||
contentState = contentState,
|
contentState = contentState,
|
||||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||||
knockAction = knockAction.value,
|
knockAction = knockAction.value,
|
||||||
|
applicationName = buildMeta.applicationName,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +157,7 @@ private fun RoomPreview.toContentState(): ContentState {
|
||||||
alias = canonicalAlias,
|
alias = canonicalAlias,
|
||||||
numberOfMembers = numberOfJoinedMembers,
|
numberOfMembers = numberOfJoinedMembers,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
|
roomType = roomType,
|
||||||
roomAvatarUrl = avatarUrl,
|
roomAvatarUrl = avatarUrl,
|
||||||
joinAuthorisationStatus = when {
|
joinAuthorisationStatus = when {
|
||||||
// Note when isInvited, roomInfo will be used, so if this happen, it will be temporary.
|
// Note when isInvited, roomInfo will be used, so if this happen, it will be temporary.
|
||||||
|
|
@ -173,6 +178,7 @@ internal fun RoomDescription.toContentState(): ContentState {
|
||||||
alias = alias,
|
alias = alias,
|
||||||
numberOfMembers = numberOfMembers,
|
numberOfMembers = numberOfMembers,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
|
roomType = RoomType.Room,
|
||||||
roomAvatarUrl = avatarUrl,
|
roomAvatarUrl = avatarUrl,
|
||||||
joinAuthorisationStatus = when (joinRule) {
|
joinAuthorisationStatus = when (joinRule) {
|
||||||
RoomDescription.JoinRule.KNOCK -> JoinAuthorisationStatus.CanKnock
|
RoomDescription.JoinRule.KNOCK -> JoinAuthorisationStatus.CanKnock
|
||||||
|
|
@ -191,6 +197,7 @@ internal fun MatrixRoomInfo.toContentState(): ContentState {
|
||||||
alias = canonicalAlias,
|
alias = canonicalAlias,
|
||||||
numberOfMembers = activeMembersCount,
|
numberOfMembers = activeMembersCount,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
|
roomType = if (isSpace) RoomType.Space else RoomType.Room,
|
||||||
roomAvatarUrl = avatarUrl,
|
roomAvatarUrl = avatarUrl,
|
||||||
joinAuthorisationStatus = when {
|
joinAuthorisationStatus = when {
|
||||||
currentUserMembership == CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(
|
currentUserMembership == CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(
|
||||||
|
|
@ -207,7 +214,8 @@ internal fun ContentState.toInviteData(): InviteData? {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is ContentState.Loaded -> InviteData(
|
is ContentState.Loaded -> InviteData(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
roomName = computedTitle,
|
// Note: name should not be null at this point, but use Id just in case...
|
||||||
|
roomName = name ?: roomId.value,
|
||||||
isDirect = isDirect
|
isDirect = isDirect
|
||||||
)
|
)
|
||||||
else -> null
|
else -> null
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -31,6 +32,7 @@ data class JoinRoomState(
|
||||||
val contentState: ContentState,
|
val contentState: ContentState,
|
||||||
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
||||||
val knockAction: AsyncAction<Unit>,
|
val knockAction: AsyncAction<Unit>,
|
||||||
|
val applicationName: String,
|
||||||
val eventSink: (JoinRoomEvents) -> Unit
|
val eventSink: (JoinRoomEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val joinAuthorisationStatus = when (contentState) {
|
val joinAuthorisationStatus = when (contentState) {
|
||||||
|
|
@ -50,17 +52,10 @@ sealed interface ContentState {
|
||||||
val alias: RoomAlias?,
|
val alias: RoomAlias?,
|
||||||
val numberOfMembers: Long?,
|
val numberOfMembers: Long?,
|
||||||
val isDirect: Boolean,
|
val isDirect: Boolean,
|
||||||
|
val roomType: RoomType,
|
||||||
val roomAvatarUrl: String?,
|
val roomAvatarUrl: String?,
|
||||||
val joinAuthorisationStatus: JoinAuthorisationStatus,
|
val joinAuthorisationStatus: JoinAuthorisationStatus,
|
||||||
) : ContentState {
|
) : ContentState {
|
||||||
val computedTitle = name ?: roomId.value
|
|
||||||
|
|
||||||
val computedSubtitle = when {
|
|
||||||
alias != null -> alias.value
|
|
||||||
name == null -> ""
|
|
||||||
else -> roomId.value
|
|
||||||
}
|
|
||||||
|
|
||||||
val showMemberCount = numberOfMembers != null
|
val showMemberCount = numberOfMembers != null
|
||||||
|
|
||||||
fun avatarData(size: AvatarSize): AvatarData {
|
fun avatarData(size: AvatarSize): AvatarData {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||||
|
|
||||||
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||||
|
|
@ -38,6 +39,13 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||||
aJoinRoomState(
|
aJoinRoomState(
|
||||||
contentState = anUnknownContentState()
|
contentState = anUnknownContentState()
|
||||||
),
|
),
|
||||||
|
aJoinRoomState(
|
||||||
|
contentState = aLoadedContentState(
|
||||||
|
name = null,
|
||||||
|
alias = null,
|
||||||
|
topic = null,
|
||||||
|
)
|
||||||
|
),
|
||||||
aJoinRoomState(
|
aJoinRoomState(
|
||||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin)
|
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin)
|
||||||
),
|
),
|
||||||
|
|
@ -67,6 +75,15 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||||
aJoinRoomState(
|
aJoinRoomState(
|
||||||
contentState = aFailureContentState(roomIdOrAlias = A_ROOM_ALIAS.toRoomIdOrAlias())
|
contentState = aFailureContentState(roomIdOrAlias = A_ROOM_ALIAS.toRoomIdOrAlias())
|
||||||
),
|
),
|
||||||
|
aJoinRoomState(
|
||||||
|
contentState = aLoadedContentState(
|
||||||
|
roomId = RoomId("!aSpaceId:domain"),
|
||||||
|
name = "A space",
|
||||||
|
alias = null,
|
||||||
|
topic = "This is the topic of a space",
|
||||||
|
roomType = RoomType.Space,
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,11 +102,12 @@ fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(room
|
||||||
|
|
||||||
fun aLoadedContentState(
|
fun aLoadedContentState(
|
||||||
roomId: RoomId = A_ROOM_ID,
|
roomId: RoomId = A_ROOM_ID,
|
||||||
name: String = "Element X android",
|
name: String? = "Element X android",
|
||||||
alias: RoomAlias? = RoomAlias("#exa:matrix.org"),
|
alias: RoomAlias? = RoomAlias("#exa:matrix.org"),
|
||||||
topic: String? = "Element X is a secure, private and decentralized messenger.",
|
topic: String? = "Element X is a secure, private and decentralized messenger.",
|
||||||
numberOfMembers: Long? = null,
|
numberOfMembers: Long? = null,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
|
roomType: RoomType = RoomType.Room,
|
||||||
roomAvatarUrl: String? = null,
|
roomAvatarUrl: String? = null,
|
||||||
joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown
|
joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown
|
||||||
) = ContentState.Loaded(
|
) = ContentState.Loaded(
|
||||||
|
|
@ -99,6 +117,7 @@ fun aLoadedContentState(
|
||||||
topic = topic,
|
topic = topic,
|
||||||
numberOfMembers = numberOfMembers,
|
numberOfMembers = numberOfMembers,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
|
roomType = roomType,
|
||||||
roomAvatarUrl = roomAvatarUrl,
|
roomAvatarUrl = roomAvatarUrl,
|
||||||
joinAuthorisationStatus = joinAuthorisationStatus
|
joinAuthorisationStatus = joinAuthorisationStatus
|
||||||
)
|
)
|
||||||
|
|
@ -112,6 +131,7 @@ fun aJoinRoomState(
|
||||||
contentState = contentState,
|
contentState = contentState,
|
||||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||||
knockAction = knockAction,
|
knockAction = knockAction,
|
||||||
|
applicationName = "AppName",
|
||||||
eventSink = eventSink
|
eventSink = eventSink
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -29,9 +31,11 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom
|
||||||
|
|
@ -54,6 +58,7 @@ import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
import io.element.android.libraries.matrix.ui.components.InviteSenderView
|
import io.element.android.libraries.matrix.ui.components.InviteSenderView
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -75,7 +80,10 @@ fun JoinRoomView(
|
||||||
JoinRoomTopBar(onBackClicked = onBackPressed)
|
JoinRoomTopBar(onBackClicked = onBackPressed)
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
JoinRoomContent(contentState = state.contentState)
|
JoinRoomContent(
|
||||||
|
contentState = state.contentState,
|
||||||
|
applicationName = state.applicationName,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
footer = {
|
footer = {
|
||||||
JoinRoomFooter(
|
JoinRoomFooter(
|
||||||
|
|
@ -94,7 +102,8 @@ fun JoinRoomView(
|
||||||
},
|
},
|
||||||
onRetry = {
|
onRetry = {
|
||||||
state.eventSink(JoinRoomEvents.RetryFetchingContent)
|
state.eventSink(JoinRoomEvents.RetryFetchingContent)
|
||||||
}
|
},
|
||||||
|
onGoBack = onBackPressed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -115,6 +124,7 @@ private fun JoinRoomFooter(
|
||||||
onJoinRoom: () -> Unit,
|
onJoinRoom: () -> Unit,
|
||||||
onKnockRoom: () -> Unit,
|
onKnockRoom: () -> Unit,
|
||||||
onRetry: () -> Unit,
|
onRetry: () -> Unit,
|
||||||
|
onGoBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (state.contentState is ContentState.Failure) {
|
if (state.contentState is ContentState.Failure) {
|
||||||
|
|
@ -124,6 +134,13 @@ private fun JoinRoomFooter(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
size = ButtonSize.Large,
|
size = ButtonSize.Large,
|
||||||
)
|
)
|
||||||
|
} else if (state.contentState is ContentState.Loaded && state.contentState.roomType == RoomType.Space) {
|
||||||
|
Button(
|
||||||
|
text = stringResource(CommonStrings.action_go_back),
|
||||||
|
onClick = onGoBack,
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
size = ButtonSize.Large,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val joinAuthorisationStatus = state.joinAuthorisationStatus
|
val joinAuthorisationStatus = state.joinAuthorisationStatus
|
||||||
when (joinAuthorisationStatus) {
|
when (joinAuthorisationStatus) {
|
||||||
|
|
@ -170,6 +187,7 @@ private fun JoinRoomFooter(
|
||||||
@Composable
|
@Composable
|
||||||
private fun JoinRoomContent(
|
private fun JoinRoomContent(
|
||||||
contentState: ContentState,
|
contentState: ContentState,
|
||||||
|
applicationName: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
when (contentState) {
|
when (contentState) {
|
||||||
|
|
@ -180,10 +198,21 @@ private fun JoinRoomContent(
|
||||||
Avatar(contentState.avatarData(AvatarSize.RoomHeader))
|
Avatar(contentState.avatarData(AvatarSize.RoomHeader))
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
RoomPreviewTitleAtom(contentState.computedTitle)
|
if (contentState.name != null) {
|
||||||
|
RoomPreviewTitleAtom(
|
||||||
|
title = contentState.name,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
RoomPreviewTitleAtom(
|
||||||
|
title = stringResource(id = CommonStrings.common_no_room_name),
|
||||||
|
fontStyle = FontStyle.Italic
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
subtitle = {
|
subtitle = {
|
||||||
RoomPreviewSubtitleAtom(contentState.computedSubtitle)
|
if (contentState.alias != null) {
|
||||||
|
RoomPreviewSubtitleAtom(contentState.alias.value)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
description = {
|
description = {
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -195,6 +224,21 @@ private fun JoinRoomContent(
|
||||||
InviteSenderView(inviteSender = inviteSender)
|
InviteSenderView(inviteSender = inviteSender)
|
||||||
}
|
}
|
||||||
RoomPreviewDescriptionAtom(contentState.topic ?: "")
|
RoomPreviewDescriptionAtom(contentState.topic ?: "")
|
||||||
|
if (contentState.roomType == RoomType.Space) {
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.screen_join_room_space_not_supported_title),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.screen_join_room_space_not_supported_description, applicationName),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
memberCount = {
|
memberCount = {
|
||||||
|
|
@ -250,7 +294,7 @@ private fun JoinRoomContent(
|
||||||
},
|
},
|
||||||
subtitle = {
|
subtitle = {
|
||||||
Text(
|
Text(
|
||||||
text = "Failed to get information about the room",
|
text = stringResource(id = CommonStrings.error_unknown),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||||
import io.element.android.features.joinroom.impl.JoinRoomPresenter
|
import io.element.android.features.joinroom.impl.JoinRoomPresenter
|
||||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -37,6 +38,7 @@ object JoinRoomModule {
|
||||||
client: MatrixClient,
|
client: MatrixClient,
|
||||||
knockRoom: KnockRoom,
|
knockRoom: KnockRoom,
|
||||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||||
|
buildMeta: BuildMeta,
|
||||||
): JoinRoomPresenter.Factory {
|
): JoinRoomPresenter.Factory {
|
||||||
return object : JoinRoomPresenter.Factory {
|
return object : JoinRoomPresenter.Factory {
|
||||||
override fun create(
|
override fun create(
|
||||||
|
|
@ -51,6 +53,7 @@ object JoinRoomModule {
|
||||||
matrixClient = client,
|
matrixClient = client,
|
||||||
knockRoom = knockRoom,
|
knockRoom = knockRoom,
|
||||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
||||||
|
buildMeta = buildMeta,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<string name="screen_join_room_join_action">"Join room"</string>
|
<string name="screen_join_room_join_action">"Join room"</string>
|
||||||
<string name="screen_join_room_knock_action">"Knock to join"</string>
|
<string name="screen_join_room_knock_action">"Knock to join"</string>
|
||||||
|
<string name="screen_join_room_space_not_supported_description">"%1$s does not support spaces yet. You can access spaces on web."</string>
|
||||||
|
<string name="screen_join_room_space_not_supported_title">"Spaces are not supported yet"</string>
|
||||||
<string name="screen_join_room_subtitle_knock">"Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."</string>
|
<string name="screen_join_room_subtitle_knock">"Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."</string>
|
||||||
<string name="screen_join_room_subtitle_no_preview">"You must be a member of this room to view the message history."</string>
|
<string name="screen_join_room_subtitle_no_preview">"You must be a member of this room to view the message history."</string>
|
||||||
<string name="screen_join_room_title_knock">"Want to join this room?"</string>
|
<string name="screen_join_room_title_knock">"Want to join this room?"</string>
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,20 @@ import io.element.android.features.joinroom.impl.di.KnockRoom
|
||||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||||
|
|
@ -61,6 +64,7 @@ class JoinRoomPresenterTest {
|
||||||
assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias()))
|
assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias()))
|
||||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown)
|
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown)
|
||||||
assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState())
|
assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState())
|
||||||
|
assertThat(state.applicationName).isEqualTo("AppName")
|
||||||
cancelAndIgnoreRemainingEvents()
|
cancelAndIgnoreRemainingEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +319,7 @@ class JoinRoomPresenterTest {
|
||||||
topic = "Room topic",
|
topic = "Room topic",
|
||||||
avatarUrl = "avatarUrl",
|
avatarUrl = "avatarUrl",
|
||||||
numberOfJoinedMembers = 2,
|
numberOfJoinedMembers = 2,
|
||||||
roomType = null,
|
roomType = RoomType.Room,
|
||||||
isHistoryWorldReadable = false,
|
isHistoryWorldReadable = false,
|
||||||
isJoined = false,
|
isJoined = false,
|
||||||
isInvited = false,
|
isInvited = false,
|
||||||
|
|
@ -339,6 +343,7 @@ class JoinRoomPresenterTest {
|
||||||
alias = RoomAlias("#alias:matrix.org"),
|
alias = RoomAlias("#alias:matrix.org"),
|
||||||
numberOfMembers = 2,
|
numberOfMembers = 2,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
|
roomType = RoomType.Room,
|
||||||
roomAvatarUrl = "avatarUrl",
|
roomAvatarUrl = "avatarUrl",
|
||||||
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin
|
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin
|
||||||
)
|
)
|
||||||
|
|
@ -412,6 +417,7 @@ class JoinRoomPresenterTest {
|
||||||
roomDescription: Optional<RoomDescription> = Optional.empty(),
|
roomDescription: Optional<RoomDescription> = Optional.empty(),
|
||||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||||
knockRoom: KnockRoom = FakeKnockRoom(),
|
knockRoom: KnockRoom = FakeKnockRoom(),
|
||||||
|
buildMeta: BuildMeta = aBuildMeta(applicationName = "AppName"),
|
||||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() }
|
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() }
|
||||||
): JoinRoomPresenter {
|
): JoinRoomPresenter {
|
||||||
return JoinRoomPresenter(
|
return JoinRoomPresenter(
|
||||||
|
|
@ -420,6 +426,7 @@ class JoinRoomPresenterTest {
|
||||||
roomDescription = roomDescription,
|
roomDescription = roomDescription,
|
||||||
matrixClient = matrixClient,
|
matrixClient = matrixClient,
|
||||||
knockRoom = knockRoom,
|
knockRoom = knockRoom,
|
||||||
|
buildMeta = buildMeta,
|
||||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter
|
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||||
import io.element.android.tests.testutils.EventsRecorder
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
|
|
@ -128,6 +129,21 @@ class JoinRoomViewTest {
|
||||||
rule.clickOn(CommonStrings.action_retry)
|
rule.clickOn(CommonStrings.action_retry)
|
||||||
eventsRecorder.assertSingle(JoinRoomEvents.RetryFetchingContent)
|
eventsRecorder.assertSingle(JoinRoomEvents.RetryFetchingContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `clicking on Go back when a space is displayed invokes the expected callback`() {
|
||||||
|
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||||
|
ensureCalledOnce {
|
||||||
|
rule.setJoinRoomView(
|
||||||
|
aJoinRoomState(
|
||||||
|
contentState = aLoadedContentState(roomType = RoomType.Space),
|
||||||
|
eventSink = eventsRecorder,
|
||||||
|
),
|
||||||
|
onBackPressed = it
|
||||||
|
)
|
||||||
|
rule.clickOn(CommonStrings.action_go_back)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinRoomView(
|
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinRoomView(
|
||||||
|
|
|
||||||
|
|
@ -42,16 +42,21 @@ private fun anEditDefaultNotificationSettingsState(
|
||||||
) = EditDefaultNotificationSettingState(
|
) = EditDefaultNotificationSettingState(
|
||||||
isOneToOne = isOneToOne,
|
isOneToOne = isOneToOne,
|
||||||
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||||
roomsWithUserDefinedMode = persistentListOf(aRoomSummary()),
|
roomsWithUserDefinedMode = persistentListOf(
|
||||||
|
aRoomSummary("Room"),
|
||||||
|
aRoomSummary(null),
|
||||||
|
),
|
||||||
changeNotificationSettingAction = changeNotificationSettingAction,
|
changeNotificationSettingAction = changeNotificationSettingAction,
|
||||||
displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer,
|
displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun aRoomSummary() = RoomSummary.Filled(
|
private fun aRoomSummary(
|
||||||
|
name: String?,
|
||||||
|
) = RoomSummary.Filled(
|
||||||
aRoomSummaryDetails(
|
aRoomSummaryDetails(
|
||||||
roomId = RoomId("!roomId:domain"),
|
roomId = RoomId("!roomId:domain"),
|
||||||
name = "Room",
|
name = name,
|
||||||
avatarUrl = null,
|
avatarUrl = null,
|
||||||
isDirect = false,
|
isDirect = false,
|
||||||
lastMessage = null,
|
lastMessage = null,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.selection.selectableGroup
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.preferences.impl.R
|
import io.element.android.features.preferences.impl.R
|
||||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||||
|
|
@ -100,7 +101,11 @@ fun EditDefaultNotificationSettingView(
|
||||||
)
|
)
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(text = summary.details.name)
|
val roomName = summary.details.name
|
||||||
|
Text(
|
||||||
|
text = roomName ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { roomName == null }
|
||||||
|
)
|
||||||
},
|
},
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Text(text = subtitle)
|
Text(text = subtitle)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||||
|
|
@ -87,8 +89,9 @@ private fun RoomListModalBottomSheetContent(
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
text = contextMenu.roomName,
|
text = contextMenu.roomName ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { contextMenu.roomName == null }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -192,22 +195,11 @@ private fun RoomListModalBottomSheetContent(
|
||||||
// Remove this preview when the issue is fixed.
|
// Remove this preview when the issue is fixed.
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun RoomListModalBottomSheetContentPreview() = ElementPreview {
|
internal fun RoomListModalBottomSheetContentPreview(
|
||||||
|
@PreviewParameter(RoomListStateContextMenuShownProvider::class) contextMenu: RoomListState.ContextMenu.Shown
|
||||||
|
) = ElementPreview {
|
||||||
RoomListModalBottomSheetContent(
|
RoomListModalBottomSheetContent(
|
||||||
contextMenu = aContextMenuShown(hasNewContent = true),
|
contextMenu = contextMenu,
|
||||||
onRoomMarkReadClicked = {},
|
|
||||||
onRoomMarkUnreadClicked = {},
|
|
||||||
onRoomSettingsClicked = {},
|
|
||||||
onLeaveRoomClicked = {},
|
|
||||||
onFavoriteChanged = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun RoomListModalBottomSheetContentForDmPreview() = ElementPreview {
|
|
||||||
RoomListModalBottomSheetContent(
|
|
||||||
contextMenu = aContextMenuShown(isDm = true),
|
|
||||||
onRoomMarkReadClicked = {},
|
onRoomMarkReadClicked = {},
|
||||||
onRoomMarkUnreadClicked = {},
|
onRoomMarkUnreadClicked = {},
|
||||||
onRoomSettingsClicked = {},
|
onRoomSettingsClicked = {},
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ class RoomListPresenter @Inject constructor(
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal fun RoomListRoomSummary.toInviteData() = InviteData(
|
internal fun RoomListRoomSummary.toInviteData() = InviteData(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
roomName = name,
|
// Note: `name` should not be null at this point, but just in case, fallback to the roomId
|
||||||
|
roomName = name ?: roomId.value,
|
||||||
isDirect = isDirect,
|
isDirect = isDirect,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ data class RoomListState(
|
||||||
data object Hidden : ContextMenu
|
data object Hidden : ContextMenu
|
||||||
data class Shown(
|
data class Shown(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val roomName: String,
|
val roomName: String?,
|
||||||
val isDm: Boolean,
|
val isDm: Boolean,
|
||||||
val isFavorite: Boolean,
|
val isFavorite: Boolean,
|
||||||
val markAsUnreadFeatureFlagEnabled: Boolean,
|
val markAsUnreadFeatureFlagEnabled: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomlist.impl
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
|
open class RoomListStateContextMenuShownProvider : PreviewParameterProvider<RoomListState.ContextMenu.Shown> {
|
||||||
|
override val values: Sequence<RoomListState.ContextMenu.Shown>
|
||||||
|
get() = sequenceOf(
|
||||||
|
aContextMenuShown(hasNewContent = true),
|
||||||
|
aContextMenuShown(isDm = true),
|
||||||
|
aContextMenuShown(roomName = null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun aContextMenuShown(
|
||||||
|
roomName: String? = "aRoom",
|
||||||
|
isDm: Boolean = false,
|
||||||
|
hasNewContent: Boolean = false,
|
||||||
|
isFavorite: Boolean = false,
|
||||||
|
) = RoomListState.ContextMenu.Shown(
|
||||||
|
roomId = RoomId("!aRoom:aDomain"),
|
||||||
|
roomName = roomName,
|
||||||
|
isDm = isDm,
|
||||||
|
markAsUnreadFeatureFlagEnabled = true,
|
||||||
|
hasNewContent = hasNewContent,
|
||||||
|
isFavorite = isFavorite,
|
||||||
|
)
|
||||||
|
|
@ -32,7 +32,6 @@ import io.element.android.features.roomlist.impl.search.aRoomListSearchState
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
@ -45,6 +44,7 @@ open class RoomListStateProvider : PreviewParameterProvider<RoomListState> {
|
||||||
aRoomListState(),
|
aRoomListState(),
|
||||||
aRoomListState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)),
|
aRoomListState(snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete)),
|
||||||
aRoomListState(hasNetworkConnection = false),
|
aRoomListState(hasNetworkConnection = false),
|
||||||
|
aRoomListState(contextMenu = aContextMenuShown(roomName = null)),
|
||||||
aRoomListState(contextMenu = aContextMenuShown(roomName = "A nice room name")),
|
aRoomListState(contextMenu = aContextMenuShown(roomName = "A nice room name")),
|
||||||
aRoomListState(contextMenu = aContextMenuShown(isFavorite = true)),
|
aRoomListState(contextMenu = aContextMenuShown(isFavorite = true)),
|
||||||
aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation)),
|
aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation)),
|
||||||
|
|
@ -109,7 +109,7 @@ internal fun aRoomListRoomSummaryList(): ImmutableList<RoomListRoomSummary> {
|
||||||
),
|
),
|
||||||
aRoomListRoomSummary(
|
aRoomListRoomSummary(
|
||||||
id = "!roomId3:domain",
|
id = "!roomId3:domain",
|
||||||
displayType = RoomSummaryDisplayType.PLACEHOLDER,
|
displayType = RoomSummaryDisplayType.PLACEHOLDER,
|
||||||
),
|
),
|
||||||
aRoomListRoomSummary(
|
aRoomListRoomSummary(
|
||||||
id = "!roomId4:domain",
|
id = "!roomId4:domain",
|
||||||
|
|
@ -117,17 +117,3 @@ internal fun aRoomListRoomSummaryList(): ImmutableList<RoomListRoomSummary> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aContextMenuShown(
|
|
||||||
roomName: String = "aRoom",
|
|
||||||
isDm: Boolean = false,
|
|
||||||
hasNewContent: Boolean = false,
|
|
||||||
isFavorite: Boolean = false,
|
|
||||||
) = RoomListState.ContextMenu.Shown(
|
|
||||||
roomId = RoomId("!aRoom:aDomain"),
|
|
||||||
roomName = roomName,
|
|
||||||
isDm = isDm,
|
|
||||||
markAsUnreadFeatureFlagEnabled = true,
|
|
||||||
hasNewContent = hasNewContent,
|
|
||||||
isFavorite = isFavorite,
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -168,7 +169,7 @@ private fun RoomSummaryScaffoldRow(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NameAndTimestampRow(
|
private fun NameAndTimestampRow(
|
||||||
name: String,
|
name: String?,
|
||||||
timestamp: String?,
|
timestamp: String?,
|
||||||
isHighlighted: Boolean,
|
isHighlighted: Boolean,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
|
|
@ -181,7 +182,8 @@ private fun NameAndTimestampRow(
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
text = name,
|
text = name ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { name == null },
|
||||||
color = MaterialTheme.roomListRoomName(),
|
color = MaterialTheme.roomListRoomName(),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
|
@ -272,7 +274,7 @@ private fun LastMessageAndIndicatorRow(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun InviteNameAndIndicatorRow(
|
private fun InviteNameAndIndicatorRow(
|
||||||
name: String,
|
name: String?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -283,7 +285,8 @@ private fun InviteNameAndIndicatorRow(
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
text = name,
|
text = name ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { name == null },
|
||||||
color = MaterialTheme.roomListRoomName(),
|
color = MaterialTheme.roomListRoomName(),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ data class RoomListRoomSummary(
|
||||||
val id: String,
|
val id: String,
|
||||||
val displayType: RoomSummaryDisplayType,
|
val displayType: RoomSummaryDisplayType,
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val name: String,
|
val name: String?,
|
||||||
val canonicalAlias: RoomAlias?,
|
val canonicalAlias: RoomAlias?,
|
||||||
val numberOfUnreadMessages: Int,
|
val numberOfUnreadMessages: Int,
|
||||||
val numberOfUnreadMentions: Int,
|
val numberOfUnreadMentions: Int,
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSu
|
||||||
listOf(
|
listOf(
|
||||||
aRoomListRoomSummary(displayType = RoomSummaryDisplayType.PLACEHOLDER),
|
aRoomListRoomSummary(displayType = RoomSummaryDisplayType.PLACEHOLDER),
|
||||||
aRoomListRoomSummary(),
|
aRoomListRoomSummary(),
|
||||||
|
aRoomListRoomSummary(name = null),
|
||||||
aRoomListRoomSummary(lastMessage = null),
|
aRoomListRoomSummary(lastMessage = null),
|
||||||
aRoomListRoomSummary(
|
aRoomListRoomSummary(
|
||||||
name = "A very long room name that should be truncated",
|
name = "A very long room name that should be truncated",
|
||||||
|
|
@ -100,7 +101,15 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSu
|
||||||
displayName = "Bob",
|
displayName = "Bob",
|
||||||
),
|
),
|
||||||
isDirect = true,
|
isDirect = true,
|
||||||
)
|
),
|
||||||
|
aRoomListRoomSummary(
|
||||||
|
name = null,
|
||||||
|
displayType = RoomSummaryDisplayType.INVITE,
|
||||||
|
inviteSender = anInviteSender(
|
||||||
|
userId = UserId("@bob:matrix.org"),
|
||||||
|
displayName = "Bob",
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
).flatten()
|
).flatten()
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +126,7 @@ internal fun anInviteSender(
|
||||||
|
|
||||||
internal fun aRoomListRoomSummary(
|
internal fun aRoomListRoomSummary(
|
||||||
id: String = "!roomId:domain",
|
id: String = "!roomId:domain",
|
||||||
name: String = "Room name",
|
name: String? = "Room name",
|
||||||
numberOfUnreadMessages: Int = 0,
|
numberOfUnreadMessages: Int = 0,
|
||||||
numberOfUnreadMentions: Int = 0,
|
numberOfUnreadMentions: Int = 0,
|
||||||
numberOfUnreadNotifications: Int = 0,
|
numberOfUnreadNotifications: Int = 0,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ fun RoomPreviewSubtitleAtom(subtitle: String, modifier: Modifier = Modifier) {
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = subtitle,
|
text = subtitle,
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = ElementTheme.colors.textSecondary,
|
color = ElementTheme.colors.textSecondary,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,23 @@ package io.element.android.libraries.designsystem.atomic.atoms
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomPreviewTitleAtom(title: String, modifier: Modifier = Modifier) {
|
fun RoomPreviewTitleAtom(
|
||||||
|
title: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
fontStyle: FontStyle? = null,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = title,
|
text = title,
|
||||||
style = ElementTheme.typography.fontHeadingMdBold,
|
style = ElementTheme.typography.fontHeadingMdBold,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
fontStyle = fontStyle,
|
||||||
color = ElementTheme.colors.textPrimary,
|
color = ElementTheme.colors.textPrimary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ data class AvatarData(
|
||||||
val size: AvatarSize,
|
val size: AvatarSize,
|
||||||
) {
|
) {
|
||||||
val initial by lazy {
|
val initial by lazy {
|
||||||
(name?.takeIf { it.isNotBlank() } ?: id)
|
// For roomIds, use "#" as initial
|
||||||
|
(name?.takeIf { it.isNotBlank() } ?: id.takeIf { !it.startsWith("!") } ?: "#")
|
||||||
.let { dn ->
|
.let { dn ->
|
||||||
var startIndex = 0
|
var startIndex = 0
|
||||||
val initial = dn[startIndex]
|
val initial = dn[startIndex]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.room
|
||||||
|
|
||||||
|
sealed interface RoomType {
|
||||||
|
data object Space : RoomType
|
||||||
|
data object Room : RoomType
|
||||||
|
data class Other(val type: String) : RoomType
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.api.room.preview
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
|
|
||||||
data class RoomPreview(
|
data class RoomPreview(
|
||||||
/** The room id for this room. */
|
/** The room id for this room. */
|
||||||
|
|
@ -33,7 +34,7 @@ data class RoomPreview(
|
||||||
/** The number of joined members. */
|
/** The number of joined members. */
|
||||||
val numberOfJoinedMembers: Long,
|
val numberOfJoinedMembers: Long,
|
||||||
/** The room type (space, custom) or nothing, if it's a regular room. */
|
/** The room type (space, custom) or nothing, if it's a regular room. */
|
||||||
val roomType: String?,
|
val roomType: RoomType,
|
||||||
/** Is the history world-readable for this room? */
|
/** Is the history world-readable for this room? */
|
||||||
val isHistoryWorldReadable: Boolean,
|
val isHistoryWorldReadable: Boolean,
|
||||||
/** Is the room joined by the current user? */
|
/** Is the room joined by the current user? */
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ sealed interface RoomSummary {
|
||||||
|
|
||||||
data class RoomSummaryDetails(
|
data class RoomSummaryDetails(
|
||||||
val roomId: RoomId,
|
val roomId: RoomId,
|
||||||
val name: String,
|
val name: String?,
|
||||||
val canonicalAlias: RoomAlias?,
|
val canonicalAlias: RoomAlias?,
|
||||||
val isDirect: Boolean,
|
val isDirect: Boolean,
|
||||||
val avatarUrl: String?,
|
val avatarUrl: String?,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.room
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomType
|
||||||
|
|
||||||
|
fun String?.toRoomType(): RoomType {
|
||||||
|
return when (this) {
|
||||||
|
null -> RoomType.Room
|
||||||
|
"m.space" -> RoomType.Space
|
||||||
|
else -> RoomType.Other(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.impl.room.preview
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||||
|
import io.element.android.libraries.matrix.impl.room.toRoomType
|
||||||
import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview
|
import org.matrix.rustcomponents.sdk.RoomPreview as RustRoomPreview
|
||||||
|
|
||||||
object RoomPreviewMapper {
|
object RoomPreviewMapper {
|
||||||
|
|
@ -30,7 +31,7 @@ object RoomPreviewMapper {
|
||||||
topic = roomPreview.topic,
|
topic = roomPreview.topic,
|
||||||
avatarUrl = roomPreview.avatarUrl,
|
avatarUrl = roomPreview.avatarUrl,
|
||||||
numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(),
|
numberOfJoinedMembers = roomPreview.numJoinedMembers.toLong(),
|
||||||
roomType = roomPreview.roomType,
|
roomType = roomPreview.roomType.toRoomType(),
|
||||||
isHistoryWorldReadable = roomPreview.isHistoryWorldReadable,
|
isHistoryWorldReadable = roomPreview.isHistoryWorldReadable,
|
||||||
isJoined = roomPreview.isJoined,
|
isJoined = roomPreview.isJoined,
|
||||||
isInvited = roomPreview.isInvited,
|
isInvited = roomPreview.isInvited,
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ val RoomListFilter.predicate
|
||||||
(roomSummary.details.numUnreadNotifications > 0 || roomSummary.details.isMarkedUnread)
|
(roomSummary.details.numUnreadNotifications > 0 || roomSummary.details.isMarkedUnread)
|
||||||
}
|
}
|
||||||
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
||||||
roomSummary is RoomSummary.Filled && roomSummary.details.name.contains(pattern, ignoreCase = true)
|
roomSummary is RoomSummary.Filled && roomSummary.details.name.orEmpty().contains(pattern, ignoreCase = true)
|
||||||
}
|
}
|
||||||
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
||||||
roomSummary.isInvited()
|
roomSummary.isInvited()
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
|
||||||
}
|
}
|
||||||
return RoomSummaryDetails(
|
return RoomSummaryDetails(
|
||||||
roomId = RoomId(roomInfo.id),
|
roomId = RoomId(roomInfo.id),
|
||||||
name = roomInfo.name ?: roomInfo.id,
|
name = roomInfo.name,
|
||||||
canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias),
|
canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias),
|
||||||
isDirect = roomInfo.isDirect,
|
isDirect = roomInfo.isDirect,
|
||||||
avatarUrl = roomInfo.avatarUrl,
|
avatarUrl = roomInfo.avatarUrl,
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ fun aRoomSummaryFilled(
|
||||||
|
|
||||||
fun aRoomSummaryDetails(
|
fun aRoomSummaryDetails(
|
||||||
roomId: RoomId = A_ROOM_ID,
|
roomId: RoomId = A_ROOM_ID,
|
||||||
name: String = A_ROOM_NAME,
|
name: String? = A_ROOM_NAME,
|
||||||
isDirect: Boolean = false,
|
isDirect: Boolean = false,
|
||||||
avatarUrl: String? = null,
|
avatarUrl: String? = null,
|
||||||
lastMessage: RoomMessage? = aRoomMessage(),
|
lastMessage: RoomMessage? = aRoomMessage(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||||
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
||||||
|
|
||||||
|
open class RoomSummaryDetailsProvider : PreviewParameterProvider<RoomSummaryDetails> {
|
||||||
|
override val values: Sequence<RoomSummaryDetails>
|
||||||
|
get() = sequenceOf(
|
||||||
|
aRoomSummaryDetails(),
|
||||||
|
aRoomSummaryDetails(name = null),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun aRoomSummaryDetails(
|
||||||
|
roomId: RoomId = RoomId("!room:domain"),
|
||||||
|
name: String? = "roomName",
|
||||||
|
canonicalAlias: RoomAlias? = null,
|
||||||
|
isDirect: Boolean = true,
|
||||||
|
avatarUrl: String? = null,
|
||||||
|
lastMessage: RoomMessage? = null,
|
||||||
|
inviter: RoomMember? = null,
|
||||||
|
notificationMode: RoomNotificationMode? = null,
|
||||||
|
hasRoomCall: Boolean = false,
|
||||||
|
isDm: Boolean = false,
|
||||||
|
numUnreadMentions: Int = 0,
|
||||||
|
numUnreadMessages: Int = 0,
|
||||||
|
numUnreadNotifications: Int = 0,
|
||||||
|
isMarkedUnread: Boolean = false,
|
||||||
|
isFavorite: Boolean = false,
|
||||||
|
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||||
|
) = RoomSummaryDetails(
|
||||||
|
roomId = roomId,
|
||||||
|
name = name,
|
||||||
|
canonicalAlias = canonicalAlias,
|
||||||
|
isDirect = isDirect,
|
||||||
|
avatarUrl = avatarUrl,
|
||||||
|
lastMessage = lastMessage,
|
||||||
|
inviter = inviter,
|
||||||
|
userDefinedNotificationMode = notificationMode,
|
||||||
|
hasRoomCall = hasRoomCall,
|
||||||
|
isDm = isDm,
|
||||||
|
numUnreadMentions = numUnreadMentions,
|
||||||
|
numUnreadMessages = numUnreadMessages,
|
||||||
|
numUnreadNotifications = numUnreadNotifications,
|
||||||
|
isMarkedUnread = isMarkedUnread,
|
||||||
|
isFavorite = isFavorite,
|
||||||
|
currentUserMembership = currentUserMembership,
|
||||||
|
)
|
||||||
|
|
@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
|
|
@ -43,12 +44,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
|
||||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -67,7 +62,8 @@ fun SelectedRoom(
|
||||||
) {
|
) {
|
||||||
Avatar(AvatarData(roomSummary.roomId.value, roomSummary.name, roomSummary.avatarUrl, AvatarSize.SelectedRoom))
|
Avatar(AvatarData(roomSummary.roomId.value, roomSummary.name, roomSummary.avatarUrl, AvatarSize.SelectedRoom))
|
||||||
Text(
|
Text(
|
||||||
text = roomSummary.name,
|
// If name is null, we do not have space to render "No room name", so just use `#` here.
|
||||||
|
text = roomSummary.name ?: "#",
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
@ -97,45 +93,11 @@ fun SelectedRoom(
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SelectedRoomPreview() = ElementPreview {
|
internal fun SelectedRoomPreview(
|
||||||
|
@PreviewParameter(RoomSummaryDetailsProvider::class) roomSummaryDetails: RoomSummaryDetails
|
||||||
|
) = ElementPreview {
|
||||||
SelectedRoom(
|
SelectedRoom(
|
||||||
roomSummary = aRoomSummaryDetails(),
|
roomSummary = roomSummaryDetails,
|
||||||
onRoomRemoved = {},
|
onRoomRemoved = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aRoomSummaryDetails(
|
|
||||||
roomId: RoomId = RoomId("!room:domain"),
|
|
||||||
name: String = "roomName",
|
|
||||||
canonicalAlias: RoomAlias? = null,
|
|
||||||
isDirect: Boolean = true,
|
|
||||||
avatarUrl: String? = null,
|
|
||||||
lastMessage: RoomMessage? = null,
|
|
||||||
inviter: RoomMember? = null,
|
|
||||||
notificationMode: RoomNotificationMode? = null,
|
|
||||||
hasRoomCall: Boolean = false,
|
|
||||||
isDm: Boolean = false,
|
|
||||||
numUnreadMentions: Int = 0,
|
|
||||||
numUnreadMessages: Int = 0,
|
|
||||||
numUnreadNotifications: Int = 0,
|
|
||||||
isMarkedUnread: Boolean = false,
|
|
||||||
isFavorite: Boolean = false,
|
|
||||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
|
||||||
) = RoomSummaryDetails(
|
|
||||||
roomId = roomId,
|
|
||||||
name = name,
|
|
||||||
canonicalAlias = canonicalAlias,
|
|
||||||
isDirect = isDirect,
|
|
||||||
avatarUrl = avatarUrl,
|
|
||||||
lastMessage = lastMessage,
|
|
||||||
inviter = inviter,
|
|
||||||
userDefinedNotificationMode = notificationMode,
|
|
||||||
hasRoomCall = hasRoomCall,
|
|
||||||
isDm = isDm,
|
|
||||||
numUnreadMentions = numUnreadMentions,
|
|
||||||
numUnreadMessages = numUnreadMessages,
|
|
||||||
numUnreadNotifications = numUnreadNotifications,
|
|
||||||
isMarkedUnread = isMarkedUnread,
|
|
||||||
isFavorite = isFavorite,
|
|
||||||
currentUserMembership = currentUserMembership,
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ class RoomSelectPresenter @AssistedInject constructor(
|
||||||
LaunchedEffect(query, summaries) {
|
LaunchedEffect(query, summaries) {
|
||||||
val filteredSummaries = summaries.filterIsInstance<RoomSummary.Filled>()
|
val filteredSummaries = summaries.filterIsInstance<RoomSummary.Filled>()
|
||||||
.map { it.details }
|
.map { it.details }
|
||||||
.filter { it.name.contains(query, ignoreCase = true) }
|
.filter { it.name.orEmpty().contains(query, ignoreCase = true) }
|
||||||
.distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received
|
.distinctBy { it.roomId } // This should be removed once we're sure no duplicate Rooms can be received
|
||||||
.toPersistentList()
|
.toPersistentList()
|
||||||
results = if (filteredSummaries.isNotEmpty()) {
|
results = if (filteredSummaries.isNotEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -68,4 +68,8 @@ private fun aForwardMessagesRoomList() = persistentListOf(
|
||||||
name = "Room with alias",
|
name = "Room with alias",
|
||||||
canonicalAlias = RoomAlias("#alias:example.org"),
|
canonicalAlias = RoomAlias("#alias:example.org"),
|
||||||
),
|
),
|
||||||
|
aRoomSummaryDetails(
|
||||||
|
roomId = RoomId("!room3:domain"),
|
||||||
|
name = null,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -235,7 +236,8 @@ private fun RoomSummaryView(
|
||||||
// Name
|
// Name
|
||||||
Text(
|
Text(
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
text = summary.name,
|
text = summary.name ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { summary.name == null },
|
||||||
color = ElementTheme.colors.textPrimary,
|
color = ElementTheme.colors.textPrimary,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@
|
||||||
<string name="common_modern">"Modern"</string>
|
<string name="common_modern">"Modern"</string>
|
||||||
<string name="common_mute">"Mute"</string>
|
<string name="common_mute">"Mute"</string>
|
||||||
<string name="common_no_results">"No results"</string>
|
<string name="common_no_results">"No results"</string>
|
||||||
|
<string name="common_no_room_name">"No room name"</string>
|
||||||
<string name="common_offline">"Offline"</string>
|
<string name="common_offline">"Offline"</string>
|
||||||
<string name="common_or">"or"</string>
|
<string name="common_or">"or"</string>
|
||||||
<string name="common_password">"Password"</string>
|
<string name="common_password">"Password"</string>
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ class KonsistPreviewTest {
|
||||||
"PollContentViewEndedPreview",
|
"PollContentViewEndedPreview",
|
||||||
"PollContentViewUndisclosedPreview",
|
"PollContentViewUndisclosedPreview",
|
||||||
"ReadReceiptBottomSheetPreview",
|
"ReadReceiptBottomSheetPreview",
|
||||||
"RoomListModalBottomSheetContentForDmPreview",
|
|
||||||
"RoomMemberListViewBannedPreview",
|
"RoomMemberListViewBannedPreview",
|
||||||
"SasEmojisPreview",
|
"SasEmojisPreview",
|
||||||
"SecureBackupSetupViewChangePreview",
|
"SecureBackupSetupViewChangePreview",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d7c1c180f53b948992d1f14d9635bcfee13fad55e772c9d74af62a747928a540
|
oid sha256:b170e67502c4df969aa1c0f442871440a39dbc63cdcac04906f297b33ae3c70b
|
||||||
size 143814
|
size 142579
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1dc731d4a0c2aeb383035b3781f1e319fe34445e83324458e997df22a3d09bdc
|
oid sha256:90e0e2a860d813f804b6888bdf5aa52982b9edf40cff2c8611ace953fa20791e
|
||||||
size 145241
|
size 133263
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2678e82f1e260b67ebbca47b41a0283d89bbc1799905c32f4e90821d7f41ce6c
|
oid sha256:9bc75857481156b0d924badae1aad0dcbb20d9c0f1b36862af3cf8634aca2c11
|
||||||
size 148232
|
size 144750
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3da95310c379b64cece500e9a942170f227dfe4c9d856690d2343ae9aaf47dc5
|
oid sha256:2a0db543ffe82db423c02a1e59533bb1f6fe867e23e7d37e70166c210da422a7
|
||||||
size 144682
|
size 148258
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2128dda46d28adeabcd77ae254b98e5305c48cfc4578b53bd4b91c222a129e39
|
oid sha256:33aefc9b980615602194c93a6b9bca8c912c1efc1eb657928be0f9d8ad2477f6
|
||||||
size 151787
|
size 144236
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:0134f458078bde571001caea88804e35e50dc89c4975bf4c7b86582662e63b08
|
oid sha256:cc95feba2fa1d4decbffaee11c5c1ad8ae9fc61b2907644a54266d8276a8f795
|
||||||
size 125696
|
size 151644
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1eae5de422832fbe03a5ba5ed8281492c2bd4ad926330fac4759bec4b31943a8
|
oid sha256:6adf0fd24544aed67ae9f5bcd44520921951cb8d410bfb3229e8a1c87181d6eb
|
||||||
size 130094
|
size 123329
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f990df4098bc1dd692e2e12c8428a6f70824d132d89dcb5c902ef61be80c5409
|
||||||
|
size 127731
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:43eb037000b7eea608910ed802921ea847c8869a4eb2b1e66b6f129fd78957b7
|
||||||
|
size 144223
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f7a49f72bcf030670dd014469cf906a7097c07d6fad17149edee92f128ab2dc6
|
oid sha256:d6ef4222ce4ea18c98bc32b83ed974eae60ba3c7d5f387fda1e8b6fc0e307bae
|
||||||
size 125866
|
size 124596
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c134bdb3e372eeaedc3669aaf92486cbb253fcf41b0023c03743bf7e61c72456
|
oid sha256:94ec2d5b5e55ff77effec46ed8f7bfc70f35c4e724a775f50bdec188380ba328
|
||||||
size 129893
|
size 116308
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2af528ba157c4eb2f779e50dd3b95d190c0c68aaa96899ebfa7bc9c843469b00
|
oid sha256:26231bb9079c91813982dd25b399d1f94c9510908be28197e8c76959382368ed
|
||||||
size 131811
|
size 129494
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:124a10348cdf9e3e5aba4bbc89c93a1af30e38f58f3e44b2255e705abe5c0c8c
|
oid sha256:ddb24be2f1f934d7e472ccdda65aa0fab68f0e0e7e140f51c9fb4bf971c77601
|
||||||
size 127801
|
size 131881
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ae55c1be7450df7cfd40ae931af2a59b9fe8adbde79e83a3cf395a435a7b534f
|
oid sha256:de4c3c96a9722e51af82d2a4291abc346c9d19ff9679e62c958613f09f86eb8a
|
||||||
size 135115
|
size 127405
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:23826df8b3dde24c1592bf2b9160ffb3995cee09e3e704789ec9fa8bbaa42db2
|
oid sha256:80a7954bf21c4d4be62d88d0bb53bd7266b9edb499312136a41ee96ce0ec4936
|
||||||
size 109429
|
size 134910
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:22ba97c9a23956b7e3d4a721a34e6c0577c28acf2a7b8a1f58e31298d05b5085
|
oid sha256:da99de442338fd55d9c747d5823db696fb50984045e0d342ad17b0142b7d0d1c
|
||||||
size 113466
|
size 107253
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:be0ec4397b7f67c293c2be11abbcf8a8b2733e4f3fd675518bb89a7b59f03ba0
|
||||||
|
size 111245
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:742a0e0b5cbb9f2ed8aebabfd1893ff1cef993998db76a48994cc4e594195a0c
|
||||||
|
size 128311
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4250ac2157e6b46695cfe3c7518a908a9dcb052153d9ba1a881595a8fe0fe666
|
oid sha256:7c60bb30db63393c46e8935d45c642b53dd6d407c92bcfd7f55bff135abe26ed
|
||||||
size 35911
|
size 45064
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fddd21bba9b8b802b151a40c7bd01e9f83cc76bd7c5c942d0a0cf9772545fc91
|
oid sha256:ed85c6af5d6fe30c34c2d840160a88ffd93e3bd9abca4894d44c1be702de1495
|
||||||
size 35835
|
size 44988
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:045ea8d491711eedee7af50e1728504bd03cfd728054c7ef3fb7bc2a68bc1814
|
oid sha256:45e65e5da30433c49a14f5929ec0158e6bb11098d3113e1144d8b041ef165be7
|
||||||
size 32322
|
size 40254
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c963fd6aedf394d4825d0f3ffdf21ca9a1c285f97b945919038cea519f6737b2
|
oid sha256:fd69ffb1407d575296a6050a36378f4be9b98ff17000f7f3e692f6e4878362ea
|
||||||
size 36865
|
size 37534
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5aef9d7eff69ca1af87515bb857c8efe555d6aa338db0dfec2264a18a391d7b9
|
oid sha256:642c92c883ebb0d26d17c124ca21e52fb6a879ed34a6b9827631dfa9df9d07a8
|
||||||
size 50273
|
size 59439
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d5882a8ae82592b3bc881790e83ef30c0dec990ee6856389feafd2d16221f721
|
oid sha256:d330b43bedc202a0d306bff27aa69bb242ed0284d49242c393a499e1f91c2dff
|
||||||
size 33119
|
size 42079
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d7968e77fa33e689cb6ffb5e8d80c7f246776b5f93bef67d68a19187b006d8cd
|
oid sha256:f2c190fa02e3841fa8c7113a2d9ea79f4cce7063ad77b03115a2e417d7cae610
|
||||||
size 33067
|
size 42031
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c7c77372ed27fa07f8e43d55e865738db36c79e55ba257e4b16edcc15a0e5baf
|
oid sha256:ce1f077a5b3db7bebde1abdec0226eaed7edd82fdd70b7f4240a0ac0b8316373
|
||||||
size 29040
|
size 36911
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fc8ed93a4d8a38d4deca4f8534aae2278a7c3e74586b59bc71b1ca1bb76b565c
|
oid sha256:e579b56faf3a62f4fa50ff218c564e0c8052f36a4af42a187b8313cb0a8b3e54
|
||||||
size 32313
|
size 33004
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:eaa1454848a2e5c5ad80600c5ed13791ccd277a76a43d8c0045ac5e3bfa8fa85
|
oid sha256:8d2b9344262412f3f546d97faaf2983601b661346ce95471bb6f2e391f051f48
|
||||||
size 47447
|
size 55594
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ee7a5cc40711acc588914db928fdcf9e3f426c923507efc86350061afa491fc0
|
||||||
|
size 14203
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue