diff --git a/core/designsystem/src/main/java/com/puzzle/designsystem/component/TextFields.kt b/core/designsystem/src/main/java/com/puzzle/designsystem/component/TextFields.kt index 45fe41bf..0acf26b2 100644 --- a/core/designsystem/src/main/java/com/puzzle/designsystem/component/TextFields.kt +++ b/core/designsystem/src/main/java/com/puzzle/designsystem/component/TextFields.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -129,7 +130,7 @@ fun PieceTextInputLong( onValueChange = { input -> limit?.let { if (input.length <= limit) onValueChange(input) } ?: onValueChange(input) }, - singleLine = true, + singleLine = false, readOnly = readOnly, keyboardOptions = KeyboardOptions( keyboardType = keyboardType, @@ -148,19 +149,22 @@ fun PieceTextInputLong( textStyle = PieceTheme.typography.bodyMM, cursorBrush = SolidColor(PieceTheme.colors.primaryDefault), decorationBox = { innerTextField -> + val isCharCountVisible: Boolean = limit != null && !readOnly Box { - if (value.isEmpty()) { - Text( - text = hint, - style = PieceTheme.typography.bodyMM, - color = PieceTheme.colors.dark3, - modifier = Modifier.align(Alignment.TopStart) - ) - } + Box(modifier = Modifier.padding(bottom = if (isCharCountVisible) 36.dp else 0.dp)) { + if (value.isEmpty()) { + Text( + text = hint, + style = PieceTheme.typography.bodyMM, + color = PieceTheme.colors.dark3, + modifier = Modifier.align(Alignment.TopStart) + ) + } - innerTextField() + innerTextField() + } - if (limit != null && isFocused && value.isNotEmpty()) { + if (isCharCountVisible) { Text( text = buildAnnotatedString { withStyle(style = SpanStyle(color = PieceTheme.colors.primaryDefault)) { @@ -169,7 +173,9 @@ fun PieceTextInputLong( append("/${limit}") }, style = PieceTheme.typography.bodySM, - modifier = Modifier.align(Alignment.BottomEnd), + modifier = Modifier + .align(Alignment.BottomEnd) + .height(20.dp), ) } } @@ -178,10 +184,7 @@ fun PieceTextInputLong( .onFocusChanged { focusState -> isFocused = focusState.isFocused } .heightIn(min = 160.dp) .clip(RoundedCornerShape(8.dp)) - .background( - if (readOnly) PieceTheme.colors.light2 - else PieceTheme.colors.light3 - ) + .background(PieceTheme.colors.light3) .padding(horizontal = 16.dp, vertical = 14.dp), ) } @@ -190,64 +193,110 @@ fun PieceTextInputLong( fun PieceTextInputAI( value: String, onValueChange: (String) -> Unit, + onSaveClick: (String) -> Unit, modifier: Modifier = Modifier, throttleTime: Long = 2000L, + readOnly: Boolean = true, onDone: () -> Unit = {}, ) { val keyboardController = LocalSoftwareKeyboardController.current var lastDoneTime by remember { mutableLongStateOf(0L) } var isFocused by remember { mutableStateOf(false) } + var isReadOnly: Boolean by remember { mutableStateOf(readOnly) } + var isLoading: Boolean by remember { mutableStateOf(value.isBlank()) } - BasicTextField( - value = value, - onValueChange = onValueChange, - singleLine = true, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions( - onDone = { - val currentTime = System.currentTimeMillis() - if (currentTime - lastDoneTime >= throttleTime) { - keyboardController?.hide() - onDone() - lastDoneTime = currentTime + Column( + modifier = modifier + ) { + BasicTextField( + value = value, + onValueChange = onValueChange, + singleLine = true, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions( + onDone = { + val currentTime = System.currentTimeMillis() + if (currentTime - lastDoneTime >= throttleTime) { + keyboardController?.hide() + onDone() + lastDoneTime = currentTime + } } - } - ), - textStyle = PieceTheme.typography.bodyMM, - cursorBrush = SolidColor(PieceTheme.colors.primaryDefault), - decorationBox = { innerTextField -> - Box { - if (value.isEmpty()) { - Text( - text = "작성해주신 내용을 AI가 요약하고 있어요", - style = PieceTheme.typography.bodyMM, - color = PieceTheme.colors.dark3, - modifier = Modifier.align(Alignment.CenterStart) + ), + textStyle = PieceTheme.typography.bodyMM, + cursorBrush = SolidColor(PieceTheme.colors.primaryDefault), + readOnly = isReadOnly, + decorationBox = { innerTextField -> + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + if (isLoading) { + Text( + text = "작성해주신 내용을 AI가 요약하고 있어요", + style = PieceTheme.typography.bodyMM, + color = PieceTheme.colors.dark3, + ) + } else { + innerTextField() + } + + val imageRes = if (isLoading) { + R.drawable.ic_textinput_3dots + } else { + if (isReadOnly) { + R.drawable.ic_textinput_pencil + } else { + R.drawable.ic_textinput_check + } + } + + Image( + painter = painterResource(imageRes), + contentDescription = null, + modifier = Modifier + .size(24.dp) + .clickable { + if (isLoading) return@clickable + + if (isReadOnly) { + isReadOnly = false + } else { + isReadOnly = true + onSaveClick(value) + } + }, ) } + }, + modifier = Modifier + .onFocusChanged { focusState -> isFocused = focusState.isFocused } + .height(52.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .background(PieceTheme.colors.primaryLight) + .padding(horizontal = 16.dp, vertical = 14.dp), + ) - innerTextField() + if (!isReadOnly) { + Row { + Spacer(modifier = Modifier.weight(1f)) - val imageRes = if (value.isEmpty()) R.drawable.ic_textinput_3dots - else if (isFocused) R.drawable.ic_textinput_check - else R.drawable.ic_textinput_pencil + Text( + text = buildAnnotatedString { + withStyle(style = SpanStyle(color = PieceTheme.colors.primaryDefault)) { + append(value.length.toString()) + } - Image( - painter = painterResource(imageRes), - contentDescription = null, - modifier = Modifier - .size(24.dp) - .align(Alignment.CenterEnd), + append("/20") + }, + style = PieceTheme.typography.bodySR, + color = PieceTheme.colors.dark3, + modifier = Modifier.padding(top = 4.dp) ) } - }, - modifier = modifier - .onFocusChanged { focusState -> isFocused = focusState.isFocused } - .height(52.dp) - .clip(RoundedCornerShape(8.dp)) - .background(PieceTheme.colors.primaryLight) - .padding(horizontal = 16.dp, vertical = 14.dp), - ) + } + } } @Composable @@ -408,6 +457,18 @@ private fun PreviewPieceTextInputAI() { PieceTextInputAI( value = "", onValueChange = {}, + onSaveClick = {}, + readOnly = true, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) + + PieceTextInputAI( + value = "Label", + onValueChange = { }, + onSaveClick = { }, + readOnly = true, modifier = Modifier .fillMaxWidth() .padding(16.dp), @@ -416,6 +477,8 @@ private fun PreviewPieceTextInputAI() { PieceTextInputAI( value = "Label", onValueChange = { }, + onSaveClick = { }, + readOnly = false, modifier = Modifier .fillMaxWidth() .padding(16.dp), diff --git a/core/designsystem/src/main/java/com/puzzle/designsystem/foundation/Typography.kt b/core/designsystem/src/main/java/com/puzzle/designsystem/foundation/Typography.kt index ff976358..a46d9ea6 100644 --- a/core/designsystem/src/main/java/com/puzzle/designsystem/foundation/Typography.kt +++ b/core/designsystem/src/main/java/com/puzzle/designsystem/foundation/Typography.kt @@ -88,6 +88,11 @@ data class PieceTypography( fontSize = 14.sp, lineHeight = 20.sp, ), + val bodySR: TextStyle = TextStyle( + fontFamily = PretendardRegular, + fontSize = 14.sp, + lineHeight = 20.sp, + ), val captionM: TextStyle = TextStyle( fontFamily = PretendardMedium, fontSize = 12.sp, diff --git a/core/designsystem/src/main/res/drawable/ic_info.xml b/core/designsystem/src/main/res/drawable/ic_info.xml new file mode 100644 index 00000000..5189d642 --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_info.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml index 93506708..091844ac 100644 --- a/core/designsystem/src/main/res/values/strings.xml +++ b/core/designsystem/src/main/res/values/strings.xml @@ -102,4 +102,12 @@ 꿈과 목표, 관심사와 취향, 연애에 관련된\n내 생각을 확인하고 수정할 수 있습니다. 가치관 Pick 퀴즈를 통해 나의 연애 스타일을 파악해보고\n선택한 답변을 수정할 수 있습니다. + + + 가치관 Talk + 가치관 Talk 수정 + 저장 + 수정 + AI 요약 + 도움말 \ No newline at end of file diff --git a/core/domain/src/main/java/com/puzzle/domain/model/matching/ValueTalk.kt b/core/domain/src/main/java/com/puzzle/domain/model/matching/ValueTalk.kt index 2bba75c5..1ee0e687 100644 --- a/core/domain/src/main/java/com/puzzle/domain/model/matching/ValueTalk.kt +++ b/core/domain/src/main/java/com/puzzle/domain/model/matching/ValueTalk.kt @@ -4,4 +4,6 @@ data class ValueTalk( val label: String = "", val title: String = "", val content: String = "", + val aiSummary: String = "", + val helpMessages: List = emptyList(), ) \ No newline at end of file diff --git a/core/navigation/src/main/java/com/puzzle/navigation/Route.kt b/core/navigation/src/main/java/com/puzzle/navigation/Route.kt index 57fb5b90..e2e8eb86 100644 --- a/core/navigation/src/main/java/com/puzzle/navigation/Route.kt +++ b/core/navigation/src/main/java/com/puzzle/navigation/Route.kt @@ -48,5 +48,8 @@ sealed class ProfileGraphDest : Route { data object RegisterProfileRoute : Route @Serializable - data object ProfileRoute : Route + data object MainProfileRoute : Route + + @Serializable + data object ValueTalkProfileRoute : Route } diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileScreen.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileScreen.kt index 4845854f..d85a14b4 100644 --- a/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileScreen.kt +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileScreen.kt @@ -36,6 +36,7 @@ import com.puzzle.common.ui.repeatOnStarted import com.puzzle.designsystem.R import com.puzzle.designsystem.component.PieceMainTopBar import com.puzzle.designsystem.foundation.PieceTheme +import com.puzzle.profile.graph.main.contract.MainProfileIntent import com.puzzle.profile.graph.main.contract.MainProfileSideEffect import com.puzzle.profile.graph.main.contract.MainProfileState @@ -60,7 +61,7 @@ internal fun MainProfileRoute( MainProfileScreen( state = state, onMyProfileClick = {}, - onValueTalkClick = {}, + onValueTalkClick = { viewModel.onIntent(MainProfileIntent.OnValueTalkClick) }, onValuePickClick = {}, ) } diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileViewModel.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileViewModel.kt index 53aea0a9..e261edbf 100644 --- a/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileViewModel.kt +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/main/MainProfileViewModel.kt @@ -5,7 +5,9 @@ import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.hilt.AssistedViewModelFactory import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory import com.puzzle.domain.model.error.ErrorHelper +import com.puzzle.navigation.NavigationEvent import com.puzzle.navigation.NavigationHelper +import com.puzzle.navigation.ProfileGraphDest import com.puzzle.profile.graph.main.contract.MainProfileIntent import com.puzzle.profile.graph.main.contract.MainProfileSideEffect import com.puzzle.profile.graph.main.contract.MainProfileState @@ -42,9 +44,16 @@ class MainProfileViewModel @AssistedInject constructor( private suspend fun processIntent(intent: MainProfileIntent) { when (intent) { is MainProfileIntent.Navigate -> _sideEffects.send(MainProfileSideEffect.Navigate(intent.navigationEvent)) + MainProfileIntent.OnValueTalkClick -> moveToValueTalkScreen() } } + private suspend fun moveToValueTalkScreen() { + _sideEffects.send( + MainProfileSideEffect.Navigate(NavigationEvent.NavigateTo(ProfileGraphDest.ValueTalkProfileRoute)) + ) + } + @AssistedFactory interface Factory : AssistedViewModelFactory { override fun create(state: MainProfileState): MainProfileViewModel diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/main/contract/MainProfileIntent.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/main/contract/MainProfileIntent.kt index ec881372..2b64b0b0 100644 --- a/feature/profile/src/main/java/com/puzzle/profile/graph/main/contract/MainProfileIntent.kt +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/main/contract/MainProfileIntent.kt @@ -4,4 +4,5 @@ import com.puzzle.navigation.NavigationEvent sealed class MainProfileIntent { data class Navigate(val navigationEvent: NavigationEvent) : MainProfileIntent() + data object OnValueTalkClick : MainProfileIntent() } \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/ValuePickScreen.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/ValuePickScreen.kt new file mode 100644 index 00000000..b02617c6 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/ValuePickScreen.kt @@ -0,0 +1,43 @@ +package com.puzzle.profile.graph.valuepick + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.LocalLifecycleOwner +import com.airbnb.mvrx.compose.collectAsState +import com.airbnb.mvrx.compose.mavericksViewModel +import com.puzzle.common.ui.repeatOnStarted +import com.puzzle.profile.graph.valuepick.contract.ValuePickSideEffect +import com.puzzle.profile.graph.valuepick.contract.ValuePickState + +@Composable +internal fun ValuePickRoute( + viewModel: ValuePickViewModel = mavericksViewModel() +) { + val state by viewModel.collectAsState() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(viewModel) { + lifecycleOwner.repeatOnStarted { + viewModel.sideEffects.collect { sideEffect -> + when (sideEffect) { + is ValuePickSideEffect.Navigate -> + viewModel.navigationHelper.navigate(sideEffect.navigationEvent) + } + } + } + } + + ValuePickScreen( + state = state, + ) +} + +@Composable +private fun ValuePickScreen( + state: ValuePickState, + modifier: Modifier = Modifier, +) { + +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/ValuePickViewModel.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/ValuePickViewModel.kt new file mode 100644 index 00000000..65c12fc2 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/ValuePickViewModel.kt @@ -0,0 +1,56 @@ +package com.puzzle.profile.graph.valuepick + +import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.hilt.AssistedViewModelFactory +import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory +import com.puzzle.domain.model.error.ErrorHelper +import com.puzzle.navigation.NavigationHelper +import com.puzzle.profile.graph.valuepick.contract.ValuePickIntent +import com.puzzle.profile.graph.valuepick.contract.ValuePickSideEffect +import com.puzzle.profile.graph.valuepick.contract.ValuePickState +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.Channel.Factory.BUFFERED +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch + +class ValuePickViewModel @AssistedInject constructor( + @Assisted initialState: ValuePickState, + internal val navigationHelper: NavigationHelper, + private val errorHelper: ErrorHelper, +) : MavericksViewModel(initialState) { + + private val intents = Channel(BUFFERED) + private val _sideEffects = Channel(BUFFERED) + val sideEffects = _sideEffects.receiveAsFlow() + + init { + intents.receiveAsFlow() + .onEach(::processIntent) + .launchIn(viewModelScope) + } + + internal fun onIntent(intent: ValuePickIntent) = viewModelScope.launch { + intents.send(intent) + } + + private suspend fun processIntent(intent: ValuePickIntent) { + when (intent) { + is ValuePickIntent.Navigate -> _sideEffects.send(ValuePickSideEffect.Navigate(intent.navigationEvent)) + } + } + + @AssistedFactory + interface Factory : AssistedViewModelFactory { + override fun create(state: ValuePickState): ValuePickViewModel + } + + companion object : + MavericksViewModelFactory by hiltMavericksViewModelFactory() + +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickIntent.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickIntent.kt new file mode 100644 index 00000000..b9f7fae1 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickIntent.kt @@ -0,0 +1,7 @@ +package com.puzzle.profile.graph.valuepick.contract + +import com.puzzle.navigation.NavigationEvent + +sealed class ValuePickIntent { + data class Navigate(val navigationEvent: NavigationEvent) : ValuePickIntent() +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickSideEffect.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickSideEffect.kt new file mode 100644 index 00000000..8e34c238 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickSideEffect.kt @@ -0,0 +1,7 @@ +package com.puzzle.profile.graph.valuepick.contract + +import com.puzzle.navigation.NavigationEvent + +sealed class ValuePickSideEffect { + data class Navigate(val navigationEvent: NavigationEvent) : ValuePickSideEffect() +} diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickState.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickState.kt new file mode 100644 index 00000000..65ce9aa3 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuepick/contract/ValuePickState.kt @@ -0,0 +1,7 @@ +package com.puzzle.profile.graph.valuepick.contract + +import com.airbnb.mvrx.MavericksState + +data class ValuePickState( + val a: String = "" +) : MavericksState \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/ValueTalkScreen.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/ValueTalkScreen.kt new file mode 100644 index 00000000..3a64ba34 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/ValueTalkScreen.kt @@ -0,0 +1,448 @@ +package com.puzzle.profile.graph.valuetalk + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +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.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.draw.clip +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalDensity +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.lifecycle.compose.LocalLifecycleOwner +import com.airbnb.mvrx.compose.collectAsState +import com.airbnb.mvrx.compose.mavericksViewModel +import com.puzzle.common.ui.repeatOnStarted +import com.puzzle.designsystem.R +import com.puzzle.designsystem.component.PieceSubTopBar +import com.puzzle.designsystem.component.PieceTextInputAI +import com.puzzle.designsystem.component.PieceTextInputLong +import com.puzzle.designsystem.foundation.PieceTheme +import com.puzzle.domain.model.matching.ValueTalk +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkIntent +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkSideEffect +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkState +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkState.Companion.PAGE_TRANSITION_DURATION +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkState.Companion.TEXT_DISPLAY_DURATION +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkState.ScreenState +import kotlinx.coroutines.delay + +@Composable +internal fun ValueTalkRoute( + viewModel: ValueTalkViewModel = mavericksViewModel(), +) { + val state by viewModel.collectAsState() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(viewModel) { + lifecycleOwner.repeatOnStarted { + viewModel.sideEffects.collect { sideEffect -> + when (sideEffect) { + is ValueTalkSideEffect.Navigate -> + viewModel.navigationHelper.navigate(sideEffect.navigationEvent) + } + } + } + } + + ValueTalkScreen( + state = state, + onBackClick = { viewModel.onIntent(ValueTalkIntent.OnBackClick) }, + onSaveClick = {}, + onAiSummarySaveClick = {}, + ) +} + +@Composable +private fun ValueTalkScreen( + state: ValueTalkState, + onSaveClick: (List) -> Unit, + onBackClick: () -> Unit, + onAiSummarySaveClick: (ValueTalk) -> Unit, + modifier: Modifier = Modifier, +) { + var screenState: ScreenState by remember { mutableStateOf(ScreenState.SAVED) } + var valueTalks: List by remember { mutableStateOf(state.valueTalks) } + var isContentEdited: Boolean by remember { mutableStateOf(false) } + var editedValueTalkLabels: List by remember { mutableStateOf(emptyList()) } + + BackHandler { + if (screenState == ScreenState.EDITING) { + // TODO : 데이터 초기화 + screenState = ScreenState.SAVED + } else { + onBackClick() + } + } + + Column( + modifier = modifier + .fillMaxSize() + .background(PieceTheme.colors.white), + ) { + PieceSubTopBar( + title = when (screenState) { + ScreenState.SAVED -> stringResource(R.string.value_talk_profile_topbar_title) + ScreenState.EDITING -> stringResource(R.string.value_talk_edit_profile_topbar_title) + }, + onNavigationClick = onBackClick, + rightComponent = { + when (screenState) { + ScreenState.SAVED -> + Text( + text = stringResource(R.string.value_talk_profile_topbar_edit), + style = PieceTheme.typography.bodyMM, + color = PieceTheme.colors.primaryDefault, + modifier = Modifier.clickable { + screenState = ScreenState.EDITING + }, + ) + + ScreenState.EDITING -> + Text( + text = stringResource(R.string.value_talk_profile_topbar_save), + style = PieceTheme.typography.bodyMM, + color = if (isContentEdited) { + PieceTheme.colors.primaryDefault + } else { + PieceTheme.colors.dark3 + }, + modifier = Modifier.clickable { + if (isContentEdited) { + valueTalks = valueTalks.map { valueTalk -> + if (editedValueTalkLabels.contains(valueTalk.label)) { + valueTalk.copy(aiSummary = "") + } else { + valueTalk + } + } + onSaveClick(valueTalks) + isContentEdited = false + editedValueTalkLabels = emptyList() + } + screenState = ScreenState.SAVED + }, + ) + } + }, + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = 20.dp, + vertical = 14.dp, + ), + ) + + ValueTalkCards( + valueTalks = valueTalks, + screenState = screenState, + onContentChange = { editedValueTalk -> + valueTalks = valueTalks.map { valueTalk -> + if (valueTalk.label == editedValueTalk.label) { + if (!editedValueTalkLabels.contains(valueTalk.label)) { + editedValueTalkLabels = editedValueTalkLabels + valueTalk.label + } + valueTalk.copy(content = editedValueTalk.content) + } else { + valueTalk + } + } + + isContentEdited = valueTalks != state.valueTalks + }, + onAiSummarySaveClick = onAiSummarySaveClick, + ) + } +} + +@Composable +private fun ValueTalkCards( + valueTalks: List, + screenState: ScreenState, + onContentChange: (ValueTalk) -> Unit, + onAiSummarySaveClick: (ValueTalk) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn(modifier = modifier.fillMaxSize()) { + itemsIndexed(valueTalks) { idx, item -> + ValueTalkCard( + item = item, + screenState = screenState, + onContentChange = onContentChange, + onAiSummarySaveClick = onAiSummarySaveClick, + modifier = Modifier.padding( + horizontal = 20.dp, + vertical = 24.dp, + ) + ) + + if (idx < valueTalks.size - 1) { + HorizontalDivider( + thickness = 12.dp, + color = PieceTheme.colors.light3, + modifier = Modifier.fillMaxWidth(), + ) + } + } + } +} + +@Composable +private fun ValueTalkCard( + item: ValueTalk, + screenState: ScreenState, + onContentChange: (ValueTalk) -> Unit, + onAiSummarySaveClick: (ValueTalk) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier) { + Text( + text = item.label, + style = PieceTheme.typography.bodySSB, + color = PieceTheme.colors.primaryDefault, + modifier = Modifier.padding(bottom = 6.dp), + ) + + Text( + text = item.title, + style = PieceTheme.typography.headingMSB, + color = PieceTheme.colors.dark1, + modifier = Modifier.padding(bottom = 20.dp), + ) + + PieceTextInputLong( + value = item.content, + onValueChange = { + onContentChange(item.copy(content = it)) + }, + limit = 300, + readOnly = when (screenState) { + ScreenState.SAVED -> true + ScreenState.EDITING -> false + }, + ) + + when (screenState) { + ScreenState.SAVED -> + AiSummaryContent( + item = item, + onAiSummarySaveClick = onAiSummarySaveClick, + ) + + ScreenState.EDITING -> + HelpMessageRow( + helpMessages = item.helpMessages, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) + } + } +} + +@Composable +private fun AiSummaryContent( + item: ValueTalk, + onAiSummarySaveClick: (ValueTalk) -> Unit +) { + var editableAiSummary: String by remember { mutableStateOf(item.aiSummary) } + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(top = 20.dp), + ) { + Text( + text = stringResource(R.string.value_talk_profile_aisummary_title), + style = PieceTheme.typography.bodySSB, + color = PieceTheme.colors.primaryDefault, + ) + + Image( + painter = painterResource(R.drawable.ic_info), + contentDescription = "정보", + colorFilter = ColorFilter.tint(PieceTheme.colors.dark3), + modifier = Modifier + .size(20.dp) + .padding(start = 4.dp), + ) + } + + PieceTextInputAI( + value = editableAiSummary, + onValueChange = { + editableAiSummary = it + }, + onSaveClick = { + onAiSummarySaveClick( + item.copy(aiSummary = it) + ) + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp), + ) +} + +@Composable +fun HelpMessageRow( + helpMessages: List, + modifier: Modifier = Modifier, +) { + // 각 Row의 높이는 고정되어 있으므로 고정값 사용 + val rowHeightDp = 26.dp + val density = LocalDensity.current + val rowHeightPx = with(density) { rowHeightDp.toPx() } + // 총 높이 = rowHeightPx * 메시지 개수 + val totalHeightPx = rowHeightPx * helpMessages.size + + // ScrollState를 이용한 자동 스크롤 + val scrollState = rememberScrollState() + var isHelpMessageVisible by remember { mutableStateOf(true) } + + LaunchedEffect(Unit) { + while (true) { + delay(TEXT_DISPLAY_DURATION) + val target = scrollState.value + rowHeightPx.toInt() + + if (target >= totalHeightPx.toInt() - rowHeightPx.toInt()) { + isHelpMessageVisible = false + delay(1000) + scrollState.scrollTo(0) + isHelpMessageVisible = true + } else { + scrollState.animateScrollTo( + value = target, + animationSpec = tween( + durationMillis = PAGE_TRANSITION_DURATION, + easing = LinearEasing + ), + ) + } + } + } + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier, + ) { + Text( + text = stringResource(id = R.string.value_talk_profile_helpmessage_title), + style = PieceTheme.typography.bodySR, + color = PieceTheme.colors.subDefault, + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .background(PieceTheme.colors.subLight) + .padding(vertical = 4.dp, horizontal = 6.dp), + ) + + AnimatedVisibility( + visible = isHelpMessageVisible, + enter = fadeIn(tween(durationMillis = 500, easing = LinearEasing)), + exit = fadeOut(tween(durationMillis = 500, easing = LinearEasing)), + ) { + Column( + modifier = Modifier + .padding(start = 8.dp) + .height(rowHeightDp) + .verticalScroll(state = scrollState, enabled = false), + ) { + helpMessages.forEach { message -> + Text( + text = message, + style = PieceTheme.typography.bodySR, + color = PieceTheme.colors.dark2, + modifier = Modifier + .height(rowHeightDp) + .padding(top = 4.dp), + ) + } + } + } + } +} + +@Preview +@Composable +private fun ValueTalkPreview() { + PieceTheme { + ValueTalkScreen( + state = ValueTalkState( + valueTalks = listOf( + ValueTalk( + label = "연애관", + title = "어떠한 사람과 어떠한 연애를 하고 싶은지 들려주세요", + content = "저는 연애에서 서로의 존중과 신뢰가 가장 중요하다고 생각합니다. 진정한 소통을 통해 서로의 감정을 이해하고, 함께 성장할 수 있는 관계를 원합니다. 일상 속 작은 것에도 감사하며, 서로의 꿈과 목표를 지지하고 응원하는 파트너가 되고 싶습니다. 또한, 유머와 즐거움을 잃지 않으며, 함께하는 순간들을 소중히 여기고 싶습니다. 사랑은 서로를 더 나은 사람으로 만들어주는 힘이 있다고 믿습니다. 서로에게 긍정적인 영향을 주며 행복한 시간을 함께하고 싶습니다!", + aiSummary = "신뢰하며, 함께 성장하고 싶어요.", + helpMessages = listOf( + "함께 하고 싶은 데이트 스타일은 무엇인가요?", + "이상적인 관계의 모습을 적어 보세요", + "연인과 함께 만들고 싶은 추억이 있나요?", + "연애에서 가장 중요시하는 가치는 무엇인가요?", + "연인 관계를 통해 어떤 가치를 얻고 싶나요?", + ), + ), + ValueTalk( + label = "관심사와 취향", + title = "무엇을 할 때 가장 행복한가요?\n요즘 어떠한 것에 관심을 두고 있나요?", + content = "저는 다양한 취미와 관심사를 가진 사람입니다. 음악을 사랑하여 콘서트에 자주 가고, 특히 인디 음악과 재즈에 매력을 느낍니다. 요리도 좋아해 새로운 레시피에 도전하는 것을 즐깁니다. 여행을 통해 새로운 맛과 문화를 경험하는 것도 큰 기쁨입니다. 또, 자연을 사랑해서 주말마다 하이킹이나 캠핑을 자주 떠납니다. 영화와 책도 좋아해, 좋은 이야기와 감동을 나누는 시간을 소중히 여깁니다. 서로의 취향을 공유하며 즐거운 시간을 보낼 수 있기를 기대합니다!", + aiSummary = "음악, 요리, 하이킹을 좋아해요.", + helpMessages = listOf( + "당신의 삶을 즐겁게 만드는 것들은 무엇인가요?", + "일상에서 소소한 행복을 느끼는 순간을 적어보세요", + "최근에 몰입했던 취미가 있다면 소개해 주세요", + "최근 마음이 따뜻해졌던 순간을 들려주세요.", + "요즘 마음을 사로잡은 콘텐츠를 공유해 보세요", + ), + ), + ValueTalk( + label = "꿈과 목표", + title = "어떤 일을 하며 무엇을 목표로 살아가나요?\n인생에서 이루고 싶은 꿈은 무엇인가요?", + content = "안녕하세요! 저는 삶의 매 순간을 소중히 여기며, 꿈과 목표를 이루기 위해 노력하는 사람입니다. 제 가장 큰 꿈은 여행을 통해 다양한 문화와 사람들을 경험하고, 그 과정에서 얻은 지혜를 나누는 것입니다. 또한, LGBTQ+ 커뮤니티를 위한 긍정적인 변화를 이끌어내고 싶습니다. 내가 이루고자 하는 목표는 나 자신을 발전시키고, 사랑하는 사람들과 함께 행복한 순간들을 만드는 것입니다. 서로의 꿈을 지지하며 함께 성장할 수 있는 관계를 기대합니다!", + aiSummary = "여행하며 LGBTQ+ 변화를 원해요.", + helpMessages = listOf( + "당신의 직업은 무엇인가요?", + "앞으로 하고 싶은 일에 대해 이야기해주세요", + "어떤 일을 할 때 가장 큰 성취감을 느끼나요?", + "당신의 버킷리스트를 알려주세요", + "당신이 꿈꾸는 삶은 어떤 모습인가요?", + ), + ) + ) + ), + onBackClick = {}, + onSaveClick = {}, + onAiSummarySaveClick = {}, + ) + } +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/ValueTalkViewModel.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/ValueTalkViewModel.kt new file mode 100644 index 00000000..ef45dd3c --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/ValueTalkViewModel.kt @@ -0,0 +1,59 @@ +package com.puzzle.profile.graph.valuetalk + +import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.hilt.AssistedViewModelFactory +import com.airbnb.mvrx.hilt.hiltMavericksViewModelFactory +import com.puzzle.domain.model.error.ErrorHelper +import com.puzzle.navigation.NavigationEvent +import com.puzzle.navigation.NavigationHelper +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkIntent +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkSideEffect +import com.puzzle.profile.graph.valuetalk.contract.ValueTalkState +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.Channel.Factory.BUFFERED +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch + +class ValueTalkViewModel @AssistedInject constructor( + @Assisted initialState: ValueTalkState, + internal val navigationHelper: NavigationHelper, + private val errorHelper: ErrorHelper, +) : MavericksViewModel(initialState) { + + private val intents = Channel(BUFFERED) + private val _sideEffects = Channel(BUFFERED) + val sideEffects = _sideEffects.receiveAsFlow() + + init { + intents.receiveAsFlow() + .onEach(::processIntent) + .launchIn(viewModelScope) + } + + internal fun onIntent(intent: ValueTalkIntent) = viewModelScope.launch { + intents.send(intent) + } + + private suspend fun processIntent(intent: ValueTalkIntent) { + when (intent) { + ValueTalkIntent.OnBackClick -> _sideEffects.send( + ValueTalkSideEffect.Navigate(NavigationEvent.NavigateUp) + ) + } + } + + @AssistedFactory + interface Factory : AssistedViewModelFactory { + override fun create(state: ValueTalkState): ValueTalkViewModel + } + + companion object : + MavericksViewModelFactory by hiltMavericksViewModelFactory() + +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkIntent.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkIntent.kt new file mode 100644 index 00000000..4130f71b --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkIntent.kt @@ -0,0 +1,5 @@ +package com.puzzle.profile.graph.valuetalk.contract + +sealed class ValueTalkIntent { + data object OnBackClick : ValueTalkIntent() +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkSideEffect.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkSideEffect.kt new file mode 100644 index 00000000..7ed5222c --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkSideEffect.kt @@ -0,0 +1,7 @@ +package com.puzzle.profile.graph.valuetalk.contract + +import com.puzzle.navigation.NavigationEvent + +sealed class ValueTalkSideEffect { + data class Navigate(val navigationEvent: NavigationEvent) : ValueTalkSideEffect() +} diff --git a/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkState.kt b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkState.kt new file mode 100644 index 00000000..2cc8e8e1 --- /dev/null +++ b/feature/profile/src/main/java/com/puzzle/profile/graph/valuetalk/contract/ValueTalkState.kt @@ -0,0 +1,59 @@ +package com.puzzle.profile.graph.valuetalk.contract + +import com.airbnb.mvrx.MavericksState +import com.puzzle.domain.model.matching.ValueTalk + +data class ValueTalkState( + val valueTalks: List = listOf( + ValueTalk( + label = "연애관", + title = "어떠한 사람과 어떠한 연애를 하고 싶은지 들려주세요", + content = "저는 연애에서 서로의 존중과 신뢰가 가장 중요하다고 생각합니다. 진정한 소통을 통해 서로의 감정을 이해하고, 함께 성장할 수 있는 관계를 원합니다. 일상 속 작은 것에도 감사하며, 서로의 꿈과 목표를 지지하고 응원하는 파트너가 되고 싶습니다. 또한, 유머와 즐거움을 잃지 않으며, 함께하는 순간들을 소중히 여기고 싶습니다. 사랑은 서로를 더 나은 사람으로 만들어주는 힘이 있다고 믿습니다. 서로에게 긍정적인 영향을 주며 행복한 시간을 함께하고 싶습니다!", + aiSummary = "신뢰하며, 함께 성장하고 싶어요.", + helpMessages = listOf( + "함께 하고 싶은 데이트 스타일은 무엇인가요?", + "이상적인 관계의 모습을 적어 보세요", + "연인과 함께 만들고 싶은 추억이 있나요?", + "연애에서 가장 중요시하는 가치는 무엇인가요?", + "연인 관계를 통해 어떤 가치를 얻고 싶나요?", + ), + ), + ValueTalk( + label = "관심사와 취향", + title = "무엇을 할 때 가장 행복한가요?\n요즘 어떠한 것에 관심을 두고 있나요?", + content = "저는 다양한 취미와 관심사를 가진 사람입니다. 음악을 사랑하여 콘서트에 자주 가고, 특히 인디 음악과 재즈에 매력을 느낍니다. 요리도 좋아해 새로운 레시피에 도전하는 것을 즐깁니다. 여행을 통해 새로운 맛과 문화를 경험하는 것도 큰 기쁨입니다. 또, 자연을 사랑해서 주말마다 하이킹이나 캠핑을 자주 떠납니다. 영화와 책도 좋아해, 좋은 이야기와 감동을 나누는 시간을 소중히 여깁니다. 서로의 취향을 공유하며 즐거운 시간을 보낼 수 있기를 기대합니다!", + aiSummary = "음악, 요리, 하이킹을 좋아해요.", + helpMessages = listOf( + "당신의 삶을 즐겁게 만드는 것들은 무엇인가요?", + "일상에서 소소한 행복을 느끼는 순간을 적어보세요", + "최근에 몰입했던 취미가 있다면 소개해 주세요", + "최근 마음이 따뜻해졌던 순간을 들려주세요.", + "요즘 마음을 사로잡은 콘텐츠를 공유해 보세요", + ), + ), + ValueTalk( + label = "꿈과 목표", + title = "어떤 일을 하며 무엇을 목표로 살아가나요?\n인생에서 이루고 싶은 꿈은 무엇인가요?", + content = "안녕하세요! 저는 삶의 매 순간을 소중히 여기며, 꿈과 목표를 이루기 위해 노력하는 사람입니다. 제 가장 큰 꿈은 여행을 통해 다양한 문화와 사람들을 경험하고, 그 과정에서 얻은 지혜를 나누는 것입니다. 또한, LGBTQ+ 커뮤니티를 위한 긍정적인 변화를 이끌어내고 싶습니다. 내가 이루고자 하는 목표는 나 자신을 발전시키고, 사랑하는 사람들과 함께 행복한 순간들을 만드는 것입니다. 서로의 꿈을 지지하며 함께 성장할 수 있는 관계를 기대합니다!", + aiSummary = "여행하며 LGBTQ+ 변화를 원해요.", + helpMessages = listOf( + "당신의 직업은 무엇인가요?", + "앞으로 하고 싶은 일에 대해 이야기해주세요", + "어떤 일을 할 때 가장 큰 성취감을 느끼나요?", + "당신의 버킷리스트를 알려주세요", + "당신이 꿈꾸는 삶은 어떤 모습인가요?", + ), + ) + ) +) : MavericksState { + + companion object { + const val TEXT_DISPLAY_DURATION = 3000L + const val PAGE_TRANSITION_DURATION = 1000 + } + + enum class ScreenState { + EDITING, + SAVED + } +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/puzzle/profile/navigation/ProfileNavigation.kt b/feature/profile/src/main/java/com/puzzle/profile/navigation/ProfileNavigation.kt index 74122b73..0683d1c4 100644 --- a/feature/profile/src/main/java/com/puzzle/profile/navigation/ProfileNavigation.kt +++ b/feature/profile/src/main/java/com/puzzle/profile/navigation/ProfileNavigation.kt @@ -7,13 +7,18 @@ import com.puzzle.navigation.ProfileGraph import com.puzzle.navigation.ProfileGraphDest import com.puzzle.profile.graph.main.MainProfileRoute import com.puzzle.profile.graph.register.RegisterProfileRoute +import com.puzzle.profile.graph.valuetalk.ValueTalkRoute fun NavGraphBuilder.profileNavGraph() { - navigation(startDestination = ProfileGraphDest.ProfileRoute) { - composable { + navigation(startDestination = ProfileGraphDest.MainProfileRoute) { + composable { MainProfileRoute() } + composable { + ValueTalkRoute() + } + composable { RegisterProfileRoute() } diff --git a/presentation/src/main/java/com/puzzle/presentation/di/ViewModelsModule.kt b/presentation/src/main/java/com/puzzle/presentation/di/ViewModelsModule.kt index 1d0d7cf1..a6b13717 100644 --- a/presentation/src/main/java/com/puzzle/presentation/di/ViewModelsModule.kt +++ b/presentation/src/main/java/com/puzzle/presentation/di/ViewModelsModule.kt @@ -10,6 +10,8 @@ import com.puzzle.matching.graph.detail.MatchingDetailViewModel import com.puzzle.matching.graph.main.MatchingViewModel import com.puzzle.profile.graph.main.MainProfileViewModel import com.puzzle.profile.graph.register.RegisterProfileViewModel +import com.puzzle.profile.graph.valuepick.ValuePickViewModel +import com.puzzle.profile.graph.valuetalk.ValueTalkViewModel import com.puzzle.setting.graph.main.SettingViewModel import com.puzzle.setting.graph.withdraw.WithdrawViewModel import dagger.Binds @@ -65,4 +67,14 @@ interface ViewModelsModule { @IntoMap @ViewModelKey(WithdrawViewModel::class) fun settingWithdrawViewModelFactory(factory: WithdrawViewModel.Factory): AssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @ViewModelKey(ValueTalkViewModel::class) + fun valueTalkViewModelFactory(factory: ValueTalkViewModel.Factory): AssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @ViewModelKey(ValuePickViewModel::class) + fun valuePickViewModelFactory(factory: ValuePickViewModel.Factory): AssistedViewModelFactory<*, *> } diff --git a/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt b/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt index 9cc85c28..97669ce0 100644 --- a/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt +++ b/presentation/src/main/java/com/puzzle/presentation/navigation/TopLevelDestinvation.kt @@ -17,7 +17,7 @@ enum class TopLevelDestination( iconDrawableId = R.drawable.ic_profile, contentDescription = "프로필", title = "프로필", - route = ProfileGraphDest.ProfileRoute::class, + route = ProfileGraphDest.MainProfileRoute::class, ), MATCHING( iconDrawableId = R.drawable.ic_profile, diff --git a/presentation/src/main/java/com/puzzle/presentation/ui/App.kt b/presentation/src/main/java/com/puzzle/presentation/ui/App.kt index de3e13b0..327d17bf 100644 --- a/presentation/src/main/java/com/puzzle/presentation/ui/App.kt +++ b/presentation/src/main/java/com/puzzle/presentation/ui/App.kt @@ -41,7 +41,7 @@ import com.puzzle.navigation.AuthGraph import com.puzzle.navigation.MatchingGraph import com.puzzle.navigation.MatchingGraphDest.MatchingDetailRoute import com.puzzle.navigation.ProfileGraphDest -import com.puzzle.navigation.ProfileGraphDest.ProfileRoute +import com.puzzle.navigation.ProfileGraphDest.MainProfileRoute import com.puzzle.navigation.Route import com.puzzle.navigation.SettingGraph import com.puzzle.navigation.SettingGraphDest @@ -142,7 +142,10 @@ private fun AppBottomBar( onClick = { when (topLevelRoute) { TopLevelDestination.MATCHING -> navigateToTopLevelDestination(MatchingGraph) - TopLevelDestination.PROFILE -> navigateToTopLevelDestination(ProfileRoute) + TopLevelDestination.PROFILE -> navigateToTopLevelDestination( + MainProfileRoute + ) + TopLevelDestination.SETTING -> navigateToTopLevelDestination(SettingGraph) } }, @@ -155,6 +158,7 @@ private val HIDDEN_BOTTOM_NAV_ROUTES = setOf( AuthGraph::class.qualifiedName, MatchingDetailRoute::class.qualifiedName, ProfileGraphDest.RegisterProfileRoute::class.qualifiedName, + ProfileGraphDest.ValueTalkProfileRoute::class.qualifiedName, SettingGraphDest.WithdrawRoute::class.qualifiedName, )