Coverage Summary for Class: MemberActionsBottomSheetKt (com.georgeci.moneysurfer.feature.workspace.members)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| MemberActionsBottomSheetKt |
0%
(0/1)
|
0%
(0/7)
|
3.6%
(1/28)
|
0%
(0/30)
|
0%
(0/620)
|
package com.georgeci.moneysurfer.feature.workspace.members
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.georgeci.moneysurfer.domain.primitives.UserId
import com.georgeci.moneysurfer.domain.primitives.WorkspaceId
import com.georgeci.moneysurfer.domain.usecase.InviteError
import com.georgeci.moneysurfer.utils.HandleSideEffect
import moneysurfer.feature.workspace.generated.resources.Res
import moneysurfer.feature.workspace.generated.resources.workspace_member_actions_confirm_remove_cancel
import moneysurfer.feature.workspace.generated.resources.workspace_member_actions_error_cannot_demote_owner
import moneysurfer.feature.workspace.generated.resources.workspace_member_actions_error_not_owner
import moneysurfer.feature.workspace.generated.resources.workspace_member_actions_error_unknown
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
/**
* Bottom-sheet entry composable for the per-member actions sheet (role picker + Remove). The
* surrounding [androidx.compose.material3.ModalBottomSheet] is provided by
* [com.georgeci.moneysurfer.navigation.BottomSheetSceneStrategy]. Visual layout lives
* in [MemberActionsContent] — this composable just maps the ViewModel's state and effects.
*/
@Composable
fun MemberActionsBottomSheet(
workspaceId: WorkspaceId,
targetUserId: UserId,
onDismiss: () -> Unit,
viewModel: MemberActionsViewModel = koinViewModel(
key = "${workspaceId.value}:${targetUserId.value}",
) { parametersOf(workspaceId, targetUserId) },
) {
val state by viewModel.collectAsStateWithLifecycle()
var snackbar by remember { mutableStateOf<String?>(null) }
val unknownText = stringResource(Res.string.workspace_member_actions_error_unknown)
val notOwnerText = stringResource(Res.string.workspace_member_actions_error_not_owner)
val cannotDemoteText = stringResource(Res.string.workspace_member_actions_error_cannot_demote_owner)
viewModel.HandleSideEffect { effect ->
when (effect) {
MemberActionsEffect.Dismiss -> onDismiss()
is MemberActionsEffect.ShowError -> {
snackbar = when (effect.error) {
InviteError.NotOwner -> notOwnerText
InviteError.CannotDemoteLastOwner -> cannotDemoteText
else -> unknownText
}
}
}
}
when (val s = state) {
is MemberActionsState.Loading -> Spacer(Modifier.height(160.dp))
is MemberActionsState.Content -> MemberActionsContent(state = s, onEvent = viewModel::onEvent)
}
snackbar?.let { msg ->
AlertDialog(
onDismissRequest = { snackbar = null },
confirmButton = {
TextButton(onClick = { snackbar = null }) {
Text(stringResource(Res.string.workspace_member_actions_confirm_remove_cancel))
}
},
text = { Text(msg) },
)
}
}