From 7b51c0196fe18ace380723b95a50ba2896daac87 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Thu, 24 Sep 2026 00:48:00 +0900 Subject: [PATCH 01/49] =?UTF-8?q?[feat/#109]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B0=94=ED=85=80=20=EB=84=A4=EB=B9=84?= =?UTF-8?q?=EB=B0=94=20=EC=88=A8=EA=B9=80=20=EC=B2=98=EB=A6=AC=20=EB=B0=8F?= =?UTF-8?q?=20=EC=83=81=EB=8B=A8=20=EB=B0=94=20=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/main/MainAppState.kt | 2 +- .../app/presentation/mypage/MyPageScreen.kt | 12 +++- .../mypage/component/MyPageTopBar.kt | 71 +++++++++++++++++++ app/src/main/res/drawable/ic_setting_32.xml | 20 ++++++ 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt create mode 100644 app/src/main/res/drawable/ic_setting_32.xml diff --git a/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt b/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt index 7df24ed3f..2abd7d413 100644 --- a/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt +++ b/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt @@ -52,7 +52,7 @@ class MainAppState( val isBottomBarVisible: StateFlow = currentDestination .map { destination -> MainTab.entries - .filterNot { it == MainTab.REGISTER } + .filterNot { it == MainTab.REGISTER || it == MainTab.MYPAGE } .any { tab -> destination?.hasRoute(tab.route::class) == true } } .stateIn( diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt index 26026b6c6..c0708f515 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Column 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.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -14,6 +13,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.presentation.mypage.component.MyPageProfileCard +import com.haphap.app.presentation.mypage.component.MyPageTopBar import com.haphap.app.presentation.mypage.component.MyPageTopSection @Composable @@ -36,9 +36,15 @@ private fun MyPageScreen( ) { Column( modifier = modifier - .fillMaxSize() - .padding(top = 43.dp), + .fillMaxSize(), ) { + MyPageTopBar( + onBackClick = {}, + onSettingClick = {}, + ) + + Spacer(modifier = Modifier.height(20.dp)) + MyPageTopSection( nameText = uiState.nameText, ) diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt b/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt new file mode 100644 index 000000000..ea3d5d7f3 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt @@ -0,0 +1,71 @@ +package com.haphap.app.presentation.mypage.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.R +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.extensions.noRippleClickable + +@Composable +fun MyPageTopBar( + onBackClick: () -> Unit, + onSettingClick: () -> Unit, + modifier: Modifier = Modifier, + isText: Boolean = true, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 5.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_back_30), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.noRippleClickable(onClick = onBackClick), + ) + + Spacer(modifier = Modifier.width(10.dp)) + + if (isText) Text( + text = "마이페이지", + style = HapHapTheme.typography.subtitle.b20, + color = HapHapTheme.colors.gray800, + ) + + Spacer(modifier = Modifier.weight(1f)) + + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_setting_32), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.noRippleClickable(onClick = onSettingClick), + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun RegisterTopBarPreview() { + HapHapTheme { + MyPageTopBar( + onBackClick = {}, + onSettingClick = {}, + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_setting_32.xml b/app/src/main/res/drawable/ic_setting_32.xml new file mode 100644 index 000000000..5cb31c8c6 --- /dev/null +++ b/app/src/main/res/drawable/ic_setting_32.xml @@ -0,0 +1,20 @@ + + + + From 98e6774467759b97675954e4b283789c3dce4cc0 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Thu, 24 Sep 2026 01:10:35 +0900 Subject: [PATCH 02/49] =?UTF-8?q?[feat/#109]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=92=A4=EB=A1=9C=20=EA=B0=80=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/main/MainNavHost.kt | 1 + .../app/presentation/mypage/MyPageContract.kt | 5 ++++- .../app/presentation/mypage/MyPageScreen.kt | 22 ++++++++++++++++++- .../presentation/mypage/MyPageViewModel.kt | 12 ++++++++++ .../mypage/navigation/MyPageNavigation.kt | 8 +++++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt index e575b4d82..39c3be8e6 100644 --- a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt +++ b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt @@ -74,6 +74,7 @@ fun MainNavHost( myPageGraph( innerPadding = innerPadding, + navController = navController, ) searchGraph( diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt index 6109b4cc2..d6ab66254 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt @@ -1,7 +1,6 @@ package com.haphap.app.presentation.mypage import androidx.compose.runtime.Immutable -import com.haphap.app.data.model.mypage.MyPageModel sealed interface MyPageContract { @Immutable @@ -12,6 +11,10 @@ sealed interface MyPageContract { val emailText: String = "", val myPageUiState: MyPageUiState = MyPageUiState.Idle, ) + + sealed class SideEffect { + data object NavigateToHome : SideEffect() + } } sealed interface MyPageUiState { diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt index c0708f515..669d583bb 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt @@ -5,26 +5,44 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.repeatOnLifecycle import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.presentation.mypage.MyPageContract.SideEffect.NavigateToHome import com.haphap.app.presentation.mypage.component.MyPageProfileCard import com.haphap.app.presentation.mypage.component.MyPageTopBar import com.haphap.app.presentation.mypage.component.MyPageTopSection @Composable fun MyPageRoute( + navigateToHome: () -> Unit, modifier: Modifier = Modifier, viewModel: MyPageViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.sideEffect.collect { sideEffect -> + when (sideEffect) { + is NavigateToHome -> navigateToHome() + } + } + } + } MyPageScreen( uiState = uiState, + onBackClick = viewModel::onBackClick, modifier = modifier, ) } @@ -32,6 +50,7 @@ fun MyPageRoute( @Composable private fun MyPageScreen( uiState: MyPageContract.State, + onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -39,7 +58,7 @@ private fun MyPageScreen( .fillMaxSize(), ) { MyPageTopBar( - onBackClick = {}, + onBackClick = onBackClick, onSettingClick = {}, ) @@ -68,6 +87,7 @@ private fun MyPageScreenPreview() { nickNameText = "익명의죠르디", emailText = "yeonsoo1234@naver.com", ), + onBackClick = {}, ) } } diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt index 5376c235b..b4222c140 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt @@ -8,7 +8,10 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +import com.haphap.app.presentation.mypage.MyPageContract.SideEffect.NavigateToHome import timber.log.Timber import javax.inject.Inject @@ -19,6 +22,9 @@ class MyPageViewModel @Inject constructor( private val _uiState = MutableStateFlow(MyPageContract.State()) val uiState: StateFlow = _uiState.asStateFlow() + private val _sideEffect = Channel() + val sideEffect = _sideEffect.receiveAsFlow() + init { getMyPage() } @@ -51,4 +57,10 @@ class MyPageViewModel @Inject constructor( _uiState.update { it.copy(myPageUiState = MyPageUiState.Loading) } } + + fun onBackClick() { + viewModelScope.launch { + _sideEffect.send(NavigateToHome) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt b/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt index 565715bee..5f57cae83 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt @@ -7,7 +7,9 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import com.haphap.app.core.extensions.clearBackStackNavOptions import com.haphap.app.core.navigation.MainTabRoute +import com.haphap.app.presentation.home.navigation.navigateToHome import com.haphap.app.presentation.mypage.MyPageRoute import kotlinx.serialization.Serializable @@ -17,9 +19,15 @@ fun NavController.navigateToMyPage( fun NavGraphBuilder.myPageGraph( innerPadding: PaddingValues, + navController: NavController, ) { composable { MyPageRoute( + navigateToHome = { + navController.navigateToHome( + navOptions = navController.clearBackStackNavOptions() + ) + }, modifier = Modifier.padding(innerPadding), ) } From f8db424cc66a8c3269cb28897c8b85e92bcb3129 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Thu, 24 Sep 2026 01:37:17 +0900 Subject: [PATCH 03/49] =?UTF-8?q?[feat/#109]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20-=20=EC=84=A4=EC=A0=95=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/main/MainNavHost.kt | 6 +++ .../app/presentation/mypage/MyPageContract.kt | 1 + .../app/presentation/mypage/MyPageScreen.kt | 8 ++- .../presentation/mypage/MyPageViewModel.kt | 7 +++ .../mypage/navigation/MyPageNavigation.kt | 4 ++ .../presentation/setting/SettingContract.kt | 20 +++++++ .../app/presentation/setting/SettingScreen.kt | 54 +++++++++++++++++++ .../presentation/setting/SettingViewModel.kt | 28 ++++++++++ .../setting/navigation/SettingNavigation.kt | 31 +++++++++++ 9 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt diff --git a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt index 39c3be8e6..6980e3657 100644 --- a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt +++ b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt @@ -15,6 +15,7 @@ import com.haphap.app.presentation.jobdetail.navigation.jobDetailGraph import com.haphap.app.presentation.mypage.navigation.myPageGraph import com.haphap.app.presentation.register.navigation.registerGraph import com.haphap.app.presentation.search.navigation.searchGraph +import com.haphap.app.presentation.setting.navigation.settingGraph import com.haphap.app.presentation.splash.navigation.splashGraph private const val NavigationAnimationDurationMillis = 700 @@ -77,6 +78,11 @@ fun MainNavHost( navController = navController, ) + settingGraph( + innerPadding = innerPadding, + navController = navController, + ) + searchGraph( innerPadding = innerPadding, navController = navController, diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt index d6ab66254..3bbadcba2 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageContract.kt @@ -14,6 +14,7 @@ sealed interface MyPageContract { sealed class SideEffect { data object NavigateToHome : SideEffect() + data object NavigateToSetting : SideEffect() } } diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt index 669d583bb..c1656dfea 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.presentation.mypage.MyPageContract.SideEffect.NavigateToHome +import com.haphap.app.presentation.mypage.MyPageContract.SideEffect.NavigateToSetting import com.haphap.app.presentation.mypage.component.MyPageProfileCard import com.haphap.app.presentation.mypage.component.MyPageTopBar import com.haphap.app.presentation.mypage.component.MyPageTopSection @@ -24,6 +25,7 @@ import com.haphap.app.presentation.mypage.component.MyPageTopSection @Composable fun MyPageRoute( navigateToHome: () -> Unit, + navigateToSetting: () -> Unit, modifier: Modifier = Modifier, viewModel: MyPageViewModel = hiltViewModel(), ) { @@ -35,6 +37,7 @@ fun MyPageRoute( viewModel.sideEffect.collect { sideEffect -> when (sideEffect) { is NavigateToHome -> navigateToHome() + is NavigateToSetting -> navigateToSetting() } } } @@ -43,6 +46,7 @@ fun MyPageRoute( MyPageScreen( uiState = uiState, onBackClick = viewModel::onBackClick, + onSettingClick = viewModel::onSettingClick, modifier = modifier, ) } @@ -51,6 +55,7 @@ fun MyPageRoute( private fun MyPageScreen( uiState: MyPageContract.State, onBackClick: () -> Unit, + onSettingClick: () -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -59,7 +64,7 @@ private fun MyPageScreen( ) { MyPageTopBar( onBackClick = onBackClick, - onSettingClick = {}, + onSettingClick = onSettingClick, ) Spacer(modifier = Modifier.height(20.dp)) @@ -88,6 +93,7 @@ private fun MyPageScreenPreview() { emailText = "yeonsoo1234@naver.com", ), onBackClick = {}, + onSettingClick = {}, ) } } diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt index b4222c140..baf02b4b8 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import com.haphap.app.presentation.mypage.MyPageContract.SideEffect.NavigateToHome +import com.haphap.app.presentation.mypage.MyPageContract.SideEffect.NavigateToSetting import timber.log.Timber import javax.inject.Inject @@ -63,4 +64,10 @@ class MyPageViewModel @Inject constructor( _sideEffect.send(NavigateToHome) } } + + fun onSettingClick() { + viewModelScope.launch { + _sideEffect.send(NavigateToSetting) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt b/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt index 5f57cae83..b516e43a3 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt @@ -11,6 +11,7 @@ import com.haphap.app.core.extensions.clearBackStackNavOptions import com.haphap.app.core.navigation.MainTabRoute import com.haphap.app.presentation.home.navigation.navigateToHome import com.haphap.app.presentation.mypage.MyPageRoute +import com.haphap.app.presentation.setting.navigation.navigateToSetting import kotlinx.serialization.Serializable fun NavController.navigateToMyPage( @@ -28,6 +29,9 @@ fun NavGraphBuilder.myPageGraph( navOptions = navController.clearBackStackNavOptions() ) }, + navigateToSetting = { + navController.navigateToSetting() + }, modifier = Modifier.padding(innerPadding), ) } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt new file mode 100644 index 000000000..a078ef4fe --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt @@ -0,0 +1,20 @@ +package com.haphap.app.presentation.setting + +sealed interface SettingContract { + data class State( + val settingUiState: SettingUiState = SettingUiState.Idle, + ) + + sealed interface SideEffect { + data object NavigateToMyPage : SideEffect + } +} + +sealed interface SettingUiState { + data object Idle : SettingUiState + data object Loading : SettingUiState + data object Success : SettingUiState + data class Failure( + val msg: String, + ) : SettingUiState +} \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt new file mode 100644 index 000000000..45254bb22 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt @@ -0,0 +1,54 @@ +package com.haphap.app.presentation.setting + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage + +@Composable +fun SettingRoute( + navigateBack: () -> Unit, + modifier: Modifier = Modifier, + viewModel: SettingViewModel = hiltViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.sideEffect.collect { sideEffect -> + when (sideEffect) { + is NavigateToMyPage -> navigateBack() + } + } + } + } + + SettingScreen( + uiState = uiState, + onBackClick = viewModel::onBackClick, + modifier = modifier, + ) +} + +@Composable +private fun SettingScreen( + uiState: SettingContract.State, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxSize(), + ) { + Text("Setting") + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt new file mode 100644 index 000000000..009d89391 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -0,0 +1,28 @@ +package com.haphap.app.presentation.setting + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingViewModel @Inject constructor() : ViewModel() { + private val _uiState = MutableStateFlow(SettingContract.State()) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _sideEffect = Channel() + val sideEffect = _sideEffect.receiveAsFlow() + + fun onBackClick() { + viewModelScope.launch { + _sideEffect.send(NavigateToMyPage) + } + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt new file mode 100644 index 000000000..83e9d314f --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt @@ -0,0 +1,31 @@ +package com.haphap.app.presentation.setting.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.haphap.app.core.navigation.Route +import com.haphap.app.presentation.setting.SettingRoute +import kotlinx.serialization.Serializable + +fun NavController.navigateToSetting( + navOptions: NavOptions? = null, +) = navigate(Setting, navOptions) + +fun NavGraphBuilder.settingGraph( + innerPadding: PaddingValues, + navController: NavController, +) { + composable { + SettingRoute( + navigateBack = { navController.popBackStack() }, + modifier = Modifier.padding(innerPadding), + ) + } +} + +@Serializable +data object Setting : Route From d8c7e01ba34992a4a5f5a4e006b98cb61c53457b Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Thu, 24 Sep 2026 01:56:47 +0900 Subject: [PATCH 04/49] =?UTF-8?q?[feat/#109]=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/designsystem/theme/Color.kt | 5 + .../app/presentation/mypage/MyPageScreen.kt | 3 +- .../mypage/component/MyPageTopBar.kt | 4 +- .../app/presentation/setting/SettingScreen.kt | 100 +++++++++++++++++- .../setting/component/SettingTopBar.kt | 59 +++++++++++ 5 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/component/SettingTopBar.kt diff --git a/app/src/main/java/com/haphap/app/core/designsystem/theme/Color.kt b/app/src/main/java/com/haphap/app/core/designsystem/theme/Color.kt index 2faef6499..5f69dac92 100644 --- a/app/src/main/java/com/haphap/app/core/designsystem/theme/Color.kt +++ b/app/src/main/java/com/haphap/app/core/designsystem/theme/Color.kt @@ -22,6 +22,8 @@ val Sub300 = Color(0xFFC3D9FF) val Sub400 = Color(0xFF041B50) val Yellow = Color(0xFFFEE500) +val SubRed = Color(0xFFFF7D7D) + val Gray50 = Color(0xFFFAFBFB) val Gray100 = Color(0xFFF3F4F7) val Gray200 = Color(0xFFD2D6DB) @@ -46,6 +48,7 @@ data class HapHapColors( val sub300: Color, val sub400: Color, val yellow: Color, + val subred: Color, val gray50: Color, val gray100: Color, val gray200: Color, @@ -69,6 +72,7 @@ val defaultHapHapColors = HapHapColors( sub300 = Sub300, sub400 = Sub400, yellow = Yellow, + subred = SubRed, gray50 = Gray50, gray100 = Gray100, gray200 = Gray200, @@ -98,6 +102,7 @@ private fun ColorPreview() { HapHapTheme.colors.sub300, HapHapTheme.colors.sub400, HapHapTheme.colors.yellow, + HapHapTheme.colors.subred, HapHapTheme.colors.gray50, HapHapTheme.colors.gray100, diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt index c1656dfea..e7502e946 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/MyPageScreen.kt @@ -59,8 +59,7 @@ private fun MyPageScreen( modifier: Modifier = Modifier, ) { Column( - modifier = modifier - .fillMaxSize(), + modifier = modifier.fillMaxSize(), ) { MyPageTopBar( onBackClick = onBackClick, diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt b/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt index ea3d5d7f3..db6380819 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt @@ -30,7 +30,7 @@ fun MyPageTopBar( Row( modifier = modifier .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 5.dp), + .padding(horizontal = 20.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, ) { Icon( @@ -61,7 +61,7 @@ fun MyPageTopBar( @Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) @Composable -private fun RegisterTopBarPreview() { +private fun MyPageTopBarPreview() { HapHapTheme { MyPageTopBar( onBackClick = {}, diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt index 45254bb22..63b293421 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt @@ -1,18 +1,25 @@ package com.haphap.app.presentation.setting import androidx.compose.foundation.layout.Column +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.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle +import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage +import com.haphap.app.presentation.setting.component.SettingTopBar @Composable fun SettingRoute( @@ -47,8 +54,97 @@ private fun SettingScreen( modifier: Modifier = Modifier, ) { Column( - modifier = modifier.fillMaxSize(), + modifier = modifier + .fillMaxSize() + .padding(horizontal = 20.dp), ) { - Text("Setting") + SettingTopBar( + onBackClick = onBackClick, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "계정", + style = HapHapTheme.typography.caption.m12, + color = HapHapTheme.colors.gray400, + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Text( + text = "계정 정보", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) + + Spacer(modifier = Modifier.height(17.dp)) + + HorizontalDivider( + color = HapHapTheme.colors.gray200, + thickness = 1.dp, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "서비스 안내", + style = HapHapTheme.typography.caption.m12, + color = HapHapTheme.colors.gray400, + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Text( + text = "이용 약관", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) + + Text( + text = "개인정보 처리 방침", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) + + Text( + text = "커뮤니티 운영 정책", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) + + Text( + text = "문의하기", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) + + Spacer(modifier = Modifier.height(17.dp)) + + HorizontalDivider( + color = HapHapTheme.colors.gray200, + thickness = 1.dp, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "로그아웃", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.subred, + ) + + Text( + text = "회원 탈퇴", + modifier = Modifier.padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/component/SettingTopBar.kt b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingTopBar.kt new file mode 100644 index 000000000..3054a2772 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingTopBar.kt @@ -0,0 +1,59 @@ +package com.haphap.app.presentation.setting.component + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.R +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.extensions.noRippleClickable + +@Composable +fun SettingTopBar( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, + isText: Boolean = true, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_back_30), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.noRippleClickable(onClick = onBackClick), + ) + + Spacer(modifier = Modifier.width(10.dp)) + + if (isText) Text( + text = "설정", + style = HapHapTheme.typography.subtitle.b20, + color = HapHapTheme.colors.gray800, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun SettingTopBarPreview() { + HapHapTheme { + SettingTopBar( + onBackClick = {}, + ) + } +} \ No newline at end of file From 08cd12ba398567e98af0f38f734aded9744a93a9 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Thu, 24 Sep 2026 02:01:47 +0900 Subject: [PATCH 05/49] =?UTF-8?q?[mod/#109]=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/setting/SettingScreen.kt | 87 ++++--------------- .../setting/component/SettingMenuItem.kt | 38 ++++++++ .../component/SettingSectionDivider.kt | 33 +++++++ .../setting/component/SettingSectionTitle.kt | 28 ++++++ 4 files changed, 114 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/component/SettingMenuItem.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionDivider.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionTitle.kt diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt index 63b293421..4fcb7b414 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt @@ -5,8 +5,6 @@ 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.HorizontalDivider -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -19,6 +17,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage +import com.haphap.app.presentation.setting.component.SettingMenuItem +import com.haphap.app.presentation.setting.component.SettingSectionDivider +import com.haphap.app.presentation.setting.component.SettingSectionTitle import com.haphap.app.presentation.setting.component.SettingTopBar @Composable @@ -64,87 +65,29 @@ private fun SettingScreen( Spacer(modifier = Modifier.height(16.dp)) - Text( - text = "계정", - style = HapHapTheme.typography.caption.m12, - color = HapHapTheme.colors.gray400, - ) + SettingSectionTitle(text = "계정") Spacer(modifier = Modifier.height(12.dp)) - Text( - text = "계정 정보", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, - ) - - Spacer(modifier = Modifier.height(17.dp)) - - HorizontalDivider( - color = HapHapTheme.colors.gray200, - thickness = 1.dp, - ) + SettingMenuItem(text = "계정 정보") - Spacer(modifier = Modifier.height(16.dp)) + SettingSectionDivider() - Text( - text = "서비스 안내", - style = HapHapTheme.typography.caption.m12, - color = HapHapTheme.colors.gray400, - ) + SettingSectionTitle(text = "서비스 안내") Spacer(modifier = Modifier.height(12.dp)) - Text( - text = "이용 약관", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, - ) - - Text( - text = "개인정보 처리 방침", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, - ) - - Text( - text = "커뮤니티 운영 정책", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, - ) - - Text( - text = "문의하기", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, - ) - - Spacer(modifier = Modifier.height(17.dp)) + SettingMenuItem(text = "이용 약관") + SettingMenuItem(text = "개인정보 처리 방침") + SettingMenuItem(text = "커뮤니티 운영 정책") + SettingMenuItem(text = "문의하기") - HorizontalDivider( - color = HapHapTheme.colors.gray200, - thickness = 1.dp, - ) - - Spacer(modifier = Modifier.height(16.dp)) + SettingSectionDivider() - Text( + SettingMenuItem( text = "로그아웃", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.subred, - ) - - Text( - text = "회원 탈퇴", - modifier = Modifier.padding(vertical = 10.dp), - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, + textColor = HapHapTheme.colors.subred, ) + SettingMenuItem(text = "회원 탈퇴") } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/component/SettingMenuItem.kt b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingMenuItem.kt new file mode 100644 index 000000000..1de180b1f --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingMenuItem.kt @@ -0,0 +1,38 @@ +package com.haphap.app.presentation.setting.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.extensions.noRippleClickable + +@Composable +fun SettingMenuItem( + text: String, + modifier: Modifier = Modifier, + textColor: Color = HapHapTheme.colors.gray700, + onClick: () -> Unit = {}, +) { + Text( + text = text, + modifier = modifier + .fillMaxWidth() + .noRippleClickable(onClick = onClick) + .padding(vertical = 10.dp), + style = HapHapTheme.typography.body.sb14, + color = textColor, + ) +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun SettingMenuItemPreview() { + HapHapTheme { + SettingMenuItem(text = "계정 정보") + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionDivider.kt b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionDivider.kt new file mode 100644 index 000000000..3ce633005 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionDivider.kt @@ -0,0 +1,33 @@ +package com.haphap.app.presentation.setting.component + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.core.designsystem.theme.HapHapTheme + +@Composable +fun SettingSectionDivider(modifier: Modifier = Modifier) { + Column(modifier = modifier) { + Spacer(modifier = Modifier.height(17.dp)) + + HorizontalDivider( + color = HapHapTheme.colors.gray200, + thickness = 1.dp, + ) + + Spacer(modifier = Modifier.height(16.dp)) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun SettingSectionDividerPreview() { + HapHapTheme { + SettingSectionDivider() + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionTitle.kt b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionTitle.kt new file mode 100644 index 000000000..72d0ddea8 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/setting/component/SettingSectionTitle.kt @@ -0,0 +1,28 @@ +package com.haphap.app.presentation.setting.component + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.haphap.app.core.designsystem.theme.HapHapTheme + +@Composable +fun SettingSectionTitle( + text: String, + modifier: Modifier = Modifier, +) { + Text( + text = text, + modifier = modifier, + style = HapHapTheme.typography.caption.m12, + color = HapHapTheme.colors.gray400, + ) +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun SettingSectionTitlePreview() { + HapHapTheme { + SettingSectionTitle(text = "계정") + } +} From 29363c84ad3002977a01f77acd911577de208e62 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 25 Sep 2026 11:50:01 +0900 Subject: [PATCH 06/49] =?UTF-8?q?[feat/#109]=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=EC=97=86=20ver=20=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/modal/HapHapDialog.kt | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt index a6c8d17d6..ca00651d3 100644 --- a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt +++ b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt @@ -31,6 +31,7 @@ import com.haphap.app.core.designsystem.type.ButtonType * @param content 다이얼로그 본문에 표시할 안내 문구 * @param onDismiss 다이얼로그 바깥 영역 또는 "취소" 버튼 클릭 시 호출되는 콜백 * @param onConfirmClick "확인" 버튼 클릭 시 호출되는 콜백 + * @param iconTrue 다이얼로그에 아이콘이 있는 지 설정하는 변수 */ @Composable @@ -39,6 +40,7 @@ fun HapHapDialog( onDismiss: () -> Unit, onConfirmClick: () -> Unit, modifier: Modifier = Modifier, + iconTrue: Boolean = false, ) { Dialog ( onDismissRequest = onDismiss, @@ -50,16 +52,23 @@ fun HapHapDialog( color = HapHapTheme.colors.white, shape = RoundedCornerShape(16.dp), ) - .padding(start = 20.dp, top = 16.dp, end = 20.dp, bottom = 21.dp), + .padding( + start = 19.dp, + top = 16.dp, + end = 19.dp, + bottom = if (iconTrue) 21.dp else 16.dp, + ), horizontalAlignment = Alignment.CenterHorizontally, ) { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_alert_53), - contentDescription = null, - tint = HapHapTheme.colors.gray200, - ) + if (iconTrue) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_alert_53), + contentDescription = null, + tint = HapHapTheme.colors.gray200, + ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(8.dp)) + } Text( text = content, @@ -98,9 +107,10 @@ fun HapHapDialog( private fun HapHapDialogPreview() { HapHapTheme { HapHapDialog( - content = "이전에 등록한 결과가 있습니다.\n결과를 변경할까요?", + content = "로그아웃 하시겠습니까?", onDismiss = {}, onConfirmClick = {}, + iconTrue = false, ) } } \ No newline at end of file From a6bdbd5f24f53bc11d3207d904d209159d5fe456 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 25 Sep 2026 11:57:47 +0900 Subject: [PATCH 07/49] =?UTF-8?q?[feat/#109]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/setting/SettingContract.kt | 1 + .../app/presentation/setting/SettingScreen.kt | 16 ++++++++++++++++ .../app/presentation/setting/SettingViewModel.kt | 14 ++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt index a078ef4fe..cebddef90 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt @@ -3,6 +3,7 @@ package com.haphap.app.presentation.setting sealed interface SettingContract { data class State( val settingUiState: SettingUiState = SettingUiState.Idle, + val isLogoutDialogVisible: Boolean = false, ) sealed interface SideEffect { diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt index 4fcb7b414..f78e01746 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle +import com.haphap.app.core.designsystem.component.modal.HapHapDialog import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage import com.haphap.app.presentation.setting.component.SettingMenuItem @@ -44,6 +45,9 @@ fun SettingRoute( SettingScreen( uiState = uiState, onBackClick = viewModel::onBackClick, + onLogoutClick = viewModel::onLogoutClick, + onLogoutDialogDismiss = viewModel::onLogoutDialogDismiss, + onLogoutConfirm = viewModel::onLogoutConfirm, modifier = modifier, ) } @@ -52,8 +56,19 @@ fun SettingRoute( private fun SettingScreen( uiState: SettingContract.State, onBackClick: () -> Unit, + onLogoutClick: () -> Unit, + onLogoutDialogDismiss: () -> Unit, + onLogoutConfirm: () -> Unit, modifier: Modifier = Modifier, ) { + if (uiState.isLogoutDialogVisible) { + HapHapDialog( + content = "로그아웃 하시겠습니까?", + onDismiss = onLogoutDialogDismiss, + onConfirmClick = onLogoutConfirm, + ) + } + Column( modifier = modifier .fillMaxSize() @@ -87,6 +102,7 @@ private fun SettingScreen( SettingMenuItem( text = "로그아웃", textColor = HapHapTheme.colors.subred, + onClick = onLogoutClick, ) SettingMenuItem(text = "회원 탈퇴") } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt index 009d89391..450520282 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -25,4 +26,17 @@ class SettingViewModel @Inject constructor() : ViewModel() { _sideEffect.send(NavigateToMyPage) } } + + fun onLogoutClick() { + _uiState.update { it.copy(isLogoutDialogVisible = true) } + } + + fun onLogoutDialogDismiss() { + _uiState.update { it.copy(isLogoutDialogVisible = false) } + } + + fun onLogoutConfirm() { + _uiState.update { it.copy(isLogoutDialogVisible = false) } + // TODO: 로그아웃 처리 + } } From 7f340b71fde6438bb0c69431a4fbd08e3cea5f54 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 25 Sep 2026 12:05:28 +0900 Subject: [PATCH 08/49] =?UTF-8?q?[mod/#109]=20=EA=B3=B5=EA=B3=A0=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=ED=99=94=EB=A9=B4=EC=9D=98=20=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=9E=88=EA=B2=8C=20=EC=88=98=EC=A0=95,=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20=EB=84=88?= =?UTF-8?q?=EB=B9=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/designsystem/component/modal/HapHapDialog.kt | 3 ++- .../presentation/register/component/RegisterSecondSection.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt index ca00651d3..8935c428c 100644 --- a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt +++ b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt @@ -47,6 +47,7 @@ fun HapHapDialog( ) { Column( modifier = modifier + .padding(horizontal = 31.dp) .fillMaxWidth() .background( color = HapHapTheme.colors.white, @@ -81,7 +82,7 @@ fun HapHapDialog( Row( modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(11.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), ) { HapHapBasicButton( text = "취소", diff --git a/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt b/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt index 0b48fb42b..2de7206b4 100644 --- a/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt +++ b/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt @@ -60,6 +60,7 @@ fun RegisterSecondSection( content = "이전에 등록한 결과가 있습니다.\n선택한 결과로 변경할까요?", onDismiss = onChangeModalCancelClick, onConfirmClick = onChangeModalConfirmClick, + iconTrue = true, ) } } From 0b5931762c9c5104f87d1b3bc01f5321cfea2c11 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Wed, 30 Sep 2026 23:01:24 +0900 Subject: [PATCH 09/49] =?UTF-8?q?[feat/#109]=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/LeaveContract.kt | 2 + .../app/presentation/leave/LeaveScreen.kt | 116 ++++++++++++++++++ .../app/presentation/leave/LeaveViewModel.kt | 2 + .../leave/component/LeaveCheckList.kt | 110 +++++++++++++++++ .../leave/component/LeaveTopBar.kt | 59 +++++++++ .../mypage/component/MyPageTopBar.kt | 1 - 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt new file mode 100644 index 000000000..ad9d175b8 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt @@ -0,0 +1,2 @@ +package com.haphap.app.presentation.leave + diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt new file mode 100644 index 000000000..e8caa5ed5 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -0,0 +1,116 @@ +package com.haphap.app.presentation.leave + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +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.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.haphap.app.core.designsystem.component.button.HapHapBasicButton +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.designsystem.type.ButtonType +import com.haphap.app.presentation.leave.component.LeaveCheckList +import com.haphap.app.presentation.leave.component.LeaveTopBar + +@Composable +private fun LeaveScreen( + onBackClick: () -> Unit, + onReasonClick: () -> Unit, + onReasonEnter: () -> Unit, + onLeaveClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(horizontal = 20.dp), + ) { + LeaveTopBar( + onBackClick = onBackClick, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "탈퇴 유의사항", + style = HapHapTheme.typography.caption.m12, + color = HapHapTheme.colors.gray400, + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // TODO: 탈퇴 유의 사항 기입 + + Spacer(modifier = Modifier.height(12.dp)) + + HorizontalDivider( + color = HapHapTheme.colors.gray200, + thickness = 1.dp, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "탈퇴 사유를 선택해주세요(필수)", + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, + ) + + Spacer(modifier = Modifier.height(12.dp)) + + LeaveCheckList( + context = "취업 준비 활동이 끝났어요.", + onCheckedChange = {}, + ) + + LeaveCheckList( + context = "원하는 정보가 부족해요.", + onCheckedChange = {}, + ) + + LeaveCheckList( + context = "서비스를 잘 사용하지 않아요.", + onCheckedChange = {}, + ) + + LeaveCheckList( + context = "개인정보(보안) 유출이 걱정돼요.", + onCheckedChange = {}, + ) + + LeaveCheckList( + context = "기타 (직접 입력)", + onCheckedChange = {}, + isEtc = true, + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + HapHapBasicButton( + text = "취소", + onClick = onBackClick, + modifier = Modifier.weight(1f), + textStyle = HapHapTheme.typography.body.b18, + colorType = ButtonType.Cancel, + ) + HapHapBasicButton( + text = "탈퇴하기", + onClick = onLeaveClick, + modifier = Modifier.weight(1f), + textStyle = HapHapTheme.typography.body.b18, + colorType = ButtonType.Primary(enabled = true), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt new file mode 100644 index 000000000..ad9d175b8 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt @@ -0,0 +1,2 @@ +package com.haphap.app.presentation.leave + diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt new file mode 100644 index 000000000..75d916522 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt @@ -0,0 +1,110 @@ +package com.haphap.app.presentation.leave.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +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.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.input.InputTransformation +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.maxLength +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.R +import com.haphap.app.core.designsystem.component.textfield.HapHapBasicTextField +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.extensions.noRippleClickable + +private const val ETC_REASON_MAX_LENGTH = 200 + +@Composable +fun LeaveCheckList( + context: String, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + checked: Boolean = false, + isEtc: Boolean = false, + etcReasonState: TextFieldState = rememberTextFieldState(), +) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 10.dp), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .noRippleClickable( + onClick = { onCheckedChange(!checked) } + ), + ) { + Icon( + imageVector = ImageVector.vectorResource( + id = if (checked) { + R.drawable.ic_check_list_selected_24 + } else { + R.drawable.ic_check_list_default_24 + } + ), + contentDescription = null, + tint = Color.Unspecified, + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = context, + color = HapHapTheme.colors.gray600, + style = HapHapTheme.typography.body.sb14, + ) + } + + if (isEtc && checked) { + Spacer(modifier = Modifier.height(2.dp)) + + HapHapBasicTextField( + state = etcReasonState, + textColor = HapHapTheme.colors.gray800, + textStyle = HapHapTheme.typography.caption.r12, + placeholder = "최대 200자까지 입력이 가능해요.", + placeholderColor = HapHapTheme.colors.gray300, + placeholderStyle = HapHapTheme.typography.caption.r12, + inputTransformation = InputTransformation.maxLength(ETC_REASON_MAX_LENGTH), + modifier = Modifier + .fillMaxWidth() + .background(color = HapHapTheme.colors.gray100) + .padding(start = 18.dp, top = 7.dp, bottom = 7.dp), + ) + } + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun LeaveCheckListPreview() { + var checked by remember { mutableStateOf(true) } + + HapHapTheme { + LeaveCheckList( + context = "기타 (직접 입력)", + onCheckedChange = { checked = it }, + checked = checked, + isEtc = true, + ) + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt new file mode 100644 index 000000000..4ac22563f --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt @@ -0,0 +1,59 @@ +package com.haphap.app.presentation.leave.component + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.R +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.extensions.noRippleClickable + +@Composable +fun LeaveTopBar( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, + isText: Boolean = true, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_back_30), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.noRippleClickable(onClick = onBackClick), + ) + + Spacer(modifier = Modifier.width(10.dp)) + + if (isText) Text( + text = "회원 탈퇴", + style = HapHapTheme.typography.subtitle.b20, + color = HapHapTheme.colors.gray800, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +private fun LeaveTopBarPreview() { + HapHapTheme { + LeaveTopBar( + onBackClick = {}, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt b/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt index db6380819..22a3b0c6b 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/component/MyPageTopBar.kt @@ -1,6 +1,5 @@ package com.haphap.app.presentation.mypage.component -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth From 5a3dae5ca501fcc65100c695408972464616603b Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 17:18:52 +0900 Subject: [PATCH 10/49] =?UTF-8?q?[feat/#109]=20=EC=82=AC=EC=9C=A0=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EC=B0=BD=20=EC=B9=B4=EC=9A=B4=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leave/component/LeaveCheckList.kt | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt index 75d916522..0f999eafb 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt @@ -19,6 +19,7 @@ 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.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector @@ -30,7 +31,7 @@ import com.haphap.app.core.designsystem.component.textfield.HapHapBasicTextField import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.extensions.noRippleClickable -private const val ETC_REASON_MAX_LENGTH = 200 +private const val ETC_REASON_MAX_LENGTH = 150 @Composable fun LeaveCheckList( @@ -77,19 +78,32 @@ fun LeaveCheckList( if (isEtc && checked) { Spacer(modifier = Modifier.height(2.dp)) - HapHapBasicTextField( - state = etcReasonState, - textColor = HapHapTheme.colors.gray800, - textStyle = HapHapTheme.typography.caption.r12, - placeholder = "최대 200자까지 입력이 가능해요.", - placeholderColor = HapHapTheme.colors.gray300, - placeholderStyle = HapHapTheme.typography.caption.r12, - inputTransformation = InputTransformation.maxLength(ETC_REASON_MAX_LENGTH), + Column( modifier = Modifier .fillMaxWidth() .background(color = HapHapTheme.colors.gray100) - .padding(start = 18.dp, top = 7.dp, bottom = 7.dp), - ) + .padding(horizontal = 10.dp, vertical = 12.dp), + ) { + HapHapBasicTextField( + state = etcReasonState, + textColor = HapHapTheme.colors.gray500, + textStyle = HapHapTheme.typography.caption.r11, + placeholder = "최대 150자까지 입력이 가능해요.", + placeholderColor = HapHapTheme.colors.gray300, + placeholderStyle = HapHapTheme.typography.caption.r11, + inputTransformation = InputTransformation.maxLength(ETC_REASON_MAX_LENGTH), + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = "${etcReasonState.text.length}/$ETC_REASON_MAX_LENGTH", + color = HapHapTheme.colors.gray400, + style = HapHapTheme.typography.caption.r11, + modifier = Modifier.align(Alignment.End), + ) + } } } } From 2ad6a97e00db972d83782bbb45c0d0cbf1f9d27f Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 17:21:32 +0900 Subject: [PATCH 11/49] [rename/#109] LeaveCheckList -> LeaveCheckItem --- .../com/haphap/app/presentation/leave/LeaveScreen.kt | 12 ++++++------ .../{LeaveCheckList.kt => LeaveCheckItem.kt} | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) rename app/src/main/java/com/haphap/app/presentation/leave/component/{LeaveCheckList.kt => LeaveCheckItem.kt} (98%) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index e8caa5ed5..03c763807 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.unit.dp import com.haphap.app.core.designsystem.component.button.HapHapBasicButton import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.designsystem.type.ButtonType -import com.haphap.app.presentation.leave.component.LeaveCheckList +import com.haphap.app.presentation.leave.component.LeaveCheckItem import com.haphap.app.presentation.leave.component.LeaveTopBar @Composable @@ -65,27 +65,27 @@ private fun LeaveScreen( Spacer(modifier = Modifier.height(12.dp)) - LeaveCheckList( + LeaveCheckItem( context = "취업 준비 활동이 끝났어요.", onCheckedChange = {}, ) - LeaveCheckList( + LeaveCheckItem( context = "원하는 정보가 부족해요.", onCheckedChange = {}, ) - LeaveCheckList( + LeaveCheckItem( context = "서비스를 잘 사용하지 않아요.", onCheckedChange = {}, ) - LeaveCheckList( + LeaveCheckItem( context = "개인정보(보안) 유출이 걱정돼요.", onCheckedChange = {}, ) - LeaveCheckList( + LeaveCheckItem( context = "기타 (직접 입력)", onCheckedChange = {}, isEtc = true, diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt similarity index 98% rename from app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt rename to app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt index 0f999eafb..95c244922 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckList.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt @@ -34,7 +34,7 @@ import com.haphap.app.core.extensions.noRippleClickable private const val ETC_REASON_MAX_LENGTH = 150 @Composable -fun LeaveCheckList( +fun LeaveCheckItem( context: String, onCheckedChange: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -110,11 +110,11 @@ fun LeaveCheckList( @Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) @Composable -private fun LeaveCheckListPreview() { +private fun LeaveCheckItemPreview() { var checked by remember { mutableStateOf(true) } HapHapTheme { - LeaveCheckList( + LeaveCheckItem( context = "기타 (직접 입력)", onCheckedChange = { checked = it }, checked = checked, From 8a5d9bb197e646ee1d3d28acd95b37d8de71b6dc Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 19:57:00 +0900 Subject: [PATCH 12/49] =?UTF-8?q?[feat/#109]=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/LeaveContract.kt | 17 +++++ .../app/presentation/leave/LeaveScreen.kt | 64 +++++++++++-------- .../app/presentation/leave/LeaveViewModel.kt | 42 ++++++++++++ .../leave/type/LeaveReasonType.kt | 9 +++ 4 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt index ad9d175b8..20a11b73c 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt @@ -1,2 +1,19 @@ package com.haphap.app.presentation.leave +import androidx.compose.runtime.Immutable +import com.haphap.app.presentation.leave.type.LeaveReasonType + +sealed interface LeaveContract { + @Immutable + data class State( + val selectedReason: LeaveReasonType? = null, + val isEtcReasonBlank: Boolean = true, + ) { + val isButtonEnabled: Boolean + get() = when (selectedReason) { + null -> false + LeaveReasonType.ETC -> !isEtcReasonBlank + else -> true + } + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index 03c763807..6ab51ccd0 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -8,22 +8,46 @@ 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.text.input.TextFieldState import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.haphap.app.core.designsystem.component.button.HapHapBasicButton import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.designsystem.type.ButtonType import com.haphap.app.presentation.leave.component.LeaveCheckItem import com.haphap.app.presentation.leave.component.LeaveTopBar +import com.haphap.app.presentation.leave.type.LeaveReasonType + +@Composable +fun LeaveRoute( + navigateBack: () -> Unit, + modifier: Modifier = Modifier, + viewModel: LeaveViewModel = hiltViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + LeaveScreen( + uiState = uiState, + etcReasonState = viewModel.etcReasonState, + onBackClick = navigateBack, + onReasonClick = viewModel::onReasonClick, + onLeaveClick = viewModel::onLeaveClick, + modifier = modifier, + ) +} @Composable private fun LeaveScreen( + uiState: LeaveContract.State, + etcReasonState: TextFieldState, onBackClick: () -> Unit, - onReasonClick: () -> Unit, - onReasonEnter: () -> Unit, + onReasonClick: (LeaveReasonType) -> Unit, onLeaveClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -65,31 +89,15 @@ private fun LeaveScreen( Spacer(modifier = Modifier.height(12.dp)) - LeaveCheckItem( - context = "취업 준비 활동이 끝났어요.", - onCheckedChange = {}, - ) - - LeaveCheckItem( - context = "원하는 정보가 부족해요.", - onCheckedChange = {}, - ) - - LeaveCheckItem( - context = "서비스를 잘 사용하지 않아요.", - onCheckedChange = {}, - ) - - LeaveCheckItem( - context = "개인정보(보안) 유출이 걱정돼요.", - onCheckedChange = {}, - ) - - LeaveCheckItem( - context = "기타 (직접 입력)", - onCheckedChange = {}, - isEtc = true, - ) + LeaveReasonType.entries.forEach { reason -> + LeaveCheckItem( + context = reason.text, + onCheckedChange = { onReasonClick(reason) }, + checked = reason == uiState.selectedReason, + isEtc = reason == LeaveReasonType.ETC, + etcReasonState = etcReasonState, + ) + } Spacer(modifier = Modifier.height(12.dp)) @@ -109,7 +117,7 @@ private fun LeaveScreen( onClick = onLeaveClick, modifier = Modifier.weight(1f), textStyle = HapHapTheme.typography.body.b18, - colorType = ButtonType.Primary(enabled = true), + colorType = ButtonType.Primary(enabled = uiState.isButtonEnabled), ) } } diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt index ad9d175b8..c788b5028 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt @@ -1,2 +1,44 @@ package com.haphap.app.presentation.leave +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.runtime.snapshotFlow +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.haphap.app.presentation.leave.type.LeaveReasonType +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class LeaveViewModel @Inject constructor() : ViewModel() { + private val _uiState = MutableStateFlow(LeaveContract.State()) + val uiState: StateFlow = _uiState.asStateFlow() + + val etcReasonState = TextFieldState() + + init { + observeEtcReason() + } + + private fun observeEtcReason() = viewModelScope.launch { + snapshotFlow { etcReasonState.text.isBlank() } + .collect { isBlank -> + _uiState.update { it.copy(isEtcReasonBlank = isBlank) } + } + } + + fun onReasonClick(reason: LeaveReasonType) { + _uiState.update { state -> + state.copy(selectedReason = if (state.selectedReason == reason) null else reason) + } + } + + fun onLeaveClick() { + if (!_uiState.value.isButtonEnabled) return + // TODO: 회원 탈퇴 처리 + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt b/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt new file mode 100644 index 000000000..1e3fbb772 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt @@ -0,0 +1,9 @@ +package com.haphap.app.presentation.leave.type + +enum class LeaveReasonType(val text: String) { + JOB_SEARCH_FINISHED("취업 준비 활동이 끝났어요."), + LACK_OF_INFORMATION("원하는 정보가 부족해요."), + RARELY_USED("서비스를 잘 사용하지 않아요."), + PRIVACY_CONCERN("개인정보(보안) 유출이 걱정돼요."), + ETC("기타 (직접 입력)"), +} From 83e1781fa854472a891d76cd03dac3bc46359760 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 20:02:23 +0900 Subject: [PATCH 13/49] =?UTF-8?q?[mod/#109]=20=EC=82=AC=EC=9C=A0=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EA=B8=B0=ED=9A=8D=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/component/LeaveCheckItem.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt index 95c244922..cbe2bc84b 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.input.InputTransformation import androidx.compose.foundation.text.input.TextFieldState -import androidx.compose.foundation.text.input.maxLength import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -91,14 +90,16 @@ fun LeaveCheckItem( placeholder = "최대 150자까지 입력이 가능해요.", placeholderColor = HapHapTheme.colors.gray300, placeholderStyle = HapHapTheme.typography.caption.r11, - inputTransformation = InputTransformation.maxLength(ETC_REASON_MAX_LENGTH), + inputTransformation = InputTransformation { + if (asCharSequence().trimStart().length > ETC_REASON_MAX_LENGTH) revertAllChanges() + }, modifier = Modifier.fillMaxWidth(), ) Spacer(modifier = Modifier.height(4.dp)) Text( - text = "${etcReasonState.text.length}/$ETC_REASON_MAX_LENGTH", + text = "${etcReasonState.text.trimStart().length}/$ETC_REASON_MAX_LENGTH", color = HapHapTheme.colors.gray400, style = HapHapTheme.typography.caption.r11, modifier = Modifier.align(Alignment.End), From 54a47bb8c5cbfb36037ce91ba91f31acff2e3f7f Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 20:33:47 +0900 Subject: [PATCH 14/49] =?UTF-8?q?[feat/#109]=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/leave/LeaveCompleteScreen.kt | 68 +++++++++++++++++++ .../app/presentation/leave/LeaveContract.kt | 5 ++ .../app/presentation/leave/LeaveScreen.kt | 21 +++++- .../app/presentation/leave/LeaveViewModel.kt | 16 +++++ .../leave/navigation/LeaveNavigation.kt | 62 +++++++++++++++++ .../app/presentation/main/MainNavHost.kt | 12 ++++ .../presentation/setting/SettingContract.kt | 1 + .../app/presentation/setting/SettingScreen.kt | 10 ++- .../presentation/setting/SettingViewModel.kt | 7 ++ .../setting/navigation/SettingNavigation.kt | 2 + 10 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt create mode 100644 app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt new file mode 100644 index 000000000..83114d943 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt @@ -0,0 +1,68 @@ +package com.haphap.app.presentation.leave + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.haphap.app.core.designsystem.component.button.HapHapBasicButton +import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.designsystem.type.ButtonType + +@Composable +fun LeaveCompleteRoute( + navigateToLogin: () -> Unit, + modifier: Modifier = Modifier, +) { + LeaveCompleteScreen( + navigateToLogin = navigateToLogin, + modifier = modifier, + ) +} + +@Composable +fun LeaveCompleteScreen( + navigateToLogin: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .background(HapHapTheme.colors.white) + .padding(start = 20.dp, end = 20.dp, top = 43.dp), + ) { + Text( + text = "회원 탈퇴가 완료되었어요.", + style = HapHapTheme.typography.subtitle.b22, + color = HapHapTheme.colors.gray800, + ) + + Spacer(modifier = Modifier.weight(1f)) + + HapHapBasicButton( + text = "완료", + textStyle = HapHapTheme.typography.body.b18, + colorType = ButtonType.Primary(enabled = true), + onClick = navigateToLogin, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 10.dp) + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun LeaveCompleteScreenPreview() { + HapHapTheme { + LeaveCompleteScreen( + navigateToLogin = {}, + ) + } +} diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt index 20a11b73c..e3ea0325b 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt @@ -16,4 +16,9 @@ sealed interface LeaveContract { else -> true } } + + sealed interface SideEffect { + data object NavigateToSetting : SideEffect + data object NavigateToLeaveComplete : SideEffect + } } diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index 6ab51ccd0..bda25a990 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -12,14 +12,20 @@ import androidx.compose.foundation.text.input.TextFieldState 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.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.repeatOnLifecycle import com.haphap.app.core.designsystem.component.button.HapHapBasicButton import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.designsystem.type.ButtonType +import com.haphap.app.presentation.leave.LeaveContract.SideEffect.NavigateToLeaveComplete +import com.haphap.app.presentation.leave.LeaveContract.SideEffect.NavigateToSetting import com.haphap.app.presentation.leave.component.LeaveCheckItem import com.haphap.app.presentation.leave.component.LeaveTopBar import com.haphap.app.presentation.leave.type.LeaveReasonType @@ -27,15 +33,28 @@ import com.haphap.app.presentation.leave.type.LeaveReasonType @Composable fun LeaveRoute( navigateBack: () -> Unit, + navigateToLeaveComplete: () -> Unit, modifier: Modifier = Modifier, viewModel: LeaveViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(lifecycleOwner) { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.sideEffect.collect { sideEffect -> + when (sideEffect) { + is NavigateToSetting -> navigateBack() + is NavigateToLeaveComplete -> navigateToLeaveComplete() + } + } + } + } LeaveScreen( uiState = uiState, etcReasonState = viewModel.etcReasonState, - onBackClick = navigateBack, + onBackClick = viewModel::onBackClick, onReasonClick = viewModel::onReasonClick, onLeaveClick = viewModel::onLeaveClick, modifier = modifier, diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt index c788b5028..e53bc33e1 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt @@ -4,11 +4,15 @@ import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.haphap.app.presentation.leave.LeaveContract.SideEffect.NavigateToLeaveComplete +import com.haphap.app.presentation.leave.LeaveContract.SideEffect.NavigateToSetting import com.haphap.app.presentation.leave.type.LeaveReasonType import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -18,6 +22,9 @@ class LeaveViewModel @Inject constructor() : ViewModel() { private val _uiState = MutableStateFlow(LeaveContract.State()) val uiState: StateFlow = _uiState.asStateFlow() + private val _sideEffect = Channel() + val sideEffect = _sideEffect.receiveAsFlow() + val etcReasonState = TextFieldState() init { @@ -31,6 +38,12 @@ class LeaveViewModel @Inject constructor() : ViewModel() { } } + fun onBackClick() { + viewModelScope.launch { + _sideEffect.send(NavigateToSetting) + } + } + fun onReasonClick(reason: LeaveReasonType) { _uiState.update { state -> state.copy(selectedReason = if (state.selectedReason == reason) null else reason) @@ -40,5 +53,8 @@ class LeaveViewModel @Inject constructor() : ViewModel() { fun onLeaveClick() { if (!_uiState.value.isButtonEnabled) return // TODO: 회원 탈퇴 처리 + viewModelScope.launch { + _sideEffect.send(NavigateToLeaveComplete) + } } } diff --git a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt new file mode 100644 index 000000000..9446bb4b1 --- /dev/null +++ b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt @@ -0,0 +1,62 @@ +package com.haphap.app.presentation.leave.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.haphap.app.core.extensions.clearBackStackNavOptions +import com.haphap.app.core.navigation.Route +import com.haphap.app.presentation.auth.navigation.navigateToLogin +import com.haphap.app.presentation.leave.LeaveCompleteRoute +import com.haphap.app.presentation.leave.LeaveRoute +import kotlinx.serialization.Serializable + +fun NavController.navigateToLeave( + navOptions: NavOptions? = null, +) = navigate(Leave, navOptions) + +fun NavController.navigateToLeaveComplete( + navOptions: NavOptions? = null, +) = navigate(LeaveComplete, navOptions) + +fun NavGraphBuilder.leaveGraph( + innerPadding: PaddingValues, + navController: NavController, +) { + composable { + LeaveRoute( + navigateBack = { navController.popBackStack() }, + navigateToLeaveComplete = { + navController.navigateToLeaveComplete( + navOptions = navController.clearBackStackNavOptions(), + ) + }, + modifier = Modifier.padding(innerPadding), + ) + } +} + +fun NavGraphBuilder.leaveCompleteGraph( + innerPadding: PaddingValues, + navController: NavController, +) { + composable { + LeaveCompleteRoute( + navigateToLogin = { + navController.navigateToLogin( + navOptions = navController.clearBackStackNavOptions(), + ) + }, + modifier = Modifier.padding(innerPadding), + ) + } +} + +@Serializable +data object Leave : Route + +@Serializable +data object LeaveComplete : Route diff --git a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt index 6980e3657..bc87b5eb4 100644 --- a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt +++ b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt @@ -12,6 +12,8 @@ import com.haphap.app.presentation.calendar.navigation.calendarGraph import com.haphap.app.presentation.home.navigation.homeGraph import com.haphap.app.presentation.joblist.navigation.jobListGraph import com.haphap.app.presentation.jobdetail.navigation.jobDetailGraph +import com.haphap.app.presentation.leave.navigation.leaveCompleteGraph +import com.haphap.app.presentation.leave.navigation.leaveGraph import com.haphap.app.presentation.mypage.navigation.myPageGraph import com.haphap.app.presentation.register.navigation.registerGraph import com.haphap.app.presentation.search.navigation.searchGraph @@ -83,6 +85,16 @@ fun MainNavHost( navController = navController, ) + leaveGraph( + innerPadding = innerPadding, + navController = navController, + ) + + leaveCompleteGraph( + innerPadding = innerPadding, + navController = navController, + ) + searchGraph( innerPadding = innerPadding, navController = navController, diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt index cebddef90..c1e436384 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt @@ -8,6 +8,7 @@ sealed interface SettingContract { sealed interface SideEffect { data object NavigateToMyPage : SideEffect + data object NavigateToLeave : SideEffect } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt index f78e01746..ab0486814 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle import com.haphap.app.core.designsystem.component.modal.HapHapDialog import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLeave import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage import com.haphap.app.presentation.setting.component.SettingMenuItem import com.haphap.app.presentation.setting.component.SettingSectionDivider @@ -26,6 +27,7 @@ import com.haphap.app.presentation.setting.component.SettingTopBar @Composable fun SettingRoute( navigateBack: () -> Unit, + navigateToLeave: () -> Unit, modifier: Modifier = Modifier, viewModel: SettingViewModel = hiltViewModel(), ) { @@ -37,6 +39,7 @@ fun SettingRoute( viewModel.sideEffect.collect { sideEffect -> when (sideEffect) { is NavigateToMyPage -> navigateBack() + is NavigateToLeave -> navigateToLeave() } } } @@ -48,6 +51,7 @@ fun SettingRoute( onLogoutClick = viewModel::onLogoutClick, onLogoutDialogDismiss = viewModel::onLogoutDialogDismiss, onLogoutConfirm = viewModel::onLogoutConfirm, + onLeaveClick = viewModel::onLeaveClick, modifier = modifier, ) } @@ -59,6 +63,7 @@ private fun SettingScreen( onLogoutClick: () -> Unit, onLogoutDialogDismiss: () -> Unit, onLogoutConfirm: () -> Unit, + onLeaveClick: () -> Unit, modifier: Modifier = Modifier, ) { if (uiState.isLogoutDialogVisible) { @@ -104,6 +109,9 @@ private fun SettingScreen( textColor = HapHapTheme.colors.subred, onClick = onLogoutClick, ) - SettingMenuItem(text = "회원 탈퇴") + SettingMenuItem( + text = "회원 탈퇴", + onClick = onLeaveClick, + ) } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt index 450520282..50fb49f70 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -2,6 +2,7 @@ package com.haphap.app.presentation.setting import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLeave import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel @@ -39,4 +40,10 @@ class SettingViewModel @Inject constructor() : ViewModel() { _uiState.update { it.copy(isLogoutDialogVisible = false) } // TODO: 로그아웃 처리 } + + fun onLeaveClick() { + viewModelScope.launch { + _sideEffect.send(NavigateToLeave) + } + } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt index 83e9d314f..9fa83fec5 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt @@ -8,6 +8,7 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.haphap.app.core.navigation.Route +import com.haphap.app.presentation.leave.navigation.navigateToLeave import com.haphap.app.presentation.setting.SettingRoute import kotlinx.serialization.Serializable @@ -22,6 +23,7 @@ fun NavGraphBuilder.settingGraph( composable { SettingRoute( navigateBack = { navController.popBackStack() }, + navigateToLeave = { navController.navigateToLeave() }, modifier = Modifier.padding(innerPadding), ) } From 293f8d2f305694f38f8116266a8879d058ba4849 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 20:44:40 +0900 Subject: [PATCH 15/49] =?UTF-8?q?[mod/#109]=20=EC=9E=90=EC=9E=98=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/LeaveScreen.kt | 106 ++++++++++-------- .../leave/component/LeaveCheckItem.kt | 57 +++++++--- 2 files changed, 100 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index bda25a990..6b2a5a3e9 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -8,7 +8,9 @@ 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.rememberScrollState import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -79,65 +81,71 @@ private fun LeaveScreen( onBackClick = onBackClick, ) - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "탈퇴 유의사항", - style = HapHapTheme.typography.caption.m12, - color = HapHapTheme.colors.gray400, - ) - - Spacer(modifier = Modifier.height(24.dp)) + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()), + ) { + Spacer(modifier = Modifier.height(16.dp)) - // TODO: 탈퇴 유의 사항 기입 + Text( + text = "탈퇴 유의사항", + style = HapHapTheme.typography.caption.m12, + color = HapHapTheme.colors.gray400, + ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(24.dp)) - HorizontalDivider( - color = HapHapTheme.colors.gray200, - thickness = 1.dp, - ) + // TODO: 탈퇴 유의 사항 기입 - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(12.dp)) - Text( - text = "탈퇴 사유를 선택해주세요(필수)", - style = HapHapTheme.typography.body.sb14, - color = HapHapTheme.colors.gray700, - ) + HorizontalDivider( + color = HapHapTheme.colors.gray200, + thickness = 1.dp, + ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(16.dp)) - LeaveReasonType.entries.forEach { reason -> - LeaveCheckItem( - context = reason.text, - onCheckedChange = { onReasonClick(reason) }, - checked = reason == uiState.selectedReason, - isEtc = reason == LeaveReasonType.ETC, - etcReasonState = etcReasonState, + Text( + text = "탈퇴 사유를 선택해주세요(필수)", + style = HapHapTheme.typography.body.sb14, + color = HapHapTheme.colors.gray700, ) - } - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(12.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - HapHapBasicButton( - text = "취소", - onClick = onBackClick, - modifier = Modifier.weight(1f), - textStyle = HapHapTheme.typography.body.b18, - colorType = ButtonType.Cancel, - ) - HapHapBasicButton( - text = "탈퇴하기", - onClick = onLeaveClick, - modifier = Modifier.weight(1f), - textStyle = HapHapTheme.typography.body.b18, - colorType = ButtonType.Primary(enabled = uiState.isButtonEnabled), - ) + LeaveReasonType.entries.forEach { reason -> + LeaveCheckItem( + context = reason.text, + onCheckedChange = { onReasonClick(reason) }, + checked = reason == uiState.selectedReason, + isEtc = reason == LeaveReasonType.ETC, + etcReasonState = etcReasonState, + ) + } + + Spacer(modifier = Modifier.height(2.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + HapHapBasicButton( + text = "취소", + onClick = onBackClick, + modifier = Modifier.weight(1f), + textStyle = HapHapTheme.typography.body.b18, + colorType = ButtonType.Cancel, + ) + HapHapBasicButton( + text = "탈퇴하기", + onClick = onLeaveClick, + modifier = Modifier.weight(1f), + textStyle = HapHapTheme.typography.body.b18, + colorType = ButtonType.Primary(enabled = uiState.isButtonEnabled), + ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt index cbe2bc84b..1d9473b36 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt @@ -1,13 +1,16 @@ package com.haphap.app.presentation.leave.component import androidx.compose.foundation.background +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column 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.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.InputTransformation import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState @@ -22,8 +25,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import com.haphap.app.R import com.haphap.app.core.designsystem.component.textfield.HapHapBasicTextField @@ -31,6 +37,7 @@ import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.extensions.noRippleClickable private const val ETC_REASON_MAX_LENGTH = 150 +private val ETC_REASON_HEIGHT_SAMPLE_TEXT = "가".repeat(ETC_REASON_MAX_LENGTH) @Composable fun LeaveCheckItem( @@ -52,6 +59,7 @@ fun LeaveCheckItem( .noRippleClickable( onClick = { onCheckedChange(!checked) } ), + verticalAlignment = Alignment.CenterVertically, ) { Icon( imageVector = ImageVector.vectorResource( @@ -75,26 +83,47 @@ fun LeaveCheckItem( } if (isEtc && checked) { - Spacer(modifier = Modifier.height(2.dp)) + Spacer(modifier = Modifier.height(12.dp)) Column( modifier = Modifier .fillMaxWidth() - .background(color = HapHapTheme.colors.gray100) + .background( + color = HapHapTheme.colors.gray100, + shape = RoundedCornerShape(12.dp), + ) .padding(horizontal = 10.dp, vertical = 12.dp), ) { - HapHapBasicTextField( - state = etcReasonState, - textColor = HapHapTheme.colors.gray500, - textStyle = HapHapTheme.typography.caption.r11, - placeholder = "최대 150자까지 입력이 가능해요.", - placeholderColor = HapHapTheme.colors.gray300, - placeholderStyle = HapHapTheme.typography.caption.r11, - inputTransformation = InputTransformation { - if (asCharSequence().trimStart().length > ETC_REASON_MAX_LENGTH) revertAllChanges() - }, - modifier = Modifier.fillMaxWidth(), - ) + val textStyle = HapHapTheme.typography.caption.r11 + val textMeasurer = rememberTextMeasurer() + val density = LocalDensity.current + + BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { + // 150자를 모두 입력했을 때의 높이를 처음부터 확보 + val maxTextHeight = remember(constraints.maxWidth, textStyle) { + val maxTextLayout = textMeasurer.measure( + text = ETC_REASON_HEIGHT_SAMPLE_TEXT, + style = textStyle, + constraints = Constraints(maxWidth = constraints.maxWidth), + ) + with(density) { maxTextLayout.size.height.toDp() } + } + + HapHapBasicTextField( + state = etcReasonState, + textColor = HapHapTheme.colors.gray500, + textStyle = textStyle, + placeholder = "최대 150자까지 입력이 가능해요.", + placeholderColor = HapHapTheme.colors.gray300, + placeholderStyle = textStyle, + inputTransformation = InputTransformation { + if (asCharSequence().trimStart().length > ETC_REASON_MAX_LENGTH) revertAllChanges() + }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = maxTextHeight), + ) + } Spacer(modifier = Modifier.height(4.dp)) From 1a595b9abe500f4e0c4c94c3181bdef9b013967f Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 20:46:00 +0900 Subject: [PATCH 16/49] =?UTF-8?q?[del/#109]=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../haphap/app/presentation/leave/component/LeaveCheckItem.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt index 1d9473b36..2ab811e67 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt @@ -99,7 +99,6 @@ fun LeaveCheckItem( val density = LocalDensity.current BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { - // 150자를 모두 입력했을 때의 높이를 처음부터 확보 val maxTextHeight = remember(constraints.maxWidth, textStyle) { val maxTextLayout = textMeasurer.measure( text = ETC_REASON_HEIGHT_SAMPLE_TEXT, From c236a1d74e757b2270e2d665aca91de33ebf985c Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 21:39:47 +0900 Subject: [PATCH 17/49] =?UTF-8?q?[feat/#109]=20=ED=82=A4=EB=B3=B4=EB=93=9C?= =?UTF-8?q?=EA=B0=80=20=EC=82=AC=EC=9C=A0=20=EC=9E=85=EB=A0=A5=20=EC=B9=B8?= =?UTF-8?q?=20=EC=95=88=20=EA=B0=80=EB=A6=AC=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../haphap/app/presentation/leave/LeaveScreen.kt | 15 ++++++++++++++- .../leave/navigation/LeaveNavigation.kt | 11 ++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index 6b2a5a3e9..27cdd48d7 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -1,13 +1,19 @@ package com.haphap.app.presentation.leave +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.union +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.verticalScroll @@ -75,6 +81,7 @@ private fun LeaveScreen( Column( modifier = modifier .fillMaxSize() + .windowInsetsPadding(WindowInsets.navigationBars.union(WindowInsets.ime)) .padding(horizontal = 20.dp), ) { LeaveTopBar( @@ -84,6 +91,7 @@ private fun LeaveScreen( Column( modifier = Modifier .fillMaxWidth() + .weight(1f) .verticalScroll(rememberScrollState()), ) { Spacer(modifier = Modifier.height(16.dp)) @@ -97,6 +105,9 @@ private fun LeaveScreen( Spacer(modifier = Modifier.height(24.dp)) // TODO: 탈퇴 유의 사항 기입 + Row( + modifier = Modifier.height(400.dp).background(color = HapHapTheme.colors.gray700) + ) { } Spacer(modifier = Modifier.height(12.dp)) @@ -128,7 +139,9 @@ private fun LeaveScreen( Spacer(modifier = Modifier.height(2.dp)) Row( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 10.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), ) { HapHapBasicButton( diff --git a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt index 9446bb4b1..0b474e56f 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt @@ -1,8 +1,11 @@ package com.haphap.app.presentation.leave.navigation import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -27,6 +30,8 @@ fun NavGraphBuilder.leaveGraph( navController: NavController, ) { composable { + val layoutDirection = LocalLayoutDirection.current + LeaveRoute( navigateBack = { navController.popBackStack() }, navigateToLeaveComplete = { @@ -34,7 +39,11 @@ fun NavGraphBuilder.leaveGraph( navOptions = navController.clearBackStackNavOptions(), ) }, - modifier = Modifier.padding(innerPadding), + modifier = Modifier.padding( + top = innerPadding.calculateTopPadding(), + start = innerPadding.calculateStartPadding(layoutDirection), + end = innerPadding.calculateEndPadding(layoutDirection), + ), ) } } From 55ab08839326218c4740ccaa1b0b787654eb5229 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 22:15:39 +0900 Subject: [PATCH 18/49] =?UTF-8?q?[feat/#109]=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EC=99=84=EB=A3=8C=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/leave/LeaveCompleteScreen.kt | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt index 83114d943..7ae066742 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveCompleteScreen.kt @@ -1,16 +1,22 @@ package com.haphap.app.presentation.leave +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer 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.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.haphap.app.R import com.haphap.app.core.designsystem.component.button.HapHapBasicButton import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.designsystem.type.ButtonType @@ -35,15 +41,33 @@ fun LeaveCompleteScreen( modifier = modifier .fillMaxSize() .background(HapHapTheme.colors.white) - .padding(start = 20.dp, end = 20.dp, top = 43.dp), + .padding(start = 20.dp, end = 20.dp, top = 36.dp), ) { Text( - text = "회원 탈퇴가 완료되었어요.", + text = "회원 탈퇴가 완료됐어요.", style = HapHapTheme.typography.subtitle.b22, color = HapHapTheme.colors.gray800, ) - Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "앞으로의 여정도 응원할게요.\n언제든 다시 만나요!", + style = HapHapTheme.typography.body.m14, + color = HapHapTheme.colors.gray500, + ) + + Spacer(modifier = Modifier.weight(132f / 342f)) + + Image( + painter = painterResource(R.drawable.img_register_check), + contentDescription = null, + modifier = Modifier + .size(210.dp) + .align(Alignment.CenterHorizontally), + ) + + Spacer(modifier = Modifier.weight(210f / 342f)) HapHapBasicButton( text = "완료", From ac54111843e15e4db7bb1d86b6f7f852a781ae23 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 22:20:52 +0900 Subject: [PATCH 19/49] =?UTF-8?q?[mod/#109]=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=9C=A0=EC=9D=98=20=EC=82=AC=ED=95=AD=20=EC=98=81=EC=97=AD=20?= =?UTF-8?q?=EC=83=89=EC=B9=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/haphap/app/presentation/leave/LeaveScreen.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index 27cdd48d7..285aa37f5 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.union import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider @@ -106,8 +107,14 @@ private fun LeaveScreen( // TODO: 탈퇴 유의 사항 기입 Row( - modifier = Modifier.height(400.dp).background(color = HapHapTheme.colors.gray700) - ) { } + modifier = Modifier + .fillMaxWidth() + .height(400.dp) + .background( + color = HapHapTheme.colors.gray200, + shape = RoundedCornerShape(19.dp), + ) + ) {} Spacer(modifier = Modifier.height(12.dp)) From 7b1db5d6f3350e34f1e658bc8e9756fca80c15fe Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 22:48:17 +0900 Subject: [PATCH 20/49] =?UTF-8?q?[feat/#109]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/remote/datasource/api/auth/AuthDataSource.kt | 3 +++ .../remote/datasource/impl/auth/AuthDataSourceImpl.kt | 6 ++++++ .../app/data/remote/dto/auth/LogoutRequestDto.kt | 11 +++++++++++ .../com/haphap/app/data/remote/service/AuthService.kt | 7 +++++++ 4 files changed, 27 insertions(+) create mode 100644 app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt index 86a7114de..6fb26d79f 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt @@ -3,7 +3,10 @@ package com.haphap.app.data.remote.datasource.api.auth import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto +import com.haphap.app.data.remote.dto.auth.LogoutRequestDto +import retrofit2.Response interface AuthDataSource { suspend fun postKakaoLogin(requestDto: KakaoLoginRequestDto): BaseResponse + suspend fun postLogout(requestDto: LogoutRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt index 446b0770e..7694f0f97 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt @@ -4,7 +4,9 @@ import com.haphap.app.data.remote.datasource.api.auth.AuthDataSource import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto +import com.haphap.app.data.remote.dto.auth.LogoutRequestDto import com.haphap.app.data.remote.service.AuthService +import retrofit2.Response import javax.inject.Inject class AuthDataSourceImpl @Inject constructor( @@ -14,4 +16,8 @@ class AuthDataSourceImpl @Inject constructor( override suspend fun postKakaoLogin(requestDto: KakaoLoginRequestDto): BaseResponse { return authService.kakaoLogin(requestDto) } + + override suspend fun postLogout(requestDto: LogoutRequestDto): Response { + return authService.logout(requestDto) + } } diff --git a/app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt b/app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt new file mode 100644 index 000000000..c3e8eeb7d --- /dev/null +++ b/app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt @@ -0,0 +1,11 @@ +package com.haphap.app.data.remote.dto.auth + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class LogoutRequestDto( + @SerialName("deviceId") + val deviceId: String, +) { +} \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt b/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt index f717b5990..edbf2229d 100644 --- a/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt +++ b/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt @@ -3,6 +3,8 @@ package com.haphap.app.data.remote.service import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto +import com.haphap.app.data.remote.dto.auth.LogoutRequestDto +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.POST @@ -11,4 +13,9 @@ interface AuthService { suspend fun kakaoLogin( @Body request: KakaoLoginRequestDto, ): BaseResponse + + @POST("api/v1/auth/logout") + suspend fun logout( + @Body request: LogoutRequestDto, + ): Response } From 9e05cbbaf90a1184e7d1a45418ae3d92eb8ab1dc Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Fri, 2 Oct 2026 23:05:43 +0900 Subject: [PATCH 21/49] =?UTF-8?q?[feat/#109]=20FCM=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=ED=95=9C=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/data/local/datasource/api/LocalFcmDataSource.kt | 1 + .../data/local/datasource/impl/LocalFcmDataSourceImpl.kt | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/com/haphap/app/data/local/datasource/api/LocalFcmDataSource.kt b/app/src/main/java/com/haphap/app/data/local/datasource/api/LocalFcmDataSource.kt index 3c7260509..f3076e910 100644 --- a/app/src/main/java/com/haphap/app/data/local/datasource/api/LocalFcmDataSource.kt +++ b/app/src/main/java/com/haphap/app/data/local/datasource/api/LocalFcmDataSource.kt @@ -3,4 +3,5 @@ package com.haphap.app.data.local.datasource.api interface LocalFcmDataSource { suspend fun getFcmToken(): String? suspend fun setFcmToken(fcmToken: String) + suspend fun clearFcmToken() } diff --git a/app/src/main/java/com/haphap/app/data/local/datasource/impl/LocalFcmDataSourceImpl.kt b/app/src/main/java/com/haphap/app/data/local/datasource/impl/LocalFcmDataSourceImpl.kt index ae81ab1cb..a1a6982fc 100644 --- a/app/src/main/java/com/haphap/app/data/local/datasource/impl/LocalFcmDataSourceImpl.kt +++ b/app/src/main/java/com/haphap/app/data/local/datasource/impl/LocalFcmDataSourceImpl.kt @@ -24,6 +24,12 @@ class LocalFcmDataSourceImpl @Inject constructor( } } + override suspend fun clearFcmToken() { + dataStore.edit { prefs -> + prefs.remove(FCM_TOKEN) + } + } + companion object { private val FCM_TOKEN = stringPreferencesKey("FCM_TOKEN") } From 22e9c9e7691d478bdd2c79872abac9c5013f4442 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Sun, 4 Oct 2026 16:20:48 +0900 Subject: [PATCH 22/49] =?UTF-8?q?[feat/#109]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20API=20Repository=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/api/auth/AuthRepository.kt | 1 + .../impl/auth/AuthRepositoryImpl.kt | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt b/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt index 8d3cc9102..f037232e5 100644 --- a/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt +++ b/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt @@ -5,4 +5,5 @@ import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto interface AuthRepository { suspend fun postKakaoLogin(accessToken: String): Result + suspend fun postLogout(): Result } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt index 67a1f5a1a..566e82541 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt @@ -1,18 +1,24 @@ package com.haphap.app.data.repository.impl.auth +import com.haphap.app.core.fcm.FirebaseMessagingManager import com.haphap.app.core.util.suspendRunCatching +import com.haphap.app.data.local.datasource.api.LocalFcmDataSource import com.haphap.app.data.local.datasource.api.LocalTokenDataSource import com.haphap.app.data.mapper.auth.toModel import com.haphap.app.data.model.auth.KakaoLoginModel import com.haphap.app.data.remote.datasource.api.auth.AuthDataSource import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto +import com.haphap.app.data.remote.dto.auth.LogoutRequestDto import com.haphap.app.data.remote.dto.checkData import com.haphap.app.data.repository.api.auth.AuthRepository import jakarta.inject.Inject +import retrofit2.HttpException class AuthRepositoryImpl @Inject constructor( private val authDataSource: AuthDataSource, private val localTokenDataSource: LocalTokenDataSource, + private val localFcmDataSource: LocalFcmDataSource, + private val firebaseMessagingManager: FirebaseMessagingManager ): AuthRepository { override suspend fun postKakaoLogin(accessToken: String): Result = suspendRunCatching { @@ -24,4 +30,18 @@ class AuthRepositoryImpl @Inject constructor( response.toModel() } + + override suspend fun postLogout(): Result = + suspendRunCatching { + try { + val deviceId = firebaseMessagingManager.getInstallationId() + ?: throw IllegalStateException("Device id is null") + val response = authDataSource.postLogout(LogoutRequestDto(deviceId)) + + if (!response.isSuccessful) throw HttpException(response) + } finally { + localTokenDataSource.clearTokens() + localFcmDataSource.clearFcmToken() + } + } } From fbb28ff8cfbd32dddc6b8cd2ebd1f1d4f54c0044 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Sun, 4 Oct 2026 22:59:21 +0900 Subject: [PATCH 23/49] =?UTF-8?q?[feat/#109]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/setting/SettingContract.kt | 1 + .../app/presentation/setting/SettingScreen.kt | 3 +++ .../presentation/setting/SettingViewModel.kt | 18 +++++++++++++++--- .../setting/navigation/SettingNavigation.kt | 3 +++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt index c1e436384..99f44cfbb 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt @@ -9,6 +9,7 @@ sealed interface SettingContract { sealed interface SideEffect { data object NavigateToMyPage : SideEffect data object NavigateToLeave : SideEffect + data object NavigateToLogin : SideEffect } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt index ab0486814..232d95fdd 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingScreen.kt @@ -19,6 +19,7 @@ import com.haphap.app.core.designsystem.component.modal.HapHapDialog import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLeave import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage +import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLogin import com.haphap.app.presentation.setting.component.SettingMenuItem import com.haphap.app.presentation.setting.component.SettingSectionDivider import com.haphap.app.presentation.setting.component.SettingSectionTitle @@ -28,6 +29,7 @@ import com.haphap.app.presentation.setting.component.SettingTopBar fun SettingRoute( navigateBack: () -> Unit, navigateToLeave: () -> Unit, + navigateToLogin: () -> Unit, modifier: Modifier = Modifier, viewModel: SettingViewModel = hiltViewModel(), ) { @@ -40,6 +42,7 @@ fun SettingRoute( when (sideEffect) { is NavigateToMyPage -> navigateBack() is NavigateToLeave -> navigateToLeave() + is NavigateToLogin -> navigateToLogin() } } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt index 50fb49f70..0f6c4c20a 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -2,8 +2,10 @@ package com.haphap.app.presentation.setting import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.haphap.app.data.repository.api.auth.AuthRepository import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLeave import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage +import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLogin import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow @@ -15,7 +17,9 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class SettingViewModel @Inject constructor() : ViewModel() { +class SettingViewModel @Inject constructor( + private val authRepository: AuthRepository, +) : ViewModel() { private val _uiState = MutableStateFlow(SettingContract.State()) val uiState: StateFlow = _uiState.asStateFlow() @@ -37,8 +41,16 @@ class SettingViewModel @Inject constructor() : ViewModel() { } fun onLogoutConfirm() { - _uiState.update { it.copy(isLogoutDialogVisible = false) } - // TODO: 로그아웃 처리 + _uiState.update { + it.copy( + isLogoutDialogVisible = false, + settingUiState = SettingUiState.Loading, + ) + } + viewModelScope.launch { + authRepository.postLogout() + _sideEffect.send(NavigateToLogin) + } } fun onLeaveClick() { diff --git a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt index 9fa83fec5..60c8496bd 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt @@ -7,7 +7,9 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import com.haphap.app.core.extensions.clearBackStackNavOptions import com.haphap.app.core.navigation.Route +import com.haphap.app.presentation.auth.navigation.navigateToLogin import com.haphap.app.presentation.leave.navigation.navigateToLeave import com.haphap.app.presentation.setting.SettingRoute import kotlinx.serialization.Serializable @@ -24,6 +26,7 @@ fun NavGraphBuilder.settingGraph( SettingRoute( navigateBack = { navController.popBackStack() }, navigateToLeave = { navController.navigateToLeave() }, + navigateToLogin = { navController.navigateToLogin(navOptions = navController.clearBackStackNavOptions())}, modifier = Modifier.padding(innerPadding), ) } From 6e1efd9a4e3329bcf9db6f7d377e1fea69123abf Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Sun, 4 Oct 2026 23:03:10 +0900 Subject: [PATCH 24/49] =?UTF-8?q?[fix/#109]=20Authorization=20=ED=97=A4?= =?UTF-8?q?=EB=8D=94=20=EC=A7=81=EC=A0=91=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/data/remote/datasource/api/auth/AuthDataSource.kt | 2 +- .../data/remote/datasource/impl/auth/AuthDataSourceImpl.kt | 4 ++-- .../java/com/haphap/app/data/remote/service/AuthService.kt | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt index 6fb26d79f..e220872f8 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt @@ -8,5 +8,5 @@ import retrofit2.Response interface AuthDataSource { suspend fun postKakaoLogin(requestDto: KakaoLoginRequestDto): BaseResponse - suspend fun postLogout(requestDto: LogoutRequestDto): Response + suspend fun postLogout(authorization: String, requestDto: LogoutRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt index 7694f0f97..1e44d585a 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt @@ -17,7 +17,7 @@ class AuthDataSourceImpl @Inject constructor( return authService.kakaoLogin(requestDto) } - override suspend fun postLogout(requestDto: LogoutRequestDto): Response { - return authService.logout(requestDto) + override suspend fun postLogout(authorization: String, requestDto: LogoutRequestDto): Response { + return authService.logout(authorization, requestDto) } } diff --git a/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt b/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt index edbf2229d..b8f3ed8e9 100644 --- a/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt +++ b/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt @@ -6,6 +6,7 @@ import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto import com.haphap.app.data.remote.dto.auth.LogoutRequestDto import retrofit2.Response import retrofit2.http.Body +import retrofit2.http.Header import retrofit2.http.POST interface AuthService { @@ -16,6 +17,7 @@ interface AuthService { @POST("api/v1/auth/logout") suspend fun logout( + @Header("Authorization") authorization: String, @Body request: LogoutRequestDto, ): Response } From fac76f438a9dba1025b0f60c3b03faaf726ea65f Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Sun, 4 Oct 2026 23:06:46 +0900 Subject: [PATCH 25/49] =?UTF-8?q?[mod/#109]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=84=B1=EA=B3=B5=ED=96=88=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=EB=A7=8C=20=EB=A1=9C=EC=BB=AC=20=ED=86=A0=ED=81=B0=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/auth/AuthRepositoryImpl.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt index 566e82541..6c28127d8 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt @@ -33,15 +33,19 @@ class AuthRepositoryImpl @Inject constructor( override suspend fun postLogout(): Result = suspendRunCatching { - try { + val accessToken = localTokenDataSource.getAccessToken() + + if (!accessToken.isNullOrBlank()) { val deviceId = firebaseMessagingManager.getInstallationId() ?: throw IllegalStateException("Device id is null") - val response = authDataSource.postLogout(LogoutRequestDto(deviceId)) - - if (!response.isSuccessful) throw HttpException(response) - } finally { - localTokenDataSource.clearTokens() - localFcmDataSource.clearFcmToken() + val response = authDataSource.postLogout( + authorization = "Bearer $accessToken", + requestDto = LogoutRequestDto(deviceId), + ) + if (!response.isSuccessful && response.code() != 401) throw HttpException(response) } + + localTokenDataSource.clearTokens() + localFcmDataSource.clearFcmToken() } } From 93791083327021dce09c35eb22c270e8d33949b0 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Sun, 4 Oct 2026 23:10:38 +0900 Subject: [PATCH 26/49] =?UTF-8?q?[mod/#109]=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/setting/SettingViewModel.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt index 0f6c4c20a..17272a738 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -33,7 +33,8 @@ class SettingViewModel @Inject constructor( } fun onLogoutClick() { - _uiState.update { it.copy(isLogoutDialogVisible = true) } + if (_uiState.value.settingUiState is SettingUiState.Loading) + return _uiState.update { it.copy(isLogoutDialogVisible = true) } } fun onLogoutDialogDismiss() { @@ -41,15 +42,22 @@ class SettingViewModel @Inject constructor( } fun onLogoutConfirm() { - _uiState.update { - it.copy( - isLogoutDialogVisible = false, - settingUiState = SettingUiState.Loading, - ) - } + if (_uiState.value.settingUiState is SettingUiState.Loading) + return _uiState.update { + it.copy( + isLogoutDialogVisible = false, + settingUiState = SettingUiState.Loading + ) + } + viewModelScope.launch { authRepository.postLogout() - _sideEffect.send(NavigateToLogin) + .onSuccess { _sideEffect.send(NavigateToLogin) } + .onFailure { + _uiState.update { + it.copy(settingUiState = SettingUiState.Idle) + } + } } } From d9ef45523f584b2a9002dcdb33d3c4f3d5931042 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Sun, 4 Oct 2026 23:16:58 +0900 Subject: [PATCH 27/49] =?UTF-8?q?[mod/#109]=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/auth/AuthRepositoryImpl.kt | 4 ++-- .../presentation/setting/SettingViewModel.kt | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt index 6c28127d8..e5d3a954e 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt @@ -44,8 +44,8 @@ class AuthRepositoryImpl @Inject constructor( ) if (!response.isSuccessful && response.code() != 401) throw HttpException(response) } - - localTokenDataSource.clearTokens() + localFcmDataSource.clearFcmToken() + localTokenDataSource.clearTokens() } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt index 17272a738..b0247a0b2 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -33,8 +33,8 @@ class SettingViewModel @Inject constructor( } fun onLogoutClick() { - if (_uiState.value.settingUiState is SettingUiState.Loading) - return _uiState.update { it.copy(isLogoutDialogVisible = true) } + if (_uiState.value.settingUiState is SettingUiState.Loading) return + _uiState.update { it.copy(isLogoutDialogVisible = true) } } fun onLogoutDialogDismiss() { @@ -42,13 +42,13 @@ class SettingViewModel @Inject constructor( } fun onLogoutConfirm() { - if (_uiState.value.settingUiState is SettingUiState.Loading) - return _uiState.update { - it.copy( - isLogoutDialogVisible = false, - settingUiState = SettingUiState.Loading - ) - } + if (_uiState.value.settingUiState is SettingUiState.Loading) return + _uiState.update { + it.copy( + isLogoutDialogVisible = false, + settingUiState = SettingUiState.Loading, + ) + } viewModelScope.launch { authRepository.postLogout() From 50d5a065fbc66be9731a83fc49988ed623893770 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 00:00:02 +0900 Subject: [PATCH 28/49] =?UTF-8?q?[feat/#109]=20=ED=9A=8C=EC=9B=90=ED=83=88?= =?UTF-8?q?=ED=87=B4=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/api/mypage/MyPageDataSource.kt | 3 +++ .../impl/mypage/MyPageDataSourceImpl.kt | 5 +++++ .../data/remote/dto/mypage/LeaveRequestDto.kt | 13 +++++++++++++ .../app/data/remote/service/MyPageService.kt | 7 +++++++ .../repository/api/mypage/MyPageRepository.kt | 1 + .../impl/mypage/MyPageRepositoryImpl.kt | 18 ++++++++++++++++++ 6 files changed, 47 insertions(+) create mode 100644 app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt index be3e4db2d..5be0cedff 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt @@ -1,8 +1,11 @@ package com.haphap.app.data.remote.datasource.api.mypage import com.haphap.app.data.remote.dto.BaseResponse +import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto import com.haphap.app.data.remote.dto.mypage.MyPageResponseDto +import retrofit2.Response interface MyPageDataSource{ suspend fun getMyPage(): BaseResponse + suspend fun deleteMember(requestDto: LeaveRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt index 6f7d39d4e..aeabff635 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt @@ -2,9 +2,11 @@ package com.haphap.app.data.remote.datasource.impl.mypage import com.haphap.app.data.remote.datasource.api.mypage.MyPageDataSource import com.haphap.app.data.remote.dto.BaseResponse +import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto import com.haphap.app.data.remote.dto.mypage.MyPageResponseDto import com.haphap.app.data.remote.service.MyPageService import jakarta.inject.Inject +import retrofit2.Response class MyPageDataSourceImpl @Inject constructor( private val myPageService: MyPageService, @@ -12,4 +14,7 @@ class MyPageDataSourceImpl @Inject constructor( override suspend fun getMyPage(): BaseResponse { return myPageService.getMyPage() } + + override suspend fun deleteMember(requestDto: LeaveRequestDto): Response = + myPageService.deleteMember(requestDto) } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt new file mode 100644 index 000000000..f9ae7c7b9 --- /dev/null +++ b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt @@ -0,0 +1,13 @@ +package com.haphap.app.data.remote.dto.mypage + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class LeaveRequestDto( + @SerialName("reason") + val reason: String, + @SerialName("etcReason") + val etcReason: String? = null, +) { +} \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt b/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt index 0f05104a3..17f15716b 100644 --- a/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt +++ b/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt @@ -1,10 +1,17 @@ package com.haphap.app.data.remote.service import com.haphap.app.data.remote.dto.BaseResponse +import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto import com.haphap.app.data.remote.dto.mypage.MyPageResponseDto +import retrofit2.Response +import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.HTTP interface MyPageService { @GET("api/v1/members/me") suspend fun getMyPage(): BaseResponse + + @HTTP(method = "DELETE", path = "api/v1/members/me", hasBody = true) + suspend fun deleteMember(@Body request: LeaveRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt b/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt index 0b2a66f2e..595efcc4e 100644 --- a/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt +++ b/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt @@ -4,4 +4,5 @@ import com.haphap.app.data.model.mypage.MyPageModel interface MyPageRepository { suspend fun getMyPage(): Result + suspend fun deleteMember(reason: String, etcReason: String?): Result } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt index 60ef58704..40d1ee466 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt @@ -1,19 +1,37 @@ package com.haphap.app.data.repository.impl.mypage import com.haphap.app.core.util.suspendRunCatching +import com.haphap.app.data.local.datasource.api.LocalFcmDataSource +import com.haphap.app.data.local.datasource.api.LocalTokenDataSource import com.haphap.app.data.mapper.mypage.toModel import com.haphap.app.data.model.mypage.MyPageModel import com.haphap.app.data.remote.datasource.api.mypage.MyPageDataSource import com.haphap.app.data.remote.dto.checkData +import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto import com.haphap.app.data.repository.api.mypage.MyPageRepository import jakarta.inject.Inject +import retrofit2.HttpException class MyPageRepositoryImpl @Inject constructor( private val myPageDataSource: MyPageDataSource, + private val localTokenDataSource: LocalTokenDataSource, + private val localFcmDataSource: LocalFcmDataSource, ): MyPageRepository { override suspend fun getMyPage(): Result = suspendRunCatching { val response = myPageDataSource.getMyPage().checkData() response.toModel() } + + override suspend fun deleteMember(reason: String, etcReason: String?): Result = + suspendRunCatching { + val response = myPageDataSource.deleteMember( + LeaveRequestDto(reason = reason, etcReason = etcReason), + ) + + if (!response.isSuccessful && response.code() != 404) throw HttpException(response) + + localFcmDataSource.clearFcmToken() + localTokenDataSource.clearTokens() + } } \ No newline at end of file From 0ed888c11cf06510189c8f41de996b993ba66e09 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 00:03:26 +0900 Subject: [PATCH 29/49] =?UTF-8?q?[mod/#109]=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=82=AC=EC=9C=A0=20enum=EC=97=90=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B0=92=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/type/LeaveReasonType.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt b/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt index 1e3fbb772..99d1c1b1e 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/type/LeaveReasonType.kt @@ -1,9 +1,9 @@ package com.haphap.app.presentation.leave.type -enum class LeaveReasonType(val text: String) { - JOB_SEARCH_FINISHED("취업 준비 활동이 끝났어요."), - LACK_OF_INFORMATION("원하는 정보가 부족해요."), - RARELY_USED("서비스를 잘 사용하지 않아요."), - PRIVACY_CONCERN("개인정보(보안) 유출이 걱정돼요."), - ETC("기타 (직접 입력)"), +enum class LeaveReasonType(val text: String, val serverValue: String) { + JOB_SEARCH_FINISHED("취업 준비 활동이 끝났어요.", "JOB_PREPARATION_FINISHED"), + LACK_OF_INFORMATION("원하는 정보가 부족해요.", "NOT_ENOUGH_INFO"), + RARELY_USED("서비스를 잘 사용하지 않아요.", "LOW_USAGE"), + PRIVACY_CONCERN("개인정보(보안) 유출이 걱정돼요.", "PRIVACY_CONCERN"), + ETC("기타 (직접 입력)", "ETC"), } From 1b06b3b101059b3eae6c0dadf62eb3b68c1a2529 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 00:06:03 +0900 Subject: [PATCH 30/49] =?UTF-8?q?[feat/#109]=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20Contract=EC=97=90=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/haphap/app/presentation/leave/LeaveContract.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt index e3ea0325b..97d5cba71 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveContract.kt @@ -8,9 +8,10 @@ sealed interface LeaveContract { data class State( val selectedReason: LeaveReasonType? = null, val isEtcReasonBlank: Boolean = true, + val isLoading: Boolean = false, ) { val isButtonEnabled: Boolean - get() = when (selectedReason) { + get() = !isLoading && when (selectedReason) { null -> false LeaveReasonType.ETC -> !isEtcReasonBlank else -> true @@ -21,4 +22,4 @@ sealed interface LeaveContract { data object NavigateToSetting : SideEffect data object NavigateToLeaveComplete : SideEffect } -} +} \ No newline at end of file From 31dbff770474631ba5c48be4509012c8564972e7 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 00:16:24 +0900 Subject: [PATCH 31/49] =?UTF-8?q?[feat/#109]=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20ViewModel=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/LeaveViewModel.kt | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt index e53bc33e1..00eaca28b 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.haphap.app.data.repository.api.mypage.MyPageRepository import com.haphap.app.presentation.leave.LeaveContract.SideEffect.NavigateToLeaveComplete import com.haphap.app.presentation.leave.LeaveContract.SideEffect.NavigateToSetting import com.haphap.app.presentation.leave.type.LeaveReasonType @@ -18,7 +19,9 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class LeaveViewModel @Inject constructor() : ViewModel() { +class LeaveViewModel @Inject constructor( + private val myPageRepository: MyPageRepository, +) : ViewModel() { private val _uiState = MutableStateFlow(LeaveContract.State()) val uiState: StateFlow = _uiState.asStateFlow() @@ -51,10 +54,23 @@ class LeaveViewModel @Inject constructor() : ViewModel() { } fun onLeaveClick() { - if (!_uiState.value.isButtonEnabled) return - // TODO: 회원 탈퇴 처리 + val state = _uiState.value + if (!state.isButtonEnabled) return + + val reason = state.selectedReason ?: return + + _uiState.update { it.copy(isLoading = true) } + viewModelScope.launch { - _sideEffect.send(NavigateToLeaveComplete) + myPageRepository.deleteMember( + reason = reason.serverValue, + etcReason = if (reason == LeaveReasonType.ETC) { + etcReasonState.text.toString().trim() + } else { + null + }, + ).onSuccess { _sideEffect.send(NavigateToLeaveComplete) } + .onFailure { _uiState.update { it.copy(isLoading = false) } } } } } From a68986804b4f3fb5f787648d09e9a35693d5dd6e Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 18:58:41 +0900 Subject: [PATCH 32/49] =?UTF-8?q?[refactor/#109]=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EB=A1=9C=EC=A7=81=20Auth=20->=20mypage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/api/auth/AuthDataSource.kt | 3 --- .../datasource/api/mypage/MyPageDataSource.kt | 2 ++ .../impl/auth/AuthDataSourceImpl.kt | 6 ----- .../impl/mypage/MyPageDataSourceImpl.kt | 4 ++++ .../dto/{auth => mypage}/LogoutRequestDto.kt | 2 +- .../app/data/remote/service/AuthService.kt | 9 ------- .../app/data/remote/service/MyPageService.kt | 5 ++++ .../repository/api/auth/AuthRepository.kt | 2 -- .../repository/api/mypage/MyPageRepository.kt | 1 + .../impl/auth/AuthRepositoryImpl.kt | 24 ------------------- .../impl/mypage/MyPageRepositoryImpl.kt | 19 +++++++++++++++ .../presentation/setting/SettingViewModel.kt | 6 ++--- 12 files changed, 35 insertions(+), 48 deletions(-) rename app/src/main/java/com/haphap/app/data/remote/dto/{auth => mypage}/LogoutRequestDto.kt (80%) diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt index e220872f8..86a7114de 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/api/auth/AuthDataSource.kt @@ -3,10 +3,7 @@ package com.haphap.app.data.remote.datasource.api.auth import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto -import com.haphap.app.data.remote.dto.auth.LogoutRequestDto -import retrofit2.Response interface AuthDataSource { suspend fun postKakaoLogin(requestDto: KakaoLoginRequestDto): BaseResponse - suspend fun postLogout(authorization: String, requestDto: LogoutRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt index 5be0cedff..ebb59aff4 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/api/mypage/MyPageDataSource.kt @@ -2,10 +2,12 @@ package com.haphap.app.data.remote.datasource.api.mypage import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto +import com.haphap.app.data.remote.dto.mypage.LogoutRequestDto import com.haphap.app.data.remote.dto.mypage.MyPageResponseDto import retrofit2.Response interface MyPageDataSource{ suspend fun getMyPage(): BaseResponse suspend fun deleteMember(requestDto: LeaveRequestDto): Response + suspend fun postLogout(requestDto: LogoutRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt index 1e44d585a..446b0770e 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/auth/AuthDataSourceImpl.kt @@ -4,9 +4,7 @@ import com.haphap.app.data.remote.datasource.api.auth.AuthDataSource import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto -import com.haphap.app.data.remote.dto.auth.LogoutRequestDto import com.haphap.app.data.remote.service.AuthService -import retrofit2.Response import javax.inject.Inject class AuthDataSourceImpl @Inject constructor( @@ -16,8 +14,4 @@ class AuthDataSourceImpl @Inject constructor( override suspend fun postKakaoLogin(requestDto: KakaoLoginRequestDto): BaseResponse { return authService.kakaoLogin(requestDto) } - - override suspend fun postLogout(authorization: String, requestDto: LogoutRequestDto): Response { - return authService.logout(authorization, requestDto) - } } diff --git a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt index aeabff635..89c427d74 100644 --- a/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt +++ b/app/src/main/java/com/haphap/app/data/remote/datasource/impl/mypage/MyPageDataSourceImpl.kt @@ -3,6 +3,7 @@ package com.haphap.app.data.remote.datasource.impl.mypage import com.haphap.app.data.remote.datasource.api.mypage.MyPageDataSource import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto +import com.haphap.app.data.remote.dto.mypage.LogoutRequestDto import com.haphap.app.data.remote.dto.mypage.MyPageResponseDto import com.haphap.app.data.remote.service.MyPageService import jakarta.inject.Inject @@ -17,4 +18,7 @@ class MyPageDataSourceImpl @Inject constructor( override suspend fun deleteMember(requestDto: LeaveRequestDto): Response = myPageService.deleteMember(requestDto) + + override suspend fun postLogout(requestDto: LogoutRequestDto): Response = + myPageService.logout(requestDto) } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt similarity index 80% rename from app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt rename to app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt index c3e8eeb7d..b4b97f05a 100644 --- a/app/src/main/java/com/haphap/app/data/remote/dto/auth/LogoutRequestDto.kt +++ b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt @@ -1,4 +1,4 @@ -package com.haphap.app.data.remote.dto.auth +package com.haphap.app.data.remote.dto.mypage import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt b/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt index b8f3ed8e9..f717b5990 100644 --- a/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt +++ b/app/src/main/java/com/haphap/app/data/remote/service/AuthService.kt @@ -3,10 +3,7 @@ package com.haphap.app.data.remote.service import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto import com.haphap.app.data.remote.dto.auth.KakaoLoginResponseDto -import com.haphap.app.data.remote.dto.auth.LogoutRequestDto -import retrofit2.Response import retrofit2.http.Body -import retrofit2.http.Header import retrofit2.http.POST interface AuthService { @@ -14,10 +11,4 @@ interface AuthService { suspend fun kakaoLogin( @Body request: KakaoLoginRequestDto, ): BaseResponse - - @POST("api/v1/auth/logout") - suspend fun logout( - @Header("Authorization") authorization: String, - @Body request: LogoutRequestDto, - ): Response } diff --git a/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt b/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt index 17f15716b..7feff9c12 100644 --- a/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt +++ b/app/src/main/java/com/haphap/app/data/remote/service/MyPageService.kt @@ -2,11 +2,13 @@ package com.haphap.app.data.remote.service import com.haphap.app.data.remote.dto.BaseResponse import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto +import com.haphap.app.data.remote.dto.mypage.LogoutRequestDto import com.haphap.app.data.remote.dto.mypage.MyPageResponseDto import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.HTTP +import retrofit2.http.POST interface MyPageService { @GET("api/v1/members/me") @@ -14,4 +16,7 @@ interface MyPageService { @HTTP(method = "DELETE", path = "api/v1/members/me", hasBody = true) suspend fun deleteMember(@Body request: LeaveRequestDto): Response + + @POST("api/v1/auth/logout") + suspend fun logout(@Body request: LogoutRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt b/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt index f037232e5..08e8f29ee 100644 --- a/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt +++ b/app/src/main/java/com/haphap/app/data/repository/api/auth/AuthRepository.kt @@ -1,9 +1,7 @@ package com.haphap.app.data.repository.api.auth import com.haphap.app.data.model.auth.KakaoLoginModel -import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto interface AuthRepository { suspend fun postKakaoLogin(accessToken: String): Result - suspend fun postLogout(): Result } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt b/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt index 595efcc4e..273c751b1 100644 --- a/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt +++ b/app/src/main/java/com/haphap/app/data/repository/api/mypage/MyPageRepository.kt @@ -5,4 +5,5 @@ import com.haphap.app.data.model.mypage.MyPageModel interface MyPageRepository { suspend fun getMyPage(): Result suspend fun deleteMember(reason: String, etcReason: String?): Result + suspend fun postLogout(): Result } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt index e5d3a954e..67a1f5a1a 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/auth/AuthRepositoryImpl.kt @@ -1,24 +1,18 @@ package com.haphap.app.data.repository.impl.auth -import com.haphap.app.core.fcm.FirebaseMessagingManager import com.haphap.app.core.util.suspendRunCatching -import com.haphap.app.data.local.datasource.api.LocalFcmDataSource import com.haphap.app.data.local.datasource.api.LocalTokenDataSource import com.haphap.app.data.mapper.auth.toModel import com.haphap.app.data.model.auth.KakaoLoginModel import com.haphap.app.data.remote.datasource.api.auth.AuthDataSource import com.haphap.app.data.remote.dto.auth.KakaoLoginRequestDto -import com.haphap.app.data.remote.dto.auth.LogoutRequestDto import com.haphap.app.data.remote.dto.checkData import com.haphap.app.data.repository.api.auth.AuthRepository import jakarta.inject.Inject -import retrofit2.HttpException class AuthRepositoryImpl @Inject constructor( private val authDataSource: AuthDataSource, private val localTokenDataSource: LocalTokenDataSource, - private val localFcmDataSource: LocalFcmDataSource, - private val firebaseMessagingManager: FirebaseMessagingManager ): AuthRepository { override suspend fun postKakaoLogin(accessToken: String): Result = suspendRunCatching { @@ -30,22 +24,4 @@ class AuthRepositoryImpl @Inject constructor( response.toModel() } - - override suspend fun postLogout(): Result = - suspendRunCatching { - val accessToken = localTokenDataSource.getAccessToken() - - if (!accessToken.isNullOrBlank()) { - val deviceId = firebaseMessagingManager.getInstallationId() - ?: throw IllegalStateException("Device id is null") - val response = authDataSource.postLogout( - authorization = "Bearer $accessToken", - requestDto = LogoutRequestDto(deviceId), - ) - if (!response.isSuccessful && response.code() != 401) throw HttpException(response) - } - - localFcmDataSource.clearFcmToken() - localTokenDataSource.clearTokens() - } } diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt index 40d1ee466..fc9b52e24 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt @@ -1,5 +1,6 @@ package com.haphap.app.data.repository.impl.mypage +import com.haphap.app.core.fcm.FirebaseMessagingManager import com.haphap.app.core.util.suspendRunCatching import com.haphap.app.data.local.datasource.api.LocalFcmDataSource import com.haphap.app.data.local.datasource.api.LocalTokenDataSource @@ -8,6 +9,7 @@ import com.haphap.app.data.model.mypage.MyPageModel import com.haphap.app.data.remote.datasource.api.mypage.MyPageDataSource import com.haphap.app.data.remote.dto.checkData import com.haphap.app.data.remote.dto.mypage.LeaveRequestDto +import com.haphap.app.data.remote.dto.mypage.LogoutRequestDto import com.haphap.app.data.repository.api.mypage.MyPageRepository import jakarta.inject.Inject import retrofit2.HttpException @@ -16,6 +18,7 @@ class MyPageRepositoryImpl @Inject constructor( private val myPageDataSource: MyPageDataSource, private val localTokenDataSource: LocalTokenDataSource, private val localFcmDataSource: LocalFcmDataSource, + private val firebaseMessagingManager: FirebaseMessagingManager, ): MyPageRepository { override suspend fun getMyPage(): Result = suspendRunCatching { @@ -31,6 +34,22 @@ class MyPageRepositoryImpl @Inject constructor( if (!response.isSuccessful && response.code() != 404) throw HttpException(response) + localFcmDataSource.clearFcmToken() + localTokenDataSource.clearTokens() + } + + override suspend fun postLogout(): Result = + suspendRunCatching { + val accessToken = localTokenDataSource.getAccessToken() + + if (!accessToken.isNullOrBlank()) { + val deviceId = firebaseMessagingManager.getInstallationId() + ?: throw IllegalStateException("Device id is null") + val response = myPageDataSource.postLogout(LogoutRequestDto(deviceId)) + + if (!response.isSuccessful && response.code() != 401) throw HttpException(response) + } + localFcmDataSource.clearFcmToken() localTokenDataSource.clearTokens() } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt index b0247a0b2..a48772e00 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingViewModel.kt @@ -2,7 +2,7 @@ package com.haphap.app.presentation.setting import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.haphap.app.data.repository.api.auth.AuthRepository +import com.haphap.app.data.repository.api.mypage.MyPageRepository import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLeave import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToMyPage import com.haphap.app.presentation.setting.SettingContract.SideEffect.NavigateToLogin @@ -18,7 +18,7 @@ import javax.inject.Inject @HiltViewModel class SettingViewModel @Inject constructor( - private val authRepository: AuthRepository, + private val myPageRepository: MyPageRepository, ) : ViewModel() { private val _uiState = MutableStateFlow(SettingContract.State()) val uiState: StateFlow = _uiState.asStateFlow() @@ -51,7 +51,7 @@ class SettingViewModel @Inject constructor( } viewModelScope.launch { - authRepository.postLogout() + myPageRepository.postLogout() .onSuccess { _sideEffect.send(NavigateToLogin) } .onFailure { _uiState.update { From c6938f2286c5d0a44413547e81a49625b41682f4 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 19:03:05 +0900 Subject: [PATCH 33/49] =?UTF-8?q?[mod/#109]=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=A5=BC=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=94=20=EC=98=88=EC=99=B8=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/haphap/app/presentation/main/MainAppState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt b/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt index 2abd7d413..7df24ed3f 100644 --- a/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt +++ b/app/src/main/java/com/haphap/app/presentation/main/MainAppState.kt @@ -52,7 +52,7 @@ class MainAppState( val isBottomBarVisible: StateFlow = currentDestination .map { destination -> MainTab.entries - .filterNot { it == MainTab.REGISTER || it == MainTab.MYPAGE } + .filterNot { it == MainTab.REGISTER } .any { tab -> destination?.hasRoute(tab.route::class) == true } } .stateIn( From 14d88ddab6c20d43b688e8a43091c27d9958ca29 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 19:33:44 +0900 Subject: [PATCH 34/49] =?UTF-8?q?[mod/#109]=20=EC=9D=B4=EB=AA=A8=EC=A7=80?= =?UTF-8?q?=20=ED=8F=AC=ED=95=A8=20=EA=B8=80=EC=9E=90=EC=88=98=20=EC=84=B8?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../haphap/app/core/extensions/StringExt.kt | 21 +++++++++++++++++++ .../leave/component/LeaveCheckItem.kt | 5 +++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/haphap/app/core/extensions/StringExt.kt b/app/src/main/java/com/haphap/app/core/extensions/StringExt.kt index db8347742..ae55ac9b6 100644 --- a/app/src/main/java/com/haphap/app/core/extensions/StringExt.kt +++ b/app/src/main/java/com/haphap/app/core/extensions/StringExt.kt @@ -1,5 +1,6 @@ package com.haphap.app.core.extensions +import java.text.BreakIterator import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -17,4 +18,24 @@ fun String.toTimeFormat(pattern: String = "HH:mm"): String { return runCatching { LocalDateTime.parse(this).format(DateTimeFormatter.ofPattern(pattern)) }.getOrDefault("") +} + +/** + * 사람이 보는 글자(grapheme cluster) 단위로 길이를 센다. + * + * [CharSequence.length]는 UTF-16 Char 개수를 세기 때문에 이모지 하나가 2개 이상으로 세어지는 반면, + * 이 함수는 "😀", "👍🏻", "👨‍👩‍👧" 같은 이모지도 1글자로 센다. + * + * @receiver 길이를 셀 문자열 + * @return 글자 수 + */ +fun CharSequence.graphemeLength(): Int { + val iterator = BreakIterator.getCharacterInstance().apply { setText(this@graphemeLength.toString()) } + + var count = 0 + while (iterator.next() != BreakIterator.DONE) { + count++ + } + + return count } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt index 2ab811e67..cd2ababfb 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp import com.haphap.app.R import com.haphap.app.core.designsystem.component.textfield.HapHapBasicTextField import com.haphap.app.core.designsystem.theme.HapHapTheme +import com.haphap.app.core.extensions.graphemeLength import com.haphap.app.core.extensions.noRippleClickable private const val ETC_REASON_MAX_LENGTH = 150 @@ -116,7 +117,7 @@ fun LeaveCheckItem( placeholderColor = HapHapTheme.colors.gray300, placeholderStyle = textStyle, inputTransformation = InputTransformation { - if (asCharSequence().trimStart().length > ETC_REASON_MAX_LENGTH) revertAllChanges() + if (asCharSequence().trimStart().graphemeLength() > ETC_REASON_MAX_LENGTH) revertAllChanges() }, modifier = Modifier .fillMaxWidth() @@ -127,7 +128,7 @@ fun LeaveCheckItem( Spacer(modifier = Modifier.height(4.dp)) Text( - text = "${etcReasonState.text.trimStart().length}/$ETC_REASON_MAX_LENGTH", + text = "${etcReasonState.text.trimStart().graphemeLength()}/$ETC_REASON_MAX_LENGTH", color = HapHapTheme.colors.gray400, style = HapHapTheme.typography.caption.r11, modifier = Modifier.align(Alignment.End), From 3c8d4d6595a3450c48a87632f7ffed5b81334a66 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 19:47:02 +0900 Subject: [PATCH 35/49] =?UTF-8?q?[mod/#109]=20=EB=8B=A4=EC=9D=B4=EC=96=BC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EA=B8=B0=EB=B3=B8=20=EC=97=AC=EB=B0=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/core/designsystem/component/modal/HapHapDialog.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt index 8935c428c..fa11762cc 100644 --- a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt +++ b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties import com.haphap.app.R import com.haphap.app.core.designsystem.component.button.HapHapBasicButton import com.haphap.app.core.designsystem.theme.HapHapTheme @@ -44,6 +45,7 @@ fun HapHapDialog( ) { Dialog ( onDismissRequest = onDismiss, + properties = DialogProperties(usePlatformDefaultWidth = false), ) { Column( modifier = modifier From b5a86ed0e93422addca1c9af3305d87235bf6e0e Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 19:49:09 +0900 Subject: [PATCH 36/49] =?UTF-8?q?[del/#109]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B4=84=ED=98=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt | 3 +-- .../com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt index f9ae7c7b9..ac8edc38e 100644 --- a/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt +++ b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LeaveRequestDto.kt @@ -9,5 +9,4 @@ data class LeaveRequestDto( val reason: String, @SerialName("etcReason") val etcReason: String? = null, -) { -} \ No newline at end of file +) \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt index b4b97f05a..d94a9bf7c 100644 --- a/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt +++ b/app/src/main/java/com/haphap/app/data/remote/dto/mypage/LogoutRequestDto.kt @@ -7,5 +7,4 @@ import kotlinx.serialization.Serializable data class LogoutRequestDto( @SerialName("deviceId") val deviceId: String, -) { -} \ No newline at end of file +) \ No newline at end of file From eff33bbc1e10f81a4357046942af4770052814fb Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 20:05:42 +0900 Subject: [PATCH 37/49] =?UTF-8?q?[mod/#109]=20@Immutable=20=EB=B6=99?= =?UTF-8?q?=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/haphap/app/presentation/setting/SettingContract.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt index 99f44cfbb..68fe84d95 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/SettingContract.kt @@ -1,6 +1,9 @@ package com.haphap.app.presentation.setting +import androidx.compose.runtime.Immutable + sealed interface SettingContract { + @Immutable data class State( val settingUiState: SettingUiState = SettingUiState.Idle, val isLogoutDialogVisible: Boolean = false, From 4e7f2159f151db470721331a2d4dcd95b680560b Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 20:29:00 +0900 Subject: [PATCH 38/49] =?UTF-8?q?[mod/#109]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20BoxWithConstraints=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leave/component/LeaveCheckItem.kt | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt index cd2ababfb..4fe363f00 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveCheckItem.kt @@ -1,7 +1,7 @@ package com.haphap.app.presentation.leave.component import androidx.compose.foundation.background -import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -25,11 +25,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import com.haphap.app.R import com.haphap.app.core.designsystem.component.textfield.HapHapBasicTextField @@ -38,7 +35,6 @@ import com.haphap.app.core.extensions.graphemeLength import com.haphap.app.core.extensions.noRippleClickable private const val ETC_REASON_MAX_LENGTH = 150 -private val ETC_REASON_HEIGHT_SAMPLE_TEXT = "가".repeat(ETC_REASON_MAX_LENGTH) @Composable fun LeaveCheckItem( @@ -89,41 +85,27 @@ fun LeaveCheckItem( Column( modifier = Modifier .fillMaxWidth() + .heightIn(min = 103.dp) .background( color = HapHapTheme.colors.gray100, shape = RoundedCornerShape(12.dp), ) .padding(horizontal = 10.dp, vertical = 12.dp), + verticalArrangement = Arrangement.SpaceBetween, ) { - val textStyle = HapHapTheme.typography.caption.r11 - val textMeasurer = rememberTextMeasurer() - val density = LocalDensity.current - - BoxWithConstraints(modifier = Modifier.fillMaxWidth()) { - val maxTextHeight = remember(constraints.maxWidth, textStyle) { - val maxTextLayout = textMeasurer.measure( - text = ETC_REASON_HEIGHT_SAMPLE_TEXT, - style = textStyle, - constraints = Constraints(maxWidth = constraints.maxWidth), - ) - with(density) { maxTextLayout.size.height.toDp() } - } - - HapHapBasicTextField( - state = etcReasonState, - textColor = HapHapTheme.colors.gray500, - textStyle = textStyle, - placeholder = "최대 150자까지 입력이 가능해요.", - placeholderColor = HapHapTheme.colors.gray300, - placeholderStyle = textStyle, - inputTransformation = InputTransformation { - if (asCharSequence().trimStart().graphemeLength() > ETC_REASON_MAX_LENGTH) revertAllChanges() - }, - modifier = Modifier - .fillMaxWidth() - .heightIn(min = maxTextHeight), - ) - } + HapHapBasicTextField( + state = etcReasonState, + textColor = HapHapTheme.colors.gray500, + textStyle = HapHapTheme.typography.caption.r11, + placeholder = "최대 150자까지 입력이 가능해요.", + placeholderColor = HapHapTheme.colors.gray300, + placeholderStyle = HapHapTheme.typography.caption.r11, + inputTransformation = InputTransformation { + if (asCharSequence().trimStart().graphemeLength() > ETC_REASON_MAX_LENGTH) revertAllChanges() + }, + modifier = Modifier + .fillMaxWidth() + ) Spacer(modifier = Modifier.height(4.dp)) From dcc2e898ee3b93326d7e9d6b6e412db0532eb9e0 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 20:40:54 +0900 Subject: [PATCH 39/49] =?UTF-8?q?[mod/#109]=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=ED=99=94=EB=A9=B4=EC=9D=98=20navGraph=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/leave/navigation/LeaveNavigation.kt | 5 ----- .../java/com/haphap/app/presentation/main/MainNavHost.kt | 6 ------ 2 files changed, 11 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt index 0b474e56f..18b38336e 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt @@ -46,12 +46,7 @@ fun NavGraphBuilder.leaveGraph( ), ) } -} -fun NavGraphBuilder.leaveCompleteGraph( - innerPadding: PaddingValues, - navController: NavController, -) { composable { LeaveCompleteRoute( navigateToLogin = { diff --git a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt index bc87b5eb4..55cb69736 100644 --- a/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt +++ b/app/src/main/java/com/haphap/app/presentation/main/MainNavHost.kt @@ -12,7 +12,6 @@ import com.haphap.app.presentation.calendar.navigation.calendarGraph import com.haphap.app.presentation.home.navigation.homeGraph import com.haphap.app.presentation.joblist.navigation.jobListGraph import com.haphap.app.presentation.jobdetail.navigation.jobDetailGraph -import com.haphap.app.presentation.leave.navigation.leaveCompleteGraph import com.haphap.app.presentation.leave.navigation.leaveGraph import com.haphap.app.presentation.mypage.navigation.myPageGraph import com.haphap.app.presentation.register.navigation.registerGraph @@ -90,11 +89,6 @@ fun MainNavHost( navController = navController, ) - leaveCompleteGraph( - innerPadding = innerPadding, - navController = navController, - ) - searchGraph( innerPadding = innerPadding, navController = navController, From 7b77682b2199e66f4fa7b4a627c24a09e5535334 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 20:58:29 +0900 Subject: [PATCH 40/49] =?UTF-8?q?[mod/#109]=20p3=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designsystem/component/modal/HapHapDialog.kt | 10 +++++----- .../haphap/app/presentation/leave/LeaveViewModel.kt | 2 +- .../app/presentation/leave/component/LeaveTopBar.kt | 12 +++++++----- .../presentation/leave/navigation/LeaveNavigation.kt | 3 ++- .../mypage/navigation/MyPageNavigation.kt | 4 +--- .../register/component/RegisterSecondSection.kt | 2 +- .../setting/navigation/SettingNavigation.kt | 11 ++++++++--- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt index fa11762cc..1ecec4f86 100644 --- a/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt +++ b/app/src/main/java/com/haphap/app/core/designsystem/component/modal/HapHapDialog.kt @@ -32,7 +32,7 @@ import com.haphap.app.core.designsystem.type.ButtonType * @param content 다이얼로그 본문에 표시할 안내 문구 * @param onDismiss 다이얼로그 바깥 영역 또는 "취소" 버튼 클릭 시 호출되는 콜백 * @param onConfirmClick "확인" 버튼 클릭 시 호출되는 콜백 - * @param iconTrue 다이얼로그에 아이콘이 있는 지 설정하는 변수 + * @param showIcon 다이얼로그에 아이콘이 있는 지 설정하는 변수 */ @Composable @@ -41,7 +41,7 @@ fun HapHapDialog( onDismiss: () -> Unit, onConfirmClick: () -> Unit, modifier: Modifier = Modifier, - iconTrue: Boolean = false, + showIcon: Boolean = false, ) { Dialog ( onDismissRequest = onDismiss, @@ -59,11 +59,11 @@ fun HapHapDialog( start = 19.dp, top = 16.dp, end = 19.dp, - bottom = if (iconTrue) 21.dp else 16.dp, + bottom = if (showIcon) 21.dp else 16.dp, ), horizontalAlignment = Alignment.CenterHorizontally, ) { - if (iconTrue) { + if (showIcon) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_alert_53), contentDescription = null, @@ -113,7 +113,7 @@ private fun HapHapDialogPreview() { content = "로그아웃 하시겠습니까?", onDismiss = {}, onConfirmClick = {}, - iconTrue = false, + showIcon = false, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt index 00eaca28b..e028db5a0 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveViewModel.kt @@ -70,7 +70,7 @@ class LeaveViewModel @Inject constructor( null }, ).onSuccess { _sideEffect.send(NavigateToLeaveComplete) } - .onFailure { _uiState.update { it.copy(isLoading = false) } } + .onFailure { _uiState.update { it.copy(isLoading = false) } } } } } diff --git a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt index 4ac22563f..47c38575c 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/component/LeaveTopBar.kt @@ -40,11 +40,13 @@ fun LeaveTopBar( Spacer(modifier = Modifier.width(10.dp)) - if (isText) Text( - text = "회원 탈퇴", - style = HapHapTheme.typography.subtitle.b20, - color = HapHapTheme.colors.gray800, - ) + if (isText) { + Text( + text = "회원 탈퇴", + style = HapHapTheme.typography.subtitle.b20, + color = HapHapTheme.colors.gray800, + ) + } } } diff --git a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt index 18b38336e..cb6375f53 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt @@ -1,5 +1,6 @@ package com.haphap.app.presentation.leave.navigation +import android.R.attr.layoutDirection import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding @@ -33,7 +34,7 @@ fun NavGraphBuilder.leaveGraph( val layoutDirection = LocalLayoutDirection.current LeaveRoute( - navigateBack = { navController.popBackStack() }, + navigateBack = navController::popBackStack, navigateToLeaveComplete = { navController.navigateToLeaveComplete( navOptions = navController.clearBackStackNavOptions(), diff --git a/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt b/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt index b516e43a3..899bce8a5 100644 --- a/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/mypage/navigation/MyPageNavigation.kt @@ -29,9 +29,7 @@ fun NavGraphBuilder.myPageGraph( navOptions = navController.clearBackStackNavOptions() ) }, - navigateToSetting = { - navController.navigateToSetting() - }, + navigateToSetting = navController::navigateToSetting, modifier = Modifier.padding(innerPadding), ) } diff --git a/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt b/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt index 2de7206b4..07de8d9fe 100644 --- a/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt +++ b/app/src/main/java/com/haphap/app/presentation/register/component/RegisterSecondSection.kt @@ -60,7 +60,7 @@ fun RegisterSecondSection( content = "이전에 등록한 결과가 있습니다.\n선택한 결과로 변경할까요?", onDismiss = onChangeModalCancelClick, onConfirmClick = onChangeModalConfirmClick, - iconTrue = true, + showIcon = true, ) } } diff --git a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt index 60c8496bd..e5ea39546 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt @@ -3,6 +3,7 @@ package com.haphap.app.presentation.setting.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontVariation.Setting import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -24,9 +25,13 @@ fun NavGraphBuilder.settingGraph( ) { composable { SettingRoute( - navigateBack = { navController.popBackStack() }, - navigateToLeave = { navController.navigateToLeave() }, - navigateToLogin = { navController.navigateToLogin(navOptions = navController.clearBackStackNavOptions())}, + navigateBack = navController::popBackStack, + navigateToLeave = navController::navigateToLeave, + navigateToLogin = { + navController.navigateToLogin( + navOptions = navController.clearBackStackNavOptions() + ) + }, modifier = Modifier.padding(innerPadding), ) } From 88be3582260f0c44b4d45b0d934e9e1296e96a9a Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 21:02:23 +0900 Subject: [PATCH 41/49] =?UTF-8?q?[mod/#109]=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=B2=84=ED=8A=BC=20bottom=20=ED=8C=A8?= =?UTF-8?q?=EB=94=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/haphap/app/presentation/leave/LeaveScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index 285aa37f5..ae9d77f8c 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -148,7 +148,7 @@ private fun LeaveScreen( Row( modifier = Modifier .fillMaxWidth() - .padding(bottom = 10.dp), + .padding(bottom = 16.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), ) { HapHapBasicButton( From 5f0716b234ae01901d8b87471e6cb5ff7807492b Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 21:09:41 +0900 Subject: [PATCH 42/49] =?UTF-8?q?[mod/#109]=20FCM=20=EC=8B=A4=ED=8C=A8?= =?UTF-8?q?=EC=99=80=20=EC=9D=B8=EC=A6=9D=20=ED=86=A0=ED=81=B0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EB=A5=BC=20=EB=B3=84=EB=8F=84=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=B7=A8=EA=B8=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/mypage/MyPageRepositoryImpl.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt index fc9b52e24..db02d9653 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt @@ -34,8 +34,11 @@ class MyPageRepositoryImpl @Inject constructor( if (!response.isSuccessful && response.code() != 404) throw HttpException(response) - localFcmDataSource.clearFcmToken() - localTokenDataSource.clearTokens() + try { + localFcmDataSource.clearFcmToken() + } finally { + localTokenDataSource.clearTokens() + } } override suspend fun postLogout(): Result = @@ -50,7 +53,10 @@ class MyPageRepositoryImpl @Inject constructor( if (!response.isSuccessful && response.code() != 401) throw HttpException(response) } - localFcmDataSource.clearFcmToken() - localTokenDataSource.clearTokens() + try { + localFcmDataSource.clearFcmToken() + } finally { + localTokenDataSource.clearTokens() + } } } \ No newline at end of file From 36ab9d885b93460d6f77fe37ad5423574a896bc3 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Mon, 5 Oct 2026 21:21:54 +0900 Subject: [PATCH 43/49] =?UTF-8?q?[mod/#109]=20deviceId=EA=B0=80=20null?= =?UTF-8?q?=EC=9D=B4=EB=A9=B4=20=EC=84=9C=EB=B2=84=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=9D=84=20=EA=B1=B4=EB=84=88=EB=9C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/mypage/MyPageRepositoryImpl.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt index db02d9653..ce46dd6e3 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt @@ -13,6 +13,7 @@ import com.haphap.app.data.remote.dto.mypage.LogoutRequestDto import com.haphap.app.data.repository.api.mypage.MyPageRepository import jakarta.inject.Inject import retrofit2.HttpException +import timber.log.Timber class MyPageRepositoryImpl @Inject constructor( private val myPageDataSource: MyPageDataSource, @@ -47,10 +48,13 @@ class MyPageRepositoryImpl @Inject constructor( if (!accessToken.isNullOrBlank()) { val deviceId = firebaseMessagingManager.getInstallationId() - ?: throw IllegalStateException("Device id is null") - val response = myPageDataSource.postLogout(LogoutRequestDto(deviceId)) - if (!response.isSuccessful && response.code() != 401) throw HttpException(response) + if (deviceId != null) { + val response = myPageDataSource.postLogout(LogoutRequestDto(deviceId)) + if (!response.isSuccessful && response.code() != 401) throw HttpException(response) + } else { + Timber.w("Installation id is null. Skip remote logout") + } } try { From 571b586394c314e6b2c7907cdf08ad0f95aecb71 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Tue, 6 Oct 2026 20:16:30 +0900 Subject: [PATCH 44/49] =?UTF-8?q?[del/#109]=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20import=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/presentation/setting/navigation/SettingNavigation.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt index e5ea39546..a59def0a3 100644 --- a/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/setting/navigation/SettingNavigation.kt @@ -3,7 +3,6 @@ package com.haphap.app.presentation.setting.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontVariation.Setting import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions From 15494a3a15e6012d5d4197e935fba2fe59dbfd3d Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Tue, 6 Oct 2026 20:29:11 +0900 Subject: [PATCH 45/49] =?UTF-8?q?[mod/#109]=20=ED=82=A4=EB=B3=B4=EB=93=9C?= =?UTF-8?q?=EB=A7=8C=ED=81=BC=EC=9D=98=20=EC=97=AC=EB=B0=B1=20=EC=A3=BC?= =?UTF-8?q?=EA=B8=B0=20=EB=B0=A9=EB=B2=95=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/haphap/app/presentation/leave/LeaveScreen.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt index ae9d77f8c..5c9e7c410 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/LeaveScreen.kt @@ -5,15 +5,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.ime -import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.union -import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.TextFieldState @@ -82,7 +78,7 @@ private fun LeaveScreen( Column( modifier = modifier .fillMaxSize() - .windowInsetsPadding(WindowInsets.navigationBars.union(WindowInsets.ime)) + .imePadding() .padding(horizontal = 20.dp), ) { LeaveTopBar( From 6c3ba7c896109f7f7da0777bc18617a7ed1db8b8 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Tue, 6 Oct 2026 20:37:32 +0900 Subject: [PATCH 46/49] =?UTF-8?q?[feat/#109]=20isNewUser=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80,=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EC=99=80=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EA=B5=AC=EB=B6=84=20->=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=99=84=EB=A3=8C=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=B6=84=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/haphap/app/data/mapper/auth/AuthMapper.kt | 1 + .../java/com/haphap/app/data/model/auth/KakaoLoginModel.kt | 1 + .../app/data/remote/dto/auth/KakaoLoginResponseDto.kt | 2 ++ .../haphap/app/presentation/auth/login/LoginContract.kt | 1 + .../com/haphap/app/presentation/auth/login/LoginScreen.kt | 6 ++++++ .../haphap/app/presentation/auth/login/LoginViewModel.kt | 7 ++++++- .../app/presentation/auth/navigation/AuthNavigation.kt | 5 +++++ 7 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/haphap/app/data/mapper/auth/AuthMapper.kt b/app/src/main/java/com/haphap/app/data/mapper/auth/AuthMapper.kt index a52425442..625e0da38 100644 --- a/app/src/main/java/com/haphap/app/data/mapper/auth/AuthMapper.kt +++ b/app/src/main/java/com/haphap/app/data/mapper/auth/AuthMapper.kt @@ -8,4 +8,5 @@ fun KakaoLoginResponseDto.toModel(): KakaoLoginModel = KakaoLoginModel( refreshToken = refreshToken, name = name, anonymousName = anonymousName, + isNewUser = isNewUser, ) \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/model/auth/KakaoLoginModel.kt b/app/src/main/java/com/haphap/app/data/model/auth/KakaoLoginModel.kt index 8a523433a..bd67b424d 100644 --- a/app/src/main/java/com/haphap/app/data/model/auth/KakaoLoginModel.kt +++ b/app/src/main/java/com/haphap/app/data/model/auth/KakaoLoginModel.kt @@ -5,4 +5,5 @@ data class KakaoLoginModel( val refreshToken: String, val name: String, val anonymousName: String, + val isNewUser: Boolean, ) \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/data/remote/dto/auth/KakaoLoginResponseDto.kt b/app/src/main/java/com/haphap/app/data/remote/dto/auth/KakaoLoginResponseDto.kt index 90fb56b3f..d306856f0 100644 --- a/app/src/main/java/com/haphap/app/data/remote/dto/auth/KakaoLoginResponseDto.kt +++ b/app/src/main/java/com/haphap/app/data/remote/dto/auth/KakaoLoginResponseDto.kt @@ -13,4 +13,6 @@ data class KakaoLoginResponseDto( val name: String, @SerialName("anonymousName") val anonymousName: String, + @SerialName("isNewUser") + val isNewUser: Boolean = false, ) \ No newline at end of file diff --git a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginContract.kt b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginContract.kt index 7fe781a12..4ae9673a2 100644 --- a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginContract.kt +++ b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginContract.kt @@ -10,6 +10,7 @@ sealed interface LoginContract { sealed class SideEffect { data class NavigateToSignUpComplete(val userName: String) : SideEffect() + data object NavigateToHome : SideEffect() } } diff --git a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt index 3e8b02b3b..b1d29ef38 100644 --- a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt @@ -36,11 +36,13 @@ import com.haphap.app.R import com.haphap.app.core.designsystem.component.circular_progress_indicator.HapHapCircularProgressIndicator import com.haphap.app.core.designsystem.theme.HapHapTheme import com.haphap.app.core.extensions.noRippleClickable +import com.haphap.app.presentation.auth.login.LoginContract.SideEffect.NavigateToHome import com.haphap.app.presentation.auth.login.LoginContract.SideEffect.NavigateToSignUpComplete @Composable fun LoginRoute( navigateToSignUpComplete: (userName: String) -> Unit, + navigateToHome: () -> Unit, modifier: Modifier = Modifier, viewModel: LoginViewModel = hiltViewModel(), ) { @@ -56,6 +58,10 @@ fun LoginRoute( is NavigateToSignUpComplete -> { navigateToSignUpComplete(sideEffect.userName) } + + is NavigateToHome -> { + navigateToHome() + } } } } diff --git a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginViewModel.kt b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginViewModel.kt index 7398c96d2..75590d8c6 100644 --- a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginViewModel.kt +++ b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.haphap.app.data.repository.api.alarm.AlarmRepository import com.haphap.app.data.repository.api.auth.AuthRepository +import com.haphap.app.presentation.auth.login.LoginContract.SideEffect.NavigateToHome import com.haphap.app.presentation.auth.login.LoginContract.SideEffect.NavigateToSignUpComplete import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.channels.Channel @@ -37,7 +38,11 @@ class LoginViewModel @Inject constructor( .onSuccess { model -> alarmRepository.registerDeviceId() _uiState.update { it.copy(loginUiState = LoginUiState.Success) } - _sideEffect.send(NavigateToSignUpComplete(model.name)) + if (model.isNewUser) { + _sideEffect.send(NavigateToSignUpComplete(model.name)) + } else { + _sideEffect.send(NavigateToHome) + } } .onFailure { throwable -> _uiState.update { diff --git a/app/src/main/java/com/haphap/app/presentation/auth/navigation/AuthNavigation.kt b/app/src/main/java/com/haphap/app/presentation/auth/navigation/AuthNavigation.kt index dd8d785fa..b4605f735 100644 --- a/app/src/main/java/com/haphap/app/presentation/auth/navigation/AuthNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/auth/navigation/AuthNavigation.kt @@ -36,6 +36,11 @@ fun NavGraphBuilder.authGraph( navOptions = navController.clearBackStackNavOptions(), ) }, + navigateToHome = { + navController.navigateToHome( + navOptions = navController.clearBackStackNavOptions() + ) + }, modifier = Modifier.padding(innerPadding), ) } From 80ada5e36999fbcf296103e65b4203b7c372763a Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Tue, 6 Oct 2026 20:42:16 +0900 Subject: [PATCH 47/49] =?UTF-8?q?[mod/#109]=20=EC=A4=91=EA=B4=84=ED=98=B8?= =?UTF-8?q?=20=EC=97=86=EC=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/haphap/app/presentation/auth/login/LoginScreen.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt index b1d29ef38..6b0fa2d5e 100644 --- a/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt +++ b/app/src/main/java/com/haphap/app/presentation/auth/login/LoginScreen.kt @@ -59,9 +59,7 @@ fun LoginRoute( navigateToSignUpComplete(sideEffect.userName) } - is NavigateToHome -> { - navigateToHome() - } + is NavigateToHome -> navigateToHome() } } } From 3cf0c413077c2e3a7d23dd146659ba0f6342cb3f Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Tue, 6 Oct 2026 22:35:27 +0900 Subject: [PATCH 48/49] =?UTF-8?q?[mod/#109]=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=EB=A7=8C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/mypage/MyPageRepositoryImpl.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt index ce46dd6e3..73112b45d 100644 --- a/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt +++ b/app/src/main/java/com/haphap/app/data/repository/impl/mypage/MyPageRepositoryImpl.kt @@ -35,11 +35,7 @@ class MyPageRepositoryImpl @Inject constructor( if (!response.isSuccessful && response.code() != 404) throw HttpException(response) - try { - localFcmDataSource.clearFcmToken() - } finally { - localTokenDataSource.clearTokens() - } + localTokenDataSource.clearTokens() } override suspend fun postLogout(): Result = @@ -57,10 +53,6 @@ class MyPageRepositoryImpl @Inject constructor( } } - try { - localFcmDataSource.clearFcmToken() - } finally { - localTokenDataSource.clearTokens() - } + localTokenDataSource.clearTokens() } } \ No newline at end of file From 75d61f2f7396b8fb34c73a1ea6a027343122f403 Mon Sep 17 00:00:00 2001 From: Hiimynameiss Date: Tue, 6 Oct 2026 22:44:37 +0900 Subject: [PATCH 49/49] =?UTF-8?q?[del/#109]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=8C=A8=EB=94=A9=20=EC=B2=98=EB=A6=AC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/leave/navigation/LeaveNavigation.kt | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt index cb6375f53..540768dfd 100644 --- a/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt +++ b/app/src/main/java/com/haphap/app/presentation/leave/navigation/LeaveNavigation.kt @@ -1,12 +1,8 @@ package com.haphap.app.presentation.leave.navigation -import android.R.attr.layoutDirection import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.calculateEndPadding -import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -31,8 +27,6 @@ fun NavGraphBuilder.leaveGraph( navController: NavController, ) { composable { - val layoutDirection = LocalLayoutDirection.current - LeaveRoute( navigateBack = navController::popBackStack, navigateToLeaveComplete = { @@ -40,11 +34,7 @@ fun NavGraphBuilder.leaveGraph( navOptions = navController.clearBackStackNavOptions(), ) }, - modifier = Modifier.padding( - top = innerPadding.calculateTopPadding(), - start = innerPadding.calculateStartPadding(layoutDirection), - end = innerPadding.calculateEndPadding(layoutDirection), - ), + modifier = Modifier.padding(innerPadding), ) }