UI

package com.yunho.feature.test.view

import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import com.yunho.feature.navigation.LocalNavController
import com.yunho.feature.test.intent.TestEvent
import com.yunho.feature.test.intent.TestSideEffect
import com.yunho.feature.test.intent.TestState
import com.yunho.feature.test.view.component.TestDialog
import com.yunho.feature.test.view.component.TestDialogState.Companion.rememberTestDialogState
import com.yunho.feature.test.view.model.TestViewModel
import org.orbitmvi.orbit.compose.collectAsState
import org.orbitmvi.orbit.compose.collectSideEffect

@Composable
fun Test(
    modifier: Modifier = Modifier,
    viewModel: TestViewModel = hiltViewModel()
) {
    val state by viewModel.collectAsState()
    val dialogState = rememberTestDialogState()
    val navController = LocalNavController.current

    viewModel.collectSideEffect { effect ->
        when (effect) {
            TestSideEffect.Navigate.Back -> navController.popBackStack()
            TestSideEffect.Hide.Dialog -> dialogState.hide()
            is TestSideEffect.Show.Dialog -> dialogState.show(effect.content)
        }
    }

    TestDialog(
        dialogState = dialogState,
        onDismissRequest = dialogState::hide,
        onEvent = viewModel::onEvent
    )

    Test(
        state = state,
        onEvent = viewModel::onEvent,
        modifier = modifier
    )
}

@Composable
fun Test(
    state: TestState,
    onEvent: (TestEvent) -> Unit,
    modifier: Modifier = Modifier
) {
    Column(modifier = modifier) {
        Button({
            onEvent(TestEvent.Click.Back)
        }) {
            Text("Test ${state.data}")
        }
    }
}

viewModel

package com.yunho.feature.test.view.model

import com.yunho.feature.base.BaseViewModel
import com.yunho.feature.test.intent.TestEvent
import com.yunho.feature.test.intent.TestSideEffect
import com.yunho.feature.test.intent.TestState
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class TestViewModel @Inject constructor(
    // TODO: 의존성 주입 파라미터
) : BaseViewModel<TestState, TestSideEffect>(initialState = TestState()) {

    fun onEvent(event: TestEvent) {
        when (event) {
            is TestEvent.Click -> onClick(event)
        }
    }

    private fun onClick(event: TestEvent.Click) {
        intent {
            when (event) {
                TestEvent.Click.Back -> postSideEffect(TestSideEffect.Navigate.Back)
            }
        }
    }
}

dialog

package com.yunho.feature.test.view.component

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.yunho.feature.test.intent.TestEvent

@Composable
fun TestDialog(
    dialogState: TestDialogState,
    onDismissRequest: () -> Unit,
    onEvent: (TestEvent) -> Unit
) {
    when {
        dialogState.content != null -> Dialog(
            onDismissRequest = onDismissRequest,
            properties = DialogProperties()
        ) {
             when (val content = dialogState.content) {
                is TestDialogState.Content.Default -> Default(content = content, onEvent = onEvent)
                null -> Unit
             }
        }

        else -> Unit
    }
}

@Composable
private fun Default(
    content: TestDialogState.Content.Default,
    onEvent: (TestEvent) -> Unit,
    modifier: Modifier = Modifier
) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(20.dp),
        color = Color.White
    ) {
        Column(
            modifier = Modifier
                .padding(horizontal = 20.dp)
                .padding(top = 24.dp, bottom = 20.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            
        }
    }
}

dialogState

package com.yunho.feature.test.view.component

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

class TestDialogState {
    var content by mutableStateOf<Content?>(null)

    fun hide() {
        content = null
    }

    fun show(content: Content) {
        this.content = content
    }

    sealed interface Content {
        data object Default : Content
    }

    companion object {
        @Composable
        fun rememberTestDialogState() = remember { TestDialogState() }
    }
}

State

package com.yunho.feature.test.intent

data class TestState(
    val data: String
) {
    constructor() : this(data = "")
}

SideEffect

package com.yunho.feature.test.intent

import com.yunho.feature.test.view.component.TestDialogState

sealed interface TestSideEffect {
    sealed interface Navigate : TestSideEffect {
        data object Back : Navigate
    }

    sealed interface Show : TestSideEffect {
        data class Dialog(val content: TestDialogState.Content) : Show
    }

    sealed interface Hide : TestSideEffect {
        data object Dialog : Hide
    }
}

Event

package com.yunho.feature.test.intent

sealed interface TestEvent {
    sealed interface Click : TestEvent {
        data object Back : Click
    }
}