diff --git a/core/src/main/java/com/terning/core/designsystem/component/button/FilteringButton.kt b/core/src/main/java/com/terning/core/designsystem/component/button/FilteringButton.kt index 69560b6dc..bc0ca38db 100644 --- a/core/src/main/java/com/terning/core/designsystem/component/button/FilteringButton.kt +++ b/core/src/main/java/com/terning/core/designsystem/component/button/FilteringButton.kt @@ -23,13 +23,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.terning.core.R -import com.terning.core.designsystem.theme.Grey400 +import com.terning.core.designsystem.theme.Grey150 +import com.terning.core.designsystem.theme.Grey200 +import com.terning.core.designsystem.theme.Grey375 import com.terning.core.designsystem.theme.TerningMain import com.terning.core.designsystem.theme.TerningPointTheme -import com.terning.core.designsystem.theme.TerningSub1 -import com.terning.core.designsystem.theme.TerningSub3 -import com.terning.core.designsystem.theme.TerningSub4 -import com.terning.core.designsystem.theme.TerningSub5 import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White import com.terning.core.util.NoRippleTheme @@ -58,19 +56,16 @@ fun FilteringButton( ) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() - val backgroundColor = when { - !isSelected && !isPressed -> White - !isSelected && isPressed -> TerningSub5 - isSelected && !isPressed -> TerningSub4 - else -> TerningSub3 - } + val backgroundColor = White val textColor = when { - !isSelected -> Grey400 + !isSelected -> Grey375 + isPressed -> Grey375 else -> TerningMain } val borderColor = when { - !isSelected -> TerningMain - else -> TerningSub1 + !isSelected && !isPressed -> Grey150 + isPressed -> Grey200 + else -> TerningMain } CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) { @@ -87,7 +82,7 @@ fun FilteringButton( color = borderColor ), shape = RoundedCornerShape(cornerRadius), - onClick = { onButtonClick() } + onClick = onButtonClick ) { Text( text = stringResource(id = text), @@ -104,14 +99,14 @@ fun FilteringButtonPreview() { TerningPointTheme { Column { FilteringButton( - isSelected = true, + isSelected = false, text = R.string.button_preview, cornerRadius = 15.dp, paddingVertical = 10.dp, onButtonClick = {} ) FilteringButton( - isSelected = false, + isSelected = true, text = R.string.button_preview, cornerRadius = 15.dp, paddingVertical = 10.dp, diff --git a/core/src/main/java/com/terning/core/extension/CalendarExt.kt b/core/src/main/java/com/terning/core/extension/CalendarExt.kt new file mode 100644 index 000000000..1bbc06b8a --- /dev/null +++ b/core/src/main/java/com/terning/core/extension/CalendarExt.kt @@ -0,0 +1,7 @@ +package com.terning.core.extension + +import java.util.Calendar + +val Calendar.currentYear: Int get() = get(Calendar.YEAR) + +val Calendar.currentMonth: Int get() = get(Calendar.MONTH) + 1 \ No newline at end of file diff --git a/core/src/main/java/com/terning/core/util/CalendarDefaults.kt b/core/src/main/java/com/terning/core/util/CalendarDefaults.kt new file mode 100644 index 000000000..2d0cb8355 --- /dev/null +++ b/core/src/main/java/com/terning/core/util/CalendarDefaults.kt @@ -0,0 +1,8 @@ +package com.terning.core.util + +object CalendarDefaults { + const val START_YEAR = 2010 + const val END_YEAR = 2030 + const val START_MONTH = 1 + const val END_MONTH = 12 +} diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringSideEffect.kt b/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringSideEffect.kt deleted file mode 100644 index 52c9b3a68..000000000 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringSideEffect.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.terning.feature.filtering.filtering - -import androidx.annotation.StringRes - -sealed class FilteringSideEffect { - data object NavigateToStartHome : FilteringSideEffect() - data class ShowToast(@StringRes val message: Int) : FilteringSideEffect() -} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringOneScreen.kt b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneRoute.kt similarity index 54% rename from feature/src/main/java/com/terning/feature/filtering/filtering/FilteringOneScreen.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneRoute.kt index 17ba94a8c..930e81fb7 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringOneScreen.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneRoute.kt @@ -1,5 +1,6 @@ -package com.terning.feature.filtering.filtering +package com.terning.feature.filtering.filteringone +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -8,55 +9,88 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.flowWithLifecycle import com.terning.core.designsystem.component.button.RectangleButton -import com.terning.core.designsystem.component.image.TerningImage import com.terning.core.designsystem.component.topappbar.BackButtonTopAppBar import com.terning.core.designsystem.theme.Grey300 import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White import com.terning.feature.R -import com.terning.feature.filtering.filtering.component.StatusOneRadioGroup +import com.terning.feature.filtering.filteringone.component.StatusOneRadioGroup @Composable -fun FilteringOneScreen( +fun FilteringOneRoute( name: String, onNextClick: (Int) -> Unit, navigateUp: () -> Unit, - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(), - onButtonClick: (Int) -> Unit = {}, + paddingValues: PaddingValues, + viewModel: FilteringOneViewModel = hiltViewModel(), ) { - val isButtonValid = remember { mutableStateOf(false) } + val state by viewModel.state.collectAsStateWithLifecycle() + + val lifecycleOwner = LocalLifecycleOwner.current - var grade by remember { mutableIntStateOf(-1) } + LaunchedEffect(viewModel.sideEffects, lifecycleOwner) { + viewModel.sideEffects.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) + .collect { sideEffect -> + when (sideEffect) { + is FilteringOneSideEffect.NavigateUp -> navigateUp() + } + } + } + FilteringOneScreen( + name = name, + onButtonClick = { index -> + viewModel.updateGradeAndButton(index) + }, + onNextClick = onNextClick, + navigateUp = viewModel::navigateUp, + buttonState = state.isButtonValid, + gradeState = state.grade, + paddingValues = paddingValues + ) +} + +@Composable +fun FilteringOneScreen( + name: String, + onNextClick: (Int) -> Unit, + navigateUp: () -> Unit, + onButtonClick: (Int) -> Unit, + buttonState: Boolean, + gradeState: Int, + paddingValues: PaddingValues = PaddingValues(), +) { Column( - modifier = modifier + modifier = Modifier .padding(paddingValues) .background(White) ) { BackButtonTopAppBar( - onBackButtonClick = { navigateUp() } + onBackButtonClick = navigateUp ) Column( modifier = Modifier.fillMaxSize() ) { - TerningImage( - painter = R.drawable.ic_filtering_status1, - modifier = modifier.padding( - top = 20.dp, + Image( + painter = painterResource(id = R.drawable.ic_filtering_status1), + modifier = Modifier.padding( + top = 28.dp, start = 24.dp - ) + ), + contentDescription = "filtering one status" ) Text( text = stringResource( @@ -64,8 +98,8 @@ fun FilteringOneScreen( name ), style = TerningTheme.typography.title3, - modifier = modifier.padding( - top = 19.dp, + modifier = Modifier.padding( + top = 20.dp, start = 24.dp ) ) @@ -76,35 +110,33 @@ fun FilteringOneScreen( ), style = TerningTheme.typography.body5, color = Grey300, - modifier = modifier.padding( - top = 3.dp, + modifier = Modifier.padding( + top = 4.dp, start = 24.dp, - bottom = 25.dp + bottom = 24.dp ) ) StatusOneRadioGroup( onButtonClick = { index -> onButtonClick(index) - isButtonValid.value = true - grade = index } ) Text( text = stringResource(id = R.string.filtering_status1_warning), style = TerningTheme.typography.detail3, - modifier = modifier.padding( + modifier = Modifier.padding( start = 24.dp, - top = 9.dp + top = 8.dp ) ) - Spacer(modifier = modifier.weight(1f)) + Spacer(modifier = Modifier.weight(1f)) RectangleButton( style = TerningTheme.typography.button0, paddingVertical = 20.dp, text = R.string.filtering_button, - onButtonClick = { onNextClick(grade) }, - modifier = modifier.padding(bottom = 12.dp), - isEnabled = isButtonValid.value + onButtonClick = { onNextClick(gradeState) }, + modifier = Modifier.padding(bottom = 12.dp), + isEnabled = buttonState ) } } @@ -118,7 +150,9 @@ fun FilteringOneScreenPreview() { name = "터닝이", onButtonClick = {}, onNextClick = {}, - navigateUp = {} + navigateUp = {}, + buttonState = true, + gradeState = 1 ) } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneSideEffect.kt b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneSideEffect.kt new file mode 100644 index 000000000..5fdbaaaa6 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneSideEffect.kt @@ -0,0 +1,5 @@ +package com.terning.feature.filtering.filteringone + +sealed class FilteringOneSideEffect { + data object NavigateUp : FilteringOneSideEffect() +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneState.kt b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneState.kt new file mode 100644 index 000000000..06c28b481 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneState.kt @@ -0,0 +1,6 @@ +package com.terning.feature.filtering.filteringone + +data class FilteringOneState( + val isButtonValid: Boolean = false, + val grade: Int = -1 +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneViewModel.kt b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneViewModel.kt new file mode 100644 index 000000000..93829d006 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringone/FilteringOneViewModel.kt @@ -0,0 +1,30 @@ +package com.terning.feature.filtering.filteringone + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class FilteringOneViewModel : ViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(FilteringOneState()) + val state: StateFlow get() = _state.asStateFlow() + + private val _sideEffects = MutableSharedFlow() + val sideEffects: SharedFlow get() = _sideEffects.asSharedFlow() + + fun updateGradeAndButton(grade: Int) { + _state.value = _state.value.copy( + grade = grade, + isButtonValid = true + ) + } + + fun navigateUp() = + viewModelScope.launch { _sideEffects.emit(FilteringOneSideEffect.NavigateUp) } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/component/StatusOneRadioGroup.kt b/feature/src/main/java/com/terning/feature/filtering/filteringone/component/StatusOneRadioGroup.kt similarity index 95% rename from feature/src/main/java/com/terning/feature/filtering/filtering/component/StatusOneRadioGroup.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringone/component/StatusOneRadioGroup.kt index 9aa695c6a..80608aa73 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/component/StatusOneRadioGroup.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringone/component/StatusOneRadioGroup.kt @@ -1,4 +1,4 @@ -package com.terning.feature.filtering.filtering.component +package com.terning.feature.filtering.filteringone.component import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth @@ -55,7 +55,7 @@ fun StatusOneRadioGroup( onButtonClick(index) }, cornerRadius = 15.dp, - paddingVertical = 15.dp + paddingVertical = 14.dp ) } } diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringOneNavigation.kt b/feature/src/main/java/com/terning/feature/filtering/filteringone/navigation/FilteringOneNavigation.kt similarity index 80% rename from feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringOneNavigation.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringone/navigation/FilteringOneNavigation.kt index 3ac282c44..d771d018f 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringOneNavigation.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringone/navigation/FilteringOneNavigation.kt @@ -8,7 +8,7 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.terning.core.navigation.Route -import com.terning.feature.filtering.filtering.FilteringOneScreen +import com.terning.feature.filtering.filteringone.FilteringOneRoute import kotlinx.serialization.Serializable fun NavController.navigateFilteringOne( @@ -27,11 +27,11 @@ fun NavGraphBuilder.filteringOneNavGraph( ) { composable { val args = it.toRoute() - FilteringOneScreen( + FilteringOneRoute( paddingValues = paddingValues, name = args.name, - onNextClick = { grade -> navHostController.navigateFilteringTwo(grade) }, - navigateUp = { navHostController.navigateUp() } + onNextClick = navHostController::navigateFilteringTwo, + navigateUp = navHostController::navigateUp ) } } diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringThreeRoute.kt b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeRoute.kt similarity index 62% rename from feature/src/main/java/com/terning/feature/filtering/filtering/FilteringThreeRoute.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeRoute.kt index 3eb6a89db..09ef94e01 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringThreeRoute.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeRoute.kt @@ -1,36 +1,38 @@ -package com.terning.feature.filtering.filtering +package com.terning.feature.filtering.filteringthree +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.flowWithLifecycle import com.terning.core.designsystem.component.button.RectangleButton -import com.terning.core.designsystem.component.datepicker.DatePickerUI -import com.terning.core.designsystem.component.image.TerningImage import com.terning.core.designsystem.component.topappbar.BackButtonTopAppBar import com.terning.core.designsystem.theme.Grey300 import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White +import com.terning.core.extension.currentMonth +import com.terning.core.extension.currentYear import com.terning.core.extension.toast import com.terning.feature.R +import com.terning.feature.filtering.filteringthree.component.YearMonthPicker import java.util.Calendar @Composable @@ -39,83 +41,84 @@ fun FilteringThreeRoute( workingPeriod: Int, navigateUp: () -> Unit, navigateToStartHome: () -> Unit, - viewModel: FilteringViewModel = hiltViewModel(), - paddingValues: PaddingValues + paddingValues: PaddingValues, + viewModel: FilteringThreeViewModel = hiltViewModel(), ) { + val state by viewModel.state.collectAsStateWithLifecycle() + val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current - val currentYear = Calendar.getInstance().get(Calendar.YEAR) - val currentMonth = Calendar.getInstance().get(Calendar.MONTH) - - var chosenYear by remember { mutableIntStateOf(currentYear) } - var chosenMonth by remember { mutableIntStateOf(currentMonth) } - LaunchedEffect(key1 = true) { - viewModel.fetchGrade(grade = grade) - viewModel.fetchWorkingPeriod(workingPeriod = workingPeriod) + with(viewModel) { + updateGrade(grade = grade) + updateWorkingPeriod(workingPeriod = workingPeriod) + } } - LaunchedEffect(key1 = chosenYear, key2 = chosenMonth) { - viewModel.fetchStartYear(chosenYear) - viewModel.fetchStartMonth(chosenMonth) + LaunchedEffect(key1 = state.startYear, key2 = state.startMonth) { + with(viewModel) { + updateStartYear(state.startYear) + updateStartMonth(state.startMonth) + } } LaunchedEffect(viewModel.sideEffects, lifecycleOwner) { viewModel.sideEffects.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) .collect { sideEffect -> when (sideEffect) { - is FilteringSideEffect.NavigateToStartHome -> navigateToStartHome() - is FilteringSideEffect.ShowToast -> context.toast(sideEffect.message) + is FilteringThreeSideEffect.NavigateToStartHome -> navigateToStartHome() + is FilteringThreeSideEffect.ShowToast -> context.toast(sideEffect.message) + is FilteringThreeSideEffect.NavigateUp -> navigateUp() } } } FilteringThreeScreen( paddingValues = paddingValues, - navigateUp = { navigateUp() }, - chosenYear = chosenYear, - chosenMonth = chosenMonth, - onNextClick = { viewModel.postFilteringWithServer() }, - onYearChosen = { chosenYear = it }, - onMonthChosen = { chosenMonth = it } + navigateUp = viewModel::navigateUp, + chosenYear = Calendar.getInstance().currentYear, + chosenMonth = Calendar.getInstance().currentMonth, + onNextClick = viewModel::postFilteringWithServer, + onYearChosen = viewModel::updateStartYear, + onMonthChosen = viewModel::updateStartMonth ) } @Composable fun FilteringThreeScreen( - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(), navigateUp: () -> Unit, chosenYear: Int, chosenMonth: Int, onYearChosen: (Int) -> Unit, onMonthChosen: (Int) -> Unit, onNextClick: () -> Unit, + paddingValues: PaddingValues = PaddingValues(), ) { Column( - modifier = modifier + modifier = Modifier .padding(paddingValues) .background(White), ) { BackButtonTopAppBar( - onBackButtonClick = { navigateUp() } + onBackButtonClick = navigateUp ) Column( modifier = Modifier.fillMaxSize() ) { - TerningImage( - painter = R.drawable.ic_filtering_status3, - modifier = modifier.padding( - top = 20.dp, + Image( + painter = painterResource(id = R.drawable.ic_filtering_status3), + modifier = Modifier.padding( + top = 28.dp, start = 24.dp - ) + ), + contentDescription = "filtering three status" ) Text( text = stringResource(id = R.string.filtering_status3_title), style = TerningTheme.typography.title3, - modifier = modifier.padding( - top = 19.dp, + modifier = Modifier.padding( + top = 20.dp, start = 24.dp ) ) @@ -123,26 +126,25 @@ fun FilteringThreeScreen( text = stringResource(id = R.string.filtering_status3_sub), style = TerningTheme.typography.body5, color = Grey300, - modifier = modifier.padding( - top = 3.dp, + modifier = Modifier.padding( + top = 4.dp, start = 24.dp, - bottom = 25.dp ) ) - Spacer(modifier = modifier.weight(1f)) - DatePickerUI( + Spacer(modifier = Modifier.height(61.dp)) + YearMonthPicker( chosenYear = chosenYear, chosenMonth = chosenMonth, onYearChosen = { onYearChosen(it) }, onMonthChosen = { onMonthChosen(it) }, ) - Spacer(modifier = modifier.weight(3f)) + Spacer(modifier = Modifier.weight(1f)) RectangleButton( style = TerningTheme.typography.button0, paddingVertical = 20.dp, text = R.string.filtering_button, - onButtonClick = { onNextClick() }, - modifier = modifier.padding(bottom = 12.dp), + onButtonClick = onNextClick, + modifier = Modifier.padding(bottom = 12.dp), ) } } diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeSideEffect.kt b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeSideEffect.kt new file mode 100644 index 000000000..3b035dc7c --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeSideEffect.kt @@ -0,0 +1,9 @@ +package com.terning.feature.filtering.filteringthree + +import androidx.annotation.StringRes + +sealed class FilteringThreeSideEffect { + data object NavigateToStartHome : FilteringThreeSideEffect() + data class ShowToast(@StringRes val message: Int) : FilteringThreeSideEffect() + data object NavigateUp : FilteringThreeSideEffect() +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringState.kt b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeState.kt similarity index 58% rename from feature/src/main/java/com/terning/feature/filtering/filtering/FilteringState.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeState.kt index b35e6fbd2..d907353f9 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringState.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeState.kt @@ -1,6 +1,6 @@ -package com.terning.feature.filtering.filtering +package com.terning.feature.filtering.filteringthree -data class FilteringState( +data class FilteringThreeState( val grade: Int = -1, val workingPeriod: Int = -1, val startYear: Int = -1, diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringViewModel.kt b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeViewModel.kt similarity index 65% rename from feature/src/main/java/com/terning/feature/filtering/filtering/FilteringViewModel.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeViewModel.kt index 692117fab..9c31c3c86 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringViewModel.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringthree/FilteringThreeViewModel.kt @@ -1,4 +1,4 @@ -package com.terning.feature.filtering.filtering +package com.terning.feature.filtering.filteringthree import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -17,30 +17,30 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class FilteringViewModel @Inject constructor( +class FilteringThreeViewModel @Inject constructor( private val filteringRepository: FilteringRepository, private val tokenRepository: TokenRepository ) : ViewModel() { - private val _state = MutableStateFlow(FilteringState()) - val state: StateFlow get() = _state.asStateFlow() + private val _state = MutableStateFlow(FilteringThreeState()) + val state: StateFlow get() = _state.asStateFlow() - private val _sideEffects = MutableSharedFlow() - val sideEffects: SharedFlow get() = _sideEffects.asSharedFlow() + private val _sideEffects = MutableSharedFlow() + val sideEffects: SharedFlow get() = _sideEffects.asSharedFlow() - fun fetchGrade(grade: Int) { + fun updateGrade(grade: Int) { _state.value = _state.value.copy(grade = grade) } - fun fetchWorkingPeriod(workingPeriod: Int) { + fun updateWorkingPeriod(workingPeriod: Int) { _state.value = _state.value.copy(workingPeriod = workingPeriod) } - fun fetchStartYear(startYear: Int) { + fun updateStartYear(startYear: Int) { _state.value = _state.value.copy(startYear = startYear) } - fun fetchStartMonth(startMonth: Int) { + fun updateStartMonth(startMonth: Int) { _state.value = _state.value.copy(startMonth = startMonth) } @@ -57,11 +57,13 @@ class FilteringViewModel @Inject constructor( ) } ).onSuccess { - _sideEffects.emit(FilteringSideEffect.NavigateToStartHome) + _sideEffects.emit(FilteringThreeSideEffect.NavigateToStartHome) }.onFailure { - _sideEffects.emit(FilteringSideEffect.ShowToast(R.string.server_failure)) + _sideEffects.emit(FilteringThreeSideEffect.ShowToast(R.string.server_failure)) } } } + fun navigateUp() = viewModelScope.launch { _sideEffects.emit(FilteringThreeSideEffect.NavigateUp) } + } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringthree/component/YearMonthPicker.kt b/feature/src/main/java/com/terning/feature/filtering/filteringthree/component/YearMonthPicker.kt new file mode 100644 index 000000000..d61c06c98 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringthree/component/YearMonthPicker.kt @@ -0,0 +1,199 @@ +package com.terning.feature.filtering.filteringthree.component + +import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import com.terning.core.designsystem.theme.Grey300 +import com.terning.core.designsystem.theme.Grey500 +import com.terning.core.designsystem.theme.TerningMain +import com.terning.core.designsystem.theme.TerningTheme +import com.terning.core.util.CalendarDefaults.END_MONTH +import com.terning.core.util.CalendarDefaults.END_YEAR +import com.terning.core.util.CalendarDefaults.START_MONTH +import com.terning.core.util.CalendarDefaults.START_YEAR +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import okhttp3.internal.toImmutableList + +private val years = + (START_YEAR..END_YEAR).map { "${it}년" }.toImmutableList() + +private val months = + (START_MONTH..END_MONTH).map { "${it}월" }.toImmutableList() + +class PickerState { + var selectedItem by mutableStateOf("") +} + +@Composable +fun rememberPickerState() = remember { PickerState() } + +@Composable +fun YearMonthPicker( + modifier: Modifier = Modifier, + chosenYear: Int, + chosenMonth: Int, + onYearChosen: (Int) -> Unit, + onMonthChosen: (Int) -> Unit, +) { + val yearPickerState = rememberPickerState() + val monthPickerState = rememberPickerState() + + val startYearIndex = years.indexOf("${chosenYear}년").takeIf { it >= 0 } ?: 0 + val startMonthIndex = months.indexOf("${chosenMonth}월").takeIf { it >= 0 } ?: 0 + + LaunchedEffect(chosenYear) { + yearPickerState.selectedItem = "${chosenYear}년" + } + + LaunchedEffect(chosenMonth) { + monthPickerState.selectedItem = "${chosenMonth}월" + } + + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 95.dp), + horizontalArrangement = Arrangement.Center + ) { + DatePicker( + modifier = Modifier.weight(1f), + pickerState = yearPickerState, + items = years, + startIndex = startYearIndex, + onItemSelected = { year -> + onYearChosen(year.dropLast(1).toInt()) + } + ) + Spacer(modifier = Modifier.width(18.dp)) + DatePicker( + modifier = Modifier.weight(1f), + pickerState = monthPickerState, + items = months, + startIndex = startMonthIndex, + onItemSelected = { month -> + onMonthChosen(month.dropLast(1).toInt()) + } + ) + } +} + +@Composable +fun DatePicker( + items: List, + modifier: Modifier = Modifier, + pickerState: PickerState = rememberPickerState(), + startIndex: Int = 0, + visibleItemCount: Int = 3, + onItemSelected: (String) -> Unit +) { + var itemHeightPixel by remember { mutableIntStateOf(0) } + val itemHeightDp = with(LocalDensity.current) { itemHeightPixel.toDp() } + + val visibleItemsMiddle = visibleItemCount / 2 + val scrollState = rememberLazyListState(initialFirstVisibleItemIndex = startIndex) + val flingBehavior = rememberSnapFlingBehavior(lazyListState = scrollState) + + LaunchedEffect(itemHeightPixel) { + if (itemHeightPixel > 0) scrollState.scrollToItem(startIndex) + } + + LaunchedEffect(scrollState) { + snapshotFlow { scrollState.firstVisibleItemIndex } + .map { index -> items.getOrNull(index) } + .distinctUntilChanged() + .collect { item -> + item?.let { + pickerState.selectedItem = it + onItemSelected(it) + } + } + } + + Box( + modifier = modifier + ) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .height(itemHeightDp * visibleItemCount), + flingBehavior = flingBehavior, + state = scrollState, + horizontalAlignment = Alignment.CenterHorizontally + ) { + items(visibleItemsMiddle) { + Spacer(modifier = Modifier.height(itemHeightDp)) + } + items(items.size) { index -> + DatePickerContent( + modifier = Modifier + .onSizeChanged { intSize: IntSize -> itemHeightPixel = intSize.height }, + text = items[index], + color = if (pickerState.selectedItem == items[index]) Grey500 else Grey300 + ) + } + items(visibleItemsMiddle) { + Spacer(modifier = Modifier.height(itemHeightDp)) + } + } + HorizontalDivider( + modifier = Modifier + .offset(y = itemHeightDp * visibleItemsMiddle) + .padding(horizontal = 7.dp), + color = TerningMain, + thickness = 1.dp + ) + HorizontalDivider( + modifier = Modifier + .offset(y = itemHeightDp * (visibleItemsMiddle + 1)) + .padding(horizontal = 7.dp), + color = TerningMain, + thickness = 1.dp + ) + } +} + +@Composable +fun DatePickerContent( + color: Color, + text: String, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier.padding(vertical = 11.dp) + ) { + Text( + text = text, + textAlign = TextAlign.Center, + style = TerningTheme.typography.title3, + color = color, + ) + } +} diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringThreeNavigation.kt b/feature/src/main/java/com/terning/feature/filtering/filteringthree/navigation/FilteringThreeNavigation.kt similarity index 81% rename from feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringThreeNavigation.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringthree/navigation/FilteringThreeNavigation.kt index 97f95beae..448a71d78 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringThreeNavigation.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringthree/navigation/FilteringThreeNavigation.kt @@ -1,4 +1,4 @@ -package com.terning.feature.filtering.filtering.navigation +package com.terning.feature.filtering.filteringthree.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavController @@ -8,7 +8,7 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.terning.core.navigation.Route -import com.terning.feature.filtering.filtering.FilteringThreeRoute +import com.terning.feature.filtering.filteringthree.FilteringThreeRoute import com.terning.feature.filtering.starthome.navigation.navigateStartHome import kotlinx.serialization.Serializable @@ -33,8 +33,8 @@ fun NavGraphBuilder.filteringThreeNavGraph( paddingValues = paddingValues, grade = args.grade, workingPeriod = args.workingPeriod, - navigateUp = { navHostController.navigateUp() }, - navigateToStartHome = { navHostController.navigateStartHome() } + navigateUp = navHostController::navigateUp, + navigateToStartHome = navHostController::navigateStartHome ) } } diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringTwoScreen.kt b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoRoute.kt similarity index 52% rename from feature/src/main/java/com/terning/feature/filtering/filtering/FilteringTwoScreen.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoRoute.kt index c35eec330..21361bdb5 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/FilteringTwoScreen.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoRoute.kt @@ -1,5 +1,6 @@ -package com.terning.feature.filtering.filtering +package com.terning.feature.filtering.filteringtwo +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -8,61 +9,94 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.flowWithLifecycle import com.terning.core.designsystem.component.button.RectangleButton -import com.terning.core.designsystem.component.image.TerningImage import com.terning.core.designsystem.component.topappbar.BackButtonTopAppBar import com.terning.core.designsystem.theme.Grey300 import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White import com.terning.feature.R -import com.terning.feature.filtering.filtering.component.StatusTwoRadioGroup +import com.terning.feature.filtering.filteringtwo.component.StatusTwoRadioGroup @Composable -fun FilteringTwoScreen( +fun FilteringTwoRoute( grade: Int, onNextClick: (Int, Int) -> Unit, navigateUp: () -> Unit, - modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(), - onButtonClick: (Int) -> Unit = {}, + paddingValues: PaddingValues, + viewModel: FilteringTwoViewModel = hiltViewModel(), ) { - val isButtonValid = remember { mutableStateOf(false) } + val state by viewModel.state.collectAsStateWithLifecycle() + + val lifecycleOwner = LocalLifecycleOwner.current - var workingPeriod by remember { mutableIntStateOf(-1) } + LaunchedEffect(viewModel.sideEffects, lifecycleOwner) { + viewModel.sideEffects.flowWithLifecycle(lifecycle = lifecycleOwner.lifecycle) + .collect { sideEffect -> + when (sideEffect) { + is FilteringTwoSideEffect.NavigateUp -> navigateUp() + } + } + } + FilteringTwoScreen( + grade = grade, + onNextClick = onNextClick, + navigateUp = viewModel::navigateUp, + onButtonClick = { index -> + viewModel.updateWorkingPeriodAndButton(index) + }, + buttonState = state.isButtonValid, + workingPeriod = state.workingPeriod, + paddingValues = paddingValues + ) +} + +@Composable +fun FilteringTwoScreen( + grade: Int, + onNextClick: (Int, Int) -> Unit, + navigateUp: () -> Unit, + onButtonClick: (Int) -> Unit, + buttonState: Boolean, + workingPeriod: Int, + paddingValues: PaddingValues = PaddingValues(), +) { Column( - modifier = modifier + modifier = Modifier .padding(paddingValues) .background(White) ) { BackButtonTopAppBar( - onBackButtonClick = { navigateUp() } + onBackButtonClick = navigateUp ) Column( modifier = Modifier.fillMaxSize() ) { - TerningImage( - painter = R.drawable.ic_filtering_status2, - modifier = modifier.padding( - top = 20.dp, + Image( + painter = painterResource(id = R.drawable.ic_filtering_status2), + modifier = Modifier.padding( + top = 28.dp, start = 24.dp - ) + ), + contentDescription = "filtering two status" ) Text( text = stringResource(id = R.string.filtering_status2_title), style = TerningTheme.typography.title3, - modifier = modifier.padding( - top = 19.dp, + modifier = Modifier.padding( + top = 20.dp, start = 24.dp ) ) @@ -70,27 +104,25 @@ fun FilteringTwoScreen( text = stringResource(id = R.string.filtering_status2_sub), style = TerningTheme.typography.body5, color = Grey300, - modifier = modifier.padding( - top = 3.dp, + modifier = Modifier.padding( + top = 4.dp, start = 24.dp, - bottom = 25.dp + bottom = 24.dp ) ) StatusTwoRadioGroup( onButtonClick = { index -> onButtonClick(index) - isButtonValid.value = true - workingPeriod = index } ) - Spacer(modifier = modifier.weight(1f)) + Spacer(modifier = Modifier.weight(1f)) RectangleButton( style = TerningTheme.typography.button0, paddingVertical = 20.dp, text = R.string.filtering_button, onButtonClick = { onNextClick(grade, workingPeriod) }, - modifier = modifier.padding(bottom = 12.dp), - isEnabled = isButtonValid.value + modifier = Modifier.padding(bottom = 12.dp), + isEnabled = buttonState ) } } @@ -103,7 +135,10 @@ fun FilteringTwoScreenPreview() { FilteringTwoScreen( grade = 1, onNextClick = { _, _ -> }, - navigateUp = { } + navigateUp = { }, + onButtonClick = { }, + buttonState = true, + workingPeriod = 1 ) } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoSideEffect.kt b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoSideEffect.kt new file mode 100644 index 000000000..a858577c1 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoSideEffect.kt @@ -0,0 +1,5 @@ +package com.terning.feature.filtering.filteringtwo + +sealed class FilteringTwoSideEffect { + data object NavigateUp : FilteringTwoSideEffect() +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoState.kt b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoState.kt new file mode 100644 index 000000000..383049e07 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoState.kt @@ -0,0 +1,6 @@ +package com.terning.feature.filtering.filteringtwo + +data class FilteringTwoState( + val isButtonValid: Boolean = false, + val workingPeriod: Int = -1 +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoViewModel.kt b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoViewModel.kt new file mode 100644 index 000000000..2076a5221 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/FilteringTwoViewModel.kt @@ -0,0 +1,30 @@ +package com.terning.feature.filtering.filteringtwo + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class FilteringTwoViewModel : ViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(FilteringTwoState()) + val state: StateFlow get() = _state.asStateFlow() + + private val _sideEffects = MutableSharedFlow() + val sideEffects: SharedFlow get() = _sideEffects.asSharedFlow() + + fun updateWorkingPeriodAndButton(workingPeriod: Int) { + _state.value = _state.value.copy( + workingPeriod = workingPeriod, + isButtonValid = true + ) + } + + fun navigateUp() = + viewModelScope.launch { _sideEffects.emit(FilteringTwoSideEffect.NavigateUp) } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/component/StatusTwoRadioGroup.kt b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/component/StatusTwoRadioGroup.kt similarity index 97% rename from feature/src/main/java/com/terning/feature/filtering/filtering/component/StatusTwoRadioGroup.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringtwo/component/StatusTwoRadioGroup.kt index 6d42ee42a..ed06f467b 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/component/StatusTwoRadioGroup.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/component/StatusTwoRadioGroup.kt @@ -1,4 +1,4 @@ -package com.terning.feature.filtering.filtering.component +package com.terning.feature.filtering.filteringtwo.component import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth diff --git a/feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringTwoNavigation.kt b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/navigation/FilteringTwoNavigation.kt similarity index 83% rename from feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringTwoNavigation.kt rename to feature/src/main/java/com/terning/feature/filtering/filteringtwo/navigation/FilteringTwoNavigation.kt index ff9f1286d..d23c3a75d 100644 --- a/feature/src/main/java/com/terning/feature/filtering/filtering/navigation/FilteringTwoNavigation.kt +++ b/feature/src/main/java/com/terning/feature/filtering/filteringtwo/navigation/FilteringTwoNavigation.kt @@ -8,7 +8,8 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.terning.core.navigation.Route -import com.terning.feature.filtering.filtering.FilteringTwoScreen +import com.terning.feature.filtering.filteringthree.navigation.navigateFilteringThree +import com.terning.feature.filtering.filteringtwo.FilteringTwoRoute import kotlinx.serialization.Serializable fun NavController.navigateFilteringTwo( @@ -27,7 +28,7 @@ fun NavGraphBuilder.filteringTwoNavGraph( ) { composable { val args = it.toRoute() - FilteringTwoScreen( + FilteringTwoRoute( paddingValues = paddingValues, grade = args.grade, onNextClick = { _, workingPeriod -> @@ -36,7 +37,7 @@ fun NavGraphBuilder.filteringTwoNavGraph( workingPeriod = workingPeriod ) }, - navigateUp = { navHostController.navigateUp() } + navigateUp = navHostController::navigateUp ) } } diff --git a/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringScreen.kt b/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt similarity index 72% rename from feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringScreen.kt rename to feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt index 2429947ce..dd7326109 100644 --- a/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringScreen.kt +++ b/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringRoute.kt @@ -2,6 +2,7 @@ package com.terning.feature.filtering.startfiltering import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -15,18 +16,17 @@ import androidx.compose.material3.Text 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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.terning.core.designsystem.component.button.RectangleButton -import com.terning.core.designsystem.component.image.TerningImage import com.terning.core.designsystem.theme.TerningPointTheme import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White @@ -34,23 +34,39 @@ import com.terning.feature.R import kotlinx.coroutines.delay @Composable -fun StartFilteringScreen( - modifier: Modifier = Modifier, +fun StartFilteringRoute( paddingValues: PaddingValues = PaddingValues(), - onNextClick: () -> Unit + onNextClick: () -> Unit, + viewModel: StartFilteringViewModel = hiltViewModel() ) { - var isVisible by remember { mutableStateOf(false) } + val state by viewModel.state.collectAsStateWithLifecycle() val configuration = LocalConfiguration.current val screenHeight = 780f / configuration.screenHeightDp LaunchedEffect(key1 = true) { delay(DELAY) - isVisible = true + viewModel.updateButtonState() } + StartFilteringScreen( + onNextClick = onNextClick, + buttonState = state.isButtonVisible, + screenHeight = screenHeight, + paddingValues = paddingValues + ) + +} + +@Composable +fun StartFilteringScreen( + onNextClick: () -> Unit, + buttonState: Boolean, + screenHeight: Float, + paddingValues: PaddingValues = PaddingValues() +) { Box( - modifier = modifier + modifier = Modifier .fillMaxSize() .padding(paddingValues) .background(White) @@ -66,11 +82,12 @@ fun StartFilteringScreen( modifier = Modifier.padding(bottom = 35.dp), textAlign = TextAlign.Center ) - TerningImage( - painter = R.drawable.ic_terning_onboarding, + Image( + painter = painterResource(id = R.drawable.ic_terning_onboarding), modifier = Modifier .fillMaxWidth() - .padding(horizontal = 24.dp) + .padding(horizontal = 24.dp), + contentDescription = "terning image" ) Spacer(modifier = Modifier.weight(2f)) } @@ -80,14 +97,14 @@ fun StartFilteringScreen( .padding(bottom = 12.dp) ) { AnimatedVisibility( - visible = isVisible, + visible = buttonState, enter = fadeIn(initialAlpha = 0.3f), ) { RectangleButton( style = TerningTheme.typography.button0, paddingVertical = 20.dp, text = R.string.start_filtering_button, - onButtonClick = { onNextClick() }, + onButtonClick = onNextClick, ) } } @@ -101,7 +118,9 @@ private const val DELAY: Long = 1000 fun StartFilteringScreenPreview() { TerningPointTheme { StartFilteringScreen( - onNextClick = {} + onNextClick = {}, + buttonState = true, + screenHeight = 1f ) } -} +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringState.kt b/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringState.kt new file mode 100644 index 000000000..005cb40f8 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringState.kt @@ -0,0 +1,5 @@ +package com.terning.feature.filtering.startfiltering + +data class StartFilteringState( + val isButtonVisible: Boolean = false +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringViewModel.kt b/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringViewModel.kt new file mode 100644 index 000000000..3cfc06193 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/startfiltering/StartFilteringViewModel.kt @@ -0,0 +1,18 @@ +package com.terning.feature.filtering.startfiltering + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class StartFilteringViewModel : ViewModel() { + + private val _state: MutableStateFlow = + MutableStateFlow(StartFilteringState()) + val state: StateFlow get() = _state.asStateFlow() + + fun updateButtonState() { + _state.value = _state.value.copy(isButtonVisible = true) + } + +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt b/feature/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt index 17280736b..38abe0cc2 100644 --- a/feature/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt +++ b/feature/src/main/java/com/terning/feature/filtering/startfiltering/navigation/StartFilteringNavigation.kt @@ -9,7 +9,7 @@ import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.terning.core.navigation.Route import com.terning.feature.filtering.filtering.navigation.navigateFilteringOne -import com.terning.feature.filtering.startfiltering.StartFilteringScreen +import com.terning.feature.filtering.startfiltering.StartFilteringRoute import kotlinx.serialization.Serializable fun NavController.navigateStartFiltering( @@ -28,7 +28,7 @@ fun NavGraphBuilder.startFilteringNavGraph( ) { composable { val args = it.toRoute() - StartFilteringScreen( + StartFilteringRoute( paddingValues = paddingValues, onNextClick = { navHostController.navigateFilteringOne(args.name) diff --git a/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeScreen.kt b/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt similarity index 81% rename from feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeScreen.kt rename to feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt index 29cdba837..246cbc8d9 100644 --- a/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeScreen.kt +++ b/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeRoute.kt @@ -16,9 +16,6 @@ import androidx.compose.material3.Text 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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration @@ -26,6 +23,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.airbnb.lottie.compose.LottieAnimation import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.LottieConstants @@ -38,23 +37,38 @@ import com.terning.feature.R import kotlinx.coroutines.delay @Composable -fun StartHomeScreen( - modifier: Modifier = Modifier, +fun StartHomeRoute( + navigateToHome: () -> Unit, paddingValues: PaddingValues = PaddingValues(), - navigateToHome: () -> Unit + viewModel: StartHomeViewModel = hiltViewModel() ) { - var isVisible by remember { mutableStateOf(false) } + val state by viewModel.state.collectAsStateWithLifecycle() val configuration = LocalConfiguration.current val screenHeight = 780f / configuration.screenHeightDp LaunchedEffect(key1 = true) { delay(DELAY) - isVisible = true + viewModel.updateButtonState() } + StartHomeScreen( + navigateToHome = navigateToHome, + buttonState = state.isButtonVisible, + screenHeight = screenHeight, + paddingValues = paddingValues + ) +} + +@Composable +fun StartHomeScreen( + navigateToHome: () -> Unit, + buttonState: Boolean, + screenHeight: Float, + paddingValues: PaddingValues = PaddingValues() +) { Box( - modifier = modifier + modifier = Modifier .fillMaxSize() .padding(paddingValues) .background(White) @@ -79,16 +93,14 @@ fun StartHomeScreen( contentAlignment = Alignment.BottomCenter ) { AnimatedVisibility( - visible = isVisible, + visible = buttonState, enter = fadeIn(initialAlpha = 0.3f), ) { RectangleButton( style = TerningTheme.typography.button0, paddingVertical = 20.dp, text = R.string.start_home_next_button, - onButtonClick = { - navigateToHome() - }, + onButtonClick = navigateToHome, ) } } @@ -122,7 +134,9 @@ fun StartHomeLottieAnimation( fun StartHomeScreenPreview() { TerningPointTheme { StartHomeScreen( - navigateToHome = {} + navigateToHome = {}, + buttonState = true, + screenHeight = 1f ) } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeState.kt b/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeState.kt new file mode 100644 index 000000000..d847568bc --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeState.kt @@ -0,0 +1,5 @@ +package com.terning.feature.filtering.starthome + +data class StartHomeState( + val isButtonVisible: Boolean = false +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeViewModel.kt b/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeViewModel.kt new file mode 100644 index 000000000..e0e038372 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/filtering/starthome/StartHomeViewModel.kt @@ -0,0 +1,16 @@ +package com.terning.feature.filtering.starthome + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class StartHomeViewModel : ViewModel() { + private val _state: MutableStateFlow = + MutableStateFlow(StartHomeState()) + val state: StateFlow get() = _state.asStateFlow() + + fun updateButtonState() { + _state.value = _state.value.copy(isButtonVisible = true) + } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/filtering/starthome/navigation/StartHomeNavigation.kt b/feature/src/main/java/com/terning/feature/filtering/starthome/navigation/StartHomeNavigation.kt index 9f493d51a..bc63ae44c 100644 --- a/feature/src/main/java/com/terning/feature/filtering/starthome/navigation/StartHomeNavigation.kt +++ b/feature/src/main/java/com/terning/feature/filtering/starthome/navigation/StartHomeNavigation.kt @@ -7,7 +7,7 @@ import androidx.navigation.NavHostController import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.terning.core.navigation.Route -import com.terning.feature.filtering.starthome.StartHomeScreen +import com.terning.feature.filtering.starthome.StartHomeRoute import com.terning.feature.home.home.navigation.navigateHome import com.terning.feature.onboarding.signin.navigation.SignIn import kotlinx.serialization.Serializable @@ -24,7 +24,7 @@ fun NavGraphBuilder.startHomeNavGraph( navHostController: NavHostController ) { composable { - StartHomeScreen( + StartHomeRoute( paddingValues = paddingValues, navigateToHome = { navHostController.navigateHome( diff --git a/feature/src/main/java/com/terning/feature/main/MainScreen.kt b/feature/src/main/java/com/terning/feature/main/MainScreen.kt index d78934eba..6403b2e31 100644 --- a/feature/src/main/java/com/terning/feature/main/MainScreen.kt +++ b/feature/src/main/java/com/terning/feature/main/MainScreen.kt @@ -27,8 +27,8 @@ import com.terning.core.designsystem.theme.White import com.terning.core.util.NoRippleInteractionSource import com.terning.feature.calendar.calendar.navigation.calendarNavGraph import com.terning.feature.filtering.filtering.navigation.filteringOneNavGraph -import com.terning.feature.filtering.filtering.navigation.filteringThreeNavGraph import com.terning.feature.filtering.filtering.navigation.filteringTwoNavGraph +import com.terning.feature.filtering.filteringthree.navigation.filteringThreeNavGraph import com.terning.feature.filtering.startfiltering.navigation.startFilteringNavGraph import com.terning.feature.filtering.starthome.navigation.startHomeNavGraph import com.terning.feature.home.changefilter.navigation.changeFilterNavGraph diff --git a/feature/src/main/java/com/terning/feature/onboarding/signup/SignUpRoute.kt b/feature/src/main/java/com/terning/feature/onboarding/signup/SignUpRoute.kt index 9a963fe57..4be2f12c7 100644 --- a/feature/src/main/java/com/terning/feature/onboarding/signup/SignUpRoute.kt +++ b/feature/src/main/java/com/terning/feature/onboarding/signup/SignUpRoute.kt @@ -123,7 +123,6 @@ fun SignUpScreen( Column( modifier = Modifier.align(Alignment.CenterHorizontally) ) { - Spacer(modifier = Modifier.height(48.dp)) ProfileWithPlusButton( modifier = Modifier.noRippleClickable { onProfileEditClick(true) diff --git a/feature/src/main/java/com/terning/feature/onboarding/signup/navigation/SignUpNavigation.kt b/feature/src/main/java/com/terning/feature/onboarding/signup/navigation/SignUpNavigation.kt index 34308cc81..07a1c7fa2 100644 --- a/feature/src/main/java/com/terning/feature/onboarding/signup/navigation/SignUpNavigation.kt +++ b/feature/src/main/java/com/terning/feature/onboarding/signup/navigation/SignUpNavigation.kt @@ -8,7 +8,7 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.terning.core.navigation.Route -import com.terning.feature.filtering.filtering.navigation.navigateFilteringOne +import com.terning.feature.filtering.startfiltering.navigation.navigateStartFiltering import com.terning.feature.onboarding.signup.SignUpRoute import kotlinx.serialization.Serializable @@ -31,7 +31,8 @@ fun NavGraphBuilder.signUpNavGraph( SignUpRoute( paddingValues = paddingValues, authId = args.authId, - navigateToStartFiltering = { name -> navHostController.navigateFilteringOne(name) } + navigateToStartFiltering = { name -> navHostController.navigateStartFiltering(name) } + ) } } diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index e258ad031..60d721208 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -111,7 +111,7 @@ 대학생 인턴, 누구보다 빠르게 시작해 보세요! 인턴이라는 좋은 기회로 단숨에 스펙업하세요! 지금까지 준비한 역량을 인턴으로 발휘해 보세요! - 사회초년생으로 도약하기 위한 마지막 단계를 경험하세요! + 사회초년생으로 도약하는 마지막 단계를 경험하세요! 희망하는 인턴 근무 기간을 선택해 주세요 선택한 기간 동안 근무할 수 있는 인턴 공고를 찾아드릴게요 1개월 ~ 3개월 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 33b6c185d..2d60e8d41 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,11 +23,9 @@ ksp = "1.9.0-1.0.12" ## Compose androidxComposeBom = "2024.04.01" -androidxComposeCompiler = "1.5.14" androidxComposeMaterial3 = "1.2.1" composeNavigation = "2.8.0-beta04" composeSavable = "1.4.3" -composeFlowRow = "0.30.0" ## Hilt hilt = "2.48.1"