Merge pull request #5378 from element-hq/feature/fga/join_space
Feature : Join Space (WIP)
This commit is contained in:
commit
6cd0af9235
94 changed files with 848 additions and 464 deletions
|
|
@ -12,6 +12,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
|
|
@ -36,3 +37,11 @@ fun RoomInfo.toInviteData(): InviteData {
|
|||
isDm = isDm,
|
||||
)
|
||||
}
|
||||
|
||||
fun SpaceRoom.toInviteData(): InviteData {
|
||||
return InviteData(
|
||||
roomId = roomId,
|
||||
roomName = name ?: roomId.value,
|
||||
isDm = false,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,4 +43,5 @@ dependencies {
|
|||
testImplementation(projects.features.invite.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.preferences.test)
|
||||
testImplementation(projects.libraries.previewutils)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.compose.runtime.setValue
|
|||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.Inject
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.SeenInvitesStore
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
|
||||
|
|
@ -42,17 +43,21 @@ import io.element.android.libraries.matrix.api.exception.ClientException
|
|||
import io.element.android.libraries.matrix.api.exception.ErrorKind
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
@Inject
|
||||
class JoinRoomPresenter(
|
||||
|
|
@ -80,6 +85,8 @@ class JoinRoomPresenter(
|
|||
): JoinRoomPresenter
|
||||
}
|
||||
|
||||
private val spaceList = matrixClient.spaceService.spaceRoomList(roomId)
|
||||
|
||||
@Composable
|
||||
override fun present(): JoinRoomState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
|
@ -87,6 +94,9 @@ class JoinRoomPresenter(
|
|||
val roomInfo by remember {
|
||||
matrixClient.getRoomInfoFlow(roomId)
|
||||
}.collectAsState(initial = Optional.empty())
|
||||
val spaceRoom by remember {
|
||||
spaceList.currentSpaceFlow()
|
||||
}.collectAsState()
|
||||
val joinAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val knockAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val cancelKnockAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
|
|
@ -96,55 +106,41 @@ class JoinRoomPresenter(
|
|||
val hideInviteAvatars by matrixClient.rememberHideInvitesAvatar()
|
||||
val canReportRoom by produceState(false) { value = matrixClient.canReportRoom() }
|
||||
|
||||
val contentState by produceState<ContentState>(
|
||||
initialValue = ContentState.Loading,
|
||||
key1 = roomInfo,
|
||||
key2 = retryCount,
|
||||
key3 = isDismissingContent,
|
||||
) {
|
||||
var contentState by remember {
|
||||
mutableStateOf<ContentState>(ContentState.Loading)
|
||||
}
|
||||
LaunchedEffect(roomInfo, retryCount, isDismissingContent, spaceRoom) {
|
||||
when {
|
||||
isDismissingContent -> value = ContentState.Dismissing
|
||||
isDismissingContent -> contentState = ContentState.Dismissing
|
||||
roomInfo.isPresent -> {
|
||||
val notJoinedRoom = matrixClient.getRoomPreview(roomIdOrAlias, serverNames).getOrNull()
|
||||
val (sender, reason) = when (roomInfo.get().currentUserMembership) {
|
||||
CurrentUserMembership.BANNED -> {
|
||||
// Workaround to get info about the sender for banned rooms
|
||||
// TODO re-do this once we have a better API in the SDK
|
||||
val membershipDetails = notJoinedRoom?.membershipDetails()?.getOrNull()
|
||||
membershipDetails?.senderMember to membershipDetails?.currentUserMember?.membershipChangeReason
|
||||
}
|
||||
CurrentUserMembership.INVITED -> {
|
||||
roomInfo.get().inviter to null
|
||||
}
|
||||
else -> null to null
|
||||
}
|
||||
val membershipDetails = notJoinedRoom?.membershipDetails()?.getOrNull()
|
||||
val joinedMembersCountOverride = notJoinedRoom?.previewInfo?.numberOfJoinedMembers
|
||||
value = roomInfo.get().toContentState(
|
||||
membershipSender = sender,
|
||||
contentState = roomInfo.get().toContentState(
|
||||
joinedMembersCountOverride = joinedMembersCountOverride,
|
||||
reason = reason,
|
||||
membershipDetails = membershipDetails,
|
||||
childrenCount = spaceRoom.getOrNull()?.childrenCount,
|
||||
)
|
||||
}
|
||||
spaceRoom.isPresent -> {
|
||||
val spaceRoom = spaceRoom.get()
|
||||
// Only use this state when space is not locally known
|
||||
contentState = if (spaceRoom.state != null) {
|
||||
ContentState.Loading
|
||||
} else {
|
||||
spaceRoom.toContentState()
|
||||
}
|
||||
}
|
||||
roomDescription.isPresent -> {
|
||||
value = roomDescription.get().toContentState()
|
||||
contentState = roomDescription.get().toContentState()
|
||||
}
|
||||
else -> {
|
||||
value = ContentState.Loading
|
||||
contentState = ContentState.Loading
|
||||
val result = matrixClient.getRoomPreview(roomIdOrAlias, serverNames)
|
||||
value = result.fold(
|
||||
contentState = result.fold(
|
||||
onSuccess = { preview ->
|
||||
val membershipInfo = when (preview.previewInfo.membership) {
|
||||
CurrentUserMembership.INVITED,
|
||||
CurrentUserMembership.BANNED,
|
||||
CurrentUserMembership.KNOCKED -> {
|
||||
preview.membershipDetails().getOrNull()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
preview.previewInfo.toContentState(
|
||||
senderMember = membershipInfo?.senderMember,
|
||||
reason = membershipInfo?.currentUserMember?.membershipChangeReason,
|
||||
)
|
||||
val membershipDetails = preview.membershipDetails().getOrNull()
|
||||
preview.previewInfo.toContentState(membershipDetails)
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
if (throwable is ClientException.MatrixApi && (throwable.kind == ErrorKind.NotFound || throwable.kind == ErrorKind.Forbidden)) {
|
||||
|
|
@ -252,30 +248,56 @@ class JoinRoomPresenter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun RoomPreviewInfo.toContentState(senderMember: RoomMember?, reason: String?): ContentState {
|
||||
private fun RoomPreviewInfo.toContentState(membershipDetails: RoomMembershipDetails?): ContentState {
|
||||
return ContentState.Loaded(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
topic = topic,
|
||||
alias = canonicalAlias,
|
||||
numberOfMembers = numberOfJoinedMembers,
|
||||
isDm = false,
|
||||
roomType = roomType,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when (membership) {
|
||||
CurrentUserMembership.INVITED -> {
|
||||
JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = toInviteData(),
|
||||
inviteSender = senderMember?.toInviteSender()
|
||||
)
|
||||
}
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(senderMember?.toInviteSender(), reason)
|
||||
CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
else -> joinRule.toJoinAuthorisationStatus()
|
||||
joinAuthorisationStatus = computeJoinAuthorisationStatus(
|
||||
membership,
|
||||
membershipDetails,
|
||||
joinRule,
|
||||
{ toInviteData() }
|
||||
),
|
||||
joinRule = joinRule,
|
||||
details = when (roomType) {
|
||||
is RoomType.Other,
|
||||
RoomType.Room -> LoadedDetails.Room(
|
||||
isDm = false,
|
||||
)
|
||||
RoomType.Space -> LoadedDetails.Space(
|
||||
childrenCount = 0,
|
||||
heroes = persistentListOf(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun SpaceRoom.toContentState(): ContentState {
|
||||
return ContentState.Loaded(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
topic = topic,
|
||||
alias = canonicalAlias,
|
||||
numberOfMembers = numJoinedMembers.toLong(),
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = computeJoinAuthorisationStatus(
|
||||
membership = state,
|
||||
membershipDetails = null,
|
||||
joinRule = joinRule,
|
||||
inviteData = { toInviteData() }
|
||||
),
|
||||
joinRule = joinRule,
|
||||
details = LoadedDetails.Space(
|
||||
childrenCount = childrenCount,
|
||||
heroes = heroes.toPersistentList(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun RoomDescription.toContentState(): ContentState {
|
||||
return ContentState.Loaded(
|
||||
|
|
@ -284,22 +306,29 @@ internal fun RoomDescription.toContentState(): ContentState {
|
|||
topic = topic,
|
||||
alias = alias,
|
||||
numberOfMembers = numberOfMembers,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when (joinRule) {
|
||||
RoomDescription.JoinRule.KNOCK -> JoinAuthorisationStatus.CanKnock
|
||||
RoomDescription.JoinRule.PUBLIC -> JoinAuthorisationStatus.CanJoin
|
||||
else -> JoinAuthorisationStatus.Unknown
|
||||
}
|
||||
},
|
||||
joinRule = when (joinRule) {
|
||||
RoomDescription.JoinRule.KNOCK -> JoinRule.Knock
|
||||
RoomDescription.JoinRule.PUBLIC -> JoinRule.Public
|
||||
RoomDescription.JoinRule.RESTRICTED -> JoinRule.Restricted(persistentListOf())
|
||||
RoomDescription.JoinRule.KNOCK_RESTRICTED -> JoinRule.KnockRestricted(persistentListOf())
|
||||
RoomDescription.JoinRule.INVITE -> JoinRule.Invite
|
||||
RoomDescription.JoinRule.UNKNOWN -> null
|
||||
},
|
||||
details = LoadedDetails.Room(isDm = false)
|
||||
)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun RoomInfo.toContentState(
|
||||
membershipSender: RoomMember?,
|
||||
joinedMembersCountOverride: Long?,
|
||||
reason: String?,
|
||||
membershipDetails: RoomMembershipDetails?,
|
||||
childrenCount: Int?,
|
||||
): ContentState {
|
||||
return ContentState.Loaded(
|
||||
roomId = id,
|
||||
|
|
@ -307,24 +336,49 @@ internal fun RoomInfo.toContentState(
|
|||
topic = topic,
|
||||
alias = canonicalAlias,
|
||||
numberOfMembers = joinedMembersCountOverride ?: joinedMembersCount,
|
||||
isDm = isDm,
|
||||
roomType = if (isSpace) RoomType.Space else RoomType.Room,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when (currentUserMembership) {
|
||||
CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = toInviteData(),
|
||||
inviteSender = membershipSender?.toInviteSender(),
|
||||
joinAuthorisationStatus = computeJoinAuthorisationStatus(
|
||||
membership = currentUserMembership,
|
||||
membershipDetails = membershipDetails,
|
||||
joinRule = joinRule,
|
||||
inviteData = { toInviteData() }
|
||||
),
|
||||
joinRule = joinRule,
|
||||
details = if (isSpace) {
|
||||
LoadedDetails.Space(
|
||||
childrenCount = childrenCount ?: 0,
|
||||
heroes = heroes,
|
||||
)
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(
|
||||
banSender = membershipSender?.toInviteSender(),
|
||||
reason = reason,
|
||||
} else {
|
||||
LoadedDetails.Room(
|
||||
isDm = isDm,
|
||||
)
|
||||
CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
else -> joinRule.toJoinAuthorisationStatus()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun computeJoinAuthorisationStatus(
|
||||
membership: CurrentUserMembership?,
|
||||
membershipDetails: RoomMembershipDetails?,
|
||||
joinRule: JoinRule?,
|
||||
inviteData: () -> InviteData,
|
||||
): JoinAuthorisationStatus {
|
||||
return when (membership) {
|
||||
CurrentUserMembership.INVITED -> {
|
||||
JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = inviteData(),
|
||||
inviteSender = membershipDetails?.senderMember?.toInviteSender()
|
||||
)
|
||||
}
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(
|
||||
membershipDetails?.senderMember?.toInviteSender(),
|
||||
membershipDetails?.membershipChangeReason
|
||||
)
|
||||
CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
else -> joinRule.toJoinAuthorisationStatus()
|
||||
}
|
||||
}
|
||||
|
||||
private fun JoinRule?.toJoinAuthorisationStatus(): JoinAuthorisationStatus {
|
||||
return when (this) {
|
||||
JoinRule.Knock,
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ 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.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal const val MAX_KNOCK_MESSAGE_LENGTH = 500
|
||||
|
||||
|
|
@ -41,9 +43,6 @@ data class JoinRoomState(
|
|||
val joinAuthorisationStatus = when (contentState) {
|
||||
is ContentState.Loaded -> {
|
||||
when {
|
||||
contentState.roomType == RoomType.Space -> {
|
||||
JoinAuthorisationStatus.IsSpace(applicationName)
|
||||
}
|
||||
isJoinActionUnauthorized -> {
|
||||
JoinAuthorisationStatus.Unauthorized
|
||||
}
|
||||
|
|
@ -77,12 +76,13 @@ sealed interface ContentState {
|
|||
val topic: String?,
|
||||
val alias: RoomAlias?,
|
||||
val numberOfMembers: Long?,
|
||||
val isDm: Boolean,
|
||||
val roomType: RoomType,
|
||||
val roomAvatarUrl: String?,
|
||||
val joinAuthorisationStatus: JoinAuthorisationStatus,
|
||||
val joinRule: JoinRule?,
|
||||
val details: LoadedDetails,
|
||||
) : ContentState {
|
||||
val showMemberCount = numberOfMembers != null
|
||||
val isSpace = details is LoadedDetails.Space
|
||||
|
||||
fun avatarData(size: AvatarSize): AvatarData {
|
||||
return AvatarData(
|
||||
|
|
@ -95,9 +95,20 @@ sealed interface ContentState {
|
|||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed interface LoadedDetails {
|
||||
data class Room(
|
||||
val isDm: Boolean,
|
||||
) : LoadedDetails
|
||||
|
||||
data class Space(
|
||||
val childrenCount: Int,
|
||||
val heroes: ImmutableList<MatrixUser>,
|
||||
) : LoadedDetails
|
||||
}
|
||||
|
||||
sealed interface JoinAuthorisationStatus {
|
||||
data object None : JoinAuthorisationStatus
|
||||
data class IsSpace(val applicationName: String) : JoinAuthorisationStatus
|
||||
data class IsInvited(val inviteData: InviteData, val inviteSender: InviteSender?) : JoinAuthorisationStatus
|
||||
data class IsBanned(val banSender: InviteSender?, val reason: String?) : JoinAuthorisationStatus
|
||||
data object IsKnocked : JoinAuthorisationStatus
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@ 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.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
||||
override val values: Sequence<JoinRoomState>
|
||||
|
|
@ -77,13 +79,17 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
|||
name = "A space",
|
||||
alias = null,
|
||||
topic = "This is the topic of a space",
|
||||
roomType = RoomType.Space,
|
||||
details = aLoadedDetailsSpace(
|
||||
childrenCount = 42,
|
||||
),
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
name = "A DM",
|
||||
isDm = true,
|
||||
details = aLoadedDetailsRoom(
|
||||
isDm = true,
|
||||
),
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
|
|
@ -156,20 +162,34 @@ fun aLoadedContentState(
|
|||
alias: RoomAlias? = RoomAlias("#exa:matrix.org"),
|
||||
topic: String? = "Element X is a secure, private and decentralized messenger.",
|
||||
numberOfMembers: Long? = null,
|
||||
isDm: Boolean = false,
|
||||
roomType: RoomType = RoomType.Room,
|
||||
roomAvatarUrl: String? = null,
|
||||
joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown,
|
||||
joinRule: JoinRule? = null,
|
||||
details: LoadedDetails = aLoadedDetailsRoom(isDm = false),
|
||||
) = ContentState.Loaded(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
alias = alias,
|
||||
topic = topic,
|
||||
numberOfMembers = numberOfMembers,
|
||||
isDm = isDm,
|
||||
roomType = roomType,
|
||||
roomAvatarUrl = roomAvatarUrl,
|
||||
joinAuthorisationStatus = joinAuthorisationStatus
|
||||
joinAuthorisationStatus = joinAuthorisationStatus,
|
||||
joinRule = joinRule,
|
||||
details = details,
|
||||
)
|
||||
|
||||
fun aLoadedDetailsRoom(
|
||||
isDm: Boolean = false,
|
||||
) = LoadedDetails.Room(
|
||||
isDm = isDm
|
||||
)
|
||||
|
||||
fun aLoadedDetailsSpace(
|
||||
childrenCount: Int = 0,
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
) = LoadedDetails.Space(
|
||||
childrenCount = childrenCount,
|
||||
heroes = heroes.toPersistentList()
|
||||
)
|
||||
|
||||
fun aJoinRoomState(
|
||||
|
|
|
|||
|
|
@ -7,21 +7,20 @@
|
|||
|
||||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
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.unit.dp
|
||||
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.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||
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.ButtonSize
|
||||
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.Text
|
||||
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.TopAppBar
|
||||
import io.element.android.libraries.designsystem.theme.placeholderBackground
|
||||
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 kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
fun JoinRoomView(
|
||||
|
|
@ -92,7 +99,7 @@ fun JoinRoomView(
|
|||
containerColor = Color.Transparent,
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 32.dp
|
||||
vertical = 24.dp
|
||||
),
|
||||
topBar = {
|
||||
JoinRoomTopBar(
|
||||
|
|
@ -220,12 +227,14 @@ private fun JoinRoomFooter(
|
|||
onClick = { onDeclineInvite(joinAuthorisationStatus.inviteData, false) },
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
leadingIcon = IconSource.Vector(CompoundIcons.Close())
|
||||
)
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_accept),
|
||||
onClick = { onAcceptInvite(joinAuthorisationStatus.inviteData) },
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
leadingIcon = IconSource.Vector(CompoundIcons.Check())
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
|
@ -278,7 +287,6 @@ private fun JoinRoomFooter(
|
|||
JoinAuthorisationStatus.Unknown -> JoinRestrictedFooter(onJoinRoom)
|
||||
JoinAuthorisationStatus.Restricted -> JoinRestrictedFooter(onJoinRoom)
|
||||
JoinAuthorisationStatus.Unauthorized -> JoinUnauthorizedFooter(onGoBack)
|
||||
is JoinAuthorisationStatus.IsSpace -> UnsupportedSpaceFooter(joinAuthorisationStatus.applicationName, onGoBack)
|
||||
JoinAuthorisationStatus.None -> Unit
|
||||
}
|
||||
}
|
||||
|
|
@ -358,28 +366,6 @@ private fun JoinRestrictedFooter(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnsupportedSpaceFooter(
|
||||
applicationName: String,
|
||||
onGoBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Announcement(
|
||||
title = stringResource(R.string.screen_join_room_space_not_supported_title),
|
||||
description = stringResource(R.string.screen_join_room_space_not_supported_description, applicationName),
|
||||
type = AnnouncementType.Informative(),
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_ok),
|
||||
onClick = onGoBack,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun JoinRoomContent(
|
||||
roomIdOrAlias: RoomIdOrAlias,
|
||||
|
|
@ -397,19 +383,40 @@ private fun JoinRoomContent(
|
|||
IsKnockedLoadedContent()
|
||||
}
|
||||
else -> {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
val inviteSender = (contentState.joinAuthorisationStatus as? JoinAuthorisationStatus.IsInvited)?.inviteSender
|
||||
if (inviteSender != null) {
|
||||
InviteSenderView(inviteSender = inviteSender, hideAvatarImage = hideAvatarsImages)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
DefaultLoadedContent(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
contentState = contentState,
|
||||
knockMessage = knockMessage,
|
||||
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 +429,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 = stringResource(R.string.screen_join_room_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
|
||||
private fun UnknownRoomContent(
|
||||
modifier: Modifier = Modifier
|
||||
|
|
@ -429,7 +475,21 @@ private fun UnknownRoomContent(
|
|||
RoomPreviewOrganism(
|
||||
modifier = modifier,
|
||||
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 = {
|
||||
RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview))
|
||||
|
|
@ -448,7 +508,7 @@ private fun IncompleteContent(
|
|||
RoomPreviewOrganism(
|
||||
modifier = modifier,
|
||||
avatar = {
|
||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||
PlaceholderAtom(width = AvatarSize.RoomPreviewHeader.dp, height = AvatarSize.RoomPreviewHeader.dp)
|
||||
},
|
||||
title = {
|
||||
when (roomIdOrAlias) {
|
||||
|
|
@ -471,43 +531,32 @@ private fun IncompleteContent(
|
|||
|
||||
@Composable
|
||||
private fun IsKnockedLoadedContent(modifier: Modifier = Modifier) {
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 16.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
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),
|
||||
)
|
||||
}
|
||||
IconTitleSubtitleMolecule(
|
||||
modifier = modifier.padding(horizontal = 8.dp),
|
||||
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
|
||||
private fun DefaultLoadedContent(
|
||||
contentState: ContentState.Loaded,
|
||||
knockMessage: String,
|
||||
hideAvatarImage: Boolean,
|
||||
onKnockMessageUpdate: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
RoomPreviewOrganism(
|
||||
modifier = modifier,
|
||||
avatar = {
|
||||
Avatar(
|
||||
contentState.avatarData(AvatarSize.RoomHeader),
|
||||
contentState.avatarData(AvatarSize.RoomPreviewHeader),
|
||||
hideImage = hideAvatarImage,
|
||||
avatarType = AvatarType.Room(),
|
||||
avatarType = if (contentState.isSpace) AvatarType.Space() else AvatarType.Room(),
|
||||
)
|
||||
},
|
||||
title = {
|
||||
if (contentState.name != null) {
|
||||
RoomPreviewTitleAtom(
|
||||
title = contentState.name,
|
||||
)
|
||||
RoomPreviewTitleAtom(title = contentState.name)
|
||||
} else {
|
||||
RoomPreviewTitleAtom(
|
||||
title = stringResource(id = CommonStrings.common_no_room_name),
|
||||
|
|
@ -516,37 +565,32 @@ private fun DefaultLoadedContent(
|
|||
}
|
||||
},
|
||||
subtitle = {
|
||||
if (contentState.alias != null) {
|
||||
RoomPreviewSubtitleAtom(contentState.alias.value)
|
||||
}
|
||||
},
|
||||
description = {
|
||||
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
|
||||
when {
|
||||
contentState.details is LoadedDetails.Space -> {
|
||||
SpaceInfoRow(
|
||||
joinRule = contentState.joinRule ?: JoinRule.Public,
|
||||
numberOfRooms = contentState.details.childrenCount,
|
||||
)
|
||||
}
|
||||
contentState.alias != null -> {
|
||||
RoomPreviewSubtitleAtom(contentState.alias.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
description = {
|
||||
RoomPreviewDescriptionAtom(
|
||||
contentState.topic ?: "",
|
||||
maxLines = if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanJoin) Int.MAX_VALUE else 2
|
||||
)
|
||||
},
|
||||
memberCount = {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
<string name="screen_join_room_fail_reason">"This room is either invite-only or there might be restrictions to access at space level."</string>
|
||||
<string name="screen_join_room_forget_action">"Forget this room"</string>
|
||||
<string name="screen_join_room_invite_required_message">"You need an invite in order to join this room"</string>
|
||||
<string name="screen_join_room_invited_by">"Invited by"</string>
|
||||
<string name="screen_join_room_join_action">"Join room"</string>
|
||||
<string name="screen_join_room_join_restricted_message">"You may need to be invited or be a member of a space in order to join."</string>
|
||||
<string name="screen_join_room_knock_action">"Send request to join"</string>
|
||||
|
|
|
|||
|
|
@ -28,13 +28,11 @@ 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.RoomId
|
||||
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.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.exception.ErrorKind
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
|
|
@ -50,8 +48,12 @@ import io.element.android.libraries.matrix.test.room.aRoomMember
|
|||
import io.element.android.libraries.matrix.test.room.aRoomPreview
|
||||
import io.element.android.libraries.matrix.test.room.aRoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
|
||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
|
||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceService
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.any
|
||||
import io.element.android.tests.testutils.lambda.assert
|
||||
|
|
@ -90,6 +92,9 @@ class JoinRoomPresenterTest {
|
|||
val roomInfo = aRoomInfo()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -107,7 +112,7 @@ class JoinRoomPresenterTest {
|
|||
assertThat(contentState.topic).isEqualTo(roomInfo.topic)
|
||||
assertThat(contentState.alias).isEqualTo(roomInfo.canonicalAlias)
|
||||
assertThat(contentState.numberOfMembers).isEqualTo(roomInfo.joinedMembersCount)
|
||||
assertThat(contentState.isDm).isEqualTo(roomInfo.isDirect)
|
||||
assertThat(contentState.details).isEqualTo(aLoadedDetailsRoom(isDm = roomInfo.isDirect))
|
||||
assertThat(contentState.roomAvatarUrl).isEqualTo(roomInfo.avatarUrl)
|
||||
}
|
||||
}
|
||||
|
|
@ -118,6 +123,9 @@ class JoinRoomPresenterTest {
|
|||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -142,7 +150,7 @@ class JoinRoomPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `present - when room is invited then join authorization is equal to invited, an inviter is provided`() = runTest {
|
||||
val inviter = aRoomMember(userId = UserId("@bob:example.com"), displayName = "Bob")
|
||||
val inviter = aRoomMember(userId = A_USER_ID_2, displayName = "Bob")
|
||||
val expectedInviteSender = inviter.toInviteSender()
|
||||
val roomInfo = aRoomInfo(
|
||||
currentUserMembership = CurrentUserMembership.INVITED,
|
||||
|
|
@ -151,7 +159,21 @@ class JoinRoomPresenterTest {
|
|||
)
|
||||
val inviteData = roomInfo.toInviteData()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
numberOfJoinedMembers = 5,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -169,6 +191,137 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when space is invited then join authorization is equal to invited, an inviter is provided`() = runTest {
|
||||
val inviter = aRoomMember(userId = A_USER_ID_2, displayName = "Bob")
|
||||
val expectedInviteSender = inviter.toInviteSender()
|
||||
val spaceHero = aMatrixUser()
|
||||
val roomInfo = aRoomInfo(
|
||||
isSpace = true,
|
||||
currentUserMembership = CurrentUserMembership.INVITED,
|
||||
joinedMembersCount = 5,
|
||||
inviter = inviter,
|
||||
heroes = listOf(spaceHero),
|
||||
)
|
||||
val inviteData = roomInfo.toInviteData()
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
numberOfJoinedMembers = 5,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = {
|
||||
FakeSpaceRoomList(
|
||||
initialSpaceFlowValue = aSpaceRoom(
|
||||
childrenCount = 3,
|
||||
)
|
||||
)
|
||||
},
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsInvited(inviteData, expectedInviteSender))
|
||||
assertThat((state.contentState as ContentState.Loaded).numberOfMembers).isEqualTo(5)
|
||||
// Space details are provided
|
||||
assertThat(state.contentState.details).isEqualTo(
|
||||
LoadedDetails.Space(
|
||||
childrenCount = 3,
|
||||
heroes = persistentListOf(spaceHero),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - space is invited - no room info`() = runTest {
|
||||
val spaceHero = aMatrixUser()
|
||||
val spaceRoom = aSpaceRoom(
|
||||
childrenCount = 3,
|
||||
heroes = listOf(spaceHero),
|
||||
)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.failure(Exception("Error"))
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = {
|
||||
FakeSpaceRoomList(
|
||||
initialSpaceFlowValue = spaceRoom,
|
||||
)
|
||||
},
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.ofNullable(null))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
// Space details are provided
|
||||
assertThat((state.contentState as ContentState.Loaded).details).isEqualTo(
|
||||
LoadedDetails.Space(
|
||||
childrenCount = 3,
|
||||
heroes = persistentListOf(spaceHero),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - space is invited - no room info - space room state set`() = runTest {
|
||||
val spaceRoom = aSpaceRoom(
|
||||
state = CurrentUserMembership.INVITED,
|
||||
)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.failure(Exception("Error"))
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = {
|
||||
FakeSpaceRoomList(
|
||||
initialSpaceFlowValue = spaceRoom,
|
||||
)
|
||||
},
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.ofNullable(null))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient
|
||||
)
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
// Space details are provided
|
||||
assertThat(state.contentState).isInstanceOf(ContentState.Loading::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is invited read the number of member from the room preview`() = runTest {
|
||||
val roomInfo = aRoomInfo(
|
||||
|
|
@ -182,10 +335,16 @@ class JoinRoomPresenterTest {
|
|||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
numberOfJoinedMembers = 10,
|
||||
)
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -209,7 +368,11 @@ class JoinRoomPresenterTest {
|
|||
anAcceptDeclineInviteState(eventSink = eventSinkRecorder)
|
||||
}
|
||||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
}
|
||||
|
|
@ -244,6 +407,9 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient,
|
||||
|
|
@ -272,6 +438,9 @@ class JoinRoomPresenterTest {
|
|||
fun `present - when room is joined with error, it is possible to clear the error`() = runTest {
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient,
|
||||
|
|
@ -334,16 +503,14 @@ class JoinRoomPresenterTest {
|
|||
currentUserMembership = CurrentUserMembership.BANNED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
Result.success(aRoomMembershipDetails())
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -371,6 +538,9 @@ class JoinRoomPresenterTest {
|
|||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.LEFT, joinRule = JoinRule.Public)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -392,6 +562,9 @@ class JoinRoomPresenterTest {
|
|||
val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.LEFT, joinRule = null)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
).apply {
|
||||
getRoomInfoFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomInfo))
|
||||
|
|
@ -423,7 +596,7 @@ class JoinRoomPresenterTest {
|
|||
assertThat(contentState.topic).isEqualTo(roomDescription.topic)
|
||||
assertThat(contentState.alias).isEqualTo(roomDescription.alias)
|
||||
assertThat(contentState.numberOfMembers).isEqualTo(roomDescription.numberOfMembers)
|
||||
assertThat(contentState.isDm).isFalse()
|
||||
assertThat(contentState.details).isEqualTo(aLoadedDetailsRoom(isDm = false))
|
||||
assertThat(contentState.roomAvatarUrl).isEqualTo(roomDescription.avatarUrl)
|
||||
}
|
||||
}
|
||||
|
|
@ -497,6 +670,9 @@ class JoinRoomPresenterTest {
|
|||
val fakeKnockRoom = FakeKnockRoom(knockRoomSuccess)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient,
|
||||
|
|
@ -542,6 +718,9 @@ class JoinRoomPresenterTest {
|
|||
val cancelKnockRoom = FakeCancelKnockRoom(cancelKnockRoomSuccess)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient,
|
||||
|
|
@ -586,6 +765,9 @@ class JoinRoomPresenterTest {
|
|||
val fakeForgetRoom = FakeForgetRoom(forgetRoomSuccess)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) },
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient,
|
||||
|
|
@ -634,10 +816,16 @@ class JoinRoomPresenterTest {
|
|||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = null,
|
||||
)
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -652,10 +840,10 @@ class JoinRoomPresenterTest {
|
|||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin,
|
||||
joinRule = JoinRule.Public,
|
||||
details = aLoadedDetailsRoom(isDm = false),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -681,16 +869,14 @@ class JoinRoomPresenterTest {
|
|||
currentUserMembership = CurrentUserMembership.INVITED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
Result.success(aRoomMembershipDetails())
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -705,8 +891,6 @@ class JoinRoomPresenterTest {
|
|||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(
|
||||
inviteData = InviteData(
|
||||
|
|
@ -724,7 +908,9 @@ class JoinRoomPresenterTest {
|
|||
),
|
||||
membershipChangeReason = null,
|
||||
),
|
||||
)
|
||||
),
|
||||
joinRule = JoinRule.Public,
|
||||
details = aLoadedDetailsRoom(isDm = false),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -751,15 +937,15 @@ class JoinRoomPresenterTest {
|
|||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
aRoomMembershipDetails(),
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -774,8 +960,6 @@ class JoinRoomPresenterTest {
|
|||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(
|
||||
banSender = InviteSender(
|
||||
|
|
@ -789,7 +973,9 @@ class JoinRoomPresenterTest {
|
|||
membershipChangeReason = null,
|
||||
),
|
||||
reason = null,
|
||||
)
|
||||
),
|
||||
joinRule = JoinRule.Public,
|
||||
details = aLoadedDetailsRoom(isDm = false),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -815,16 +1001,14 @@ class JoinRoomPresenterTest {
|
|||
currentUserMembership = CurrentUserMembership.KNOCKED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
Result.success(aRoomMembershipDetails())
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -839,10 +1023,10 @@ class JoinRoomPresenterTest {
|
|||
topic = "Room topic",
|
||||
alias = RoomAlias("#alias:matrix.org"),
|
||||
numberOfMembers = 2,
|
||||
isDm = false,
|
||||
roomType = RoomType.Room,
|
||||
roomAvatarUrl = "avatarUrl",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked,
|
||||
joinRule = JoinRule.Public,
|
||||
details = aLoadedDetailsRoom(isDm = false),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -854,9 +1038,17 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Private))
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(joinRule = JoinRule.Private),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -874,9 +1066,17 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Custom("custom")))
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(joinRule = JoinRule.Custom("custom")),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -894,9 +1094,17 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Invite))
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(joinRule = JoinRule.Invite),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -914,9 +1122,19 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.KnockRestricted(persistentListOf())))
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
joinRule = JoinRule.KnockRestricted(persistentListOf())
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -934,9 +1152,17 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Restricted(persistentListOf())))
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(joinRule = JoinRule.Restricted(persistentListOf())),
|
||||
roomMembershipDetails = {
|
||||
Result.success(aRoomMembershipDetails())
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -949,32 +1175,15 @@ 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.IsSpace("AppName"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.failure(AN_EXCEPTION)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -1004,7 +1213,10 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.failure(AN_EXCEPTION)
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -1029,7 +1241,10 @@ class JoinRoomPresenterTest {
|
|||
val client = FakeMatrixClient(
|
||||
getNotJoinedRoomResult = { _, _ ->
|
||||
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden", null))
|
||||
}
|
||||
},
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
|
|
@ -1068,7 +1283,11 @@ internal fun createJoinRoomPresenter(
|
|||
roomDescription: Optional<RoomDescription> = Optional.empty(),
|
||||
serverNames: List<String> = emptyList(),
|
||||
trigger: JoinedRoom.Trigger = JoinedRoom.Trigger.Invite,
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
matrixClient: MatrixClient = FakeMatrixClient(
|
||||
spaceService = FakeSpaceService(
|
||||
spaceRoomListResult = { FakeSpaceRoomList() },
|
||||
),
|
||||
),
|
||||
joinRoomLambda: (RoomIdOrAlias, List<String>, JoinedRoom.Trigger) -> Result<Unit> = { _, _, _ ->
|
||||
Result.success(Unit)
|
||||
},
|
||||
|
|
@ -1095,3 +1314,8 @@ internal fun createJoinRoomPresenter(
|
|||
seenInvitesStore = seenInvitesStore,
|
||||
)
|
||||
}
|
||||
|
||||
private fun aRoomMembershipDetails() = RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||
import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.test.anInviteData
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
|
|
@ -218,21 +217,6 @@ class JoinRoomViewTest {
|
|||
eventsRecorder.assertSingle(JoinRoomEvents.RetryFetchingContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on ok 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,
|
||||
),
|
||||
onBackClick = it
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on ok when user is unauthorized the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ private fun RoomAliasResolverContent(
|
|||
RoomPreviewOrganism(
|
||||
modifier = modifier,
|
||||
avatar = {
|
||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||
PlaceholderAtom(width = AvatarSize.RoomPreviewHeader.dp, height = AvatarSize.RoomPreviewHeader.dp)
|
||||
},
|
||||
title = {
|
||||
RoomPreviewSubtitleAtom(roomAlias.value)
|
||||
|
|
|
|||
|
|
@ -396,10 +396,10 @@ private fun RoomHeaderSection(
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = AvatarData(roomId.value, roomName, avatarUrl, AvatarSize.RoomHeader),
|
||||
avatarData = AvatarData(roomId.value, roomName, avatarUrl, AvatarSize.RoomDetailsHeader),
|
||||
avatarType = AvatarType.Room(
|
||||
heroes = heroes.map { user ->
|
||||
user.getAvatarData(size = AvatarSize.RoomHeader)
|
||||
user.getAvatarData(size = AvatarSize.RoomDetailsHeader)
|
||||
}.toPersistentList(),
|
||||
isTombstoned = isTombstoned,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import kotlinx.collections.immutable.toPersistentSet
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Optional
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
@Inject
|
||||
class SpacePresenter(
|
||||
|
|
@ -64,7 +66,7 @@ class SpacePresenter(
|
|||
}
|
||||
}.collectAsState()
|
||||
|
||||
val currentSpace by remember { spaceRoomList.currentSpaceFlow() }.collectAsState(null)
|
||||
val currentSpace by remember { spaceRoomList.currentSpaceFlow() }.collectAsState(Optional.empty())
|
||||
|
||||
fun handleEvents(event: SpaceEvents) {
|
||||
when (event) {
|
||||
|
|
@ -72,7 +74,7 @@ class SpacePresenter(
|
|||
}
|
||||
}
|
||||
return SpaceState(
|
||||
currentSpace = currentSpace,
|
||||
currentSpace = currentSpace.getOrNull(),
|
||||
children = children.toPersistentList(),
|
||||
seenSpaceInvites = seenSpaceInvites,
|
||||
hideInvitesAvatar = hideInvitesAvatar,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,15 @@ open class SpaceStateProvider : PreviewParameterProvider<SpaceState> {
|
|||
override val values: Sequence<SpaceState>
|
||||
get() = sequenceOf(
|
||||
aSpaceState(),
|
||||
aSpaceState(hasMoreToLoad = true),
|
||||
aSpaceState(
|
||||
parentSpace = aSpaceRoom(
|
||||
name = null,
|
||||
numJoinedMembers = 5,
|
||||
childrenCount = 10,
|
||||
worldReadable = true,
|
||||
),
|
||||
hasMoreToLoad = true,
|
||||
),
|
||||
aSpaceState(
|
||||
hasMoreToLoad = true,
|
||||
children = aListOfSpaceRooms(),
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ private fun SpaceAvatarAndNameRow(
|
|||
.semantics {
|
||||
heading()
|
||||
},
|
||||
text = name ?: stringResource(CommonStrings.common_no_room_name),
|
||||
text = name ?: stringResource(CommonStrings.common_no_space_name),
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
fontStyle = FontStyle.Italic.takeIf { name == null },
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,18 @@ import io.element.android.compound.theme.ElementTheme
|
|||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
@Composable
|
||||
fun RoomPreviewDescriptionAtom(description: String, modifier: Modifier = Modifier) {
|
||||
fun RoomPreviewDescriptionAtom(
|
||||
description: String,
|
||||
modifier: Modifier = Modifier,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = description,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 3,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
maxLines = maxLines,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ fun RoomPreviewSubtitleAtom(subtitle: String, modifier: Modifier = Modifier) {
|
|||
Text(
|
||||
modifier = modifier,
|
||||
text = subtitle,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ fun RoomPreviewTitleAtom(
|
|||
Text(
|
||||
modifier = modifier,
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontHeadingMdBold,
|
||||
style = ElementTheme.typography.fontHeadingLgBold,
|
||||
textAlign = TextAlign.Center,
|
||||
fontStyle = fontStyle,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
|
|
|
|||
|
|
@ -34,14 +34,13 @@ fun RoomPreviewOrganism(
|
|||
title()
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
subtitle()
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
if (memberCount != null) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
memberCount()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
if (description != null) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
description()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ enum class AvatarSize(val dp: Dp) {
|
|||
CurrentUserTopBar(32.dp),
|
||||
|
||||
IncomingCall(140.dp),
|
||||
RoomHeader(96.dp),
|
||||
RoomDetailsHeader(96.dp),
|
||||
RoomListItem(52.dp),
|
||||
|
||||
SpaceListItem(52.dp),
|
||||
|
|
@ -69,5 +69,7 @@ enum class AvatarSize(val dp: Dp) {
|
|||
|
||||
OrganizationHeader(64.dp),
|
||||
SpaceHeader(64.dp),
|
||||
RoomPreviewHeader(64.dp),
|
||||
RoomPreviewInviter(56.dp),
|
||||
SpaceMember(24.dp),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.api.spaces
|
|||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.Optional
|
||||
|
||||
interface SpaceRoomList {
|
||||
sealed interface PaginationStatus {
|
||||
|
|
@ -16,7 +17,7 @@ interface SpaceRoomList {
|
|||
data class Idle(val hasMoreToLoad: Boolean) : PaginationStatus
|
||||
}
|
||||
|
||||
fun currentSpaceFlow(): Flow<SpaceRoom?>
|
||||
fun currentSpaceFlow(): StateFlow<Optional<SpaceRoom>>
|
||||
|
||||
val spaceRoomsFlow: Flow<List<SpaceRoom>>
|
||||
val paginationStatusFlow: StateFlow<PaginationStatus>
|
||||
|
|
|
|||
|
|
@ -13,13 +13,14 @@ import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
|||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
|
||||
import java.util.Optional
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
|
||||
|
||||
class RustSpaceRoomList(
|
||||
|
|
@ -31,7 +32,7 @@ class RustSpaceRoomList(
|
|||
) : SpaceRoomList {
|
||||
private val inner = CompletableDeferred<InnerSpaceRoomList>()
|
||||
|
||||
override fun currentSpaceFlow(): Flow<SpaceRoom?> {
|
||||
override fun currentSpaceFlow(): StateFlow<Optional<SpaceRoom>> {
|
||||
return spaceRoomCache.getSpaceRoomFlow(roomId)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ package io.element.android.libraries.matrix.impl.spaces
|
|||
import io.element.android.libraries.core.coroutine.mapState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* An in memory cache of space rooms.
|
||||
|
|
@ -20,8 +21,8 @@ import kotlinx.coroutines.flow.update
|
|||
*/
|
||||
class SpaceRoomCache {
|
||||
private val inMemoryCache = MutableStateFlow<Map<RoomId, SpaceRoom?>>(emptyMap())
|
||||
fun getSpaceRoomFlow(roomId: RoomId): Flow<SpaceRoom?> {
|
||||
return inMemoryCache.mapState { it[roomId] }
|
||||
fun getSpaceRoomFlow(roomId: RoomId): StateFlow<Optional<SpaceRoom>> {
|
||||
return inMemoryCache.mapState { Optional.ofNullable(it[roomId]) }
|
||||
}
|
||||
|
||||
fun update(spaceRooms: List<SpaceRoom>) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import kotlinx.coroutines.test.runTest
|
|||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.SpaceListUpdate
|
||||
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
|
||||
|
||||
class RustSpaceRoomListTest {
|
||||
|
|
@ -93,10 +94,10 @@ class RustSpaceRoomListTest {
|
|||
spaceRoomCache = spaceRoomCache,
|
||||
)
|
||||
sut.currentSpaceFlow().test {
|
||||
assertThat(awaitItem()).isNull()
|
||||
assertThat(awaitItem().getOrNull()).isNull()
|
||||
val spaceRoom = aSpaceRoom(roomId = A_ROOM_ID)
|
||||
spaceRoomCache.update(listOf(spaceRoom))
|
||||
assertThat(awaitItem()).isEqualTo(spaceRoom)
|
||||
assertThat(awaitItem().getOrNull()).isEqualTo(spaceRoom)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class SpaceRoomCacheTest {
|
|||
fun `getSpaceRoomFlow emits items`() = runTest {
|
||||
val sut = SpaceRoomCache()
|
||||
sut.getSpaceRoomFlow(A_ROOM_ID).test {
|
||||
assertThat(awaitItem()).isNull()
|
||||
assertThat(awaitItem().isEmpty).isTrue()
|
||||
val room = aSpaceRoom(
|
||||
roomId = A_ROOM_ID,
|
||||
roomType = RoomType.Room,
|
||||
|
|
@ -34,7 +34,7 @@ class SpaceRoomCacheTest {
|
|||
roomType = RoomType.Space,
|
||||
)
|
||||
sut.update(listOf(space))
|
||||
assertThat(awaitItem()).isEqualTo(space)
|
||||
assertThat(awaitItem().get()).isEqualTo(space)
|
||||
val spaceOther = aSpaceRoom(
|
||||
roomId = A_ROOM_ID_2,
|
||||
roomType = RoomType.Space,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import java.util.Optional
|
||||
|
||||
class FakeSpaceRoomList(
|
||||
initialSpaceFlowValue: SpaceRoom? = null,
|
||||
|
|
@ -22,11 +23,11 @@ class FakeSpaceRoomList(
|
|||
initialSpaceRoomList: SpaceRoomList.PaginationStatus = SpaceRoomList.PaginationStatus.Loading,
|
||||
private val paginateResult: () -> Result<Unit> = { lambdaError() },
|
||||
) : SpaceRoomList {
|
||||
private val currentSpaceMutableStateFlow: MutableStateFlow<SpaceRoom?> = MutableStateFlow(initialSpaceFlowValue)
|
||||
override fun currentSpaceFlow(): Flow<SpaceRoom?> = currentSpaceMutableStateFlow.asStateFlow()
|
||||
private val currentSpaceMutableStateFlow: MutableStateFlow<Optional<SpaceRoom>> = MutableStateFlow(Optional.ofNullable(initialSpaceFlowValue))
|
||||
override fun currentSpaceFlow(): StateFlow<Optional<SpaceRoom>> = currentSpaceMutableStateFlow.asStateFlow()
|
||||
|
||||
fun emitCurrentSpace(value: SpaceRoom?) {
|
||||
currentSpaceMutableStateFlow.value = value
|
||||
currentSpaceMutableStateFlow.value = Optional.ofNullable(value)
|
||||
}
|
||||
|
||||
private val _spaceRoomsFlow: MutableStateFlow<List<SpaceRoom>> = MutableStateFlow(initialSpaceRoomsValue)
|
||||
|
|
|
|||
|
|
@ -7,19 +7,16 @@
|
|||
|
||||
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.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
|
||||
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.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
|
|
@ -29,6 +26,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
|||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -47,47 +45,45 @@ fun SpaceHeaderView(
|
|||
modifier: Modifier = Modifier,
|
||||
topicMaxLines: Int = Int.MAX_VALUE,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 32.dp, bottom = 24.dp, start = 16.dp, end = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.Space(false),
|
||||
)
|
||||
name?.let {
|
||||
Text(
|
||||
text = name,
|
||||
style = ElementTheme.typography.fontHeadingLgBold,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Center,
|
||||
RoomPreviewOrganism(
|
||||
modifier = modifier.padding(24.dp),
|
||||
avatar = {
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.Space(),
|
||||
)
|
||||
}
|
||||
if (joinRule != null) {
|
||||
SpaceInfoRow(
|
||||
joinRule = joinRule,
|
||||
numberOfRooms = numberOfRooms,
|
||||
},
|
||||
title = {
|
||||
if (name != null) {
|
||||
RoomPreviewTitleAtom(title = name)
|
||||
} else {
|
||||
RoomPreviewTitleAtom(
|
||||
title = stringResource(id = CommonStrings.common_no_space_name),
|
||||
fontStyle = FontStyle.Italic
|
||||
)
|
||||
}
|
||||
},
|
||||
subtitle = {
|
||||
if (joinRule != null) {
|
||||
SpaceInfoRow(
|
||||
joinRule = joinRule,
|
||||
numberOfRooms = numberOfRooms,
|
||||
)
|
||||
}
|
||||
},
|
||||
description = if (topic.isNullOrBlank()) {
|
||||
null
|
||||
} else {
|
||||
{ RoomPreviewDescriptionAtom(description = topic, maxLines = topicMaxLines) }
|
||||
},
|
||||
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
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ fun SpaceRoomItemView(
|
|||
onLongClick = onLongClick,
|
||||
) {
|
||||
NameAndIndicatorRow(
|
||||
isSpace = spaceRoom.isSpace,
|
||||
name = spaceRoom.name,
|
||||
showIndicator = showUnreadIndicator
|
||||
)
|
||||
|
|
@ -130,6 +131,7 @@ private fun SubtitleRow(
|
|||
|
||||
@Composable
|
||||
private fun NameAndIndicatorRow(
|
||||
isSpace: Boolean,
|
||||
name: String?,
|
||||
showIndicator: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -142,7 +144,7 @@ private fun NameAndIndicatorRow(
|
|||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
text = name ?: stringResource(id = CommonStrings.common_no_room_name),
|
||||
text = name ?: stringResource(id = if (isSpace) CommonStrings.common_no_space_name else CommonStrings.common_no_room_name),
|
||||
fontStyle = FontStyle.Italic.takeIf { name == null },
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ class DefaultNotificationConversationService(
|
|||
targetSize = defaultShortcutIconSize.toLong()
|
||||
)?.let(IconCompat::createWithBitmap)
|
||||
?: 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)
|
||||
|
||||
val shortcutInfo = ShortcutInfoCompat.Builder(context, createShortcutId(sessionId, roomId))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4053802814238aa74a5c606a4b37e0afcb923282c3e7cece737b0ec4f6828351
|
||||
size 106332
|
||||
oid sha256:fbdebc1c9361339dd0db1051e59162ed62fa2787c46457848da5ee6a30474588
|
||||
size 106570
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:04ec1bd0bd667d5319843f480f2b7ca4b1b4364db4fbe22d85c10d291891642a
|
||||
size 42696
|
||||
oid sha256:7a6d3be47ab7d9234657d4d088389c8aadb4d9e073d8c91f4f81dded1b6662a6
|
||||
size 42160
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d02259f3163c49b5c39d38d7a84af50583f6a0ce2a66e8ad013ac18278de65c0
|
||||
size 103621
|
||||
oid sha256:7bb7d1c08f5b2551117aa1aff8a3afbabd0f4100e0fae11cf415bd8e851c0307
|
||||
size 103835
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0d00883e68471491d8edf06eee394e4310707dbd03782f57bb7f783822790b61
|
||||
size 41372
|
||||
oid sha256:b3f70a15def31e67e84afe7f9545d280bdbca01c6dd63864662f19976df3bf33
|
||||
size 40960
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7423c3f11ec8661d7fb3f57d7d78459e784221644229515080e0b0dc6ce59b6e
|
||||
size 10129
|
||||
oid sha256:8c88f9a3ecbcf7db13846a6074c75fca2ce6bdbe0edd850bfc139e01a4fdc0c8
|
||||
size 9956
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:08ed98b594a6730d52ab344aff6bea8a38216a609549be267f9046dd69858903
|
||||
size 38478
|
||||
oid sha256:7fdd74d6df903a0cedfbfc2f30430b5802e4f0bc7b2711e20ae47d297066b056
|
||||
size 40297
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bf0a1c9fea2a067d6156985cce2fd2a2551f6111e7739206f7bd70a563a97fcf
|
||||
size 45333
|
||||
oid sha256:ed26457abf2a6d89d61e911944b4e9f62de248c1939f997d0f8764e4c1ae08e9
|
||||
size 43088
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:24d9f9b2a63773f5bc49b7f8f8b85d1dd13e8e62175b61bca534b04626d19b0f
|
||||
size 46336
|
||||
oid sha256:46b410cba77b24d8657031f5fde29d4f25a639300862641ce0f8ad0de8ff99cb
|
||||
size 44076
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ca5217bba4fe65af4ac2a0aaaefa941a5a5ed0735dbd9bd69a648a157ca0c51b
|
||||
size 29343
|
||||
oid sha256:950e90b7c2c67fc95c9d6698dc7436885ee80dcf9da7d8467b0027c04c03c8f5
|
||||
size 29243
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:41a3c9cbec371b6aacabcb954315a65939b579c5963db63fc11789326ad2fc1b
|
||||
size 29068
|
||||
oid sha256:37ac3a032e758c4abf9ac8a87e887c7e015f2d35160d59a7e263963300bd0941
|
||||
size 31558
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0249fb4df9e88abaa28cc3a10e2cadc23b3b84581d74893d525076683f0a5588
|
||||
size 36386
|
||||
oid sha256:44b9c4608696c6a690e7f00f661b3cb55d03826b11569b6e8ce0a48f9754dd39
|
||||
size 38547
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e1c6c00bca10e0bf725ab5debe3cd9dbb14be89f29872ca70df8f3886d1cec19
|
||||
size 40928
|
||||
oid sha256:f92065e3de67f39ce27ae3e321f59fa2d18b5927752dc760450ec275935b1959
|
||||
size 43405
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:96ecb81ebfc7545eca7931538a443b7190b7903d8ebf5bf70bb95eb3b42e6a38
|
||||
size 30092
|
||||
oid sha256:919101bfd215af97a098bdfae4980445bd1d0560f96fe9ce13a870e2d8817fff
|
||||
size 34247
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:09e79f148754898125af4972bbfecfb1e3fc8f2de918ba9dd12d9766ef09e4f1
|
||||
size 31417
|
||||
oid sha256:2f9707b754041c871c408e12613faa186fa4f4f760ca793caf20c62bae07246b
|
||||
size 32021
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c87682ca77c0ee163e437dae9f177e52937c6099e1d3583f2138735fd5ecc8f
|
||||
size 26669
|
||||
oid sha256:5ad9105ca2659f51b16b41f9317812590f38adb8431a64a139bb47f54fdf9eff
|
||||
size 29754
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8319fdce8ae47fd6dc7aea8f6c98e9e4de7322f0b9adc4262695ff3990a63052
|
||||
size 39520
|
||||
oid sha256:04180611d2a959d07bbbd051c4386f6428d841c5ea74e197a80149370db76e04
|
||||
size 41830
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0ae6db46d01c157fcb8bb436b43e38da13723c498a222e68f792ac170ea2e2ac
|
||||
size 27841
|
||||
oid sha256:7c4a828b9aa4e4963e3cb39e6f65f82ff5e595fdccb4dc5b48db532eeac1fb00
|
||||
size 31388
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:484657673c4ae676b330320ba213485a16c6f31ea025b16b4f2c3a53053eaf7a
|
||||
size 29860
|
||||
oid sha256:bdc24799de625f9826fcf87a624e76b530e7f0e44c294d49f842ed2de7a9c085
|
||||
size 32981
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a239f4f8003693097fee6e9dddc31ed1ce7a53f7c2b9dab9db55059ca51df64f
|
||||
size 37645
|
||||
oid sha256:247a1cec9f0729ef1b23dc5ad730521bb7bfc117b4e9cc901ec922ee946f942e
|
||||
size 39401
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e10c07c74c54920daac28945f2fcafb8956edcd6df5aaac1d7dc757bd119ee3d
|
||||
size 26750
|
||||
oid sha256:bcc00accf1fa84c48831378faa27558d5ba24961dd3ba0c5f0587329cdbaa9bf
|
||||
size 29110
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:657dfa3cd260803b3386ae6c3d44f6cc4277c2ef2572680145c419dfd9690e7a
|
||||
size 33276
|
||||
oid sha256:da07e3041ec5814feb0d14e8478d0c817d1496155db2daa612048b94530df20b
|
||||
size 38200
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:58776319728d3367a347d62cf120803582b05c564f9cd76b2c8f406e70673821
|
||||
size 9949
|
||||
oid sha256:c91724171606aa0c36987908af0abce5c05b3311469a08e532532307d1224c60
|
||||
size 9842
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4ae699d09bc1287d12d9974a6801f6b1648f0d6bd63eb9ac10302be37cae5383
|
||||
size 38181
|
||||
oid sha256:f5a6da57cc5659ba3f3705cba41fd23c7628ba2477fcfc427b8e26bde37ed812
|
||||
size 39960
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:511985d79fe13467eec62772dbe6cc69dec1f1c9f9a3e66428fe9b22ce279d1f
|
||||
size 44937
|
||||
oid sha256:5b9cf46f0039e0f91e8372cd5f9a15b7b54b75c80e0a38f9ebc2548315d57c9f
|
||||
size 42657
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a084ed7657a2f263283a477e700df9fb6219e8268380f764c2e5e4055465044d
|
||||
size 45898
|
||||
oid sha256:5a4f65342c0e84179aceadc5d75fc763ed45c23f4db44bc16441c50990b193ed
|
||||
size 43582
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:51680be3eede0c4a53683b298415a07f3e9ea688ac39fe29373e1e80b0f4540b
|
||||
size 28778
|
||||
oid sha256:e2ef8069be91c6e20263da790c0b8b777515f46bdc4769532b321ae86c9152b8
|
||||
size 28701
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7172438656b08d5c244fc570fd25dc074562b7599f15eda8241ddb7c9b275fcd
|
||||
size 28765
|
||||
oid sha256:5f47c18fda5c4839e1098955f57c4a3e5b9856203c6a84c31903aa5e0b2da46b
|
||||
size 30971
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9aaf1721d3b8c3a000402eb605b7ba742560fae7297514a15f74fd97c2f838a1
|
||||
size 35406
|
||||
oid sha256:112286f6a18865db2333a68cb804e930eb873bb04596bada2f825e2223b4f537
|
||||
size 37362
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b7b113b174727ce08ed2b23f320d49b556e869249302c0b84c1b5916ae8864ac
|
||||
size 40563
|
||||
oid sha256:ff86f906357d0bf611bfc9cd235e66e0247dd7bb66903cb0e5730224d5dc897c
|
||||
size 43008
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:444f717f986b154297294df6ba731befc3e2049f89a3294e26fd6d90f0831996
|
||||
size 29353
|
||||
oid sha256:cc849f3158de5cd3cdc64a57e613f3b3a5cd70794e7984d7cfa1f8f425184592
|
||||
size 33440
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f9bd6d13f3c4f7a715e9e8698db252bc33823fe180e6d279eabcc9de618dcaee
|
||||
size 31182
|
||||
oid sha256:ed211c9bc258185de6ea4cc414fd214f256ad1649c26a8a28f25fd1b29c34783
|
||||
size 31630
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:acdd2cd73f2a37190bbbeb829af68a28dae8d73f5462396db4c6238ee8319351
|
||||
size 26733
|
||||
oid sha256:6a88ffdfd4259a2e85b3a8375591eab3b0f12e927eb83f9c6820995acf44d79e
|
||||
size 29607
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:731f81d6ca0d4677dc56b58316c7396a7ad1ee08c785aecbb9eea4a0dea6b77a
|
||||
size 38428
|
||||
oid sha256:7fbe20a3d4e0c3706ce81c1f52c65ff4f749b0afd6bdd3efb4100ee1019afc51
|
||||
size 40580
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9d3a672f30777ff5b487bca938e612de8e34de94dc5ac189659ced73ed49bffa
|
||||
size 26309
|
||||
oid sha256:cff1f0a8a34cfd4eb55487540a02c70e629d7f6123355c95380498817053eb78
|
||||
size 29575
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e0d60be4104d5f124e49e8a7434204bd0593b46fca838e787aefa40769ac1778
|
||||
size 29202
|
||||
oid sha256:71e56b906decbea31fcf5581dc92dcbb6be79f6a6962595786e9533cc62254ad
|
||||
size 32045
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7129f65572d7b4f6b302fa803f878b269f2ee7b11a684c3e1ec051f9192fc8fa
|
||||
size 36927
|
||||
oid sha256:18e36242d664e1f2179bb3aef3f77a5bbbf5cd44aece2d2c9a3d7d70d2356b92
|
||||
size 38769
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:83b636552b1a28db8bb81e870486acd861ee131f51f8a8980fb3f1471e55ba3f
|
||||
size 24709
|
||||
oid sha256:37270eed539ed52a113375dbfa516f443838530ef7995bb6372764afbc25e75d
|
||||
size 26787
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8c862dd75be71248752d53cc605860c55fe4eecce75eba545ab4471a13585296
|
||||
size 32951
|
||||
oid sha256:0b73e8a2429db6324f471908f46ccd2f6723d59caf4acc738c07d6e402a19285
|
||||
size 37752
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1b0c1728d598c5bf307a2b9a193c2746831c83de892fc53ccb7da1bb183b8297
|
||||
size 8475
|
||||
oid sha256:6d4663e9c2c439b05d6d1abe7ae7098539f5263461e9051b0b97305a31b5aa33
|
||||
size 8395
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e10c07c74c54920daac28945f2fcafb8956edcd6df5aaac1d7dc757bd119ee3d
|
||||
size 26750
|
||||
oid sha256:5365eb3dd7e10fde8591e6d5ee6dfadef88a9fa25339309ce08445f89af88789
|
||||
size 29187
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:78826ac37783b67bab2ef7c93a5ad1fcc1fd167926ed3e6d8c92eb826c8b603f
|
||||
size 23462
|
||||
oid sha256:5c66ca10f770fa2f29716f7c737be0acef03e364e9a27a85b81e4ef63e768b6f
|
||||
size 24012
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a3f39d62131784e82104ee0033e4da9e2de45eacc1527b878fdaa9dd64d45aa2
|
||||
size 8324
|
||||
oid sha256:c5b7cb8b7ef8b58a5fa77b7c1c6a911798940337ea9646383ee0cb082dc68f7e
|
||||
size 8263
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:83b636552b1a28db8bb81e870486acd861ee131f51f8a8980fb3f1471e55ba3f
|
||||
size 24709
|
||||
oid sha256:c02fa02a7f5787d375ffa6e9bb69cde0b23ca87512ee8120d318becd6de13bef
|
||||
size 26763
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7114651af7b2d0ae7caf7df0bb81bb117fc829bd9795a57e173729673c23d231
|
||||
size 21082
|
||||
oid sha256:5852e500de1f27083f033726a89d2dda594497d2af7fbe843091cdbd06ddd975
|
||||
size 21628
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7e69f830b7f15de531fc46efc02964fb641a4e3754b5108fb8020e97bbfb4b56
|
||||
size 15698
|
||||
oid sha256:4c53e7edfe8b9ea00bb97f9ab6e8289081b5c36aad54a58d4f535fc533338797
|
||||
size 15733
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b5fba00f44ab63c07978ec0143f2f1fefc8fd1d78aed22a367e831c105b806c7
|
||||
size 17306
|
||||
oid sha256:0075dca9fd927dc7df4e987fe5cca8d2a2ca5b268bc46916dcd49724264a3859
|
||||
size 19834
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c7b387db1b0637745dfc93559b9afa3c637e5a492bf19f6f1d0dbe802ed73bb
|
||||
size 47156
|
||||
oid sha256:9d784e5d4d4af937a33825f7098d0849fa044d96e4cf21c7244f51432d3f32c7
|
||||
size 46725
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9892ec0c06bfa6e0b3a95fd35efa97e44f2e1f6264a324d6fdd994847f1830d1
|
||||
size 45897
|
||||
oid sha256:1a1c851207069720dd83cf32da6da80d25a4a4c40cba9884548f9dea09ca6654
|
||||
size 45383
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:91ad7e392e21afe93606956e8e7eab7f0b075574e782ff15e873a71fc3ab16ab
|
||||
size 15637
|
||||
oid sha256:ea671a40116dfe7a0e4ac97b58cbbc48b3f70fe08b5613c4b21ec13bc6850c3f
|
||||
size 15589
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9d50be9f20ed0e75e337d790edb329996abf9e626591882088817301338fadb6
|
||||
size 17213
|
||||
oid sha256:8b69241047e1d5787620331adc33f2b78084f47cf02c531a4e41011816590a09
|
||||
size 19523
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d76f7b10d8c88f39ab36581be35dd2a5ca5e7c3a92c69b96d4f3ec87f2e93468
|
||||
size 46564
|
||||
oid sha256:ae27a6a05d85587b066203ec8828db0ec1cec2171e1f3a63c6b0c26e0b71555e
|
||||
size 46151
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:057ceb522ff354d0d7e8b73d59eb53c559f9c22a2727d24744dc6a34b9daaa55
|
||||
size 45034
|
||||
oid sha256:067d6b499ce3ec737792a773c2959aea44829cb27a8fecbe1925ec884bc7f53f
|
||||
size 44650
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ce1aba18ea8a6b45d9c40de9af961952e261c7c6200ab13cdfa67906d995208b
|
||||
size 14888
|
||||
oid sha256:8da97746633081690a09a79e3f9974257bbe0f292ad5b8ee2eed454c1273c01e
|
||||
size 19747
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6a4d295ae71ba2709845312b84983b79348069ded46b30091b9fa769a587cbb7
|
||||
size 14337
|
||||
oid sha256:1aaa84f80b3691ed83308966e53deb8dbc97c7943d2f68a8036a6ead603ea627
|
||||
size 18270
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:da8f54591a52475c6f47fa083a1bb397e1164dd3e735562388fde4ff45644150
|
||||
size 16275
|
||||
oid sha256:ddaae721cac21a188dc3862e3e14e80eeb13c92826bccfb8364ecbd72948d9bd
|
||||
size 23653
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:207d569640554b3b11cadb03a231ae0dfd85a3c470695cc8428cb2e295d98776
|
||||
size 18858
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:68ae347075a6bef936e9c905a12b6b561a01737ab886436d73f4987783a1b45f
|
||||
size 17541
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8caa1fbeb5250684363a21549cdcfff60489059c9d1f0ca115503e63675b3511
|
||||
size 22394
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ce1aba18ea8a6b45d9c40de9af961952e261c7c6200ab13cdfa67906d995208b
|
||||
size 14888
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6a4d295ae71ba2709845312b84983b79348069ded46b30091b9fa769a587cbb7
|
||||
size 14337
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:da8f54591a52475c6f47fa083a1bb397e1164dd3e735562388fde4ff45644150
|
||||
size 16275
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3f52fa29e031ab362baed77ac18228960d67eb5d6bdf909b6c1fb3777f5eacc5
|
||||
size 19629
|
||||
oid sha256:8cb143e42d0bc07652d74c74ba641a5332ee9bebb071fa5efba59ecacad04be5
|
||||
size 21857
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4b6a0bdd2239b473f541e95588f2531a2761d0d9872cda61cdd87e2e195b14cf
|
||||
size 17425
|
||||
oid sha256:5eecc64fbbf67e53578ba6c974905c16d000a5dc8b2e0fa99c41e46dea36424b
|
||||
size 19651
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3898d22d9de94323eb04d5385af9a1084434c521f8b51f5af74ef1159dd2d1cc
|
||||
size 25138
|
||||
oid sha256:c425ffed237de0e71451baf51f674051c3ab6481ecceeff15c7f2f73193fd3ab
|
||||
size 26866
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ebb657ce2227ccefcf9055c8c90342d6581ac0d916cd9f7e6a4990fa2bdc4177
|
||||
size 61352
|
||||
oid sha256:21acaf9ea606378f5f1e83795e41bec9edc2da05143efba04b70f67cce2c80b0
|
||||
size 61626
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:97c8ea5d2d83222ee16c0a27c783e04ec12dbd26aff427c28a21c9c581ad057d
|
||||
size 60558
|
||||
oid sha256:e20d4dc80e8f983516cde0361333f941818cade431fb38ae318cfa9a589ab0d8
|
||||
size 60846
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue