Merge pull request #6150 from element-hq/feature/fga/space_ui_tweaks
Iterate on Space related UI
This commit is contained in:
commit
b271e06973
176 changed files with 695 additions and 583 deletions
|
|
@ -82,10 +82,6 @@ class ConfigureRoomPresenter(
|
|||
private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
|
||||
private var pendingPermissionRequest = false
|
||||
|
||||
init {
|
||||
dataStore.setIsSpace(isSpace)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun present(): ConfigureRoomState {
|
||||
val canAddRoomToSpace by featureFlagService.isFeatureEnabledFlow(FeatureFlags.CreateSpaces).collectAsState(false)
|
||||
|
|
@ -123,9 +119,10 @@ class ConfigureRoomPresenter(
|
|||
} else {
|
||||
persistentListOf()
|
||||
}
|
||||
|
||||
val parentSpace = spaces.find { it.roomId == initialParentSpaceId }
|
||||
parentSpace?.let { dataStore.setParentSpace(it) }
|
||||
parentSpace?.let {
|
||||
dataStore.setParentSpace(parentSpace = parentSpace, updateVisibility = true)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(cameraPermissionState.permissionGranted) {
|
||||
|
|
@ -152,21 +149,42 @@ class ConfigureRoomPresenter(
|
|||
// 2. If it has a parent space.
|
||||
// 3. If knocking is enabled.
|
||||
val parentSpace = createRoomConfig.parentSpace
|
||||
val availableJoinRules = remember(createRoomConfig.parentSpace, isSpace, isKnockFeatureEnabled) {
|
||||
val availableJoinRules = remember(parentSpace, isSpace, isKnockFeatureEnabled) {
|
||||
when {
|
||||
isSpace && parentSpace != null -> TODO("Adding a space to a parent space is not supported yet! How did you get here?")
|
||||
parentSpace == null || parentSpace.joinRule == JoinRule.Public -> listOfNotNull(
|
||||
JoinRuleItem.PublicVisibility.Public,
|
||||
JoinRuleItem.PublicVisibility.AskToJoin.takeIf { !isSpace && isKnockFeatureEnabled },
|
||||
JoinRuleItem.Private,
|
||||
JoinRuleItem.PrivateVisibility.Private,
|
||||
).toImmutableList()
|
||||
else -> listOfNotNull(
|
||||
JoinRuleItem.PublicVisibility.Restricted(parentSpace.roomId),
|
||||
JoinRuleItem.PublicVisibility.AskToJoinRestricted(parentSpace.roomId).takeIf { !isSpace && isKnockFeatureEnabled },
|
||||
JoinRuleItem.Private,
|
||||
JoinRuleItem.PrivateVisibility.Restricted(parentSpace.roomId),
|
||||
JoinRuleItem.PrivateVisibility.AskToJoinRestricted(parentSpace.roomId).takeIf { isKnockFeatureEnabled },
|
||||
JoinRuleItem.PrivateVisibility.Private,
|
||||
).toImmutableList()
|
||||
}
|
||||
}
|
||||
val currentJoinRule = createRoomConfig.visibilityState.joinRuleItem
|
||||
LaunchedEffect(availableJoinRules, currentJoinRule) {
|
||||
// Find matching rule by type (ignoring parentSpaceId parameter for Restricted types)
|
||||
val matchingRule = when (currentJoinRule) {
|
||||
is JoinRuleItem.PrivateVisibility.Restricted ->
|
||||
availableJoinRules.filterIsInstance<JoinRuleItem.PrivateVisibility.Restricted>().firstOrNull()
|
||||
is JoinRuleItem.PrivateVisibility.AskToJoinRestricted ->
|
||||
availableJoinRules.filterIsInstance<JoinRuleItem.PrivateVisibility.AskToJoinRestricted>().firstOrNull()
|
||||
else -> availableJoinRules.find { it == currentJoinRule }
|
||||
}
|
||||
when {
|
||||
matchingRule == null -> {
|
||||
// No matching type fallback to Private (always available)
|
||||
dataStore.setJoinRule(JoinRuleItem.PrivateVisibility.Private)
|
||||
}
|
||||
matchingRule != currentJoinRule -> {
|
||||
// Same type but different params (e.g., different parentSpaceId), update
|
||||
dataStore.setJoinRule(matchingRule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createRoom(config: CreateRoomConfig) {
|
||||
createRoomAction.value = AsyncAction.Uninitialized
|
||||
|
|
@ -193,7 +211,7 @@ class ConfigureRoomPresenter(
|
|||
}
|
||||
}
|
||||
is ConfigureRoomEvents.SetParentSpace -> {
|
||||
dataStore.setParentSpace(event.space)
|
||||
dataStore.setParentSpace(event.space, false)
|
||||
}
|
||||
ConfigureRoomEvents.CancelCreateRoom -> {
|
||||
createRoomAction.value = AsyncAction.Uninitialized
|
||||
|
|
@ -210,6 +228,7 @@ class ConfigureRoomPresenter(
|
|||
roomAddressValidity = roomAddressValidity.value,
|
||||
availableJoinRules = availableJoinRules,
|
||||
spaces = spaces,
|
||||
isSpace = isSpace,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
|
@ -220,35 +239,41 @@ class ConfigureRoomPresenter(
|
|||
) = launch {
|
||||
suspend {
|
||||
val avatarUrl = config.avatarUri?.let { uploadAvatar(it.toUri()) }
|
||||
val params = if (config.visibilityState is RoomVisibilityState.Public) {
|
||||
CreateRoomParameters(
|
||||
name = config.roomName,
|
||||
topic = config.topic,
|
||||
isEncrypted = false,
|
||||
isDirect = false,
|
||||
visibility = RoomVisibility.Public,
|
||||
joinRuleOverride = config.visibilityState.joinRuleItem.toJoinRule()
|
||||
// No need to specify the public join rule override, since the preset is already PUBLIC_CHAT
|
||||
.takeIf { it != JoinRule.Public },
|
||||
preset = RoomPreset.PUBLIC_CHAT,
|
||||
invite = config.invites.map { it.userId },
|
||||
avatar = avatarUrl,
|
||||
roomAliasName = config.visibilityState.roomAddress(),
|
||||
isSpace = isSpace,
|
||||
)
|
||||
} else {
|
||||
CreateRoomParameters(
|
||||
name = config.roomName,
|
||||
topic = config.topic,
|
||||
isEncrypted = config.visibilityState is RoomVisibilityState.Private,
|
||||
isDirect = false,
|
||||
visibility = RoomVisibility.Private,
|
||||
historyVisibilityOverride = RoomHistoryVisibility.Invited,
|
||||
preset = RoomPreset.PRIVATE_CHAT,
|
||||
invite = config.invites.map { it.userId },
|
||||
avatar = avatarUrl,
|
||||
isSpace = isSpace,
|
||||
)
|
||||
val params = when (config.visibilityState) {
|
||||
is RoomVisibilityState.Public -> {
|
||||
CreateRoomParameters(
|
||||
name = config.roomName,
|
||||
topic = config.topic,
|
||||
isEncrypted = false,
|
||||
isDirect = false,
|
||||
visibility = RoomVisibility.Public,
|
||||
joinRuleOverride = config.visibilityState.joinRuleItem.toJoinRule()
|
||||
// No need to specify the public join rule override, since the preset is already PUBLIC_CHAT
|
||||
.takeIf { it != JoinRule.Public },
|
||||
preset = RoomPreset.PUBLIC_CHAT,
|
||||
invite = config.invites.map { it.userId },
|
||||
avatar = avatarUrl,
|
||||
roomAliasName = config.visibilityState.roomAddress(),
|
||||
isSpace = isSpace,
|
||||
)
|
||||
}
|
||||
is RoomVisibilityState.Private -> {
|
||||
CreateRoomParameters(
|
||||
name = config.roomName,
|
||||
topic = config.topic,
|
||||
isEncrypted = true,
|
||||
isDirect = false,
|
||||
visibility = RoomVisibility.Private,
|
||||
historyVisibilityOverride = RoomHistoryVisibility.Invited,
|
||||
joinRuleOverride = config.visibilityState.joinRuleItem.toJoinRule()
|
||||
// No need to specify the Invite join rule override, since the preset is already PRIVATE_CHAT
|
||||
.takeIf { it != JoinRule.Invite },
|
||||
preset = RoomPreset.PRIVATE_CHAT,
|
||||
invite = config.invites.map { it.userId },
|
||||
avatar = avatarUrl,
|
||||
isSpace = isSpace,
|
||||
)
|
||||
}
|
||||
}
|
||||
val roomId = matrixClient.createRoom(params)
|
||||
.onFailure { failure ->
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import io.element.android.libraries.permissions.api.PermissionsState
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class ConfigureRoomState(
|
||||
val isSpace: Boolean,
|
||||
val config: CreateRoomConfig,
|
||||
val avatarActions: ImmutableList<AvatarAction>,
|
||||
val createRoomAction: AsyncAction<RoomId>,
|
||||
|
|
@ -28,5 +29,6 @@ data class ConfigureRoomState(
|
|||
val eventSink: (ConfigureRoomEvents) -> Unit
|
||||
) {
|
||||
val isValid: Boolean = config.roomName?.isNotEmpty() == true &&
|
||||
(config.visibilityState is RoomVisibilityState.Private || roomAddressValidity == RoomAddressValidity.Valid)
|
||||
(config.visibilityState is RoomVisibilityState.Private || roomAddressValidity == RoomAddressValidity.Valid) &&
|
||||
config.visibilityState.joinRuleItem in availableJoinRules
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
|
|||
roomAddressValidity = RoomAddressValidity.Valid,
|
||||
),
|
||||
aConfigureRoomState(
|
||||
isSpace = true,
|
||||
config = CreateRoomConfig(
|
||||
isSpace = true,
|
||||
roomName = "Space 101",
|
||||
topic = "Space topic for this space when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines",
|
||||
visibilityState = RoomVisibilityState.Public(
|
||||
|
|
@ -95,13 +95,11 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
|
|||
),
|
||||
aConfigureRoomState(
|
||||
config = CreateRoomConfig(
|
||||
isSpace = false,
|
||||
roomName = "Room 101",
|
||||
topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines",
|
||||
parentSpace = null,
|
||||
visibilityState = RoomVisibilityState.Public(
|
||||
roomAddress = RoomAddress.AutoFilled("Space-101"),
|
||||
joinRuleItem = JoinRuleItem.PublicVisibility.Restricted(aSpaceRoom().roomId),
|
||||
visibilityState = RoomVisibilityState.Private(
|
||||
joinRuleItem = JoinRuleItem.PrivateVisibility.Restricted(aSpaceRoom().roomId),
|
||||
),
|
||||
),
|
||||
spaces = listOf(aSpaceRoom()),
|
||||
|
|
@ -109,13 +107,11 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
|
|||
),
|
||||
aConfigureRoomState(
|
||||
config = CreateRoomConfig(
|
||||
isSpace = false,
|
||||
roomName = "Room 101",
|
||||
topic = "Room topic for this room when the text goes onto multiple lines and is really long, there shouldn’t be more than 3 lines",
|
||||
parentSpace = aSpaceRoom(canonicalAlias = RoomAlias("#a-space-room:example.org")),
|
||||
visibilityState = RoomVisibilityState.Public(
|
||||
roomAddress = RoomAddress.AutoFilled("Space-101"),
|
||||
joinRuleItem = JoinRuleItem.PublicVisibility.Restricted(aSpaceRoom().roomId),
|
||||
visibilityState = RoomVisibilityState.Private(
|
||||
joinRuleItem = JoinRuleItem.PrivateVisibility.Restricted(aSpaceRoom().roomId),
|
||||
),
|
||||
),
|
||||
spaces = listOf(aSpaceRoom()),
|
||||
|
|
@ -126,6 +122,7 @@ open class ConfigureRoomStateProvider : PreviewParameterProvider<ConfigureRoomSt
|
|||
|
||||
fun aConfigureRoomState(
|
||||
config: CreateRoomConfig = CreateRoomConfig(),
|
||||
isSpace: Boolean = false,
|
||||
isKnockFeatureEnabled: Boolean = true,
|
||||
avatarActions: List<AvatarAction> = emptyList(),
|
||||
createRoomAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||
|
|
@ -134,21 +131,22 @@ fun aConfigureRoomState(
|
|||
roomAddressValidity: RoomAddressValidity = RoomAddressValidity.Valid,
|
||||
availableVisibilityOptions: List<JoinRuleItem> = if (config.parentSpace != null) {
|
||||
listOfNotNull(
|
||||
JoinRuleItem.PublicVisibility.Restricted(config.parentSpace.roomId),
|
||||
JoinRuleItem.PublicVisibility.AskToJoinRestricted(config.parentSpace.roomId).takeIf { isKnockFeatureEnabled },
|
||||
JoinRuleItem.Private,
|
||||
JoinRuleItem.PrivateVisibility.Restricted(config.parentSpace.roomId),
|
||||
JoinRuleItem.PrivateVisibility.AskToJoinRestricted(config.parentSpace.roomId).takeIf { isKnockFeatureEnabled },
|
||||
JoinRuleItem.PrivateVisibility.Private,
|
||||
)
|
||||
} else {
|
||||
listOfNotNull(
|
||||
JoinRuleItem.PublicVisibility.Public,
|
||||
JoinRuleItem.PublicVisibility.AskToJoin.takeIf { isKnockFeatureEnabled },
|
||||
JoinRuleItem.Private,
|
||||
JoinRuleItem.PrivateVisibility.Private,
|
||||
)
|
||||
},
|
||||
spaces: List<SpaceRoom> = emptyList(),
|
||||
eventSink: (ConfigureRoomEvents) -> Unit = { },
|
||||
) = ConfigureRoomState(
|
||||
config = config,
|
||||
isSpace = isSpace,
|
||||
avatarActions = avatarActions.toImmutableList(),
|
||||
createRoomAction = createRoomAction,
|
||||
cameraPermissionState = cameraPermissionState,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
|
@ -76,7 +78,7 @@ fun ConfigureRoomView(
|
|||
onCreateRoomSuccess: (RoomId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isSpace = state.config.isSpace
|
||||
val isSpace = state.isSpace
|
||||
val focusManager = LocalFocusManager.current
|
||||
val isAvatarActionsSheetVisible = remember { mutableStateOf(false) }
|
||||
|
||||
|
|
@ -105,7 +107,6 @@ fun ConfigureRoomView(
|
|||
.imePadding()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.consumeWindowInsets(padding),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
RoomNameWithAvatar(
|
||||
isSpace = isSpace,
|
||||
|
|
@ -115,20 +116,20 @@ fun ConfigureRoomView(
|
|||
onAvatarClick = ::onAvatarClick,
|
||||
onChangeRoomName = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
RoomTopic(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
topic = state.config.topic.orEmpty(),
|
||||
onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) },
|
||||
)
|
||||
|
||||
if (!state.config.isSpace && state.spaces.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
if (!state.isSpace && state.spaces.isNotEmpty()) {
|
||||
SelectParentSpaceOptions(
|
||||
spaces = state.spaces,
|
||||
selectedSpace = state.config.parentSpace,
|
||||
onSelectSpace = { state.eventSink(ConfigureRoomEvents.SetParentSpace(it)) },
|
||||
)
|
||||
}
|
||||
|
||||
RoomJoinRuleOptions(
|
||||
options = state.availableJoinRules,
|
||||
selected = state.config.visibilityState.joinRuleItem,
|
||||
|
|
@ -138,20 +139,17 @@ fun ConfigureRoomView(
|
|||
state.eventSink(ConfigureRoomEvents.JoinRuleChanged(it))
|
||||
},
|
||||
)
|
||||
|
||||
if (state.config.visibilityState !is RoomVisibilityState.Private) {
|
||||
Column {
|
||||
ListSectionHeader(title = stringResource(R.string.screen_create_room_room_address_section_title))
|
||||
RoomAddressField(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
address = state.config.visibilityState.roomAddress().getOrNull().orEmpty(),
|
||||
homeserverName = state.homeserverName,
|
||||
addressValidity = state.roomAddressValidity,
|
||||
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
|
||||
label = null,
|
||||
supportingText = stringResource(R.string.screen_create_room_room_address_section_footer),
|
||||
)
|
||||
}
|
||||
ListSectionHeader(title = stringResource(R.string.screen_create_room_room_address_section_title))
|
||||
RoomAddressField(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
address = state.config.visibilityState.roomAddress().getOrNull().orEmpty(),
|
||||
homeserverName = state.homeserverName,
|
||||
addressValidity = state.roomAddressValidity,
|
||||
onAddressChange = { state.eventSink(ConfigureRoomEvents.RoomAddressChanged(it)) },
|
||||
label = null,
|
||||
supportingText = stringResource(R.string.screen_create_room_room_address_section_footer),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,7 +215,9 @@ private fun RoomNameWithAvatar(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.padding(end = 8.dp).size(AvatarSize.EditRoomDetails.dp),
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.size(AvatarSize.EditRoomDetails.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val avatarState = remember(avatarUri) {
|
||||
|
|
@ -272,12 +272,13 @@ private fun RoomTopic(
|
|||
internal fun ConfigureRoomOptions(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
hasDivider: Boolean = true,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.selectableGroup()
|
||||
) {
|
||||
ListSectionHeader(title = title)
|
||||
ListSectionHeader(title = title, hasDivider = hasDivider)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
@ -302,10 +303,10 @@ private fun RoomJoinRuleOptions(
|
|||
size = RoundedIconAtomSize.Big,
|
||||
imageVector = when (item) {
|
||||
JoinRuleItem.PublicVisibility.Public -> CompoundIcons.Public()
|
||||
is JoinRuleItem.PublicVisibility.Restricted -> CompoundIcons.Space()
|
||||
is JoinRuleItem.PrivateVisibility.Restricted -> CompoundIcons.Space()
|
||||
JoinRuleItem.PublicVisibility.AskToJoin,
|
||||
is JoinRuleItem.PublicVisibility.AskToJoinRestricted -> CompoundIcons.UserAdd()
|
||||
JoinRuleItem.Private -> CompoundIcons.Lock()
|
||||
is JoinRuleItem.PrivateVisibility.AskToJoinRestricted -> CompoundIcons.UserAdd()
|
||||
JoinRuleItem.PrivateVisibility.Private -> CompoundIcons.Lock()
|
||||
},
|
||||
tint = if (isSelected) ElementTheme.colors.iconPrimary else ElementTheme.colors.iconSecondary,
|
||||
backgroundTint = Color.Transparent,
|
||||
|
|
@ -314,28 +315,28 @@ private fun RoomJoinRuleOptions(
|
|||
headlineContent = {
|
||||
val title = when (item) {
|
||||
JoinRuleItem.PublicVisibility.Public -> stringResource(R.string.screen_create_room_room_access_section_public_option_title)
|
||||
is JoinRuleItem.PublicVisibility.Restricted -> stringResource(R.string.screen_create_room_room_access_section_restricted_option_title)
|
||||
is JoinRuleItem.PrivateVisibility.Restricted -> stringResource(R.string.screen_create_room_room_access_section_restricted_option_title)
|
||||
JoinRuleItem.PublicVisibility.AskToJoin -> stringResource(R.string.screen_create_room_room_access_section_knocking_option_title)
|
||||
is JoinRuleItem.PublicVisibility.AskToJoinRestricted -> stringResource(
|
||||
is JoinRuleItem.PrivateVisibility.AskToJoinRestricted -> stringResource(
|
||||
R.string.screen_create_room_room_access_section_knocking_restricted_option_title
|
||||
)
|
||||
JoinRuleItem.Private -> stringResource(R.string.screen_create_room_room_access_section_private_option_title)
|
||||
JoinRuleItem.PrivateVisibility.Private -> stringResource(R.string.screen_create_room_room_access_section_private_option_title)
|
||||
}
|
||||
Text(text = title)
|
||||
},
|
||||
supportingContent = {
|
||||
val description = when (item) {
|
||||
JoinRuleItem.PublicVisibility.Public -> stringResource(R.string.screen_create_room_room_access_section_public_option_description)
|
||||
is JoinRuleItem.PublicVisibility.Restricted -> stringResource(
|
||||
is JoinRuleItem.PrivateVisibility.Restricted -> stringResource(
|
||||
R.string.screen_create_room_room_access_section_restricted_option_description,
|
||||
parentSpace?.displayName.orEmpty()
|
||||
)
|
||||
JoinRuleItem.PublicVisibility.AskToJoin -> stringResource(R.string.screen_create_room_room_access_section_knocking_option_description)
|
||||
is JoinRuleItem.PublicVisibility.AskToJoinRestricted -> stringResource(
|
||||
is JoinRuleItem.PrivateVisibility.AskToJoinRestricted -> stringResource(
|
||||
R.string.screen_create_room_room_access_section_knocking_restricted_option_description,
|
||||
parentSpace?.displayName.orEmpty()
|
||||
)
|
||||
JoinRuleItem.Private -> stringResource(R.string.screen_create_room_room_access_section_private_option_description)
|
||||
JoinRuleItem.PrivateVisibility.Private -> stringResource(R.string.screen_create_room_room_access_section_private_option_description)
|
||||
}
|
||||
Text(text = description)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,11 +14,10 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
data class CreateRoomConfig(
|
||||
val isSpace: Boolean = false,
|
||||
val roomName: String? = null,
|
||||
val topic: String? = null,
|
||||
val avatarUri: String? = null,
|
||||
val invites: ImmutableList<MatrixUser> = persistentListOf(),
|
||||
val visibilityState: RoomVisibilityState = RoomVisibilityState.Private(),
|
||||
val visibilityState: RoomVisibilityState = RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private),
|
||||
val parentSpace: SpaceRoom? = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,9 @@ class CreateRoomConfigStore(
|
|||
createRoomConfigFlow.getAndUpdate { config ->
|
||||
config.copy(
|
||||
visibilityState = when (joinRule) {
|
||||
JoinRuleItem.Private -> RoomVisibilityState.Private()
|
||||
is JoinRuleItem.PrivateVisibility -> RoomVisibilityState.Private(
|
||||
joinRuleItem = joinRule
|
||||
)
|
||||
is JoinRuleItem.PublicVisibility -> {
|
||||
val roomAliasName = roomAliasHelper.roomAliasNameFromRoomDisplayName(config.roomName.orEmpty())
|
||||
RoomVisibilityState.Public(
|
||||
|
|
@ -99,17 +101,16 @@ class CreateRoomConfigStore(
|
|||
}
|
||||
}
|
||||
|
||||
fun setIsSpace(isSpace: Boolean) {
|
||||
createRoomConfigFlow.getAndUpdate { config ->
|
||||
config.copy(isSpace = isSpace)
|
||||
}
|
||||
}
|
||||
|
||||
fun setParentSpace(parentSpace: SpaceRoom?) {
|
||||
fun setParentSpace(parentSpace: SpaceRoom?, updateVisibility: Boolean) {
|
||||
createRoomConfigFlow.getAndUpdate { config ->
|
||||
val visibilityState = if (parentSpace != null && updateVisibility) {
|
||||
RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Restricted(parentSpace.roomId))
|
||||
} else {
|
||||
config.visibilityState
|
||||
}
|
||||
config.copy(
|
||||
parentSpace = parentSpace,
|
||||
visibilityState = RoomVisibilityState.Private(),
|
||||
visibilityState = visibilityState
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
*/
|
||||
@Immutable
|
||||
sealed interface JoinRuleItem {
|
||||
data object Private : JoinRuleItem
|
||||
sealed interface PrivateVisibility : JoinRuleItem {
|
||||
data object Private : PrivateVisibility
|
||||
data class Restricted(val parentSpaceId: RoomId) : PrivateVisibility
|
||||
data class AskToJoinRestricted(val parentSpaceId: RoomId) : PrivateVisibility
|
||||
}
|
||||
|
||||
/**
|
||||
* Those join rule items that represent public visibility of the room/space.
|
||||
|
|
@ -27,18 +31,16 @@ sealed interface JoinRuleItem {
|
|||
sealed interface PublicVisibility : JoinRuleItem {
|
||||
data object Public : PublicVisibility
|
||||
data object AskToJoin : PublicVisibility
|
||||
data class Restricted(val parentSpaceId: RoomId) : PublicVisibility
|
||||
data class AskToJoinRestricted(val parentSpaceId: RoomId) : PublicVisibility
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a [JoinRuleItem] option into a [JoinRule].
|
||||
*/
|
||||
fun toJoinRule(): JoinRule = when (this) {
|
||||
Private -> JoinRule.Invite
|
||||
PrivateVisibility.Private -> JoinRule.Invite
|
||||
is PrivateVisibility.Restricted -> JoinRule.Restricted(persistentListOf(AllowRule.RoomMembership(parentSpaceId)))
|
||||
is PrivateVisibility.AskToJoinRestricted -> JoinRule.KnockRestricted(persistentListOf(AllowRule.RoomMembership(parentSpaceId)))
|
||||
PublicVisibility.Public -> JoinRule.Public
|
||||
PublicVisibility.AskToJoin -> JoinRule.Knock
|
||||
is PublicVisibility.Restricted -> JoinRule.Restricted(persistentListOf(AllowRule.RoomMembership(parentSpaceId)))
|
||||
is PublicVisibility.AskToJoinRestricted -> JoinRule.KnockRestricted(persistentListOf(AllowRule.RoomMembership(parentSpaceId)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import java.util.Optional
|
|||
|
||||
sealed interface RoomVisibilityState {
|
||||
val joinRuleItem: JoinRuleItem
|
||||
data class Private(override val joinRuleItem: JoinRuleItem.Private = JoinRuleItem.Private) : RoomVisibilityState
|
||||
data class Private(override val joinRuleItem: JoinRuleItem.PrivateVisibility) : RoomVisibilityState
|
||||
|
||||
data class Public(
|
||||
val roomAddress: RoomAddress,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
package io.element.android.features.createroom.impl.configureroom
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
|
|
@ -21,7 +20,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.createroom.impl.R
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
|
|
@ -30,7 +29,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
|||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
|
||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
|
||||
|
|
@ -55,6 +53,7 @@ internal fun SelectParentSpaceOptions(
|
|||
var displaySelectSpaceBottomSheet by remember { mutableStateOf(false) }
|
||||
ConfigureRoomOptions(
|
||||
title = stringResource(CommonStrings.common_space),
|
||||
hasDivider = false,
|
||||
modifier = modifier
|
||||
) {
|
||||
ListItem(
|
||||
|
|
@ -62,22 +61,16 @@ internal fun SelectParentSpaceOptions(
|
|||
Text(
|
||||
text = selectedSpace?.displayName
|
||||
?: stringResource(R.string.screen_create_room_space_selection_no_space_title),
|
||||
maxLines = 1
|
||||
maxLines = 1,
|
||||
color = ElementTheme.colors.textPrimary
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
text = if (selectedSpace != null) {
|
||||
selectedSpace.canonicalAlias?.value.orEmpty()
|
||||
} else {
|
||||
stringResource(R.string.screen_create_room_space_selection_no_space_description)
|
||||
},
|
||||
maxLines = 1
|
||||
)
|
||||
supportingContent = selectedSpace?.canonicalAlias?.let { alias ->
|
||||
{
|
||||
Text(text = alias.value, maxLines = 1)
|
||||
}
|
||||
},
|
||||
leadingContent = if (selectedSpace == null) {
|
||||
ListItemContent.Icon(IconSource.Vector(CompoundIcons.Home()))
|
||||
} else {
|
||||
leadingContent = selectedSpace?.let {
|
||||
ListItemContent.Custom({
|
||||
val avatarData = AvatarData(
|
||||
id = selectedSpace.roomId.value,
|
||||
|
|
@ -119,7 +112,7 @@ internal fun SelectParentSpaceOptions(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.SelectParentSpaceBottomSheet(
|
||||
private fun SelectParentSpaceBottomSheet(
|
||||
spaces: ImmutableList<SpaceRoom>,
|
||||
selectedSpace: SpaceRoom?,
|
||||
onSelectSpace: (SpaceRoom?) -> Unit,
|
||||
|
|
@ -133,19 +126,10 @@ private fun ColumnScope.SelectParentSpaceBottomSheet(
|
|||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
stringResource(R.string.screen_create_room_space_selection_no_space_title),
|
||||
text = stringResource(R.string.screen_create_room_space_selection_no_space_option),
|
||||
maxLines = 1
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
stringResource(R.string.screen_create_room_space_selection_no_space_description),
|
||||
maxLines = 1
|
||||
)
|
||||
},
|
||||
leadingContent = ListItemContent.Icon(
|
||||
IconSource.Vector(CompoundIcons.Home())
|
||||
),
|
||||
trailingContent = ListItemContent.RadioButton(
|
||||
selected = selectedSpace == null
|
||||
),
|
||||
|
|
@ -157,29 +141,31 @@ private fun ColumnScope.SelectParentSpaceBottomSheet(
|
|||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
space.displayName,
|
||||
text = space.displayName,
|
||||
maxLines = 1
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
space.canonicalAlias?.value.orEmpty(),
|
||||
maxLines = 1
|
||||
)
|
||||
supportingContent = space.canonicalAlias?.let { alias ->
|
||||
{
|
||||
Text(
|
||||
text = alias.value,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
},
|
||||
leadingContent = ListItemContent.Custom({
|
||||
val avatarData =
|
||||
AvatarData(
|
||||
id = space.roomId.value,
|
||||
name = space.displayName,
|
||||
url = space.avatarUrl,
|
||||
size = AvatarSize.SelectParentSpace,
|
||||
)
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.Space()
|
||||
val avatarData =
|
||||
AvatarData(
|
||||
id = space.roomId.value,
|
||||
name = space.displayName,
|
||||
url = space.avatarUrl,
|
||||
size = AvatarSize.SelectParentSpace,
|
||||
)
|
||||
}),
|
||||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.Space()
|
||||
)
|
||||
}),
|
||||
trailingContent = ListItemContent.RadioButton(
|
||||
selected = selectedSpace == space
|
||||
),
|
||||
|
|
@ -201,7 +187,8 @@ internal fun SelectParentSpaceBottomSheetPreview() =
|
|||
canonicalAlias = RoomAlias(
|
||||
"#a-room-alias:example.org"
|
||||
)
|
||||
)
|
||||
),
|
||||
aSpaceRoom()
|
||||
),
|
||||
selectedSpace = null,
|
||||
) {}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,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.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
|
|
@ -89,7 +90,7 @@ class ConfigureRoomPresenterTest {
|
|||
assertThat(initialState.config.topic).isNull()
|
||||
assertThat(initialState.config.invites).isEmpty()
|
||||
assertThat(initialState.config.avatarUri).isNull()
|
||||
assertThat(initialState.config.visibilityState).isEqualTo(RoomVisibilityState.Private())
|
||||
assertThat(initialState.config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
assertThat(initialState.createRoomAction).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||
assertThat(initialState.homeserverName).isEqualTo("matrix.org")
|
||||
}
|
||||
|
|
@ -234,7 +235,8 @@ class ConfigureRoomPresenterTest {
|
|||
|
||||
matrixClient.givenCreateRoomResult(createRoomResult)
|
||||
|
||||
val parentSpace = aSpaceRoom()
|
||||
// Use a public parent space so AskToJoin is a valid option
|
||||
val parentSpace = aSpaceRoom(joinRule = JoinRule.Public)
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace))
|
||||
assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace)
|
||||
|
||||
|
|
@ -275,7 +277,8 @@ class ConfigureRoomPresenterTest {
|
|||
|
||||
matrixClient.givenCreateRoomResult(createRoomResult)
|
||||
|
||||
val parentSpace = aSpaceRoom()
|
||||
// Use a public parent space so AskToJoin is a valid option
|
||||
val parentSpace = aSpaceRoom(joinRule = JoinRule.Public)
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(parentSpace))
|
||||
assertThat(awaitItem().config.parentSpace).isEqualTo(parentSpace)
|
||||
|
||||
|
|
@ -484,16 +487,19 @@ class ConfigureRoomPresenterTest {
|
|||
assertThat(awaitItem().config.visibilityState).isInstanceOf(RoomVisibilityState.Public::class.java)
|
||||
|
||||
// Then check changing the parent space resets it to private
|
||||
// (via LaunchedEffect fallback since Public is not in availableJoinRules for non-public parent)
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(aSpaceRoom()))
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private())
|
||||
skipItems(1) // Skip intermediate state
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
|
||||
// If we change the join rule back to public
|
||||
initialState.eventSink(ConfigureRoomEvents.JoinRuleChanged(JoinRuleItem.PublicVisibility.Public))
|
||||
assertThat(awaitItem().config.visibilityState).isInstanceOf(RoomVisibilityState.Public::class.java)
|
||||
skipItems(1) // Skip intermediate state (Public is still invalid)
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
|
||||
// Then remove the parent space, it'll be private again
|
||||
// Then remove the parent space, the join rule stays private
|
||||
initialState.eventSink(ConfigureRoomEvents.SetParentSpace(null))
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private())
|
||||
assertThat(awaitItem().config.visibilityState).isEqualTo(RoomVisibilityState.Private(JoinRuleItem.PrivateVisibility.Private))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ import org.junit.Test
|
|||
class JoinRuleItemTest {
|
||||
@Test
|
||||
fun `toJoinRule works as expected`() {
|
||||
assertThat(JoinRuleItem.Private.toJoinRule()).isEqualTo(JoinRule.Invite)
|
||||
assertThat(JoinRuleItem.PrivateVisibility.Private.toJoinRule()).isEqualTo(JoinRule.Invite)
|
||||
assertThat(JoinRuleItem.PublicVisibility.Public.toJoinRule()).isEqualTo(JoinRule.Public)
|
||||
assertThat(JoinRuleItem.PublicVisibility.AskToJoin.toJoinRule()).isEqualTo(JoinRule.Knock)
|
||||
assertThat(JoinRuleItem.PublicVisibility.Restricted(A_ROOM_ID).toJoinRule())
|
||||
assertThat(JoinRuleItem.PrivateVisibility.Restricted(A_ROOM_ID).toJoinRule())
|
||||
.isEqualTo(JoinRule.Restricted(persistentListOf(AllowRule.RoomMembership(A_ROOM_ID))))
|
||||
assertThat(JoinRuleItem.PublicVisibility.AskToJoinRestricted(A_ROOM_ID).toJoinRule())
|
||||
assertThat(JoinRuleItem.PrivateVisibility.AskToJoinRestricted(A_ROOM_ID).toJoinRule())
|
||||
.isEqualTo(JoinRule.KnockRestricted(persistentListOf(AllowRule.RoomMembership(A_ROOM_ID))))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ fun HomeSpacesView(
|
|||
item {
|
||||
SpaceHeaderView(
|
||||
avatarData = space.spaceRoom.getAvatarData(AvatarSize.SpaceHeader),
|
||||
alias = space.spaceRoom.canonicalAlias,
|
||||
name = space.spaceRoom.displayName,
|
||||
topic = space.spaceRoom.topic,
|
||||
visibility = space.spaceRoom.visibility,
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ 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.RoomPreviewAliasAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewDescriptionAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewTitleAtom
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonRowMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
|
||||
|
|
@ -514,7 +514,7 @@ private fun IncompleteContent(
|
|||
title = {
|
||||
when (roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> {
|
||||
RoomPreviewSubtitleAtom(roomIdOrAlias.identifier)
|
||||
RoomPreviewAliasAtom(roomIdOrAlias.identifier)
|
||||
}
|
||||
is RoomIdOrAlias.Id -> {
|
||||
PlaceholderAtom(width = 200.dp, height = 22.dp)
|
||||
|
|
@ -566,13 +566,12 @@ private fun DefaultLoadedContent(
|
|||
}
|
||||
},
|
||||
subtitle = {
|
||||
when {
|
||||
contentState.details is LoadedDetails.Space -> {
|
||||
SpaceInfoRow(visibility = SpaceRoomVisibility.fromJoinRule(contentState.joinRule))
|
||||
}
|
||||
contentState.alias != null -> {
|
||||
RoomPreviewSubtitleAtom(contentState.alias.value)
|
||||
}
|
||||
if (contentState.alias != null) {
|
||||
RoomPreviewAliasAtom(contentState.alias.value)
|
||||
}
|
||||
if (contentState.details is LoadedDetails.Space) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
SpaceInfoRow(visibility = SpaceRoomVisibility.fromJoinRule(contentState.joinRule))
|
||||
}
|
||||
},
|
||||
description = {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewSubtitleAtom
|
||||
import io.element.android.libraries.designsystem.atomic.atoms.RoomPreviewAliasAtom
|
||||
import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism
|
||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
|
|
@ -54,7 +54,7 @@ fun RoomAliasResolverView(
|
|||
containerColor = Color.Transparent,
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 32.dp
|
||||
vertical = 24.dp
|
||||
),
|
||||
topBar = {
|
||||
RoomAliasResolverTopBar(onBackClick = onBackClick)
|
||||
|
|
@ -121,7 +121,7 @@ private fun RoomAliasResolverContent(
|
|||
PlaceholderAtom(width = AvatarSize.RoomPreviewHeader.dp, height = AvatarSize.RoomPreviewHeader.dp)
|
||||
},
|
||||
title = {
|
||||
RoomPreviewSubtitleAtom(roomAlias.value)
|
||||
RoomPreviewAliasAtom(roomAlias.value)
|
||||
},
|
||||
subtitle = {
|
||||
if (isLoading) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:OptIn(FlowPreview::class)
|
||||
|
||||
package io.element.android.features.space.impl.root
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -47,10 +49,13 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@Inject
|
||||
class SpacePresenter(
|
||||
|
|
@ -80,13 +85,16 @@ class SpacePresenter(
|
|||
val localCoroutineScope = rememberCoroutineScope()
|
||||
|
||||
val hasMoreToLoad by remember {
|
||||
spaceRoomList.paginationStatusFlow.mapState { status ->
|
||||
when (status) {
|
||||
is SpaceRoomList.PaginationStatus.Idle -> status.hasMoreToLoad
|
||||
SpaceRoomList.PaginationStatus.Loading -> true
|
||||
spaceRoomList.paginationStatusFlow
|
||||
.mapState { status ->
|
||||
when (status) {
|
||||
is SpaceRoomList.PaginationStatus.Idle -> status.hasMoreToLoad
|
||||
SpaceRoomList.PaginationStatus.Loading -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}.collectAsState()
|
||||
// Debounce to give more time for spaceRoomList to updates
|
||||
.debounce(100.milliseconds)
|
||||
}.collectAsState(true)
|
||||
|
||||
val permissions by room.permissionsAsState(SpacePermissions.DEFAULT) { perms ->
|
||||
perms.spacePermissions()
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import io.element.android.compound.theme.ElementTheme
|
|||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.space.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.InviteButtonsRowMolecule
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
|
|
@ -76,6 +77,7 @@ import io.element.android.libraries.designsystem.theme.components.HorizontalDivi
|
|||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
|
|
@ -170,6 +172,7 @@ fun SpaceView(
|
|||
state.eventSink(SpaceEvents.ShowTopicViewer(topic))
|
||||
},
|
||||
onCreateRoomClick = onCreateRoomClick,
|
||||
onAddRoomClick = onAddRoomClick,
|
||||
)
|
||||
JoinFailuresEffect(
|
||||
hasAnyFailure = state.hasAnyJoinFailures,
|
||||
|
|
@ -243,6 +246,7 @@ private fun SpaceViewContent(
|
|||
onRoomClick: (spaceRoom: SpaceRoom) -> Unit,
|
||||
onTopicClick: (String) -> Unit,
|
||||
onCreateRoomClick: () -> Unit,
|
||||
onAddRoomClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier.fillMaxSize()) {
|
||||
|
|
@ -256,6 +260,7 @@ private fun SpaceViewContent(
|
|||
Column {
|
||||
SpaceHeaderView(
|
||||
avatarData = spaceInfo.getAvatarData(AvatarSize.SpaceHeader),
|
||||
alias = spaceInfo.canonicalAlias,
|
||||
name = spaceInfo.name,
|
||||
topic = spaceInfo.topic,
|
||||
topicMaxLines = 2,
|
||||
|
|
@ -271,7 +276,10 @@ private fun SpaceViewContent(
|
|||
|
||||
if (state.children.isEmpty() && state.canEditSpaceGraph && !state.hasMoreToLoad) {
|
||||
item {
|
||||
EmptySpaceView(onCreateRoomClick = onCreateRoomClick)
|
||||
EmptySpaceView(
|
||||
onCreateRoomClick = onCreateRoomClick,
|
||||
onAddRoomClick = onAddRoomClick,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
itemsIndexed(
|
||||
|
|
@ -332,7 +340,10 @@ private fun SpaceViewContent(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun EmptySpaceView(onCreateRoomClick: () -> Unit) {
|
||||
private fun EmptySpaceView(
|
||||
onCreateRoomClick: () -> Unit,
|
||||
onAddRoomClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(bottom = 24.dp),
|
||||
|
|
@ -340,15 +351,25 @@ private fun EmptySpaceView(onCreateRoomClick: () -> Unit) {
|
|||
IconTitleSubtitleMolecule(
|
||||
title = stringResource(R.string.screen_space_empty_state_title),
|
||||
subTitle = null,
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.Room()),
|
||||
iconStyle = BigIcon.Style.Default(vectorIcon = CompoundIcons.Room(), usePrimaryTint = true),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.padding(top = 40.dp, start = 24.dp, end = 24.dp, bottom = 24.dp),
|
||||
)
|
||||
Button(
|
||||
text = stringResource(R.string.screen_space_add_room_action),
|
||||
leadingIcon = IconSource.Vector(CompoundIcons.Plus()),
|
||||
onClick = onCreateRoomClick,
|
||||
)
|
||||
ButtonColumnMolecule(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_add_existing_rooms),
|
||||
leadingIcon = IconSource.Vector(CompoundIcons.Plus()),
|
||||
onClick = onAddRoomClick,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
OutlinedButton(
|
||||
text = stringResource(CommonStrings.action_create_room),
|
||||
onClick = onCreateRoomClick,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
|||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.space.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
|
|
@ -215,9 +214,25 @@ class SpaceViewTest {
|
|||
),
|
||||
onCreateRoomClick = onCreateRoomClick,
|
||||
)
|
||||
rule.clickOn(R.string.screen_space_add_room_action)
|
||||
rule.clickOn(CommonStrings.action_create_room)
|
||||
onCreateRoomClick.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking add existing room button calls the expected callback`() {
|
||||
val onAddRoomClick = lambdaRecorder<Unit> { }
|
||||
rule.setSpaceView(
|
||||
aSpaceState(
|
||||
children = emptyList(),
|
||||
hasMoreToLoad = false,
|
||||
isManageMode = true,
|
||||
canManageRooms = true,
|
||||
),
|
||||
onAddRoomClick = onAddRoomClick,
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_add_existing_rooms)
|
||||
onAddRoomClick.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setSpaceView(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.atomic.atoms
|
||||
|
||||
import android.content.ClipData
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.Clipboard
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.toClipEntry
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.toDp
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun RoomPreviewAliasAtom(
|
||||
alias: String,
|
||||
modifier: Modifier = Modifier,
|
||||
copiable: Boolean = true
|
||||
) {
|
||||
val clipboard: Clipboard = LocalClipboard.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clickable(enabled = copiable) {
|
||||
coroutineScope.launch {
|
||||
val clipData = ClipData.newPlainText(alias, alias)
|
||||
clipboard.setClipEntry(clipData.toClipEntry())
|
||||
}
|
||||
},
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(weight = 1f, fill = false),
|
||||
text = alias,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
if (copiable) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Copy(),
|
||||
contentDescription = stringResource(id = CommonStrings.action_copy),
|
||||
tint = ElementTheme.colors.iconSecondaryAlpha,
|
||||
modifier = Modifier.size(ElementTheme.typography.fontBodyLgRegular.fontSize.toDp())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun RoomPreviewAliasAtomPreview() = ElementPreview {
|
||||
RoomPreviewAliasAtom(
|
||||
alias = "#room-alias:matrix.org",
|
||||
copiable = true
|
||||
)
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.atomic.atoms
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
@Composable
|
||||
fun RoomPreviewSubtitleAtom(subtitle: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = subtitle,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
package io.element.android.libraries.designsystem.atomic.organisms
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
|
|
@ -19,12 +20,12 @@ import androidx.compose.ui.unit.dp
|
|||
|
||||
@Composable
|
||||
fun RoomPreviewOrganism(
|
||||
avatar: @Composable () -> Unit,
|
||||
title: @Composable () -> Unit,
|
||||
subtitle: @Composable () -> Unit,
|
||||
avatar: @Composable ColumnScope.() -> Unit,
|
||||
title: @Composable ColumnScope.() -> Unit,
|
||||
subtitle: @Composable ColumnScope.() -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
description: @Composable (() -> Unit)? = null,
|
||||
memberCount: @Composable (() -> Unit)? = null,
|
||||
description: @Composable (ColumnScope.() -> Unit)? = null,
|
||||
memberCount: @Composable (ColumnScope.() -> Unit)? = null,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package io.element.android.libraries.designsystem.components.avatar.internal
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -16,6 +17,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
|
||||
|
|
@ -34,19 +36,26 @@ internal fun SpaceAvatar(
|
|||
contentDescription: String? = null,
|
||||
) {
|
||||
val size = forcedAvatarSize ?: avatarData.size.dp
|
||||
val avatarShape = avatarType.avatarShape(size)
|
||||
val commonModifier = modifier
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = ElementTheme.colors.iconQuaternaryAlpha,
|
||||
shape = avatarShape,
|
||||
)
|
||||
when {
|
||||
avatarType.isTombstoned -> TombstonedRoomAvatar(
|
||||
size = size,
|
||||
avatarShape = avatarType.avatarShape(size),
|
||||
modifier = modifier,
|
||||
avatarShape = avatarShape,
|
||||
modifier = commonModifier,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
else -> InitialOrImageAvatar(
|
||||
avatarData = avatarData,
|
||||
hideAvatarImage = hideAvatarImage,
|
||||
avatarShape = avatarType.avatarShape(size),
|
||||
avatarShape = avatarShape,
|
||||
forcedAvatarSize = forcedAvatarSize,
|
||||
modifier = modifier,
|
||||
modifier = commonModifier,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ import io.element.android.libraries.matrix.api.room.join.JoinRule
|
|||
sealed interface SpaceRoomVisibility {
|
||||
data object Private : SpaceRoomVisibility
|
||||
data object Public : SpaceRoomVisibility
|
||||
data object Restricted : SpaceRoomVisibility
|
||||
data object SpaceMembers : SpaceRoomVisibility
|
||||
|
||||
companion object {
|
||||
fun fromJoinRule(joinRule: JoinRule?): SpaceRoomVisibility = when (joinRule) {
|
||||
JoinRule.Public -> Public
|
||||
is JoinRule.Restricted, is JoinRule.KnockRestricted -> Restricted
|
||||
is JoinRule.Restricted, is JoinRule.KnockRestricted -> SpaceMembers
|
||||
// Else fallback to Private
|
||||
else -> Private
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,17 @@
|
|||
package io.element.android.libraries.matrix.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.libraries.designsystem.atomic.atoms.RoomPreviewAliasAtom
|
||||
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
|
||||
|
|
@ -26,6 +30,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
|||
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
|
@ -39,6 +44,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
fun SpaceHeaderView(
|
||||
avatarData: AvatarData,
|
||||
name: String?,
|
||||
alias: RoomAlias?,
|
||||
topic: String?,
|
||||
visibility: SpaceRoomVisibility,
|
||||
heroes: ImmutableList<MatrixUser>,
|
||||
|
|
@ -66,7 +72,15 @@ fun SpaceHeaderView(
|
|||
}
|
||||
},
|
||||
subtitle = {
|
||||
SpaceInfoRow(visibility = visibility)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
if (alias != null) {
|
||||
RoomPreviewAliasAtom(alias = alias.value)
|
||||
}
|
||||
SpaceInfoRow(visibility = visibility)
|
||||
}
|
||||
},
|
||||
description = if (topic.isNullOrBlank()) {
|
||||
null
|
||||
|
|
@ -100,6 +114,7 @@ internal fun SpaceHeaderViewPreview() = ElementPreview {
|
|||
url = "anUrl",
|
||||
size = AvatarSize.SpaceHeader,
|
||||
),
|
||||
alias = RoomAlias("#spaceAlias:matrix.org"),
|
||||
name = "Space name",
|
||||
topic = "Space topic: " + LoremIpsum(40).values.first(),
|
||||
topicMaxLines = 2,
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ internal fun SpaceInfoRowPreview() = ElementPreview {
|
|||
visibility = SpaceRoomVisibility.Public
|
||||
)
|
||||
SpaceInfoRow(
|
||||
visibility = SpaceRoomVisibility.Restricted
|
||||
visibility = SpaceRoomVisibility.SpaceMembers
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,9 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -100,22 +98,16 @@ fun SpaceRoomItemView(
|
|||
showIndicator = showUnreadIndicator
|
||||
)
|
||||
Spacer(modifier = Modifier.height(1.dp))
|
||||
SubtitleRow(
|
||||
visibilityIcon = spaceRoom.visibilityIcon(),
|
||||
subtitle = spaceRoom.subtitle()
|
||||
)
|
||||
VisibilityRow(visibility = spaceRoom.visibility)
|
||||
Spacer(modifier = Modifier.height(1.dp))
|
||||
val info = spaceRoom.info()
|
||||
if (info.isNotBlank()) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
text = info,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
text = pluralStringResource(CommonPlurals.common_member_count, spaceRoom.numJoinedMembers, spaceRoom.numJoinedMembers),
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
if (bottomAction != null) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
|
@ -129,29 +121,26 @@ fun SpaceRoomItemView(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SubtitleRow(
|
||||
visibilityIcon: ImageVector?,
|
||||
subtitle: String,
|
||||
private fun VisibilityRow(
|
||||
visibility: SpaceRoomVisibility,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (visibilityIcon != null) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.padding(end = 4.dp),
|
||||
imageVector = visibilityIcon,
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconTertiary,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.padding(end = 4.dp),
|
||||
imageVector = visibility.icon,
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconTertiary,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
text = subtitle,
|
||||
text = visibility.label,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
|
|
@ -219,36 +208,6 @@ private fun SpaceRoomItemScaffold(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun SpaceRoom.subtitle(): String {
|
||||
return if (isSpace) {
|
||||
visibility.label
|
||||
} else {
|
||||
pluralStringResource(CommonPlurals.common_member_count, numJoinedMembers, numJoinedMembers)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun SpaceRoom.info(): String {
|
||||
return if (isSpace) {
|
||||
pluralStringResource(CommonPlurals.common_member_count, numJoinedMembers, numJoinedMembers)
|
||||
} else {
|
||||
topic.orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SpaceRoom.visibilityIcon(): ImageVector? {
|
||||
// Don't show any icon for restricted rooms as it's the default and would add noise
|
||||
return if (visibility == SpaceRoomVisibility.Restricted) {
|
||||
null
|
||||
} else {
|
||||
visibility.icon
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun SpaceRoomItemViewPreview(@PreviewParameter(SpaceRoomProvider::class) spaceRoom: SpaceRoom) = ElementPreview {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ val SpaceRoomVisibility.icon: ImageVector
|
|||
return when (this) {
|
||||
SpaceRoomVisibility.Private -> CompoundIcons.LockSolid()
|
||||
SpaceRoomVisibility.Public -> CompoundIcons.Public()
|
||||
SpaceRoomVisibility.Restricted -> CompoundIcons.Space()
|
||||
SpaceRoomVisibility.SpaceMembers -> CompoundIcons.Space()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,8 +41,8 @@ val SpaceRoomVisibility.label: String
|
|||
@ReadOnlyComposable
|
||||
get() {
|
||||
return when (this) {
|
||||
SpaceRoomVisibility.Private -> stringResource(CommonStrings.common_private_space)
|
||||
SpaceRoomVisibility.Public -> stringResource(CommonStrings.common_public_space)
|
||||
SpaceRoomVisibility.Restricted -> stringResource(CommonStrings.common_shared_space)
|
||||
SpaceRoomVisibility.Private -> stringResource(CommonStrings.common_private)
|
||||
SpaceRoomVisibility.Public -> stringResource(CommonStrings.common_public)
|
||||
SpaceRoomVisibility.SpaceMembers -> stringResource(CommonStrings.common_space_members)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:20c185481e6a13fbccae6e4d8c02752222f8cfcc5c03398c255666beb5df5cc4
|
||||
size 34243
|
||||
oid sha256:8a5a6c238f044364415ec56a4e44eb89803fbdbd0a5c0043e9119b916bfeef56
|
||||
size 34306
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2d0fb8d4c5acde1b03908f15eb959d3e544917a53c529f5971804eb674f989fa
|
||||
size 41893
|
||||
oid sha256:8523912153c301442253a41c236ada8ba934ca8079b7c1d9463dbfdb20325649
|
||||
size 41892
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a601ab64bac5f2c11480892e4b81dcaf3b9fe72afd222f6f42d1e5f577220a8c
|
||||
size 42786
|
||||
oid sha256:ce22c06eb1d6140dc852ef76a4f6acb7dfc175019fa1d33c1695a551aaaecb05
|
||||
size 42785
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f8a2dd4b01f32b9325e9adf8340f18b6776ff20c06f4e27590bde207d802b3c8
|
||||
oid sha256:e3f6e32f8bc237e14e6ad4ca1da93e70631355b062ad8de698b22e36f19a5fce
|
||||
size 44542
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2d0fb8d4c5acde1b03908f15eb959d3e544917a53c529f5971804eb674f989fa
|
||||
size 41893
|
||||
oid sha256:8523912153c301442253a41c236ada8ba934ca8079b7c1d9463dbfdb20325649
|
||||
size 41892
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f0f2a05605785d5a9484e6e34b03c6074125927f6b8130f5e60f54e3922d7bc4
|
||||
size 42863
|
||||
oid sha256:3ce5823f8724dc03df2a171182c3cab522c30c3cf912d3efd798c572aa80a872
|
||||
size 42860
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:356117f3617c58b79fcbe95aaba2899afdc4735d5832cc274622bc1689572a5a
|
||||
size 46689
|
||||
oid sha256:6092a495d695de848c63919121ea6199ebbacd5a996becd1ebdcf685fad21146
|
||||
size 37738
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d4bf55777af9384a34c438de10887f5b6dff107b837f4bb7a2f974644c256047
|
||||
size 48431
|
||||
oid sha256:3fb3642812dbfac7151986fecbb7a8bdcea31f9367f901420d4003e920f4c852
|
||||
size 40893
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fd25259e89b4a8833c884727d23fb503b53099b698ff2eafd812bf70422b5cbe
|
||||
size 35631
|
||||
oid sha256:a85264a26b9db2bb9bdace44aaa0ca0a3c175a89efd442252d57ccd1ab74370a
|
||||
size 35665
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:30a4f34c42cb57b3cbd08114fef8531175890d46d0033f0dc5d1337189e33d1c
|
||||
size 43653
|
||||
oid sha256:94fd7ed41f8d38f00805aa6b7224f28a34565136c5b10319e4638c9f97a68cd9
|
||||
size 43652
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ab5d721149a96e53f850d7792653b0b71d41124c0db82d653ad79c66c72dabee
|
||||
size 44600
|
||||
oid sha256:e1b7b5086ee131e28daf6afcc6f889486b1815bf2cc8dbf83fec772ca670a902
|
||||
size 44601
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b7a8c9c3cb8f877d3dc2deb1b918666143c73aed34fdd3d3961bc2530f32d0f5
|
||||
oid sha256:1857d55f616858ae0f6692b1c817f472c9695db6d63c30b9e64b6522ae813a69
|
||||
size 46369
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:30a4f34c42cb57b3cbd08114fef8531175890d46d0033f0dc5d1337189e33d1c
|
||||
size 43653
|
||||
oid sha256:94fd7ed41f8d38f00805aa6b7224f28a34565136c5b10319e4638c9f97a68cd9
|
||||
size 43652
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d3cb687ded32d38164cb67657b495958f1632457d1e49ede0d8c9f1b1f5a287b
|
||||
size 44675
|
||||
oid sha256:75a247266aecc4b08ea0e3eef37086b172e0b48df9c4e294f27a899ac52b18ca
|
||||
size 44674
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d857d4c831cd4f786b29f392ffa05716756f0cb3c59199057fed92abbd78901e
|
||||
size 48485
|
||||
oid sha256:10b1e26af1f81e0b2dc7e991c1ec5fab230edae7c1996c532011b5689bc7d27a
|
||||
size 39249
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0e95d2194cfc8b979624ce3b97250a85051d67bd8f5f61bb1e60d274c08f4649
|
||||
size 50132
|
||||
oid sha256:b5041b3c70ef02579037940afbaf9799cef72151069940c3fcd07ed87492d1dd
|
||||
size 42533
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:27ce57d26309360b24f5b62bd45ab2bf0420a42559556105c6dd42d0d0afead9
|
||||
size 23476
|
||||
oid sha256:e015ed8f3b90780c35fc12fee64119659e43510d379658cf46338ce3b86fc561
|
||||
size 25563
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a18eb487e585eb0a37c4a39c94d5c897c57fbcdbe450ca6b7b34b3baad6262a5
|
||||
size 22530
|
||||
oid sha256:f912f91abf5e09b6039b3668c92f1bc3bed87b05ceb14678509b9f9a8754fe7c
|
||||
size 24998
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:98d4e9152dee861cc7afdb3efe57b086c3d9d58d38eeb1345749d31debbf6017
|
||||
size 21266
|
||||
oid sha256:6b76e09d9138021a2dd9338c4ffc39c1d530711eb4c96343edd2450366b8c6cd
|
||||
size 22089
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c24a3b08afe224e9919dd3818c72bd2ff8dd3f38663a6465a10b301ad91e8787
|
||||
size 20377
|
||||
oid sha256:7bc1f006db0f7763fde512dcfa1d731be8040979bdd5b44cd2ca0b70f651ffa5
|
||||
size 20984
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5ed03e5c6103dd4d96a0c4a8fda97808801f853cc3e05595e339a67e0b228027
|
||||
size 30472
|
||||
oid sha256:bfc98a004d03c8464f7e0716122c765791b6417004a2b669a213f4ecd57004e7
|
||||
size 32416
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dfeca128a35edebe19f59749bedcd7335422377fd2627f95f81d7e3e28dd61e5
|
||||
size 18173
|
||||
oid sha256:ad7a4ea61d969d99e4ba7406721b6d852f86b0f3fdf472b555074dc654566d48
|
||||
size 18670
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:543f80c91d04f44c4c62a06c9e44b334826d5bd92f2b17a4eeeb7e15dfbed0c1
|
||||
size 29510
|
||||
oid sha256:def089ca29a9a53fa373c6e5890358702e596964ddc52eb1bb11aaea8f273a00
|
||||
size 31067
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8735caab11d89a689591c1d1ad5c1483d0c1fc01ce7b5933c7546ce2c2641d4a
|
||||
size 17162
|
||||
oid sha256:30158c9c805b21a1212b0621927e89ee14116d51a2604704d0736e8a07de67fc
|
||||
size 17570
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:388207cd5b424fbb95f070eac393db9330ae1b641795d1bb815874435ef9f623
|
||||
size 89027
|
||||
oid sha256:a9e55679970ff85c3c66013eac5c755c879de18d619f0b75e0f6dc9d40fc5d72
|
||||
size 86233
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:542d8ba6a6031fe2789cf111f333eef22acf95281f57421ace2c7b5b0a599cc2
|
||||
size 41140
|
||||
oid sha256:d7bf08648b5c3a09390e09d0ba1e34d43b5ac40e73c2be86048d80f3c78cc455
|
||||
size 39482
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:542d8ba6a6031fe2789cf111f333eef22acf95281f57421ace2c7b5b0a599cc2
|
||||
size 41140
|
||||
oid sha256:d7bf08648b5c3a09390e09d0ba1e34d43b5ac40e73c2be86048d80f3c78cc455
|
||||
size 39482
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:134e561fc4082339725c241a79fa55e0b5b1e134c046d4454cb7a9e71ea5e1b7
|
||||
size 87174
|
||||
oid sha256:442a1d4dcf025734b956347fe865203eefeb64c0483852771c65307de739930a
|
||||
size 84421
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c31cd78bc054610be05012cdba7eb0cbc770435b0e12bc065f6eae4a773ca39e
|
||||
size 40121
|
||||
oid sha256:20762b4fb5695b33bec82470aceb257ad75acbecad287521d8df94a96331784f
|
||||
size 38184
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c31cd78bc054610be05012cdba7eb0cbc770435b0e12bc065f6eae4a773ca39e
|
||||
size 40121
|
||||
oid sha256:20762b4fb5695b33bec82470aceb257ad75acbecad287521d8df94a96331784f
|
||||
size 38184
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d398e399f468705b9f78283535c2b0e3f44f8be0456cf99bb9b3611746cd0af2
|
||||
size 54380
|
||||
oid sha256:c34974246d111739cb6a94443f2fa41ad439831e293fa651ba842bdc985175b3
|
||||
size 53099
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7fce6676e186e13f14ea8cc8436fdbcadcf41bf84a1999104794bd2245814337
|
||||
size 52805
|
||||
oid sha256:352499d53b913de50890061e923c439ddacf2aed029960a4feafd7f146c59266
|
||||
size 51368
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4884f1972998a969fa3a8948494bcd0247c1149b9340b1be845ffa432a0a0d9c
|
||||
size 9978
|
||||
oid sha256:4bdf2532d697fec8ea51cb071d3ea0476dae4d238a54612120b7d528bc9ce12d
|
||||
size 10286
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dfe2f91dd046dcd7416cc2767f467d793e14b1587379e328659389515d37ff84
|
||||
size 39363
|
||||
oid sha256:1df7c2cb351acf1ecd96ec4c654319bba4b25e3e52a8fbb3fa4c02b4b5f08c1c
|
||||
size 39544
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b57f48029efc014cd088ac6c36c3f6ecbb1b9c6c8f3ec77590112d09f700dbce
|
||||
size 43131
|
||||
oid sha256:ce2491439a30e286e18f05ddee5a5e85c4313731f5f52195c6fb5f9c5957f0b3
|
||||
size 43319
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:874a0b5d64b35c45a0293ebddbeb887377a8a40134af68828ebea0de34ca8694
|
||||
size 44119
|
||||
oid sha256:9f7f29c9877dfcf6a7b06ae0ff002ec56de13aa2b1baf49594b2388e9b9ec06c
|
||||
size 44314
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:23c88713ba8991df8b1beff3762ebdd48f3f6e3c31ff3f7713c440bc12435114
|
||||
size 30118
|
||||
oid sha256:3192ffaac9a7881b650fae6d241558bead45e7c646fa0fd5d5c21d22b90e4b47
|
||||
size 30308
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9683fc7b78efa029be47bdb20a2049bcddff1277f7170b67f887c0cc8ee0fb10
|
||||
size 34591
|
||||
oid sha256:c0e479a53bca5e6fe847de449a68206037b4f1159b8780ff07e2c74f384d8387
|
||||
size 34803
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d2bf6ba4782f4c2dc891ea0410ed4797487b7f0f6292867e018b89efd33b46d8
|
||||
size 42478
|
||||
oid sha256:0ceec3639f3aca6207d1f853c88205eb2f54acbd558842e235988dd516a37cc4
|
||||
size 42704
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6847b8f932fde9694cdcc3b911f4c90d6836ade22a2ce4fc2db7518812ef5008
|
||||
size 28735
|
||||
oid sha256:d09d2518ce2156fb3b5ffb1e0a0d2c76a88029d622107742bd5915208cfc6e5e
|
||||
size 28928
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:41de5451185f9ebd276baa7cb06bdea542cc6d9edd53ee08717f04204a0f4963
|
||||
size 39600
|
||||
oid sha256:e05723495b1bc12b0396e3036d488860606b5beb57363589e11db7c9f7b2867e
|
||||
size 39789
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b24654360b865e485ee280db126029a8f0851127d3c1606810a2ab11aaa0359d
|
||||
size 30585
|
||||
oid sha256:767970884d9b5464101fc15a945650978386a210f3a81ad2250a70a8504f535e
|
||||
size 30659
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7c4de5a07ef4b6c7a03db84d161a467e929dacda83bf363eef41443dad59f6c1
|
||||
size 33002
|
||||
oid sha256:fe891574b6035f927fedf26b4e26a26df96cfb8d881e9a2b39925aec1b1104e4
|
||||
size 33195
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7240e42cb7593cde2c9f4ebb9e3808f8ebe1de2251518074fdafd96aaee3a09d
|
||||
size 39424
|
||||
oid sha256:4f69db01001fd8204afd3f2f1375784ed8053a2c302489ed90d8661148ee42ae
|
||||
size 39617
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fc05216680219eadfa9df834287831d13c3f37ce6eef45b1b49c26124b562f19
|
||||
size 29089
|
||||
oid sha256:aa9b4c071e95d809b400ab2a1b19191689c20cb5d0d6502620f35a960a3ceb83
|
||||
size 29313
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:22f979dfb2c48d2d03ee439fde88e9fc355a667860c5c55574dc57644e1a9aac
|
||||
size 35398
|
||||
oid sha256:1d4b9e06fbfa9ff90f51d5942e80bb3f5c00d731f36cc3b519b513332caf56c0
|
||||
size 35233
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ec6efb77f63ec2e06866aa56c1a8eaa563f651a80144e5da1c4d27ff59275411
|
||||
size 9829
|
||||
oid sha256:d9c851f5baa8880390deaae0c54d0506f77f88129e5c276660e0f5ef1e159642
|
||||
size 10044
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f1c917c487c0eb32ca844c24ae725921174a8b0446fda2d1263879ce19d90d3f
|
||||
size 39060
|
||||
oid sha256:8f5d65ee186f09ff9c7750eb931c4674efb2c22281ab4d5ba4190ddfdd2c5c94
|
||||
size 39282
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:50c653b8a22a8bdde89f0729568858810991bf43e0628cf65989b487519ce488
|
||||
size 42639
|
||||
oid sha256:d20973d72a98df4a2c76e3a81ec8d1e912f4f488cdfe13c1281c945e42a37a02
|
||||
size 42877
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a92f0c0aab2a62bd94f5701b308ae48fab3f342ffb4923071ace642c3b32ae74
|
||||
size 43562
|
||||
oid sha256:7314b927a0211aa1ddae175c999c6e9fd666d9d4390dc3895198414405bc1abc
|
||||
size 43800
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a457e82ef171deb34b17c70494cd686cab16a5550f17edcbf72d27752f15435d
|
||||
size 29606
|
||||
oid sha256:041b36720ac5db3bf85576db8165929e7997df7976272bbf77f0e3e60271471d
|
||||
size 29837
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:12fa84ff4f3766a34de6eae4d8158e5090d2cf692afbc0cea973c7393ad99729
|
||||
size 33774
|
||||
oid sha256:75ac6b746a460063e22b9b21cca10268daca5a7f2b0a9139cad1aca04185c1c8
|
||||
size 34009
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fbe6574b3732b8d3ccde1f8188c6385f92b1109e058e0ff256d5e48451e681b0
|
||||
size 42110
|
||||
oid sha256:c05c3ba3c5a7c8c85ffe49c501098d5f1ded6011f7f1dc8230dea8e072438139
|
||||
size 42353
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0fdb9e57ee8cd20345f336d38f8be1e3c8d5205ba3854f4ffaaeb3294512df14
|
||||
size 28613
|
||||
oid sha256:887cadea9d50c14e36a9a8dd449a4deab978c84190650f4887dab687077e3d9f
|
||||
size 28875
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a9a9aacc227163f349dd0c3525f5f2bdb7959e9352c27f96160c4711de0dc8a9
|
||||
size 38443
|
||||
oid sha256:433bf40feee3d3ecb5563437eea2620dee42508c99fe601b699e5e978ab86343
|
||||
size 38671
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:69534c11f667a064d5d50a98770cdfc8e82f0988123a7a3ea3e285ef6f94725f
|
||||
size 28895
|
||||
oid sha256:9dca4ebe0873cdbec589272b80ea6adf56a9bf870c82495d836675faf25478b5
|
||||
size 28981
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3a7f6665c335429fc21f8868583022b82cb4ac94fc9cd4b2150b8c5e371c0fd1
|
||||
size 31959
|
||||
oid sha256:ec91c04137bdb66020f82ad9a1d309c5714d0dacea0005cd998ee2f4adaf22d5
|
||||
size 32209
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:333e605408b2373140bddd69d0ae07d3be531e90ecd36f0d594886465711e50c
|
||||
size 38675
|
||||
oid sha256:b6d880dc8d13df730c0db5c7be130354b5f78ca6a582c973295a56b9b9fbd953
|
||||
size 38911
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1262b7be57eda70a38d2e2a205c406688c0c306751c460a5493ec3cf812fed6d
|
||||
size 26812
|
||||
oid sha256:9b2466cee9954445038fb31c337aec4906dbbd9ec91cdabdb085fd0c14bdb979
|
||||
size 26979
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:31d6232428dd0fe61b41c645b86ce10dc6a2f0455785ac114808fa95c66c36ba
|
||||
size 35069
|
||||
oid sha256:de906db5c115ed3f4e92d235d869346ff57fc3139a28683e345b48e5f4a0a0ff
|
||||
size 34665
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0e9ced8663df865c6bdce2e2c05bdf11a48f7b8273a0d34ed7da775bce87568a
|
||||
size 8428
|
||||
oid sha256:9697082874aae750b9e0a88f8c3ad83ce21860af7099e76e72a57a5ee4305989
|
||||
size 8669
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:56c9ac5e7b9d851e662e4544f961ca82685e1f4c93b3df84f44426e4ab40ac73
|
||||
size 29166
|
||||
oid sha256:aa9b4c071e95d809b400ab2a1b19191689c20cb5d0d6502620f35a960a3ceb83
|
||||
size 29313
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:97a73b393c4aa2eaebc39809cc2cbc368dec4baf1d95f08b118b586a60aca841
|
||||
size 23997
|
||||
oid sha256:ac69126e44e60c1223c7775260dd3302de500dedd345ffb8f79bb07604e25223
|
||||
size 24138
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:38b209ff4fe8aca1f46b3ce1d140c1a83ca1620affec81909eb2916528d9974c
|
||||
size 8250
|
||||
oid sha256:67c48295ecdb460323ac959bb1d4937e1d43e9dc0d0328c052a893df6f635813
|
||||
size 8436
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:44492987b98ee92ddc9901ed766fdbecc346bccb7c10e0ee942b7ea79bd481da
|
||||
size 26789
|
||||
oid sha256:9b2466cee9954445038fb31c337aec4906dbbd9ec91cdabdb085fd0c14bdb979
|
||||
size 26979
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8217409aaf7e1b3960f0af4491b71db2f7fd59fdb58488c366e8d6f485955fe0
|
||||
size 21653
|
||||
oid sha256:44dadcebc45f3b591e461ede09cc3f2d4804f6f5224bf8dd5a1db2028d424610
|
||||
size 21859
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:144ec8da9382f33c7566452515a379eb7fec3e7c1cbd1c6c79a871e12093e6ee
|
||||
size 48043
|
||||
oid sha256:0a4d3666020f8ce450587ab8511ef74bda38e24b4b25936d6827444d49a988ba
|
||||
size 48172
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:53cf288c75a59cb97d2c0d648273e233b0ac9ba41f04b2cc57f7fe24657cf924
|
||||
size 47069
|
||||
oid sha256:5e26bfb2260902eca919bf76eb85f48108acb60991f3347b727b4f28b5415448
|
||||
size 47244
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1e09986f8b500061cfc9108af757b581334d8babb3c5c17433dd53dd3a8f52f3
|
||||
size 48709
|
||||
oid sha256:f52cf95e22042776bb61b7e01810d41e4f1f8b6eaf0319b40c5ea957f1a8603e
|
||||
size 50251
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1e09986f8b500061cfc9108af757b581334d8babb3c5c17433dd53dd3a8f52f3
|
||||
size 48709
|
||||
oid sha256:f52cf95e22042776bb61b7e01810d41e4f1f8b6eaf0319b40c5ea957f1a8603e
|
||||
size 50251
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f953609cb044e439198f7072a4468c54269b646e3c39686440334903eb12797d
|
||||
size 49279
|
||||
oid sha256:9e223457eafee936749ba938e2dc8ccefb64bae01510af8a6cb1c8bf59646b59
|
||||
size 50829
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c481049fcd33ca40aac538bf82ca56ef27cce2448c148cc45ba95493b668ef03
|
||||
size 47890
|
||||
oid sha256:cba57ac31d14044e0380ea9e81114cb2925565a955c6254f24c2dcb2e61662cb
|
||||
size 49180
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c481049fcd33ca40aac538bf82ca56ef27cce2448c148cc45ba95493b668ef03
|
||||
size 47890
|
||||
oid sha256:cba57ac31d14044e0380ea9e81114cb2925565a955c6254f24c2dcb2e61662cb
|
||||
size 49180
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:15ed6659fbf148a23b4090ba8896bad91d82279df71552c2798755b190a0cdca
|
||||
size 48369
|
||||
oid sha256:dbfc4dac4fd2139132348e3d07fa1713f3047c210f2cfc824044a1bf657b0b3e
|
||||
size 49665
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue