design (join room) : update design of join room screen
This commit is contained in:
parent
f675c95740
commit
9b2a1a711d
12 changed files with 198 additions and 148 deletions
|
|
@ -101,7 +101,6 @@ sealed interface ContentState {
|
||||||
|
|
||||||
sealed interface JoinAuthorisationStatus {
|
sealed interface JoinAuthorisationStatus {
|
||||||
data object None : JoinAuthorisationStatus
|
data object None : JoinAuthorisationStatus
|
||||||
data class IsSpace(val applicationName: String) : JoinAuthorisationStatus
|
|
||||||
data class IsInvited(val inviteData: InviteData, val inviteSender: InviteSender?) : JoinAuthorisationStatus
|
data class IsInvited(val inviteData: InviteData, val inviteSender: InviteSender?) : JoinAuthorisationStatus
|
||||||
data class IsBanned(val banSender: InviteSender?, val reason: String?) : JoinAuthorisationStatus
|
data class IsBanned(val banSender: InviteSender?, val reason: String?) : JoinAuthorisationStatus
|
||||||
data object IsKnocked : JoinAuthorisationStatus
|
data object IsKnocked : JoinAuthorisationStatus
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,20 @@
|
||||||
|
|
||||||
package io.element.android.features.joinroom.impl
|
package io.element.android.features.joinroom.impl
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
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.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
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.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
|
@ -38,6 +37,7 @@ 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
|
||||||
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.features.invite.api.InviteData
|
import io.element.android.features.invite.api.InviteData
|
||||||
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
|
||||||
|
|
@ -65,14 +65,21 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Button
|
import io.element.android.libraries.designsystem.theme.components.Button
|
||||||
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
||||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
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.TextButton
|
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
|
import io.element.android.libraries.designsystem.theme.placeholderBackground
|
||||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||||
import io.element.android.libraries.matrix.ui.components.InviteSenderView
|
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||||
|
import io.element.android.libraries.matrix.ui.components.SpaceInfoRow
|
||||||
|
import io.element.android.libraries.matrix.ui.components.SpaceMembersView
|
||||||
|
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun JoinRoomView(
|
fun JoinRoomView(
|
||||||
|
|
@ -92,7 +99,7 @@ fun JoinRoomView(
|
||||||
containerColor = Color.Transparent,
|
containerColor = Color.Transparent,
|
||||||
contentPadding = PaddingValues(
|
contentPadding = PaddingValues(
|
||||||
horizontal = 16.dp,
|
horizontal = 16.dp,
|
||||||
vertical = 32.dp
|
vertical = 24.dp
|
||||||
),
|
),
|
||||||
topBar = {
|
topBar = {
|
||||||
JoinRoomTopBar(
|
JoinRoomTopBar(
|
||||||
|
|
@ -220,12 +227,14 @@ private fun JoinRoomFooter(
|
||||||
onClick = { onDeclineInvite(joinAuthorisationStatus.inviteData, false) },
|
onClick = { onDeclineInvite(joinAuthorisationStatus.inviteData, false) },
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
size = ButtonSize.LargeLowPadding,
|
size = ButtonSize.LargeLowPadding,
|
||||||
|
leadingIcon = IconSource.Vector(CompoundIcons.Close())
|
||||||
)
|
)
|
||||||
Button(
|
Button(
|
||||||
text = stringResource(CommonStrings.action_accept),
|
text = stringResource(CommonStrings.action_accept),
|
||||||
onClick = { onAcceptInvite(joinAuthorisationStatus.inviteData) },
|
onClick = { onAcceptInvite(joinAuthorisationStatus.inviteData) },
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
size = ButtonSize.LargeLowPadding,
|
size = ButtonSize.LargeLowPadding,
|
||||||
|
leadingIcon = IconSource.Vector(CompoundIcons.Check())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
@ -278,7 +287,6 @@ private fun JoinRoomFooter(
|
||||||
JoinAuthorisationStatus.Unknown -> JoinRestrictedFooter(onJoinRoom)
|
JoinAuthorisationStatus.Unknown -> JoinRestrictedFooter(onJoinRoom)
|
||||||
JoinAuthorisationStatus.Restricted -> JoinRestrictedFooter(onJoinRoom)
|
JoinAuthorisationStatus.Restricted -> JoinRestrictedFooter(onJoinRoom)
|
||||||
JoinAuthorisationStatus.Unauthorized -> JoinUnauthorizedFooter(onGoBack)
|
JoinAuthorisationStatus.Unauthorized -> JoinUnauthorizedFooter(onGoBack)
|
||||||
is JoinAuthorisationStatus.IsSpace -> UnsupportedSpaceFooter(joinAuthorisationStatus.applicationName, onGoBack)
|
|
||||||
JoinAuthorisationStatus.None -> Unit
|
JoinAuthorisationStatus.None -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -397,19 +405,40 @@ private fun JoinRoomContent(
|
||||||
IsKnockedLoadedContent()
|
IsKnockedLoadedContent()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(
|
||||||
val inviteSender = (contentState.joinAuthorisationStatus as? JoinAuthorisationStatus.IsInvited)?.inviteSender
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
if (inviteSender != null) {
|
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||||
InviteSenderView(inviteSender = inviteSender, hideAvatarImage = hideAvatarsImages)
|
) {
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
|
||||||
}
|
|
||||||
DefaultLoadedContent(
|
DefaultLoadedContent(
|
||||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
|
||||||
contentState = contentState,
|
contentState = contentState,
|
||||||
knockMessage = knockMessage,
|
|
||||||
hideAvatarImage = hideAvatarsImages,
|
hideAvatarImage = hideAvatarsImages,
|
||||||
onKnockMessageUpdate = onKnockMessageUpdate
|
|
||||||
)
|
)
|
||||||
|
when (contentState.joinAuthorisationStatus) {
|
||||||
|
is JoinAuthorisationStatus.IsInvited -> {
|
||||||
|
val inviteSender = contentState.joinAuthorisationStatus.inviteSender
|
||||||
|
if (inviteSender != null) {
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
InvitedByView(inviteSender, hideAvatarsImages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is JoinAuthorisationStatus.CanKnock -> {
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
val supportingText = if (knockMessage.isNotEmpty()) {
|
||||||
|
"${knockMessage.length}/$MAX_KNOCK_MESSAGE_LENGTH"
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.screen_join_room_knock_message_description)
|
||||||
|
}
|
||||||
|
TextField(
|
||||||
|
value = knockMessage,
|
||||||
|
onValueChange = onKnockMessageUpdate,
|
||||||
|
maxLines = 3,
|
||||||
|
minLines = 3,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
supportingText = supportingText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -422,6 +451,45 @@ private fun JoinRoomContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun InvitedByView(
|
||||||
|
sender: InviteSender,
|
||||||
|
hideAvatarImage: Boolean,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Invited by",
|
||||||
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
|
color = ElementTheme.colors.textSecondary
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
Avatar(
|
||||||
|
avatarData = sender.avatarData,
|
||||||
|
avatarType = AvatarType.User,
|
||||||
|
hideImage = hideAvatarImage,
|
||||||
|
forcedAvatarSize = AvatarSize.RoomPreviewInviter.dp
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = sender.displayName,
|
||||||
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
|
color = ElementTheme.colors.textPrimary
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = sender.userId.value,
|
||||||
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
|
color = ElementTheme.colors.textSecondary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun UnknownRoomContent(
|
private fun UnknownRoomContent(
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
|
|
@ -429,7 +497,21 @@ private fun UnknownRoomContent(
|
||||||
RoomPreviewOrganism(
|
RoomPreviewOrganism(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
avatar = {
|
avatar = {
|
||||||
Spacer(modifier = Modifier.size(AvatarSize.RoomHeader.dp))
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(AvatarSize.RoomPreviewHeader.dp)
|
||||||
|
.background(
|
||||||
|
color = ElementTheme.colors.placeholderBackground,
|
||||||
|
shape = CircleShape
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
tint = ElementTheme.colors.iconPrimary,
|
||||||
|
imageVector = CompoundIcons.VisibilityOff(),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview))
|
RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview))
|
||||||
|
|
@ -448,7 +530,7 @@ private fun IncompleteContent(
|
||||||
RoomPreviewOrganism(
|
RoomPreviewOrganism(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
avatar = {
|
avatar = {
|
||||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
PlaceholderAtom(width = AvatarSize.RoomPreviewHeader.dp, height = AvatarSize.RoomPreviewHeader.dp)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
when (roomIdOrAlias) {
|
when (roomIdOrAlias) {
|
||||||
|
|
@ -471,43 +553,32 @@ private fun IncompleteContent(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun IsKnockedLoadedContent(modifier: Modifier = Modifier) {
|
private fun IsKnockedLoadedContent(modifier: Modifier = Modifier) {
|
||||||
BoxWithConstraints(
|
IconTitleSubtitleMolecule(
|
||||||
modifier = modifier
|
modifier = modifier.padding(horizontal = 8.dp),
|
||||||
.fillMaxHeight()
|
iconStyle = BigIcon.Style.SuccessSolid,
|
||||||
.padding(horizontal = 16.dp),
|
title = stringResource(R.string.screen_join_room_knock_sent_title),
|
||||||
contentAlignment = Alignment.Center,
|
subTitle = stringResource(R.string.screen_join_room_knock_sent_description),
|
||||||
) {
|
)
|
||||||
IconTitleSubtitleMolecule(
|
|
||||||
modifier = Modifier.sizeIn(minHeight = maxHeight * 0.7f),
|
|
||||||
iconStyle = BigIcon.Style.SuccessSolid,
|
|
||||||
title = stringResource(R.string.screen_join_room_knock_sent_title),
|
|
||||||
subTitle = stringResource(R.string.screen_join_room_knock_sent_description),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DefaultLoadedContent(
|
private fun DefaultLoadedContent(
|
||||||
contentState: ContentState.Loaded,
|
contentState: ContentState.Loaded,
|
||||||
knockMessage: String,
|
|
||||||
hideAvatarImage: Boolean,
|
hideAvatarImage: Boolean,
|
||||||
onKnockMessageUpdate: (String) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
RoomPreviewOrganism(
|
RoomPreviewOrganism(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
avatar = {
|
avatar = {
|
||||||
Avatar(
|
Avatar(
|
||||||
contentState.avatarData(AvatarSize.RoomHeader),
|
contentState.avatarData(AvatarSize.RoomPreviewHeader),
|
||||||
hideImage = hideAvatarImage,
|
hideImage = hideAvatarImage,
|
||||||
avatarType = AvatarType.Room(),
|
avatarType = if (contentState.isSpace) AvatarType.Space() else AvatarType.Room(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
if (contentState.name != null) {
|
if (contentState.name != null) {
|
||||||
RoomPreviewTitleAtom(
|
RoomPreviewTitleAtom(title = contentState.name)
|
||||||
title = contentState.name,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
RoomPreviewTitleAtom(
|
RoomPreviewTitleAtom(
|
||||||
title = stringResource(id = CommonStrings.common_no_room_name),
|
title = stringResource(id = CommonStrings.common_no_room_name),
|
||||||
|
|
@ -516,37 +587,32 @@ private fun DefaultLoadedContent(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
subtitle = {
|
subtitle = {
|
||||||
if (contentState.alias != null) {
|
when {
|
||||||
RoomPreviewSubtitleAtom(contentState.alias.value)
|
contentState.isSpace -> {
|
||||||
}
|
SpaceInfoRow(
|
||||||
},
|
joinRule = contentState.joinRule ?: JoinRule.Public,
|
||||||
description = {
|
numberOfRooms = contentState.childrenCount ?: 0,
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
) {
|
|
||||||
RoomPreviewDescriptionAtom(contentState.topic ?: "")
|
|
||||||
if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) {
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
val supportingText = if (knockMessage.isNotEmpty()) {
|
|
||||||
"${knockMessage.length}/$MAX_KNOCK_MESSAGE_LENGTH"
|
|
||||||
} else {
|
|
||||||
stringResource(R.string.screen_join_room_knock_message_description)
|
|
||||||
}
|
|
||||||
TextField(
|
|
||||||
value = knockMessage,
|
|
||||||
onValueChange = onKnockMessageUpdate,
|
|
||||||
maxLines = 3,
|
|
||||||
minLines = 3,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
supportingText = supportingText
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
contentState.alias != null -> {
|
||||||
|
RoomPreviewSubtitleAtom(contentState.alias.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
description = {
|
||||||
|
RoomPreviewDescriptionAtom(
|
||||||
|
contentState.topic ?: "",
|
||||||
|
maxLines = if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanJoin) Int.MAX_VALUE else 2
|
||||||
|
)
|
||||||
|
},
|
||||||
memberCount = {
|
memberCount = {
|
||||||
if (contentState.showMemberCount) {
|
if (contentState.showMemberCount) {
|
||||||
MembersCountMolecule(memberCount = contentState.numberOfMembers?.toInt() ?: 0)
|
val membersCount = contentState.numberOfMembers?.toInt() ?: 0
|
||||||
|
if (contentState.isSpace) {
|
||||||
|
SpaceMembersView(persistentListOf(), membersCount)
|
||||||
|
} else {
|
||||||
|
MembersCountMolecule(memberCount = membersCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -655,7 +655,10 @@ class JoinRoomPresenterTest {
|
||||||
isDm = false,
|
isDm = false,
|
||||||
roomType = RoomType.Room,
|
roomType = RoomType.Room,
|
||||||
roomAvatarUrl = "avatarUrl",
|
roomAvatarUrl = "avatarUrl",
|
||||||
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin
|
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin,
|
||||||
|
joinRule = JoinRule.Public,
|
||||||
|
childrenCount = null,
|
||||||
|
heroes = persistentListOf()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -724,7 +727,10 @@ class JoinRoomPresenterTest {
|
||||||
),
|
),
|
||||||
membershipChangeReason = null,
|
membershipChangeReason = null,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
joinRule = JoinRule.Public,
|
||||||
|
childrenCount = null,
|
||||||
|
heroes = persistentListOf()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -789,7 +795,10 @@ class JoinRoomPresenterTest {
|
||||||
membershipChangeReason = null,
|
membershipChangeReason = null,
|
||||||
),
|
),
|
||||||
reason = null,
|
reason = null,
|
||||||
)
|
),
|
||||||
|
joinRule = JoinRule.Public,
|
||||||
|
childrenCount = null,
|
||||||
|
heroes = persistentListOf()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -842,7 +851,10 @@ class JoinRoomPresenterTest {
|
||||||
isDm = false,
|
isDm = false,
|
||||||
roomType = RoomType.Room,
|
roomType = RoomType.Room,
|
||||||
roomAvatarUrl = "avatarUrl",
|
roomAvatarUrl = "avatarUrl",
|
||||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked
|
joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked,
|
||||||
|
joinRule = JoinRule.Public,
|
||||||
|
childrenCount = null,
|
||||||
|
heroes = persistentListOf()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -949,26 +961,6 @@ class JoinRoomPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - when room is not known RoomPreview is loaded as Space`() = runTest {
|
|
||||||
val client = FakeMatrixClient(
|
|
||||||
getNotJoinedRoomResult = { _, _ ->
|
|
||||||
Result.success(
|
|
||||||
aRoomPreview(info = aRoomPreviewInfo(isSpace = true))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
val presenter = createJoinRoomPresenter(
|
|
||||||
matrixClient = client
|
|
||||||
)
|
|
||||||
presenter.test {
|
|
||||||
skipItems(1)
|
|
||||||
awaitItem().also { state ->
|
|
||||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.UnsupportedSpace("AppName"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - when room is not known RoomPreview is loaded with error`() = runTest {
|
fun `present - when room is not known RoomPreview is loaded with error`() = runTest {
|
||||||
val client = FakeMatrixClient(
|
val client = FakeMatrixClient(
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ private fun RoomAliasResolverContent(
|
||||||
RoomPreviewOrganism(
|
RoomPreviewOrganism(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
avatar = {
|
avatar = {
|
||||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
PlaceholderAtom(width = AvatarSize.RoomPreviewHeader.dp, height = AvatarSize.RoomPreviewHeader.dp)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
RoomPreviewSubtitleAtom(roomAlias.value)
|
RoomPreviewSubtitleAtom(roomAlias.value)
|
||||||
|
|
|
||||||
|
|
@ -396,10 +396,10 @@ private fun RoomHeaderSection(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Avatar(
|
Avatar(
|
||||||
avatarData = AvatarData(roomId.value, roomName, avatarUrl, AvatarSize.RoomHeader),
|
avatarData = AvatarData(roomId.value, roomName, avatarUrl, AvatarSize.RoomDetailsHeader),
|
||||||
avatarType = AvatarType.Room(
|
avatarType = AvatarType.Room(
|
||||||
heroes = heroes.map { user ->
|
heroes = heroes.map { user ->
|
||||||
user.getAvatarData(size = AvatarSize.RoomHeader)
|
user.getAvatarData(size = AvatarSize.RoomDetailsHeader)
|
||||||
}.toPersistentList(),
|
}.toPersistentList(),
|
||||||
isTombstoned = isTombstoned,
|
isTombstoned = isTombstoned,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,18 @@ 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 RoomPreviewDescriptionAtom(description: String, modifier: Modifier = Modifier) {
|
fun RoomPreviewDescriptionAtom(
|
||||||
|
description: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
maxLines: Int = Int.MAX_VALUE,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = description,
|
text = description,
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = ElementTheme.colors.textSecondary,
|
color = ElementTheme.colors.textPrimary,
|
||||||
maxLines = 3,
|
maxLines = maxLines,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ fun RoomPreviewSubtitleAtom(subtitle: String, modifier: Modifier = Modifier) {
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = subtitle,
|
text = subtitle,
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = ElementTheme.colors.textSecondary,
|
color = ElementTheme.colors.textSecondary,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ fun RoomPreviewTitleAtom(
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = title,
|
text = title,
|
||||||
style = ElementTheme.typography.fontHeadingMdBold,
|
style = ElementTheme.typography.fontHeadingLgBold,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
fontStyle = fontStyle,
|
fontStyle = fontStyle,
|
||||||
color = ElementTheme.colors.textPrimary,
|
color = ElementTheme.colors.textPrimary,
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,13 @@ fun RoomPreviewOrganism(
|
||||||
title()
|
title()
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
subtitle()
|
subtitle()
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
if (memberCount != null) {
|
if (memberCount != null) {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
memberCount()
|
memberCount()
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
if (description != null) {
|
if (description != null) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
description()
|
description()
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ enum class AvatarSize(val dp: Dp) {
|
||||||
CurrentUserTopBar(32.dp),
|
CurrentUserTopBar(32.dp),
|
||||||
|
|
||||||
IncomingCall(140.dp),
|
IncomingCall(140.dp),
|
||||||
RoomHeader(96.dp),
|
RoomDetailsHeader(96.dp),
|
||||||
RoomListItem(52.dp),
|
RoomListItem(52.dp),
|
||||||
|
|
||||||
SpaceListItem(52.dp),
|
SpaceListItem(52.dp),
|
||||||
|
|
@ -68,5 +68,7 @@ enum class AvatarSize(val dp: Dp) {
|
||||||
|
|
||||||
OrganizationHeader(64.dp),
|
OrganizationHeader(64.dp),
|
||||||
SpaceHeader(64.dp),
|
SpaceHeader(64.dp),
|
||||||
|
RoomPreviewHeader(64.dp),
|
||||||
|
RoomPreviewInviter(56.dp),
|
||||||
SpaceMember(24.dp),
|
SpaceMember(24.dp),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,14 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.ui.components
|
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.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
|
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
|
||||||
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.RoomPreviewDescriptionAtom
|
||||||
|
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom
|
||||||
|
import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
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.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||||
|
|
@ -47,47 +42,40 @@ fun SpaceHeaderView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
topicMaxLines: Int = Int.MAX_VALUE,
|
topicMaxLines: Int = Int.MAX_VALUE,
|
||||||
) {
|
) {
|
||||||
Column(
|
RoomPreviewOrganism(
|
||||||
modifier = modifier
|
modifier = modifier.padding(24.dp),
|
||||||
.fillMaxWidth()
|
avatar = {
|
||||||
.padding(top = 32.dp, bottom = 24.dp, start = 16.dp, end = 16.dp),
|
Avatar(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
avatarData = avatarData,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
avatarType = AvatarType.Space(),
|
||||||
) {
|
|
||||||
Avatar(
|
|
||||||
avatarData = avatarData,
|
|
||||||
avatarType = AvatarType.Space(false),
|
|
||||||
)
|
|
||||||
name?.let {
|
|
||||||
Text(
|
|
||||||
text = name,
|
|
||||||
style = ElementTheme.typography.fontHeadingLgBold,
|
|
||||||
color = ElementTheme.colors.textPrimary,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
if (joinRule != null) {
|
title = {
|
||||||
SpaceInfoRow(
|
name?.let {
|
||||||
joinRule = joinRule,
|
RoomPreviewTitleAtom(title = name)
|
||||||
numberOfRooms = numberOfRooms,
|
}
|
||||||
|
},
|
||||||
|
subtitle = {
|
||||||
|
if (joinRule != null) {
|
||||||
|
SpaceInfoRow(
|
||||||
|
joinRule = joinRule,
|
||||||
|
numberOfRooms = numberOfRooms,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
description = if (topic != null) {
|
||||||
|
{ RoomPreviewDescriptionAtom(description = topic, maxLines = topicMaxLines) }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
|
memberCount = {
|
||||||
|
SpaceMembersView(
|
||||||
|
heroes = heroes,
|
||||||
|
numberOfMembers = numberOfMembers,
|
||||||
|
modifier = Modifier.padding(horizontal = 32.dp),
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
SpaceMembersView(
|
)
|
||||||
heroes = heroes,
|
|
||||||
numberOfMembers = numberOfMembers,
|
|
||||||
modifier = Modifier.padding(horizontal = 32.dp),
|
|
||||||
)
|
|
||||||
topic?.let {
|
|
||||||
Text(
|
|
||||||
text = topic,
|
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
|
||||||
color = ElementTheme.colors.textPrimary,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
maxLines = topicMaxLines,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class DefaultNotificationConversationService(
|
||||||
targetSize = defaultShortcutIconSize.toLong()
|
targetSize = defaultShortcutIconSize.toLong()
|
||||||
)?.let(IconCompat::createWithBitmap)
|
)?.let(IconCompat::createWithBitmap)
|
||||||
?: InitialsAvatarBitmapGenerator(useDarkTheme = useDarkTheme)
|
?: InitialsAvatarBitmapGenerator(useDarkTheme = useDarkTheme)
|
||||||
.generateBitmap(defaultShortcutIconSize, AvatarData(id = roomId.value, name = roomName, size = AvatarSize.RoomHeader))
|
.generateBitmap(defaultShortcutIconSize, AvatarData(id = roomId.value, name = roomName, size = AvatarSize.RoomDetailsHeader))
|
||||||
?.let(IconCompat::createWithAdaptiveBitmap)
|
?.let(IconCompat::createWithAdaptiveBitmap)
|
||||||
|
|
||||||
val shortcutInfo = ShortcutInfoCompat.Builder(context, createShortcutId(sessionId, roomId))
|
val shortcutInfo = ShortcutInfoCompat.Builder(context, createShortcutId(sessionId, roomId))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue