Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ package io.element.android.features.space.impl

sealed interface SpaceEvents {
data object LoadMore : SpaceEvents
data object StartLeaveSpace : SpaceEvents
data object LeaveSpace : SpaceEvents
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have some naming/structure we usually follow for these cases?

I think we'd usually have something like data class LeaveSpace(val needsConfirmation: Boolean) and use tell apart the 2 states. Or we'd also sometimes have LeaveSpace and some DoLeaveSpace events, if I'm not mistaken.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, we also have AsyncAction with a confirmation state, which is maybe better here. I'll give it a try

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data object CancelLeaveSpace : SpaceEvents
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,30 @@ package io.element.android.features.space.impl

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.Inject
import io.element.android.features.invite.api.SeenInvitesStore
import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.features.space.impl.leave.LeaveSpaceBottomSheetState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.mapState
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -65,10 +71,22 @@ class SpacePresenter(
}.collectAsState()

val currentSpace by remember { spaceRoomList.currentSpaceFlow() }.collectAsState(null)
val leaveSpaceBottomSheetState = remember { mutableStateOf<LeaveSpaceBottomSheetState>(LeaveSpaceBottomSheetState.Hidden) }

fun handleEvents(event: SpaceEvents) {
when (event) {
SpaceEvents.LoadMore -> coroutineScope.paginate()
SpaceEvents.CancelLeaveSpace -> {
leaveSpaceBottomSheetState.value = LeaveSpaceBottomSheetState.Hidden
}
SpaceEvents.LeaveSpace -> coroutineScope.launch {
leaveSpaceBottomSheetState.value = LeaveSpaceBottomSheetState.Hidden
client.getRoom(inputs.roomId)?.leave()
}
SpaceEvents.StartLeaveSpace -> coroutineScope.startLeaveSpace(
spaceName = currentSpace?.name,
leaveSpaceBottomSheetState,
)
}
}
return SpaceState(
Expand All @@ -77,11 +95,31 @@ class SpacePresenter(
seenSpaceInvites = seenSpaceInvites,
hideInvitesAvatar = hideInvitesAvatar,
hasMoreToLoad = hasMoreToLoad,
leaveSpaceBottomSheetState = leaveSpaceBottomSheetState.value,
eventSink = ::handleEvents,
)
}

private fun CoroutineScope.paginate() = launch {
spaceRoomList.paginate()
}

private fun CoroutineScope.startLeaveSpace(
spaceName: String?,
leaveSpaceBottomSheetState: MutableState<LeaveSpaceBottomSheetState>,
) = launch {
leaveSpaceBottomSheetState.value = LeaveSpaceBottomSheetState.Shown(
spaceName = spaceName,
roomsWhereUserIsTheOnlyAdmin = AsyncData.Loading(),
)
// TODO Fetch the actual list of rooms where the user is the only admin
delay(1000)
// Update state only if not cancelled by the user
if (leaveSpaceBottomSheetState.value is LeaveSpaceBottomSheetState.Shown) {
leaveSpaceBottomSheetState.value = LeaveSpaceBottomSheetState.Shown(
spaceName = spaceName,
roomsWhereUserIsTheOnlyAdmin = AsyncData.Success(persistentListOf()),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package io.element.android.features.space.impl

import io.element.android.features.space.impl.leave.LeaveSpaceBottomSheetState
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import kotlinx.collections.immutable.ImmutableList
Expand All @@ -18,5 +19,6 @@ data class SpaceState(
val seenSpaceInvites: ImmutableSet<RoomId>,
val hideInvitesAvatar: Boolean,
val hasMoreToLoad: Boolean,
val leaveSpaceBottomSheetState: LeaveSpaceBottomSheetState,
val eventSink: (SpaceEvents) -> Unit
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
package io.element.android.features.space.impl

import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.space.impl.leave.LeaveSpaceBottomSheetState
import io.element.android.features.space.impl.leave.aLeaveSpaceBottomSheetStateShown
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.previewutils.room.aSpaceRoom
Expand All @@ -26,7 +28,11 @@ open class SpaceStateProvider : PreviewParameterProvider<SpaceState> {
aSpaceState(
hasMoreToLoad = false,
children = aListOfSpaceRooms()
)
),
aSpaceState(
hasMoreToLoad = false,
leaveSpaceBottomSheetState = aLeaveSpaceBottomSheetStateShown(),
),
// Add other states here
)
}
Expand All @@ -42,12 +48,14 @@ fun aSpaceState(
seenSpaceInvites: Set<RoomId> = emptySet(),
hideInvitesAvatar: Boolean = false,
hasMoreToLoad: Boolean = false,
leaveSpaceBottomSheetState: LeaveSpaceBottomSheetState = LeaveSpaceBottomSheetState.Hidden,
) = SpaceState(
currentSpace = parentSpace,
children = children.toImmutableList(),
seenSpaceInvites = seenSpaceInvites.toImmutableSet(),
hideInvitesAvatar = hideInvitesAvatar,
hasMoreToLoad = hasMoreToLoad,
leaveSpaceBottomSheetState = leaveSpaceBottomSheetState,
eventSink = {}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
Expand All @@ -28,6 +31,9 @@ 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.space.impl.leave.LeaveSpaceBottomSheet
import io.element.android.features.space.impl.leave.LeaveSpaceBottomSheetState
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
Expand All @@ -36,12 +42,15 @@ import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
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.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.matrix.ui.components.SpaceHeaderView
import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView
import io.element.android.libraries.matrix.ui.model.getAvatarData
Expand All @@ -58,7 +67,10 @@ fun SpaceView(
Scaffold(
modifier = modifier,
topBar = {
SpaceViewTopBar(currentSpace = state.currentSpace, onBackClick = onBackClick)
SpaceViewTopBar(
state = state,
onBackClick = onBackClick,
)
},
content = { padding ->
Box(
Expand All @@ -71,6 +83,18 @@ fun SpaceView(
}
},
)

if (state.leaveSpaceBottomSheetState is LeaveSpaceBottomSheetState.Shown) {
LeaveSpaceBottomSheet(
state = state.leaveSpaceBottomSheetState,
onLeaveSpace = {
state.eventSink(SpaceEvents.LeaveSpace)
},
onDismiss = {
state.eventSink(SpaceEvents.CancelLeaveSpace)
}
)
}
}

@Composable
Expand Down Expand Up @@ -141,10 +165,11 @@ private fun LoadingMoreIndicator(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SpaceViewTopBar(
currentSpace: SpaceRoom?,
state: SpaceState,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val currentSpace = state.currentSpace
TopAppBar(
modifier = modifier,
navigationIcon = {
Expand All @@ -159,6 +184,34 @@ private fun SpaceViewTopBar(
}
},
actions = {
var showMenu by remember { mutableStateOf(false) }
IconButton(
onClick = { showMenu = !showMenu }
) {
Icon(
imageVector = CompoundIcons.OverflowVertical(),
contentDescription = null,
)
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
DropdownMenuItem(
onClick = {
showMenu = false
state.eventSink(SpaceEvents.StartLeaveSpace)
},
text = { Text(stringResource(id = CommonStrings.action_leave)) },
leadingIcon = {
Icon(
imageVector = CompoundIcons.Leave(),
tint = ElementTheme.colors.iconSecondary,
contentDescription = null,
)
}
)
}
},
)
}
Expand Down
Loading