From 6b4e6a3a32c232c103e44c798a5700f23d673cc2 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Thu, 30 Jan 2025 15:01:49 +0900 Subject: [PATCH 01/21] =?UTF-8?q?[TNT-185]=20fix:=20=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A7=81=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20Diet=20->=20Meal=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/java/co/kr/tnt/ui/model/RecordChip.kt | 8 ++++---- core/ui/src/main/res/values/strings.xml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/ui/src/main/java/co/kr/tnt/ui/model/RecordChip.kt b/core/ui/src/main/java/co/kr/tnt/ui/model/RecordChip.kt index eeb88886..6b1ac170 100644 --- a/core/ui/src/main/java/co/kr/tnt/ui/model/RecordChip.kt +++ b/core/ui/src/main/java/co/kr/tnt/ui/model/RecordChip.kt @@ -29,10 +29,10 @@ sealed interface RecordChip { return when (type) { is RecordType.MealType -> { val title = when (type) { - RecordType.MealType.BREAKFAST -> stringResource(R.string.diet_breakfast) - RecordType.MealType.LUNCH -> stringResource(R.string.diet_lunch) - RecordType.MealType.DINNER -> stringResource(R.string.diet_dinner) - RecordType.MealType.SNACK -> stringResource(R.string.diet_snack) + RecordType.MealType.BREAKFAST -> stringResource(R.string.meal_breakfast) + RecordType.MealType.LUNCH -> stringResource(R.string.meal_lunch) + RecordType.MealType.DINNER -> stringResource(R.string.meal_dinner) + RecordType.MealType.SNACK -> stringResource(R.string.meal_snack) } val emoji = when (type) { RecordType.MealType.BREAKFAST -> "๐ŸŒž" diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 3c5c610e..04603085 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -34,11 +34,11 @@ cm kg - - ์•„์นจ - ์ ์‹ฌ - ์ €๋… - ๊ฐ„์‹ + + ์•„์นจ + ์ ์‹ฌ + ์ €๋… + ๊ฐ„์‹ ์ƒ์ฒด ์šด๋™ From bf7bfd271eae08f6f4af00a9ded82bedead429ea Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Thu, 30 Jan 2025 15:14:17 +0900 Subject: [PATCH 02/21] =?UTF-8?q?[TNT-185]=20feat:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20mypage=20=EB=AA=A8=EB=93=88=20=EC=B5=9C?= =?UTF-8?q?=EC=B4=88=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/trainee/mypage/.gitignore | 1 + feature/trainee/mypage/build.gradle.kts | 13 +++++ .../mypage/src/main/AndroidManifest.xml | 4 ++ .../trainee/mypage/TraineeMyPageContract.kt | 25 ++++++++++ .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 49 +++++++++++++++++++ .../trainee/mypage/TraineeMyPageViewModel.kt | 21 ++++++++ settings.gradle.kts | 1 + 7 files changed, 114 insertions(+) create mode 100644 feature/trainee/mypage/.gitignore create mode 100644 feature/trainee/mypage/build.gradle.kts create mode 100644 feature/trainee/mypage/src/main/AndroidManifest.xml create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt diff --git a/feature/trainee/mypage/.gitignore b/feature/trainee/mypage/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/trainee/mypage/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/trainee/mypage/build.gradle.kts b/feature/trainee/mypage/build.gradle.kts new file mode 100644 index 00000000..0a78f0e8 --- /dev/null +++ b/feature/trainee/mypage/build.gradle.kts @@ -0,0 +1,13 @@ +import co.kr.tnt.setNamespace + +plugins { + id("tnt.android.feature") +} + +android { + setNamespace("feature.trainee.mypage") +} + +dependencies { + implementation(libs.kotlinx.immutable) +} diff --git a/feature/trainee/mypage/src/main/AndroidManifest.xml b/feature/trainee/mypage/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8bdb7e14 --- /dev/null +++ b/feature/trainee/mypage/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt new file mode 100644 index 00000000..1989b81b --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -0,0 +1,25 @@ +package co.kr.tnt.trainee.mypage + +import co.kr.tnt.ui.base.UiEvent +import co.kr.tnt.ui.base.UiSideEffect +import co.kr.tnt.ui.base.UiState + +internal class TraineeMyPageContract { + data class TraineeMyPageUiState( + val image: String? = "", + val name: String = "", + val isConnected: Boolean = false, + val isPushEnabled: Boolean = false, + val appVersion: String = "", + ) : UiState + + sealed interface TraineeMyPageUiEvent : UiEvent { + data object OnConnectClick : TraineeMyPageUiEvent + data object ToggleNotification : TraineeMyPageUiEvent + } + + sealed interface TraineeMyPageEffect : UiSideEffect { + data class ShowToast(val message: String) : TraineeMyPageEffect + data object NavigateToConnect : TraineeMyPageEffect + } +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt new file mode 100644 index 00000000..870d0672 --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -0,0 +1,49 @@ +package co.kr.tnt.trainee.mypage + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import co.kr.tnt.designsystem.theme.TnTTheme +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState + +@Composable +internal fun TraineeMyPageRoute( + navigateToPrevious: () -> Unit, + viewModel: TraineeMyPageViewModel = hiltViewModel(), +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + TraineeMyPageScreen( + state = uiState, + ) +} + +@Composable +private fun TraineeMyPageScreen( + state: TraineeMyPageUiState, +) { + Scaffold(containerColor = TnTTheme.colors.commonColors.Common0) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(innerPadding), + ) { + } + } +} + +@Preview +@Composable +private fun TraineeMyPagePreview() { + TnTTheme { + TraineeMyPageScreen( + state = TraineeMyPageUiState(), + ) + } +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt new file mode 100644 index 00000000..61329873 --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -0,0 +1,21 @@ +package co.kr.tnt.trainee.mypage + +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState +import co.kr.tnt.ui.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +internal class TraineeMyPageViewModel @Inject constructor() : + BaseViewModel( + TraineeMyPageUiState(), + ) { + override suspend fun handleEvent(event: TraineeMyPageUiEvent) { + when (event) { + TraineeMyPageUiEvent.OnConnectClick -> TODO() + TraineeMyPageUiEvent.ToggleNotification -> TODO() + } + } + } diff --git a/settings.gradle.kts b/settings.gradle.kts index ddcff407..2f0cf3c6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,4 +50,5 @@ include( ":feature:trainer:connect", ":feature:trainee:signup", ":feature:trainee:connect", + ":feature:trainee:mypage", ) From c9b256314b44341d773ada1690c1797250d18ae2 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Thu, 30 Jan 2025 17:51:04 +0900 Subject: [PATCH 03/21] =?UTF-8?q?[TNT-185]=20feat:=20switch=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/tnt/designsystem/component/Toggle.kt | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Toggle.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Toggle.kt index e657a8d7..a3c15c9f 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Toggle.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Toggle.kt @@ -1,16 +1,28 @@ package co.kr.tnt.designsystem.component +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape 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.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -39,6 +51,43 @@ fun TnTCheckToggle( ) } +@Composable +fun TnTSwitch( + checked: Boolean, + onCheckedChange: () -> Unit, + modifier: Modifier = Modifier, +) { + val trackColor = if (checked) { + TnTTheme.colors.redColors.Red500 + } else { + TnTTheme.colors.neutralColors.Neutral300 + } + + val thumbPosition by animateFloatAsState( + targetValue = if (checked) 20f else 0f, + animationSpec = tween(durationMillis = 200, easing = FastOutSlowInEasing), + label = "thumbPosition", + ) + + Box( + modifier = modifier + .width(44.dp) + .height(24.dp) + .clip(RoundedCornerShape(12.dp)) + .background(trackColor) + .clickable(onClick = onCheckedChange) + .padding(horizontal = 2.dp), + ) { + Box( + modifier = Modifier + .size(20.dp) + .offset(x = thumbPosition.dp) + .align(Alignment.CenterStart) + .background(Color.White, CircleShape), + ) + } +} + @Preview @Composable private fun TnTCheckTogglePreview() { @@ -51,3 +100,16 @@ private fun TnTCheckTogglePreview() { ) } } + +@Preview +@Composable +private fun TnTSwitchPreview() { + TnTTheme { + var checked by remember { mutableStateOf(true) } + + TnTSwitch( + checked = checked, + onCheckedChange = { checked = !checked }, + ) + } +} From 4f91321e19c7fbaaf97cfa5e3569756b811d2370 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Thu, 30 Jan 2025 17:55:01 +0900 Subject: [PATCH 04/21] =?UTF-8?q?[TNT-185]=20feat:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/res/values/strings.xml | 11 + feature/main/build.gradle.kts | 7 +- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 219 +++++++++++++++++- .../trainee/mypage/TraineeMyPageViewModel.kt | 4 +- .../mypage/src/main/res/values/strings.xml | 6 + 5 files changed, 240 insertions(+), 7 deletions(-) create mode 100644 feature/trainee/mypage/src/main/res/values/strings.xml diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 04603085..e4701a86 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -49,4 +49,15 @@ %dํšŒ์ฐจ ์ˆ˜์—… + + + ๊ฐœ์ธ์ •๋ณด ์ˆ˜์ • + ์•ฑ ํ‘ธ์‹œ ์•Œ๋ฆผ + ์„œ๋น„์Šค ์ด์šฉ์•ฝ๊ด€ + ๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ๋ฐฉ์นจ + ๋ฒ„์ „ ์ •๋ณด + ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด์„ ์Šค + ๋กœ๊ทธ์•„์›ƒ + ๊ณ„์ • ํƒˆํ‡ด + diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index d828b3f8..6544e409 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -11,11 +11,12 @@ android { dependencies { implementation(projects.feature.home) implementation(projects.feature.login) - implementation(projects.feature.trainer.connect) - implementation(projects.feature.trainee.connect) + implementation(projects.feature.roleselect) implementation(projects.feature.trainee.signup) implementation(projects.feature.trainer.signup) - implementation(projects.feature.roleselect) + implementation(projects.feature.trainer.connect) + implementation(projects.feature.trainee.connect) + implementation(projects.feature.trainee.mypage) implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 870d0672..4ae4803e 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -1,49 +1,264 @@ package co.kr.tnt.trainee.mypage +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import co.kr.tnt.designsystem.component.TnTProfileImage +import co.kr.tnt.designsystem.component.TnTSwitch +import co.kr.tnt.designsystem.component.button.TnTTextButton +import co.kr.tnt.designsystem.component.button.model.ButtonSize +import co.kr.tnt.designsystem.component.button.model.ButtonType import co.kr.tnt.designsystem.theme.TnTTheme +import co.kr.tnt.feature.trainee.mypage.R import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState +import co.kr.tnt.ui.model.DefaultUserProfile +import co.kr.tnt.core.ui.R as uiResource @Composable internal fun TraineeMyPageRoute( navigateToPrevious: () -> Unit, + navigateToConnect: () -> Unit, viewModel: TraineeMyPageViewModel = hiltViewModel(), ) { + val context = LocalContext.current val uiState by viewModel.uiState.collectAsStateWithLifecycle() TraineeMyPageScreen( state = uiState, + onEditButtonClick = TODO(), + onConnectButtonClick = TODO(), + onPushNotificationToggle = TODO(), + onServiceTermClick = TODO(), + onPrivacyClick = TODO(), + onOpenSourceClick = TODO(), + onLogoutClick = TODO(), + onDeleteAccountClick = TODO(), ) } @Composable private fun TraineeMyPageScreen( state: TraineeMyPageUiState, + onEditButtonClick: () -> Unit, + onConnectButtonClick: () -> Unit, + onPushNotificationToggle: () -> Unit, + onServiceTermClick: () -> Unit, + onPrivacyClick: () -> Unit, + onOpenSourceClick: () -> Unit, + onLogoutClick: () -> Unit, + onDeleteAccountClick: () -> Unit, ) { - Scaffold(containerColor = TnTTheme.colors.commonColors.Common0) { innerPadding -> + Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(innerPadding), ) { + TnTProfileImage( + defaultImage = painterResource(DefaultUserProfile.Trainee.image), + imageSize = 132.dp, + showEditButton = false, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + ) + Text( + text = state.name, + color = TnTTheme.colors.neutralColors.Neutral950, + style = TnTTheme.typography.h2, + ) + Spacer(Modifier.height(10.dp)) + TnTTextButton( + text = stringResource(uiResource.string.modifying_personal_info), + size = ButtonSize.Small, + type = ButtonType.Gray, + onClick = onEditButtonClick, + ) + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + ) { + if (state.isConnected.not()) { + WhiteButton( + text = stringResource(R.string.connect_with_trainer), + height = 47.dp, + onClick = onConnectButtonClick, + ) + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .height(48.dp) + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(horizontal = 20.dp), + ) { + Text( + text = stringResource(uiResource.string.app_push_notification), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + TnTSwitch( + checked = state.isPushEnabled, + onCheckedChange = onPushNotificationToggle, + ) + } + Column( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + WhiteButton( + text = stringResource(uiResource.string.terms_of_service), + height = 40.dp, + onClick = onServiceTermClick, + ) + WhiteButton( + text = stringResource(uiResource.string.privacy_policy), + height = 40.dp, + onClick = onPrivacyClick, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .height(40.dp) + .padding(horizontal = 20.dp, vertical = 8.dp), + ) { + Text( + text = stringResource(uiResource.string.app_version), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + Text( + text = state.appVersion, + color = TnTTheme.colors.neutralColors.Neutral400, + style = TnTTheme.typography.body2Medium, + ) + } + WhiteButton( + text = stringResource(uiResource.string.open_source_license), + height = 40.dp, + onClick = onOpenSourceClick, + ) + } + if (state.isConnected) { + WhiteButton( + text = stringResource(R.string.disconnect_with_trainer), + height = 47.dp, + onClick = onConnectButtonClick, + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + Text( + text = stringResource(uiResource.string.logout), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 12.dp) + .clickable(onClick = onLogoutClick), + ) + Text( + text = stringResource(uiResource.string.delete_account), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 12.dp) + .clickable(onClick = onDeleteAccountClick), + ) + } + } } } } +@Composable +private fun WhiteButton( + text: String, + height: Dp, + modifier: Modifier = Modifier, + onClick: () -> Unit, +) { + Button( + onClick = onClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonColors( + containerColor = TnTTheme.colors.commonColors.Common0, + contentColor = TnTTheme.colors.neutralColors.Neutral700, + disabledContainerColor = TnTTheme.colors.commonColors.Common0, + disabledContentColor = TnTTheme.colors.neutralColors.Neutral700, + ), + contentPadding = PaddingValues(horizontal = 20.dp, vertical = 12.dp), + modifier = modifier + .fillMaxWidth() + .height(height) + .defaultMinSize(minWidth = Dp.Hairline), + ) { + Text( + text = text, + style = TnTTheme.typography.body2Medium, + modifier = Modifier.fillMaxWidth(), + ) + } +} + @Preview @Composable private fun TraineeMyPagePreview() { TnTTheme { TraineeMyPageScreen( - state = TraineeMyPageUiState(), + state = TraineeMyPageUiState( + image = null, + name = "๊น€ํšŒ์›", + isConnected = true, + isPushEnabled = true, + appVersion = "0.0.0", + ), + onEditButtonClick = {}, + onConnectButtonClick = {}, + onPushNotificationToggle = {}, + onServiceTermClick = {}, + onPrivacyClick = {}, + onOpenSourceClick = {}, + onLogoutClick = {}, + onDeleteAccountClick = {}, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index 61329873..73d0862e 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -14,8 +14,8 @@ internal class TraineeMyPageViewModel @Inject constructor() : ) { override suspend fun handleEvent(event: TraineeMyPageUiEvent) { when (event) { - TraineeMyPageUiEvent.OnConnectClick -> TODO() - TraineeMyPageUiEvent.ToggleNotification -> TODO() + TraineeMyPageUiEvent.OnConnectClick -> {} + TraineeMyPageUiEvent.ToggleNotification -> {} } } } diff --git a/feature/trainee/mypage/src/main/res/values/strings.xml b/feature/trainee/mypage/src/main/res/values/strings.xml new file mode 100644 index 00000000..c5885e1b --- /dev/null +++ b/feature/trainee/mypage/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ + ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐ๋Š๊ธฐ + + From 30383cac1e9bcf7343667fde4cb9c07189e8f7e5 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Thu, 30 Jan 2025 21:00:30 +0900 Subject: [PATCH 05/21] =?UTF-8?q?[TNT-185]=20feat:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20Navigation=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ์—ฐ๊ฒฐํ•˜๊ธฐ ํ™”๋ฉด ์—ฐ๊ฒฐ - ๊ฐ ํด๋ฆญ ์ด๋ฒคํŠธ ์„ค์ • --- .../java/co/kr/tnt/navigation/RouteModel.kt | 13 +++-- .../main/java/co/kr/tnt/main/ui/TnTNavHost.kt | 5 ++ .../trainee/mypage/TraineeMyPageContract.kt | 17 +++++-- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 45 +++++++++++++---- .../trainee/mypage/TraineeMyPageViewModel.kt | 48 ++++++++++++++++++- .../navigation/TraineeMyPageNavigation.kt | 27 +++++++++++ 6 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt diff --git a/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt b/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt index 4869e611..86c589b5 100644 --- a/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt +++ b/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt @@ -12,6 +12,13 @@ sealed interface Route { @Serializable data object Login : Route + @Serializable + data class RoleSelection( + val authId: String, + val authType: String, + val email: String, + ) : Route + @Serializable data class TrainerSignUp( val authId: String, @@ -33,9 +40,5 @@ sealed interface Route { data class TraineeConnect(val isFromMyPage: Boolean) : Route @Serializable - data class RoleSelection( - val authId: String, - val authType: String, - val email: String, - ) : Route + data object TraineeMyPage : Route } diff --git a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt index 9dc688a8..97e1d28d 100644 --- a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt +++ b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt @@ -13,6 +13,7 @@ import co.kr.tnt.roleselect.navigateToRoleSelection import co.kr.tnt.roleselect.roleSelectionScreen import co.kr.tnt.trainee.connect.navigation.navigateToTraineeConnect import co.kr.tnt.trainee.connect.navigation.traineeConnectScreen +import co.kr.tnt.trainee.mypage.navigation.traineeMyPageScreen import co.kr.tnt.trainee.signup.navigation.navigateToTraineeSignUp import co.kr.tnt.trainee.signup.navigation.traineeSignUpScreen import co.kr.tnt.trainer.connect.navigation.navigateToTrainerConnect @@ -88,6 +89,10 @@ fun TnTNavHost( navController.navigateToHome(isTrainer = false, clearBackStack = true) }, ) + traineeMyPageScreen( + navigateToPrevious = { navController.popBackStack() }, + navigateToTraineeConnect = { navController.navigateToTraineeConnect(isFromMyPage = true) }, + ) homeNavGraph() } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index 1989b81b..2d820183 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -7,19 +7,28 @@ import co.kr.tnt.ui.base.UiState internal class TraineeMyPageContract { data class TraineeMyPageUiState( val image: String? = "", - val name: String = "", + val name: String = "์ด๋ฆ„", val isConnected: Boolean = false, - val isPushEnabled: Boolean = false, - val appVersion: String = "", + val isPushEnabled: Boolean = true, + val appVersion: String = "0.0.0", ) : UiState sealed interface TraineeMyPageUiEvent : UiEvent { - data object OnConnectClick : TraineeMyPageUiEvent + data object OnEditButtonClick : TraineeMyPageUiEvent + data object OnConnectButtonClick : TraineeMyPageUiEvent + data object OnDisconnectButtonClick : TraineeMyPageUiEvent data object ToggleNotification : TraineeMyPageUiEvent + data object OnServiceTermClick : TraineeMyPageUiEvent + data object OnPrivacyClick : TraineeMyPageUiEvent + data object OnOpenSourceClick : TraineeMyPageUiEvent + data object OnLogoutClick : TraineeMyPageUiEvent + data object OnDeleteAccountClick : TraineeMyPageUiEvent + data object OnBackClick : TraineeMyPageUiEvent } sealed interface TraineeMyPageEffect : UiSideEffect { data class ShowToast(val message: String) : TraineeMyPageEffect data object NavigateToConnect : TraineeMyPageEffect + data object NavigateToPrevious : TraineeMyPageEffect } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 4ae4803e..62c08579 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -1,5 +1,7 @@ package co.kr.tnt.trainee.mypage +import android.widget.Toast +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -17,6 +19,7 @@ import androidx.compose.material3.ButtonColors import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -36,6 +39,8 @@ import co.kr.tnt.designsystem.component.button.model.ButtonSize import co.kr.tnt.designsystem.component.button.model.ButtonType import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.feature.trainee.mypage.R +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState import co.kr.tnt.ui.model.DefaultUserProfile import co.kr.tnt.core.ui.R as uiResource @@ -51,22 +56,38 @@ internal fun TraineeMyPageRoute( TraineeMyPageScreen( state = uiState, - onEditButtonClick = TODO(), - onConnectButtonClick = TODO(), - onPushNotificationToggle = TODO(), - onServiceTermClick = TODO(), - onPrivacyClick = TODO(), - onOpenSourceClick = TODO(), - onLogoutClick = TODO(), - onDeleteAccountClick = TODO(), + onBackClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnBackClick) }, + onEditButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnEditButtonClick) }, + onConnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnConnectButtonClick) }, + onDisconnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDisconnectButtonClick) }, + onPushNotificationToggle = { viewModel.setEvent(TraineeMyPageUiEvent.ToggleNotification) }, + onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick) }, + onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick) }, + onOpenSourceClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnOpenSourceClick) }, + onLogoutClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnLogoutClick) }, + onDeleteAccountClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDeleteAccountClick) }, ) + + LaunchedEffect(viewModel.effect) { + viewModel.effect.collect { effect -> + when (effect) { + TraineeMyPageEffect.NavigateToPrevious -> navigateToPrevious() + TraineeMyPageEffect.NavigateToConnect -> navigateToConnect() + is TraineeMyPageEffect.ShowToast -> { + Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show() + } + } + } + } } @Composable private fun TraineeMyPageScreen( state: TraineeMyPageUiState, + onBackClick: () -> Unit, onEditButtonClick: () -> Unit, onConnectButtonClick: () -> Unit, + onDisconnectButtonClick: () -> Unit, onPushNotificationToggle: () -> Unit, onServiceTermClick: () -> Unit, onPrivacyClick: () -> Unit, @@ -74,6 +95,8 @@ private fun TraineeMyPageScreen( onLogoutClick: () -> Unit, onDeleteAccountClick: () -> Unit, ) { + BackHandler { onBackClick() } + Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -92,7 +115,7 @@ private fun TraineeMyPageScreen( color = TnTTheme.colors.neutralColors.Neutral950, style = TnTTheme.typography.h2, ) - Spacer(Modifier.height(10.dp)) + Spacer(Modifier.height(8.dp)) TnTTextButton( text = stringResource(uiResource.string.modifying_personal_info), size = ButtonSize.Small, @@ -177,7 +200,7 @@ private fun TraineeMyPageScreen( WhiteButton( text = stringResource(R.string.disconnect_with_trainer), height = 47.dp, - onClick = onConnectButtonClick, + onClick = onDisconnectButtonClick, ) } Column( @@ -259,6 +282,8 @@ private fun TraineeMyPagePreview() { onOpenSourceClick = {}, onLogoutClick = {}, onDeleteAccountClick = {}, + onDisconnectButtonClick = {}, + onBackClick = {}, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index 73d0862e..c725e3b4 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -14,8 +14,52 @@ internal class TraineeMyPageViewModel @Inject constructor() : ) { override suspend fun handleEvent(event: TraineeMyPageUiEvent) { when (event) { - TraineeMyPageUiEvent.OnConnectClick -> {} - TraineeMyPageUiEvent.ToggleNotification -> {} + TraineeMyPageUiEvent.OnEditButtonClick -> navigateToPersonalInfo() + TraineeMyPageUiEvent.OnConnectButtonClick -> navigateToConnect() + TraineeMyPageUiEvent.OnDisconnectButtonClick -> showDisconnectPopup() + TraineeMyPageUiEvent.ToggleNotification -> togglePushNotification() + TraineeMyPageUiEvent.OnServiceTermClick -> openWebView(url = "https://www.naver.com") + TraineeMyPageUiEvent.OnPrivacyClick -> openWebView(url = "https://www.naver.com") + TraineeMyPageUiEvent.OnOpenSourceClick -> navigateToOpenSource() + TraineeMyPageUiEvent.OnLogoutClick -> logout() + TraineeMyPageUiEvent.OnDeleteAccountClick -> deleteAccount() + TraineeMyPageUiEvent.OnBackClick -> navigateBack() } } + + private fun navigateToPersonalInfo() { + // TODO ๊ฐœ์ธ ์ •๋ณด ์ˆ˜์ • ํ™”๋ฉด + } + + private fun navigateToConnect() { + sendEffect(TraineeMyPageEffect.NavigateToConnect) + } + + private fun showDisconnectPopup() { + // TODO ์—ฐ๊ฒฐ ๋Š๊ธฐ ํŒ์—… ๋…ธ์ถœ + } + + private fun togglePushNotification() { + updateState { copy(isPushEnabled = !isPushEnabled) } + } + + private fun openWebView(url: String) { + // TODO ์›น๋ทฐ ๋„์šฐ๊ธฐ + } + + private fun navigateToOpenSource() { + // TODO ์˜คํ”ˆ์†Œ์Šค ํ…์ŠคํŠธ ๋„์šฐ๊ธฐ + } + + private fun logout() { + // TODO ๋กœ๊ทธ์•„์›ƒ ํŒ์—… ๋…ธ์ถœ + } + + private fun deleteAccount() { + // TODO ๊ณ„์ • ์‚ญ์ œ ํŒ์—… ๋…ธ์ถœ + } + + private fun navigateBack() { + sendEffect(TraineeMyPageEffect.NavigateToPrevious) + } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt new file mode 100644 index 00000000..e270c552 --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt @@ -0,0 +1,27 @@ +package co.kr.tnt.trainee.mypage.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptionsBuilder +import androidx.navigation.compose.composable +import co.kr.tnt.navigation.Route +import co.kr.tnt.trainee.mypage.TraineeMyPageRoute + +fun NavController.navigateToTraineeMyPage( + navOptions: NavOptionsBuilder.() -> Unit = {}, +) = navigate( + route = Route.TraineeMyPage, + builder = navOptions, +) + +fun NavGraphBuilder.traineeMyPageScreen( + navigateToPrevious: () -> Unit, + navigateToTraineeConnect: () -> Unit, +) { + composable { + TraineeMyPageRoute( + navigateToPrevious = navigateToPrevious, + navigateToConnect = navigateToTraineeConnect, + ) + } +} From c4d3ee8fc9d61aac0e7ba085387a6d5615a046b0 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 00:39:01 +0900 Subject: [PATCH 06/21] =?UTF-8?q?[TNT-185]=20feat:=20TnTIconPopupDialog=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/co/kr/tnt/designsystem/component/Dialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Dialog.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Dialog.kt index a311f2e0..7b76735c 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Dialog.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/Dialog.kt @@ -156,7 +156,7 @@ fun TnTSingleButtonPopupDialog( fun TnTIconPopupDialog( title: String, content: String, - topIcon: Painter, + topIcon: Painter = painterResource(R.drawable.ic_round_warning), leftButtonText: String, rightButtonText: String, modifier: Modifier = Modifier, From 438dd3dc99497fa8b473e765983b545916cbe1f3 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 00:52:01 +0900 Subject: [PATCH 07/21] =?UTF-8?q?[TNT-185]=20feat:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20PopupType=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dialog์— ๋„์šธ ๋ฌธ๊ตฌ return --- .../kr/tnt/trainee/mypage/model/PopupType.kt | 30 +++++++++++++++++++ .../mypage/src/main/res/values/strings.xml | 14 +++++++++ 2 files changed, 44 insertions(+) create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt new file mode 100644 index 00000000..e907c9e6 --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt @@ -0,0 +1,30 @@ +package co.kr.tnt.trainee.mypage.model + +import androidx.annotation.StringRes +import co.kr.tnt.feature.trainee.mypage.R + +enum class PopupType( + @StringRes val firstPopupTitle: Int, + @StringRes val firstPopupContent: Int, + @StringRes val secondPopupTitle: Int, + @StringRes val secondPopupContent: Int, +) { + LOGOUT( + firstPopupTitle = R.string.logout_title, + firstPopupContent = R.string.logout_content, + secondPopupTitle = R.string.logout_complete_title, + secondPopupContent = R.string.logout_content, + ), + DELETE_ACCOUNT( + firstPopupTitle = R.string.delete_account_title, + firstPopupContent = R.string.delete_account_content, + secondPopupTitle = R.string.delete_account_complete_title, + secondPopupContent = R.string.delete_account_complete_content, + ), + DISCONNECT( + firstPopupTitle = R.string.disconnect_title, + firstPopupContent = R.string.disconnect_content, + secondPopupTitle = R.string.disconnect_complete_title, + secondPopupContent = R.string.disconnect_complete_content, + ), +} diff --git a/feature/trainee/mypage/src/main/res/values/strings.xml b/feature/trainee/mypage/src/main/res/values/strings.xml index c5885e1b..710858c1 100644 --- a/feature/trainee/mypage/src/main/res/values/strings.xml +++ b/feature/trainee/mypage/src/main/res/values/strings.xml @@ -3,4 +3,18 @@ ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐ๋Š๊ธฐ + %s ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐ์„ ํ•ด์ œํ• ๊นŒ์š”? + ํž˜๊ป˜ ๋‚˜๋ˆด๋˜ ๊ธฐ๋ก๋“ค์ด ์‚ฌ๋ผ์ ธ์š” + %s ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐ์ด ํ•ด์ œ๋˜์—ˆ์–ด์š” + ๋” ํญ๋ฐœ์ ์ธ ์ผ€๋ฏธ๋กœ ๋‹ค์‹œ ๋งŒ๋‚˜์š”! + + ํ˜„์žฌ ๊ณ„์ •์„ ๋กœ๊ทธ์•„์›ƒ ํ• ๊นŒ์š”? + ์–ธ์ œ๋“ ์ง€ ๋‹ค์‹œ ๋กœ๊ทธ์ธ ํ•  ์ˆ˜ ์žˆ์–ด์š”! + ๋กœ๊ทธ์•„์›ƒ์ด ์™„๋ฃŒ๋˜์—ˆ์–ด์š” + + ๊ณ„์ •์„ ํƒˆํ‡ดํ• ๊นŒ์š”? + ์šด๋™ ๋ฐ ์‹๋‹จ ๊ธฐ๋ก์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ๋ผ์ ธ์š”! + ๊ณ„์ • ํƒˆํ‡ด๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์–ด์š” + ๋‹ค์Œ์— ๋” ํญ๋ฐœ์ ์ธ ์ผ€๋ฏธ๋กœ ๋‹ค์‹œ ๋งŒ๋‚˜์š”! ๐Ÿ’ฃ + From 14287fb3d27c90e652ddf37fd35b8c6847216a9f Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 01:00:32 +0900 Subject: [PATCH 08/21] =?UTF-8?q?[TNT-185]=20feat:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83,=20=EA=B3=84=EC=A0=95=20=ED=83=88=ED=87=B4,=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EB=81=8A=EA=B8=B0=20=ED=8C=9D=EC=97=85=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 - handleSecondPopupConfirm ํ†ตํ•ด์„œ ํŒ์—…์— ๋งž๋Š” API ํ˜ธ์ถœ - ๋กœ๊ทธ์•„์›ƒ, ๊ณ„์ • ํƒˆํ‡ด ์‹œ ๋กœ๊ทธ์ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ --- core/ui/src/main/res/values/strings.xml | 1 + .../tnt/login/navigation/LoginNavigation.kt | 9 +- .../main/java/co/kr/tnt/main/ui/TnTNavHost.kt | 2 + .../trainee/mypage/TraineeMyPageContract.kt | 11 ++- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 44 +++++++++ .../trainee/mypage/TraineeMyPageViewModel.kt | 96 ++++++++++++++++++- .../navigation/TraineeMyPageNavigation.kt | 2 + 7 files changed, 159 insertions(+), 6 deletions(-) diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index e4701a86..edc0d4ac 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -25,6 +25,7 @@ ์—ฐ๊ฒฐํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๊ธฐ ๊ฑด๋„ˆ๋›ฐ๊ธฐ + ์ทจ์†Œ ์ด๋ฆ„ ๋‚˜์ด diff --git a/feature/login/src/main/java/co/kr/tnt/login/navigation/LoginNavigation.kt b/feature/login/src/main/java/co/kr/tnt/login/navigation/LoginNavigation.kt index 7b02f712..f0cca535 100644 --- a/feature/login/src/main/java/co/kr/tnt/login/navigation/LoginNavigation.kt +++ b/feature/login/src/main/java/co/kr/tnt/login/navigation/LoginNavigation.kt @@ -4,15 +4,22 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder import androidx.navigation.compose.composable +import androidx.navigation.navOptions import co.kr.tnt.domain.model.LoginResult import co.kr.tnt.login.LoginRoute import co.kr.tnt.navigation.Route fun NavController.navigateToLogin( + clearBackStack: Boolean = false, navOptions: NavOptionsBuilder.() -> Unit = {}, ) = navigate( route = Route.Login, - builder = navOptions, + navOptions = navOptions { + if (clearBackStack) { + popUpTo(graph.startDestinationId) { inclusive = true } + } + navOptions() + }, ) fun NavGraphBuilder.loginScreen( diff --git a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt index 97e1d28d..4853b6e7 100644 --- a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt +++ b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt @@ -8,6 +8,7 @@ import androidx.navigation.compose.NavHost import co.kr.tnt.home.navigation.homeNavGraph import co.kr.tnt.home.navigation.navigateToHome import co.kr.tnt.login.navigation.loginScreen +import co.kr.tnt.login.navigation.navigateToLogin import co.kr.tnt.navigation.Route import co.kr.tnt.roleselect.navigateToRoleSelection import co.kr.tnt.roleselect.roleSelectionScreen @@ -92,6 +93,7 @@ fun TnTNavHost( traineeMyPageScreen( navigateToPrevious = { navController.popBackStack() }, navigateToTraineeConnect = { navController.navigateToTraineeConnect(isFromMyPage = true) }, + navigateToLogin = { navController.navigateToLogin(clearBackStack = true) }, ) homeNavGraph() } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index 2d820183..acedefba 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -1,5 +1,6 @@ package co.kr.tnt.trainee.mypage +import co.kr.tnt.trainee.mypage.model.PopupType import co.kr.tnt.ui.base.UiEvent import co.kr.tnt.ui.base.UiSideEffect import co.kr.tnt.ui.base.UiState @@ -7,10 +8,14 @@ import co.kr.tnt.ui.base.UiState internal class TraineeMyPageContract { data class TraineeMyPageUiState( val image: String? = "", - val name: String = "์ด๋ฆ„", + val name: String = "", + val trainerName: String = "", val isConnected: Boolean = false, val isPushEnabled: Boolean = true, val appVersion: String = "0.0.0", + val popupType: PopupType = PopupType.LOGOUT, + val showFirstPopup: Boolean = false, + val showSecondPopup: Boolean = false, ) : UiState sealed interface TraineeMyPageUiEvent : UiEvent { @@ -23,12 +28,16 @@ internal class TraineeMyPageContract { data object OnOpenSourceClick : TraineeMyPageUiEvent data object OnLogoutClick : TraineeMyPageUiEvent data object OnDeleteAccountClick : TraineeMyPageUiEvent + data object OnConfirmFirstPopup : TraineeMyPageUiEvent + data object OnDismissPopup : TraineeMyPageUiEvent data object OnBackClick : TraineeMyPageUiEvent + data object OnConfirmSecondPopup : TraineeMyPageUiEvent } sealed interface TraineeMyPageEffect : UiSideEffect { data class ShowToast(val message: String) : TraineeMyPageEffect data object NavigateToConnect : TraineeMyPageEffect data object NavigateToPrevious : TraineeMyPageEffect + data object NavigateToLogin : TraineeMyPageEffect } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 62c08579..52ddbc69 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -32,7 +32,9 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import co.kr.tnt.designsystem.component.TnTIconPopupDialog import co.kr.tnt.designsystem.component.TnTProfileImage +import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog import co.kr.tnt.designsystem.component.TnTSwitch import co.kr.tnt.designsystem.component.button.TnTTextButton import co.kr.tnt.designsystem.component.button.model.ButtonSize @@ -42,6 +44,7 @@ import co.kr.tnt.feature.trainee.mypage.R import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState +import co.kr.tnt.trainee.mypage.model.PopupType import co.kr.tnt.ui.model.DefaultUserProfile import co.kr.tnt.core.ui.R as uiResource @@ -49,6 +52,7 @@ import co.kr.tnt.core.ui.R as uiResource internal fun TraineeMyPageRoute( navigateToPrevious: () -> Unit, navigateToConnect: () -> Unit, + navigateToLogin: () -> Unit, viewModel: TraineeMyPageViewModel = hiltViewModel(), ) { val context = LocalContext.current @@ -66,6 +70,9 @@ internal fun TraineeMyPageRoute( onOpenSourceClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnOpenSourceClick) }, onLogoutClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnLogoutClick) }, onDeleteAccountClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDeleteAccountClick) }, + onDismissPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnDismissPopup) }, + onConfirmFirstPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmFirstPopup) }, + onConfirmSecondPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmSecondPopup) }, ) LaunchedEffect(viewModel.effect) { @@ -73,6 +80,7 @@ internal fun TraineeMyPageRoute( when (effect) { TraineeMyPageEffect.NavigateToPrevious -> navigateToPrevious() TraineeMyPageEffect.NavigateToConnect -> navigateToConnect() + TraineeMyPageEffect.NavigateToLogin -> navigateToLogin() is TraineeMyPageEffect.ShowToast -> { Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show() } @@ -94,6 +102,9 @@ private fun TraineeMyPageScreen( onOpenSourceClick: () -> Unit, onLogoutClick: () -> Unit, onDeleteAccountClick: () -> Unit, + onConfirmFirstPopup: () -> Unit, + onConfirmSecondPopup: () -> Unit, + onDismissPopup: () -> Unit, ) { BackHandler { onBackClick() } @@ -230,6 +241,36 @@ private fun TraineeMyPageScreen( } } } + + if (state.showFirstPopup) { + TnTIconPopupDialog( + title = if (state.popupType == PopupType.DISCONNECT) { + stringResource(state.popupType.firstPopupTitle, state.trainerName) + } else { + stringResource(state.popupType.firstPopupTitle) + }, + content = stringResource(state.popupType.firstPopupContent), + leftButtonText = stringResource(uiResource.string.cancel), + rightButtonText = stringResource(uiResource.string.ok), + onLeftButtonClick = onDismissPopup, + onRightButtonClick = onConfirmFirstPopup, + onDismiss = onDismissPopup, + ) + } + + if (state.showSecondPopup) { + TnTSingleButtonPopupDialog( + title = if (state.popupType == PopupType.DISCONNECT) { + stringResource(state.popupType.secondPopupTitle, state.trainerName) + } else { + stringResource(state.popupType.secondPopupTitle) + }, + content = stringResource(state.popupType.secondPopupContent), + buttonText = stringResource(uiResource.string.ok), + onButtonClick = onConfirmSecondPopup, + onDismiss = onDismissPopup, + ) + } } @Composable @@ -284,6 +325,9 @@ private fun TraineeMyPagePreview() { onDeleteAccountClick = {}, onDisconnectButtonClick = {}, onBackClick = {}, + onConfirmFirstPopup = {}, + onDismissPopup = {}, + onConfirmSecondPopup = {}, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index c725e3b4..94fb3438 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -1,10 +1,13 @@ package co.kr.tnt.trainee.mypage +import androidx.lifecycle.viewModelScope import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState +import co.kr.tnt.trainee.mypage.model.PopupType import co.kr.tnt.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -24,11 +27,38 @@ internal class TraineeMyPageViewModel @Inject constructor() : TraineeMyPageUiEvent.OnLogoutClick -> logout() TraineeMyPageUiEvent.OnDeleteAccountClick -> deleteAccount() TraineeMyPageUiEvent.OnBackClick -> navigateBack() + TraineeMyPageUiEvent.OnConfirmFirstPopup -> confirmFirstPopup() + TraineeMyPageUiEvent.OnConfirmSecondPopup -> handleSecondPopupConfirm() + TraineeMyPageUiEvent.OnDismissPopup -> dismissPopup() + } + } + + init { + loadUserData() + } + + private fun loadUserData() { + viewModelScope.launch { + try { + // TODO ์œ ์ € ์ •๋ณด API ํ˜ธ์ถœ + updateState { + copy( + image = null, + name = "๊น€ํšŒ์›", + trainerName = "", + isConnected = true, + isPushEnabled = true, + appVersion = "0.0.0", + ) + } + } catch (e: Exception) { + // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ + } } } private fun navigateToPersonalInfo() { - // TODO ๊ฐœ์ธ ์ •๋ณด ์ˆ˜์ • ํ™”๋ฉด + // TODO ๊ฐœ์ธ ์ •๋ณด ์ˆ˜์ • ํ™”๋ฉด ์—ฐ๊ฒฐ } private fun navigateToConnect() { @@ -36,7 +66,14 @@ internal class TraineeMyPageViewModel @Inject constructor() : } private fun showDisconnectPopup() { - // TODO ์—ฐ๊ฒฐ ๋Š๊ธฐ ํŒ์—… ๋…ธ์ถœ + // TODO ํŠธ๋ ˆ์ด๋„ˆ ์ด๋ฆ„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ? -> API ๋‚˜์˜ค๋ฉด ์ˆ˜์ • + updateState { + copy( + trainerName = "๊น€ํ”ผํ‹ฐ", + showFirstPopup = true, + popupType = PopupType.DISCONNECT, + ) + } } private fun togglePushNotification() { @@ -52,14 +89,65 @@ internal class TraineeMyPageViewModel @Inject constructor() : } private fun logout() { - // TODO ๋กœ๊ทธ์•„์›ƒ ํŒ์—… ๋…ธ์ถœ + updateState { copy(showFirstPopup = true, popupType = PopupType.LOGOUT) } } private fun deleteAccount() { - // TODO ๊ณ„์ • ์‚ญ์ œ ํŒ์—… ๋…ธ์ถœ + updateState { copy(showFirstPopup = true, popupType = PopupType.DELETE_ACCOUNT) } } private fun navigateBack() { sendEffect(TraineeMyPageEffect.NavigateToPrevious) } + + private fun confirmFirstPopup() { + updateState { copy(showFirstPopup = false, showSecondPopup = true) } + } + + private fun dismissPopup() { + updateState { copy(showFirstPopup = false, showSecondPopup = false) } + } + + private fun handleSecondPopupConfirm() { + when (currentState.popupType) { + PopupType.LOGOUT -> performLogout() + PopupType.DELETE_ACCOUNT -> performAccountDeletion() + PopupType.DISCONNECT -> performDisconnect() + } + } + + private fun performLogout() { + viewModelScope.launch { + try { + // TODO ๋กœ๊ทธ์•„์›ƒ API ํ˜ธ์ถœ + updateState { copy(showSecondPopup = false) } + sendEffect(TraineeMyPageEffect.NavigateToLogin) + } catch (e: Exception) { + // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ + } + } + } + + private fun performAccountDeletion() { + viewModelScope.launch { + try { + // TODO ํšŒ์› ํƒˆํ‡ด API ํ˜ธ์ถœ + updateState { copy(showSecondPopup = false) } + sendEffect(TraineeMyPageEffect.NavigateToLogin) + } catch (e: Exception) { + // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ + } + } + } + + private fun performDisconnect() { + viewModelScope.launch { + try { + // TODO ์—ฐ๊ฒฐ ํ•ด์ œ API ํ˜ธ์ถœ + updateState { copy(isConnected = false, showSecondPopup = false) } + } catch (e: Exception) { + // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ + } + } + } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt index e270c552..267a739f 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt @@ -17,11 +17,13 @@ fun NavController.navigateToTraineeMyPage( fun NavGraphBuilder.traineeMyPageScreen( navigateToPrevious: () -> Unit, navigateToTraineeConnect: () -> Unit, + navigateToLogin: () -> Unit, ) { composable { TraineeMyPageRoute( navigateToPrevious = navigateToPrevious, navigateToConnect = navigateToTraineeConnect, + navigateToLogin = navigateToLogin, ) } } From 2df84c15c6e5e5bd14a6f56e1de17853acacd6b4 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 01:18:36 +0900 Subject: [PATCH 09/21] =?UTF-8?q?[TNT-185]=20feat:=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EC=9D=B4=EC=9A=A9=EC=95=BD=EA=B4=80,=20=EA=B0=9C?= =?UTF-8?q?=EC=9D=B8=EC=A0=95=EB=B3=B4=20=EC=B2=98=EB=A6=AC=EB=B0=A9?= =?UTF-8?q?=EC=B9=A8=20=EC=9B=B9=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API ์—ฐ๋™ ํ•˜๋ฉด์„œ ์ˆ˜์ •ํ•  ์˜ˆ์ • --- .../trainee/mypage/TraineeMyPageContract.kt | 5 +++-- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 4 ++-- .../trainee/mypage/TraineeMyPageViewModel.kt | 20 +++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index acedefba..b9148379 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -1,5 +1,6 @@ package co.kr.tnt.trainee.mypage +import android.content.Context import co.kr.tnt.trainee.mypage.model.PopupType import co.kr.tnt.ui.base.UiEvent import co.kr.tnt.ui.base.UiSideEffect @@ -23,8 +24,8 @@ internal class TraineeMyPageContract { data object OnConnectButtonClick : TraineeMyPageUiEvent data object OnDisconnectButtonClick : TraineeMyPageUiEvent data object ToggleNotification : TraineeMyPageUiEvent - data object OnServiceTermClick : TraineeMyPageUiEvent - data object OnPrivacyClick : TraineeMyPageUiEvent + data class OnServiceTermClick(val context: Context) : TraineeMyPageUiEvent + data class OnPrivacyClick(val context: Context) : TraineeMyPageUiEvent data object OnOpenSourceClick : TraineeMyPageUiEvent data object OnLogoutClick : TraineeMyPageUiEvent data object OnDeleteAccountClick : TraineeMyPageUiEvent diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 52ddbc69..610d259a 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -65,8 +65,8 @@ internal fun TraineeMyPageRoute( onConnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnConnectButtonClick) }, onDisconnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDisconnectButtonClick) }, onPushNotificationToggle = { viewModel.setEvent(TraineeMyPageUiEvent.ToggleNotification) }, - onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick) }, - onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick) }, + onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick(context)) }, + onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick(context)) }, onOpenSourceClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnOpenSourceClick) }, onLogoutClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnLogoutClick) }, onDeleteAccountClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDeleteAccountClick) }, diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index 94fb3438..bf49feda 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -1,5 +1,8 @@ package co.kr.tnt.trainee.mypage +import android.content.Context +import android.content.Intent +import android.net.Uri import androidx.lifecycle.viewModelScope import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent @@ -21,8 +24,14 @@ internal class TraineeMyPageViewModel @Inject constructor() : TraineeMyPageUiEvent.OnConnectButtonClick -> navigateToConnect() TraineeMyPageUiEvent.OnDisconnectButtonClick -> showDisconnectPopup() TraineeMyPageUiEvent.ToggleNotification -> togglePushNotification() - TraineeMyPageUiEvent.OnServiceTermClick -> openWebView(url = "https://www.naver.com") - TraineeMyPageUiEvent.OnPrivacyClick -> openWebView(url = "https://www.naver.com") + is TraineeMyPageUiEvent.OnServiceTermClick -> openWebView( + url = "https://www.naver.com", + context = event.context, + ) + is TraineeMyPageUiEvent.OnPrivacyClick -> openWebView( + url = "https://github.com/", + context = event.context, + ) TraineeMyPageUiEvent.OnOpenSourceClick -> navigateToOpenSource() TraineeMyPageUiEvent.OnLogoutClick -> logout() TraineeMyPageUiEvent.OnDeleteAccountClick -> deleteAccount() @@ -80,8 +89,11 @@ internal class TraineeMyPageViewModel @Inject constructor() : updateState { copy(isPushEnabled = !isPushEnabled) } } - private fun openWebView(url: String) { - // TODO ์›น๋ทฐ ๋„์šฐ๊ธฐ + private fun openWebView(url: String, context: Context) { + // TODO ์›น๋ทฐ ์ˆ˜์ • + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url.ifEmpty { "https://www.google.com" })) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) } private fun navigateToOpenSource() { From 23ffad1989ba07f8de69cdf805387c2c2c110fde Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 02:38:20 +0900 Subject: [PATCH 10/21] =?UTF-8?q?[TNT-185]=20fix:=20=EC=9B=B9=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ์™ธ๋ถ€ ์•ฑ์ด ์•„๋‹Œ ์›น๋ทฐ๋กœ ๋„์šฐ๋„๋ก ์ˆ˜์ • - state๋กœ ์›น๋ทฐ ๋ณด์—ฌ์ง€๊ธฐ ์—ฌ๋ถ€์™€ url ๊ด€๋ฆฌ --- .../trainee/mypage/TraineeMyPageContract.kt | 8 +- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 268 ++++++++++-------- .../trainee/mypage/TraineeMyPageViewModel.kt | 27 +- 3 files changed, 173 insertions(+), 130 deletions(-) diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index b9148379..a2c068b8 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -1,6 +1,5 @@ package co.kr.tnt.trainee.mypage -import android.content.Context import co.kr.tnt.trainee.mypage.model.PopupType import co.kr.tnt.ui.base.UiEvent import co.kr.tnt.ui.base.UiSideEffect @@ -15,6 +14,8 @@ internal class TraineeMyPageContract { val isPushEnabled: Boolean = true, val appVersion: String = "0.0.0", val popupType: PopupType = PopupType.LOGOUT, + val url: String = "", + val showWebView: Boolean = false, val showFirstPopup: Boolean = false, val showSecondPopup: Boolean = false, ) : UiState @@ -24,8 +25,8 @@ internal class TraineeMyPageContract { data object OnConnectButtonClick : TraineeMyPageUiEvent data object OnDisconnectButtonClick : TraineeMyPageUiEvent data object ToggleNotification : TraineeMyPageUiEvent - data class OnServiceTermClick(val context: Context) : TraineeMyPageUiEvent - data class OnPrivacyClick(val context: Context) : TraineeMyPageUiEvent + data object OnServiceTermClick : TraineeMyPageUiEvent + data object OnPrivacyClick : TraineeMyPageUiEvent data object OnOpenSourceClick : TraineeMyPageUiEvent data object OnLogoutClick : TraineeMyPageUiEvent data object OnDeleteAccountClick : TraineeMyPageUiEvent @@ -33,6 +34,7 @@ internal class TraineeMyPageContract { data object OnDismissPopup : TraineeMyPageUiEvent data object OnBackClick : TraineeMyPageUiEvent data object OnConfirmSecondPopup : TraineeMyPageUiEvent + data object OnWebViewBackClick : TraineeMyPageUiEvent } sealed interface TraineeMyPageEffect : UiSideEffect { diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 610d259a..9a27b608 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -1,5 +1,8 @@ package co.kr.tnt.trainee.mypage +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.background @@ -10,6 +13,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -30,6 +34,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import co.kr.tnt.designsystem.component.TnTIconPopupDialog @@ -65,14 +70,15 @@ internal fun TraineeMyPageRoute( onConnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnConnectButtonClick) }, onDisconnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDisconnectButtonClick) }, onPushNotificationToggle = { viewModel.setEvent(TraineeMyPageUiEvent.ToggleNotification) }, - onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick(context)) }, - onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick(context)) }, + onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick) }, + onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick) }, onOpenSourceClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnOpenSourceClick) }, onLogoutClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnLogoutClick) }, onDeleteAccountClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDeleteAccountClick) }, onDismissPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnDismissPopup) }, onConfirmFirstPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmFirstPopup) }, onConfirmSecondPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmSecondPopup) }, + onDismissWebView = { viewModel.setEvent(TraineeMyPageUiEvent.OnWebViewBackClick) }, ) LaunchedEffect(viewModel.effect) { @@ -105,138 +111,146 @@ private fun TraineeMyPageScreen( onConfirmFirstPopup: () -> Unit, onConfirmSecondPopup: () -> Unit, onDismissPopup: () -> Unit, + onDismissWebView: () -> Unit, ) { - BackHandler { onBackClick() } + if (state.showWebView) { + WebViewScreen( + url = state.url, + onBackPress = onDismissWebView, + ) + } else { + BackHandler { onBackClick() } - Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(innerPadding), - ) { - TnTProfileImage( - defaultImage = painterResource(DefaultUserProfile.Trainee.image), - imageSize = 132.dp, - showEditButton = false, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - ) - Text( - text = state.name, - color = TnTTheme.colors.neutralColors.Neutral950, - style = TnTTheme.typography.h2, - ) - Spacer(Modifier.height(8.dp)) - TnTTextButton( - text = stringResource(uiResource.string.modifying_personal_info), - size = ButtonSize.Small, - type = ButtonType.Gray, - onClick = onEditButtonClick, - ) + Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> Column( - verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier - .fillMaxWidth() - .padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(innerPadding), ) { - if (state.isConnected.not()) { - WhiteButton( - text = stringResource(R.string.connect_with_trainer), - height = 47.dp, - onClick = onConnectButtonClick, - ) - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, + TnTProfileImage( + defaultImage = painterResource(DefaultUserProfile.Trainee.image), + imageSize = 132.dp, + showEditButton = false, modifier = Modifier .fillMaxWidth() - .height(48.dp) - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(horizontal = 20.dp), - ) { - Text( - text = stringResource(uiResource.string.app_push_notification), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - TnTSwitch( - checked = state.isPushEnabled, - onCheckedChange = onPushNotificationToggle, - ) - } + .padding(vertical = 12.dp), + ) + Text( + text = state.name, + color = TnTTheme.colors.neutralColors.Neutral950, + style = TnTTheme.typography.h2, + ) + Spacer(Modifier.height(8.dp)) + TnTTextButton( + text = stringResource(uiResource.string.modifying_personal_info), + size = ButtonSize.Small, + type = ButtonType.Gray, + onClick = onEditButtonClick, + ) Column( + verticalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), + .fillMaxWidth() + .padding(20.dp), ) { - WhiteButton( - text = stringResource(uiResource.string.terms_of_service), - height = 40.dp, - onClick = onServiceTermClick, - ) - WhiteButton( - text = stringResource(uiResource.string.privacy_policy), - height = 40.dp, - onClick = onPrivacyClick, - ) + if (state.isConnected.not()) { + WhiteButton( + text = stringResource(R.string.connect_with_trainer), + height = 47.dp, + onClick = onConnectButtonClick, + ) + } Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .height(40.dp) - .padding(horizontal = 20.dp, vertical = 8.dp), + .height(48.dp) + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(horizontal = 20.dp), ) { Text( - text = stringResource(uiResource.string.app_version), + text = stringResource(uiResource.string.app_push_notification), color = TnTTheme.colors.neutralColors.Neutral700, style = TnTTheme.typography.body2Medium, ) - Text( - text = state.appVersion, - color = TnTTheme.colors.neutralColors.Neutral400, - style = TnTTheme.typography.body2Medium, + TnTSwitch( + checked = state.isPushEnabled, + onCheckedChange = onPushNotificationToggle, ) } - WhiteButton( - text = stringResource(uiResource.string.open_source_license), - height = 40.dp, - onClick = onOpenSourceClick, - ) - } - if (state.isConnected) { - WhiteButton( - text = stringResource(R.string.disconnect_with_trainer), - height = 47.dp, - onClick = onDisconnectButtonClick, - ) - } - Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), - ) { - Text( - text = stringResource(uiResource.string.logout), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, + Column( modifier = Modifier - .padding(horizontal = 20.dp, vertical = 12.dp) - .clickable(onClick = onLogoutClick), - ) - Text( - text = stringResource(uiResource.string.delete_account), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + WhiteButton( + text = stringResource(uiResource.string.terms_of_service), + height = 40.dp, + onClick = onServiceTermClick, + ) + WhiteButton( + text = stringResource(uiResource.string.privacy_policy), + height = 40.dp, + onClick = onPrivacyClick, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .height(40.dp) + .padding(horizontal = 20.dp, vertical = 8.dp), + ) { + Text( + text = stringResource(uiResource.string.app_version), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + Text( + text = state.appVersion, + color = TnTTheme.colors.neutralColors.Neutral400, + style = TnTTheme.typography.body2Medium, + ) + } + WhiteButton( + text = stringResource(uiResource.string.open_source_license), + height = 40.dp, + onClick = onOpenSourceClick, + ) + } + if (state.isConnected) { + WhiteButton( + text = stringResource(R.string.disconnect_with_trainer), + height = 47.dp, + onClick = onDisconnectButtonClick, + ) + } + Column( modifier = Modifier - .padding(horizontal = 20.dp, vertical = 12.dp) - .clickable(onClick = onDeleteAccountClick), - ) + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + Text( + text = stringResource(uiResource.string.logout), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 12.dp) + .clickable(onClick = onLogoutClick), + ) + Text( + text = stringResource(uiResource.string.delete_account), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 12.dp) + .clickable(onClick = onDeleteAccountClick), + ) + } } } } @@ -303,6 +317,37 @@ private fun WhiteButton( } } +@Composable +fun WebViewScreen(url: String, onBackPress: () -> Unit) { + var webView: WebView? = null + + AndroidView( + factory = { context -> + WebView(context).apply { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + settings.loadWithOverviewMode = true + settings.useWideViewPort = true + settings.builtInZoomControls = true + settings.displayZoomControls = false + settings.cacheMode = WebSettings.LOAD_NO_CACHE + webViewClient = WebViewClient() + loadUrl(url) + webView = this + } + }, + modifier = Modifier.fillMaxSize(), + ) + + BackHandler { + if (webView?.canGoBack() == true) { + webView?.goBack() + } else { + onBackPress() + } + } +} + @Preview @Composable private fun TraineeMyPagePreview() { @@ -328,6 +373,7 @@ private fun TraineeMyPagePreview() { onConfirmFirstPopup = {}, onDismissPopup = {}, onConfirmSecondPopup = {}, + onDismissWebView = {}, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index bf49feda..b7167e66 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -1,8 +1,5 @@ package co.kr.tnt.trainee.mypage -import android.content.Context -import android.content.Intent -import android.net.Uri import androidx.lifecycle.viewModelScope import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent @@ -24,14 +21,9 @@ internal class TraineeMyPageViewModel @Inject constructor() : TraineeMyPageUiEvent.OnConnectButtonClick -> navigateToConnect() TraineeMyPageUiEvent.OnDisconnectButtonClick -> showDisconnectPopup() TraineeMyPageUiEvent.ToggleNotification -> togglePushNotification() - is TraineeMyPageUiEvent.OnServiceTermClick -> openWebView( - url = "https://www.naver.com", - context = event.context, - ) - is TraineeMyPageUiEvent.OnPrivacyClick -> openWebView( - url = "https://github.com/", - context = event.context, - ) + TraineeMyPageUiEvent.OnServiceTermClick -> openWebView() + TraineeMyPageUiEvent.OnPrivacyClick -> openWebView() + TraineeMyPageUiEvent.OnWebViewBackClick -> dismissWebView() TraineeMyPageUiEvent.OnOpenSourceClick -> navigateToOpenSource() TraineeMyPageUiEvent.OnLogoutClick -> logout() TraineeMyPageUiEvent.OnDeleteAccountClick -> deleteAccount() @@ -89,11 +81,14 @@ internal class TraineeMyPageViewModel @Inject constructor() : updateState { copy(isPushEnabled = !isPushEnabled) } } - private fun openWebView(url: String, context: Context) { - // TODO ์›น๋ทฐ ์ˆ˜์ • - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url.ifEmpty { "https://www.google.com" })) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - context.startActivity(intent) + private fun openWebView() { + // TODO Url API ํ˜ธ์ถœ? + val url = "https://www.naver.com" + updateState { copy(showWebView = !showWebView, url = url) } + } + + private fun dismissWebView() { + updateState { copy(showWebView = !showWebView) } } private fun navigateToOpenSource() { From 046a5b5e33cd19347f4193901bdef8395fc91263 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 02:49:18 +0900 Subject: [PATCH 11/21] =?UTF-8?q?[TNT-185]=20feat:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=95=98=EC=96=80=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20core:ui=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../co/kr/tnt/ui/component/TnTMyPageButton.kt | 58 +++++++++++++++++++ .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 46 ++------------- 2 files changed, 64 insertions(+), 40 deletions(-) create mode 100644 core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt diff --git a/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt b/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt new file mode 100644 index 00000000..6e033541 --- /dev/null +++ b/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt @@ -0,0 +1,58 @@ +package co.kr.tnt.ui.component + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +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 androidx.compose.ui.unit.dp +import co.kr.tnt.designsystem.theme.TnTTheme + +@Composable +fun TnTMyPageButton( + text: String, + height: Dp, + modifier: Modifier = Modifier, + onClick: () -> Unit, +) { + Button( + onClick = onClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonColors( + containerColor = TnTTheme.colors.commonColors.Common0, + contentColor = TnTTheme.colors.neutralColors.Neutral700, + disabledContainerColor = TnTTheme.colors.commonColors.Common0, + disabledContentColor = TnTTheme.colors.neutralColors.Neutral700, + ), + contentPadding = PaddingValues(horizontal = 20.dp, vertical = 12.dp), + modifier = modifier + .fillMaxWidth() + .height(height) + .defaultMinSize(minWidth = Dp.Hairline), + ) { + Text( + text = text, + style = TnTTheme.typography.body2Medium, + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@Preview +@Composable +private fun TnTMyPageButtonPreview() { + TnTTheme { + TnTMyPageButton( + text = "ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ", + height = 47.dp, + onClick = {}, + ) + } +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 9a27b608..80898478 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -9,17 +9,13 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize 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.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -32,7 +28,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.hilt.navigation.compose.hiltViewModel @@ -50,6 +45,7 @@ import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState import co.kr.tnt.trainee.mypage.model.PopupType +import co.kr.tnt.ui.component.TnTMyPageButton import co.kr.tnt.ui.model.DefaultUserProfile import co.kr.tnt.core.ui.R as uiResource @@ -153,7 +149,7 @@ private fun TraineeMyPageScreen( .padding(20.dp), ) { if (state.isConnected.not()) { - WhiteButton( + TnTMyPageButton( text = stringResource(R.string.connect_with_trainer), height = 47.dp, onClick = onConnectButtonClick, @@ -185,12 +181,12 @@ private fun TraineeMyPageScreen( .background(TnTTheme.colors.commonColors.Common0) .padding(vertical = 12.dp), ) { - WhiteButton( + TnTMyPageButton( text = stringResource(uiResource.string.terms_of_service), height = 40.dp, onClick = onServiceTermClick, ) - WhiteButton( + TnTMyPageButton( text = stringResource(uiResource.string.privacy_policy), height = 40.dp, onClick = onPrivacyClick, @@ -214,14 +210,14 @@ private fun TraineeMyPageScreen( style = TnTTheme.typography.body2Medium, ) } - WhiteButton( + TnTMyPageButton( text = stringResource(uiResource.string.open_source_license), height = 40.dp, onClick = onOpenSourceClick, ) } if (state.isConnected) { - WhiteButton( + TnTMyPageButton( text = stringResource(R.string.disconnect_with_trainer), height = 47.dp, onClick = onDisconnectButtonClick, @@ -287,36 +283,6 @@ private fun TraineeMyPageScreen( } } -@Composable -private fun WhiteButton( - text: String, - height: Dp, - modifier: Modifier = Modifier, - onClick: () -> Unit, -) { - Button( - onClick = onClick, - shape = RoundedCornerShape(12.dp), - colors = ButtonColors( - containerColor = TnTTheme.colors.commonColors.Common0, - contentColor = TnTTheme.colors.neutralColors.Neutral700, - disabledContainerColor = TnTTheme.colors.commonColors.Common0, - disabledContentColor = TnTTheme.colors.neutralColors.Neutral700, - ), - contentPadding = PaddingValues(horizontal = 20.dp, vertical = 12.dp), - modifier = modifier - .fillMaxWidth() - .height(height) - .defaultMinSize(minWidth = Dp.Hairline), - ) { - Text( - text = text, - style = TnTTheme.typography.body2Medium, - modifier = Modifier.fillMaxWidth(), - ) - } -} - @Composable fun WebViewScreen(url: String, onBackPress: () -> Unit) { var webView: WebView? = null From e9c8421333622025a4fa3b7777dcc406a0163a00 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 15:07:38 +0900 Subject: [PATCH 12/21] =?UTF-8?q?[TNT-185]=20fix:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=81=AC=EA=B8=B0=EC=97=90=20=EB=8C=80=EC=9D=91=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../co/kr/tnt/ui/component/TnTMyPageButton.kt | 53 +++++++++++-------- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 26 ++++----- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt b/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt index 6e033541..31cbdb28 100644 --- a/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt +++ b/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt @@ -3,13 +3,15 @@ package co.kr.tnt.ui.component import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors +import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -18,41 +20,46 @@ import co.kr.tnt.designsystem.theme.TnTTheme @Composable fun TnTMyPageButton( text: String, - height: Dp, + verticalPadding: Dp, modifier: Modifier = Modifier, onClick: () -> Unit, ) { - Button( - onClick = onClick, - shape = RoundedCornerShape(12.dp), - colors = ButtonColors( - containerColor = TnTTheme.colors.commonColors.Common0, - contentColor = TnTTheme.colors.neutralColors.Neutral700, - disabledContainerColor = TnTTheme.colors.commonColors.Common0, - disabledContentColor = TnTTheme.colors.neutralColors.Neutral700, - ), - contentPadding = PaddingValues(horizontal = 20.dp, vertical = 12.dp), - modifier = modifier - .fillMaxWidth() - .height(height) - .defaultMinSize(minWidth = Dp.Hairline), - ) { - Text( - text = text, - style = TnTTheme.typography.body2Medium, - modifier = Modifier.fillMaxWidth(), - ) + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { + Button( + onClick = onClick, + shape = RoundedCornerShape(12.dp), + colors = ButtonColors( + containerColor = TnTTheme.colors.commonColors.Common0, + contentColor = TnTTheme.colors.neutralColors.Neutral700, + disabledContainerColor = TnTTheme.colors.commonColors.Common0, + disabledContentColor = TnTTheme.colors.neutralColors.Neutral700, + ), + elevation = null, + contentPadding = PaddingValues(horizontal = 20.dp, vertical = verticalPadding), + modifier = modifier + .fillMaxWidth() + .defaultMinSize(Dp.Hairline), + ) { + Text( + text = text, + style = TnTTheme.typography.body2Medium, + modifier = Modifier.fillMaxWidth(), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + } } } @Preview +@Preview(fontScale = 1.5f) @Composable private fun TnTMyPageButtonPreview() { TnTTheme { TnTMyPageButton( text = "ํŠธ๋ ˆ์ด๋„ˆ์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ", - height = 47.dp, onClick = {}, + verticalPadding = 8.dp, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 80898478..3f423dfb 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -15,7 +15,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.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -120,7 +122,9 @@ private fun TraineeMyPageScreen( Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(innerPadding), + modifier = Modifier + .padding(innerPadding) + .verticalScroll(rememberScrollState()), ) { TnTProfileImage( defaultImage = painterResource(DefaultUserProfile.Trainee.image), @@ -151,8 +155,8 @@ private fun TraineeMyPageScreen( if (state.isConnected.not()) { TnTMyPageButton( text = stringResource(R.string.connect_with_trainer), - height = 47.dp, onClick = onConnectButtonClick, + verticalPadding = 14.dp, ) } Row( @@ -160,10 +164,9 @@ private fun TraineeMyPageScreen( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .height(48.dp) .clip(RoundedCornerShape(12.dp)) .background(TnTTheme.colors.commonColors.Common0) - .padding(horizontal = 20.dp), + .padding(horizontal = 20.dp, vertical = 12.dp), ) { Text( text = stringResource(uiResource.string.app_push_notification), @@ -183,20 +186,19 @@ private fun TraineeMyPageScreen( ) { TnTMyPageButton( text = stringResource(uiResource.string.terms_of_service), - height = 40.dp, onClick = onServiceTermClick, + verticalPadding = 8.dp, ) TnTMyPageButton( text = stringResource(uiResource.string.privacy_policy), - height = 40.dp, onClick = onPrivacyClick, + verticalPadding = 8.dp, ) Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .height(40.dp) .padding(horizontal = 20.dp, vertical = 8.dp), ) { Text( @@ -212,15 +214,15 @@ private fun TraineeMyPageScreen( } TnTMyPageButton( text = stringResource(uiResource.string.open_source_license), - height = 40.dp, onClick = onOpenSourceClick, + verticalPadding = 8.dp, ) } if (state.isConnected) { TnTMyPageButton( text = stringResource(R.string.disconnect_with_trainer), - height = 47.dp, onClick = onDisconnectButtonClick, + verticalPadding = 14.dp, ) } Column( @@ -235,7 +237,7 @@ private fun TraineeMyPageScreen( color = TnTTheme.colors.neutralColors.Neutral700, style = TnTTheme.typography.body2Medium, modifier = Modifier - .padding(horizontal = 20.dp, vertical = 12.dp) + .padding(horizontal = 20.dp, vertical = 8.dp) .clickable(onClick = onLogoutClick), ) Text( @@ -243,7 +245,7 @@ private fun TraineeMyPageScreen( color = TnTTheme.colors.neutralColors.Neutral700, style = TnTTheme.typography.body2Medium, modifier = Modifier - .padding(horizontal = 20.dp, vertical = 12.dp) + .padding(horizontal = 20.dp, vertical = 8.dp) .clickable(onClick = onDeleteAccountClick), ) } @@ -322,7 +324,7 @@ private fun TraineeMyPagePreview() { state = TraineeMyPageUiState( image = null, name = "๊น€ํšŒ์›", - isConnected = true, + isConnected = false, isPushEnabled = true, appVersion = "0.0.0", ), From 7c0fcc53efe98c0621d5332cc612abe63f8f0cf1 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 15:50:44 +0900 Subject: [PATCH 13/21] =?UTF-8?q?[TNT-185]=20fix:=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20Button=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=82=AC=EC=9D=B4=EC=A6=88=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - height ์ œํ•œ์„ ์—†์• ๊ณ  minHeight๋ฅผ ์„ค์ • --- .../designsystem/component/button/Button.kt | 110 +++++++++++------- 1 file changed, 70 insertions(+), 40 deletions(-) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt index 9861b69b..7f183dc0 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt @@ -1,11 +1,12 @@ package co.kr.tnt.designsystem.component.button import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button @@ -17,6 +18,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -45,14 +47,14 @@ fun TnTTextButton( color = type.borderColor(enabled), ), contentPadding = size.contentPadding, - modifier = modifier - .height(size.height) - // Small, XSmall ๋ฒ„ํŠผ์„ ์œ„ํ•ด Button minWidth ์„ค์ • - .defaultMinSize(minWidth = Dp.Hairline), + // Small, XSmall ๋ฒ„ํŠผ์„ ์œ„ํ•ด Button minWidth, minHeight ์„ค์ • + modifier = modifier.defaultMinSize(minWidth = Dp.Hairline, minHeight = size.height), ) { Text( text = text, style = size.textStyle(), + overflow = TextOverflow.Ellipsis, + maxLines = 1, ) } } @@ -77,28 +79,40 @@ fun TnTIconButton( color = type.borderColor(enabled), ), contentPadding = size.contentPadding, - modifier = modifier - .height(size.height) - // Small, XSmall ๋ฒ„ํŠผ์„ ์œ„ํ•ด Button minWidth ์„ค์ • - .defaultMinSize(minWidth = Dp.Hairline), + // Small, XSmall ๋ฒ„ํŠผ์„ ์œ„ํ•ด Button minWidth ์„ค์ • + modifier = modifier.defaultMinSize(minWidth = Dp.Hairline, minHeight = size.height), ) { when (iconPosition) { is IconPosition.Leading -> { - iconPosition.icon() - Text( - text = text, - style = size.textStyle(), - modifier = Modifier.padding(start = 4.dp), - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + iconPosition.icon() + Text( + text = text, + style = size.textStyle(), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.weight(1f, false), + ) + } } is IconPosition.Trailing -> { - Text( - text = text, - style = size.textStyle(), - modifier = Modifier.padding(end = 4.dp), - ) - iconPosition.icon() + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = text, + style = size.textStyle(), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.weight(1f, false), + ) + iconPosition.icon() + } } } } @@ -137,9 +151,9 @@ fun TnTBottomButton( fun TnTTnTTextButtonPreview() { TnTTheme { TnTTextButton( - size = ButtonSize.Medium, + size = ButtonSize.Small, type = ButtonType.Primary, - text = "ํ…์ŠคํŠธ", + text = "ํ…์ŠคํŠธddddddddddddd", enabled = true, onClick = { }, modifier = Modifier.wrapContentSize(), @@ -154,7 +168,7 @@ private fun TnTOutLinedButtonPreview() { TnTTextButton( size = ButtonSize.Medium, type = ButtonType.RedOutline, - text = "ํ…์ŠคํŠธ", + text = "ํ…์ŠคํŠธdddddddddddddddddddddd", enabled = true, onClick = { }, modifier = Modifier.wrapContentSize(), @@ -162,24 +176,40 @@ private fun TnTOutLinedButtonPreview() { } } -@Preview(showBackground = true, widthDp = 150, heightDp = 100) +@Preview(showBackground = true, widthDp = 150, heightDp = 130) @Composable private fun TnTIconButtonPreview() { TnTTheme { - TnTIconButton( - size = ButtonSize.Medium, - type = ButtonType.GrayOutline, - iconPosition = IconPosition.Trailing { - Icon( - painter = painterResource(R.drawable.ic_delete), - contentDescription = null, - ) - }, - text = "ํ…์ŠคํŠธ", - enabled = true, - onClick = { }, - modifier = Modifier.wrapContentSize(), - ) + Column(verticalArrangement = Arrangement.spacedBy(5.dp)) { + TnTIconButton( + size = ButtonSize.Medium, + type = ButtonType.GrayOutline, + iconPosition = IconPosition.Trailing { + Icon( + painter = painterResource(R.drawable.ic_delete), + contentDescription = null, + ) + }, + text = "ํ…์ŠคํŠธttttttttttttttttt", + enabled = true, + onClick = { }, + modifier = Modifier.wrapContentSize(), + ) + TnTIconButton( + size = ButtonSize.Medium, + type = ButtonType.GrayOutline, + iconPosition = IconPosition.Leading { + Icon( + painter = painterResource(R.drawable.ic_delete), + contentDescription = null, + ) + }, + text = "ํ…์ŠคํŠธtttttttttttttttttt", + enabled = true, + onClick = { }, + modifier = Modifier.wrapContentSize(), + ) + } } } From 863ed3a1aa0008e2a449de05f69378899f82528f Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 15:52:05 +0900 Subject: [PATCH 14/21] =?UTF-8?q?[TNT-185]=20feat:=20=EB=91=90=EB=B2=88?= =?UTF-8?q?=EC=A7=B8=20=ED=8C=9D=EC=97=85=20=EB=92=A4=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=20=EB=88=8C=EB=9F=AC=EB=8F=84=20=ED=99=95=EC=9D=B8=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=EC=B2=98=EB=9F=BC=20=EB=8F=99=EC=9E=91?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 3f423dfb..8c6f6e3e 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -280,7 +280,7 @@ private fun TraineeMyPageScreen( content = stringResource(state.popupType.secondPopupContent), buttonText = stringResource(uiResource.string.ok), onButtonClick = onConfirmSecondPopup, - onDismiss = onDismissPopup, + onDismiss = onConfirmSecondPopup, ) } } From c63a8c9fdd5e9c0569476af34ea3596be7c42d05 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 16:23:57 +0900 Subject: [PATCH 15/21] =?UTF-8?q?[TNT-185]=20fix:=20LocalMinimumInteractiv?= =?UTF-8?q?eComponentSize=20=EC=B5=9C=EC=86=8C=20=EB=86=92=EC=9D=B4?= =?UTF-8?q?=EC=9D=B8=2039.dp=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 0.dp๋กœ ์„ค์ •ํ•˜๋ฉด ๋ฆฌํ”Œ ํšจ๊ณผ๊ฐ€ ๋ณด์ด์ง€ ์•Š์Œ --- core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt b/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt index 31cbdb28..0c45369e 100644 --- a/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt +++ b/core/ui/src/main/java/co/kr/tnt/ui/component/TnTMyPageButton.kt @@ -24,7 +24,7 @@ fun TnTMyPageButton( modifier: Modifier = Modifier, onClick: () -> Unit, ) { - CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 39.dp) { Button( onClick = onClick, shape = RoundedCornerShape(12.dp), From bdd955567753d59319ea5c8886bff3b21ca531e0 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 23:17:51 +0900 Subject: [PATCH 16/21] =?UTF-8?q?[TNT-185]=20refactor:=20=EB=91=90=20?= =?UTF-8?q?=EC=A4=84=20=EC=9D=B4=EC=83=81=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A4=84=EB=B0=94=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designsystem/component/button/Button.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt index 7f183dc0..319e51e2 100644 --- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt +++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/button/Button.kt @@ -48,7 +48,10 @@ fun TnTTextButton( ), contentPadding = size.contentPadding, // Small, XSmall ๋ฒ„ํŠผ์„ ์œ„ํ•ด Button minWidth, minHeight ์„ค์ • - modifier = modifier.defaultMinSize(minWidth = Dp.Hairline, minHeight = size.height), + modifier = modifier.defaultMinSize( + minWidth = Dp.Hairline, + minHeight = size.height, + ), ) { Text( text = text, @@ -80,7 +83,10 @@ fun TnTIconButton( ), contentPadding = size.contentPadding, // Small, XSmall ๋ฒ„ํŠผ์„ ์œ„ํ•ด Button minWidth ์„ค์ • - modifier = modifier.defaultMinSize(minWidth = Dp.Hairline, minHeight = size.height), + modifier = modifier.defaultMinSize( + minWidth = Dp.Hairline, + minHeight = size.height, + ), ) { when (iconPosition) { is IconPosition.Leading -> { @@ -153,7 +159,7 @@ fun TnTTnTTextButtonPreview() { TnTTextButton( size = ButtonSize.Small, type = ButtonType.Primary, - text = "ํ…์ŠคํŠธddddddddddddd", + text = "ํ…์ŠคํŠธ", enabled = true, onClick = { }, modifier = Modifier.wrapContentSize(), @@ -168,7 +174,7 @@ private fun TnTOutLinedButtonPreview() { TnTTextButton( size = ButtonSize.Medium, type = ButtonType.RedOutline, - text = "ํ…์ŠคํŠธdddddddddddddddddddddd", + text = "ํ…์ŠคํŠธ", enabled = true, onClick = { }, modifier = Modifier.wrapContentSize(), @@ -190,7 +196,7 @@ private fun TnTIconButtonPreview() { contentDescription = null, ) }, - text = "ํ…์ŠคํŠธttttttttttttttttt", + text = "ํ…์ŠคํŠธ", enabled = true, onClick = { }, modifier = Modifier.wrapContentSize(), @@ -204,7 +210,7 @@ private fun TnTIconButtonPreview() { contentDescription = null, ) }, - text = "ํ…์ŠคํŠธtttttttttttttttttt", + text = "ํ…์ŠคํŠธ", enabled = true, onClick = { }, modifier = Modifier.wrapContentSize(), From 1baad737ace1498d6260c5295e8dce2b9f1e7b33 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Fri, 31 Jan 2025 23:19:22 +0900 Subject: [PATCH 17/21] =?UTF-8?q?[TNT-185]=20refactor:=20try=20catch=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 --- .../trainee/mypage/TraineeMyPageViewModel.kt | 52 +++++++------------ 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index b7167e66..544ff919 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -40,20 +40,16 @@ internal class TraineeMyPageViewModel @Inject constructor() : private fun loadUserData() { viewModelScope.launch { - try { - // TODO ์œ ์ € ์ •๋ณด API ํ˜ธ์ถœ - updateState { - copy( - image = null, - name = "๊น€ํšŒ์›", - trainerName = "", - isConnected = true, - isPushEnabled = true, - appVersion = "0.0.0", - ) - } - } catch (e: Exception) { - // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ + // TODO ์œ ์ € ์ •๋ณด API ํ˜ธ์ถœ + updateState { + copy( + image = null, + name = "๊น€ํšŒ์›", + trainerName = "", + isConnected = true, + isPushEnabled = true, + appVersion = "0.0.0", + ) } } } @@ -125,36 +121,24 @@ internal class TraineeMyPageViewModel @Inject constructor() : private fun performLogout() { viewModelScope.launch { - try { - // TODO ๋กœ๊ทธ์•„์›ƒ API ํ˜ธ์ถœ - updateState { copy(showSecondPopup = false) } - sendEffect(TraineeMyPageEffect.NavigateToLogin) - } catch (e: Exception) { - // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ - } + // TODO ๋กœ๊ทธ์•„์›ƒ API ํ˜ธ์ถœ + updateState { copy(showSecondPopup = false) } + sendEffect(TraineeMyPageEffect.NavigateToLogin) } } private fun performAccountDeletion() { viewModelScope.launch { - try { - // TODO ํšŒ์› ํƒˆํ‡ด API ํ˜ธ์ถœ - updateState { copy(showSecondPopup = false) } - sendEffect(TraineeMyPageEffect.NavigateToLogin) - } catch (e: Exception) { - // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ - } + // TODO ํšŒ์› ํƒˆํ‡ด API ํ˜ธ์ถœ + updateState { copy(showSecondPopup = false) } + sendEffect(TraineeMyPageEffect.NavigateToLogin) } } private fun performDisconnect() { viewModelScope.launch { - try { - // TODO ์—ฐ๊ฒฐ ํ•ด์ œ API ํ˜ธ์ถœ - updateState { copy(isConnected = false, showSecondPopup = false) } - } catch (e: Exception) { - // TODO ์—๋Ÿฌ ์ฒ˜๋ฆฌ - } + // TODO ์—ฐ๊ฒฐ ํ•ด์ œ API ํ˜ธ์ถœ + updateState { copy(isConnected = false, showSecondPopup = false) } } } } From ea65e4bc70e042cbf3d46f96fc1204d44bac78d6 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Sat, 1 Feb 2025 00:08:34 +0900 Subject: [PATCH 18/21] =?UTF-8?q?[TNT-185]=20refactor:=20dialog=EB=A1=9C?= =?UTF-8?q?=20=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=EC=9B=B9=EB=B7=B0=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=94=B0=EB=A1=9C=20=EB=B9=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ํ™”๋ฉด ์—ฐ๊ฒฐ์€ ์•„์ง ์•ˆ ๋จ --- .../trainee/mypage/TraineeMyPageContract.kt | 30 +- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 284 ++++++++---------- .../trainee/mypage/TraineeMyPageViewModel.kt | 28 +- .../mypage/TraineeMyPageWebViewScreen.kt | 47 +++ .../tnt/trainee/mypage/model/DialogState.kt | 30 ++ .../kr/tnt/trainee/mypage/model/PopupType.kt | 30 -- 6 files changed, 236 insertions(+), 213 deletions(-) create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/DialogState.kt delete mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index a2c068b8..75227a9f 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -1,6 +1,6 @@ package co.kr.tnt.trainee.mypage -import co.kr.tnt.trainee.mypage.model.PopupType +import co.kr.tnt.trainee.mypage.model.DialogState import co.kr.tnt.ui.base.UiEvent import co.kr.tnt.ui.base.UiSideEffect import co.kr.tnt.ui.base.UiState @@ -13,11 +13,11 @@ internal class TraineeMyPageContract { val isConnected: Boolean = false, val isPushEnabled: Boolean = true, val appVersion: String = "0.0.0", - val popupType: PopupType = PopupType.LOGOUT, + val dialogState: DialogState = DialogState.LOGOUT, val url: String = "", val showWebView: Boolean = false, - val showFirstPopup: Boolean = false, - val showSecondPopup: Boolean = false, + val showWarningDialog: Boolean = false, + val showCompleteDialog: Boolean = false, ) : UiState sealed interface TraineeMyPageUiEvent : UiEvent { @@ -43,4 +43,26 @@ internal class TraineeMyPageContract { data object NavigateToPrevious : TraineeMyPageEffect data object NavigateToLogin : TraineeMyPageEffect } + + enum class TraineeMyPagePage { + MyPage, + WebView, + ; + + companion object { + fun getPreviousPage(currentPage: TraineeMyPagePage): TraineeMyPagePage { + return when (currentPage) { + WebView -> MyPage + else -> error("No previous page defined for $currentPage") + } + } + + fun getNextPage(currentPage: TraineeMyPagePage): TraineeMyPagePage { + return when (currentPage) { + MyPage -> WebView + else -> error("No next page defined for $currentPage") + } + } + } + } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 8c6f6e3e..1ed03c8a 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -1,8 +1,5 @@ package co.kr.tnt.trainee.mypage -import android.webkit.WebSettings -import android.webkit.WebView -import android.webkit.WebViewClient import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.background @@ -11,7 +8,6 @@ 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 @@ -31,7 +27,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import co.kr.tnt.designsystem.component.TnTIconPopupDialog @@ -46,7 +41,7 @@ import co.kr.tnt.feature.trainee.mypage.R import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState -import co.kr.tnt.trainee.mypage.model.PopupType +import co.kr.tnt.trainee.mypage.model.DialogState import co.kr.tnt.ui.component.TnTMyPageButton import co.kr.tnt.ui.model.DefaultUserProfile import co.kr.tnt.core.ui.R as uiResource @@ -76,7 +71,6 @@ internal fun TraineeMyPageRoute( onDismissPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnDismissPopup) }, onConfirmFirstPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmFirstPopup) }, onConfirmSecondPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmSecondPopup) }, - onDismissWebView = { viewModel.setEvent(TraineeMyPageUiEvent.OnWebViewBackClick) }, ) LaunchedEffect(viewModel.effect) { @@ -109,159 +103,151 @@ private fun TraineeMyPageScreen( onConfirmFirstPopup: () -> Unit, onConfirmSecondPopup: () -> Unit, onDismissPopup: () -> Unit, - onDismissWebView: () -> Unit, ) { - if (state.showWebView) { - WebViewScreen( - url = state.url, - onBackPress = onDismissWebView, - ) - } else { - BackHandler { onBackClick() } + BackHandler { onBackClick() } - Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> + Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding(innerPadding) + .verticalScroll(rememberScrollState()), + ) { + TnTProfileImage( + defaultImage = painterResource(DefaultUserProfile.Trainee.image), + imageSize = 132.dp, + showEditButton = false, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + ) + Text( + text = state.name, + color = TnTTheme.colors.neutralColors.Neutral950, + style = TnTTheme.typography.h2, + ) + Spacer(Modifier.height(8.dp)) + TnTTextButton( + text = stringResource(uiResource.string.modifying_personal_info), + size = ButtonSize.Small, + type = ButtonType.Gray, + onClick = onEditButtonClick, + ) Column( - horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier - .padding(innerPadding) - .verticalScroll(rememberScrollState()), + .fillMaxWidth() + .padding(20.dp), ) { - TnTProfileImage( - defaultImage = painterResource(DefaultUserProfile.Trainee.image), - imageSize = 132.dp, - showEditButton = false, + if (state.isConnected.not()) { + TnTMyPageButton( + text = stringResource(R.string.connect_with_trainer), + onClick = onConnectButtonClick, + verticalPadding = 14.dp, + ) + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .padding(vertical = 12.dp), - ) - Text( - text = state.name, - color = TnTTheme.colors.neutralColors.Neutral950, - style = TnTTheme.typography.h2, - ) - Spacer(Modifier.height(8.dp)) - TnTTextButton( - text = stringResource(uiResource.string.modifying_personal_info), - size = ButtonSize.Small, - type = ButtonType.Gray, - onClick = onEditButtonClick, - ) + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(horizontal = 20.dp, vertical = 12.dp), + ) { + Text( + text = stringResource(uiResource.string.app_push_notification), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + TnTSwitch( + checked = state.isPushEnabled, + onCheckedChange = onPushNotificationToggle, + ) + } Column( - verticalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier - .fillMaxWidth() - .padding(20.dp), + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), ) { - if (state.isConnected.not()) { - TnTMyPageButton( - text = stringResource(R.string.connect_with_trainer), - onClick = onConnectButtonClick, - verticalPadding = 14.dp, - ) - } + TnTMyPageButton( + text = stringResource(uiResource.string.terms_of_service), + onClick = onServiceTermClick, + verticalPadding = 8.dp, + ) + TnTMyPageButton( + text = stringResource(uiResource.string.privacy_policy), + onClick = onPrivacyClick, + verticalPadding = 8.dp, + ) Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(horizontal = 20.dp, vertical = 12.dp), - ) { - Text( - text = stringResource(uiResource.string.app_push_notification), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - TnTSwitch( - checked = state.isPushEnabled, - onCheckedChange = onPushNotificationToggle, - ) - } - Column( - modifier = Modifier - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), - ) { - TnTMyPageButton( - text = stringResource(uiResource.string.terms_of_service), - onClick = onServiceTermClick, - verticalPadding = 8.dp, - ) - TnTMyPageButton( - text = stringResource(uiResource.string.privacy_policy), - onClick = onPrivacyClick, - verticalPadding = 8.dp, - ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 8.dp), - ) { - Text( - text = stringResource(uiResource.string.app_version), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - Text( - text = state.appVersion, - color = TnTTheme.colors.neutralColors.Neutral400, - style = TnTTheme.typography.body2Medium, - ) - } - TnTMyPageButton( - text = stringResource(uiResource.string.open_source_license), - onClick = onOpenSourceClick, - verticalPadding = 8.dp, - ) - } - if (state.isConnected) { - TnTMyPageButton( - text = stringResource(R.string.disconnect_with_trainer), - onClick = onDisconnectButtonClick, - verticalPadding = 14.dp, - ) - } - Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), + .padding(horizontal = 20.dp, vertical = 8.dp), ) { Text( - text = stringResource(uiResource.string.logout), + text = stringResource(uiResource.string.app_version), color = TnTTheme.colors.neutralColors.Neutral700, style = TnTTheme.typography.body2Medium, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 8.dp) - .clickable(onClick = onLogoutClick), ) Text( - text = stringResource(uiResource.string.delete_account), - color = TnTTheme.colors.neutralColors.Neutral700, + text = state.appVersion, + color = TnTTheme.colors.neutralColors.Neutral400, style = TnTTheme.typography.body2Medium, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 8.dp) - .clickable(onClick = onDeleteAccountClick), ) } + TnTMyPageButton( + text = stringResource(uiResource.string.open_source_license), + onClick = onOpenSourceClick, + verticalPadding = 8.dp, + ) + } + if (state.isConnected) { + TnTMyPageButton( + text = stringResource(R.string.disconnect_with_trainer), + onClick = onDisconnectButtonClick, + verticalPadding = 14.dp, + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + Text( + text = stringResource(uiResource.string.logout), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 8.dp) + .clickable(onClick = onLogoutClick), + ) + Text( + text = stringResource(uiResource.string.delete_account), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 8.dp) + .clickable(onClick = onDeleteAccountClick), + ) } } } } - if (state.showFirstPopup) { + if (state.showWarningDialog) { TnTIconPopupDialog( - title = if (state.popupType == PopupType.DISCONNECT) { - stringResource(state.popupType.firstPopupTitle, state.trainerName) + title = if (state.dialogState == DialogState.DISCONNECT) { + stringResource(state.dialogState.warningDialogTitle, state.trainerName) } else { - stringResource(state.popupType.firstPopupTitle) + stringResource(state.dialogState.warningDialogTitle) }, - content = stringResource(state.popupType.firstPopupContent), + content = stringResource(state.dialogState.warningDialogContent), leftButtonText = stringResource(uiResource.string.cancel), rightButtonText = stringResource(uiResource.string.ok), onLeftButtonClick = onDismissPopup, @@ -270,14 +256,14 @@ private fun TraineeMyPageScreen( ) } - if (state.showSecondPopup) { + if (state.showCompleteDialog) { TnTSingleButtonPopupDialog( - title = if (state.popupType == PopupType.DISCONNECT) { - stringResource(state.popupType.secondPopupTitle, state.trainerName) + title = if (state.dialogState == DialogState.DISCONNECT) { + stringResource(state.dialogState.completeDialogTitle, state.trainerName) } else { - stringResource(state.popupType.secondPopupTitle) + stringResource(state.dialogState.completeDialogTitle) }, - content = stringResource(state.popupType.secondPopupContent), + content = stringResource(state.dialogState.completeDialogContent), buttonText = stringResource(uiResource.string.ok), onButtonClick = onConfirmSecondPopup, onDismiss = onConfirmSecondPopup, @@ -285,37 +271,6 @@ private fun TraineeMyPageScreen( } } -@Composable -fun WebViewScreen(url: String, onBackPress: () -> Unit) { - var webView: WebView? = null - - AndroidView( - factory = { context -> - WebView(context).apply { - settings.javaScriptEnabled = true - settings.domStorageEnabled = true - settings.loadWithOverviewMode = true - settings.useWideViewPort = true - settings.builtInZoomControls = true - settings.displayZoomControls = false - settings.cacheMode = WebSettings.LOAD_NO_CACHE - webViewClient = WebViewClient() - loadUrl(url) - webView = this - } - }, - modifier = Modifier.fillMaxSize(), - ) - - BackHandler { - if (webView?.canGoBack() == true) { - webView?.goBack() - } else { - onBackPress() - } - } -} - @Preview @Composable private fun TraineeMyPagePreview() { @@ -341,7 +296,6 @@ private fun TraineeMyPagePreview() { onConfirmFirstPopup = {}, onDismissPopup = {}, onConfirmSecondPopup = {}, - onDismissWebView = {}, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index 544ff919..4ee6214b 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.viewModelScope import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState -import co.kr.tnt.trainee.mypage.model.PopupType +import co.kr.tnt.trainee.mypage.model.DialogState import co.kr.tnt.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -67,8 +67,8 @@ internal class TraineeMyPageViewModel @Inject constructor() : updateState { copy( trainerName = "๊น€ํ”ผํ‹ฐ", - showFirstPopup = true, - popupType = PopupType.DISCONNECT, + showWarningDialog = true, + dialogState = DialogState.DISCONNECT, ) } } @@ -92,11 +92,11 @@ internal class TraineeMyPageViewModel @Inject constructor() : } private fun logout() { - updateState { copy(showFirstPopup = true, popupType = PopupType.LOGOUT) } + updateState { copy(showWarningDialog = true, dialogState = DialogState.LOGOUT) } } private fun deleteAccount() { - updateState { copy(showFirstPopup = true, popupType = PopupType.DELETE_ACCOUNT) } + updateState { copy(showWarningDialog = true, dialogState = DialogState.DELETE_ACCOUNT) } } private fun navigateBack() { @@ -104,25 +104,25 @@ internal class TraineeMyPageViewModel @Inject constructor() : } private fun confirmFirstPopup() { - updateState { copy(showFirstPopup = false, showSecondPopup = true) } + updateState { copy(showWarningDialog = false, showCompleteDialog = true) } } private fun dismissPopup() { - updateState { copy(showFirstPopup = false, showSecondPopup = false) } + updateState { copy(showWarningDialog = false, showCompleteDialog = false) } } private fun handleSecondPopupConfirm() { - when (currentState.popupType) { - PopupType.LOGOUT -> performLogout() - PopupType.DELETE_ACCOUNT -> performAccountDeletion() - PopupType.DISCONNECT -> performDisconnect() + when (currentState.dialogState) { + DialogState.LOGOUT -> performLogout() + DialogState.DELETE_ACCOUNT -> performAccountDeletion() + DialogState.DISCONNECT -> performDisconnect() } } private fun performLogout() { viewModelScope.launch { // TODO ๋กœ๊ทธ์•„์›ƒ API ํ˜ธ์ถœ - updateState { copy(showSecondPopup = false) } + updateState { copy(showCompleteDialog = false) } sendEffect(TraineeMyPageEffect.NavigateToLogin) } } @@ -130,7 +130,7 @@ internal class TraineeMyPageViewModel @Inject constructor() : private fun performAccountDeletion() { viewModelScope.launch { // TODO ํšŒ์› ํƒˆํ‡ด API ํ˜ธ์ถœ - updateState { copy(showSecondPopup = false) } + updateState { copy(showCompleteDialog = false) } sendEffect(TraineeMyPageEffect.NavigateToLogin) } } @@ -138,7 +138,7 @@ internal class TraineeMyPageViewModel @Inject constructor() : private fun performDisconnect() { viewModelScope.launch { // TODO ์—ฐ๊ฒฐ ํ•ด์ œ API ํ˜ธ์ถœ - updateState { copy(isConnected = false, showSecondPopup = false) } + updateState { copy(isConnected = false, showCompleteDialog = false) } } } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt new file mode 100644 index 00000000..a11292f3 --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt @@ -0,0 +1,47 @@ +package co.kr.tnt.trainee.mypage + +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView + +@Composable +fun TraineeMyPageWebViewScreen(url: String, onBackPress: () -> Unit) { + var webView: WebView? = null + + BackHandler { + if (webView?.canGoBack() == true) { + webView?.goBack() + } else { + onBackPress() + } + } + + Scaffold { innerPadding -> + AndroidView( + factory = { context -> + WebView(context).apply { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + settings.loadWithOverviewMode = true + settings.useWideViewPort = true + settings.builtInZoomControls = true + settings.displayZoomControls = false + settings.cacheMode = WebSettings.LOAD_NO_CACHE + webViewClient = WebViewClient() + loadUrl(url) + webView = this + } + }, + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + ) + } +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/DialogState.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/DialogState.kt new file mode 100644 index 00000000..b33054fb --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/DialogState.kt @@ -0,0 +1,30 @@ +package co.kr.tnt.trainee.mypage.model + +import androidx.annotation.StringRes +import co.kr.tnt.feature.trainee.mypage.R + +enum class DialogState( + @StringRes val warningDialogTitle: Int, + @StringRes val warningDialogContent: Int, + @StringRes val completeDialogTitle: Int, + @StringRes val completeDialogContent: Int, +) { + LOGOUT( + warningDialogTitle = R.string.logout_title, + warningDialogContent = R.string.logout_content, + completeDialogTitle = R.string.logout_complete_title, + completeDialogContent = R.string.logout_content, + ), + DELETE_ACCOUNT( + warningDialogTitle = R.string.delete_account_title, + warningDialogContent = R.string.delete_account_content, + completeDialogTitle = R.string.delete_account_complete_title, + completeDialogContent = R.string.delete_account_complete_content, + ), + DISCONNECT( + warningDialogTitle = R.string.disconnect_title, + warningDialogContent = R.string.disconnect_content, + completeDialogTitle = R.string.disconnect_complete_title, + completeDialogContent = R.string.disconnect_complete_content, + ), +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt deleted file mode 100644 index e907c9e6..00000000 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/model/PopupType.kt +++ /dev/null @@ -1,30 +0,0 @@ -package co.kr.tnt.trainee.mypage.model - -import androidx.annotation.StringRes -import co.kr.tnt.feature.trainee.mypage.R - -enum class PopupType( - @StringRes val firstPopupTitle: Int, - @StringRes val firstPopupContent: Int, - @StringRes val secondPopupTitle: Int, - @StringRes val secondPopupContent: Int, -) { - LOGOUT( - firstPopupTitle = R.string.logout_title, - firstPopupContent = R.string.logout_content, - secondPopupTitle = R.string.logout_complete_title, - secondPopupContent = R.string.logout_content, - ), - DELETE_ACCOUNT( - firstPopupTitle = R.string.delete_account_title, - firstPopupContent = R.string.delete_account_content, - secondPopupTitle = R.string.delete_account_complete_title, - secondPopupContent = R.string.delete_account_complete_content, - ), - DISCONNECT( - firstPopupTitle = R.string.disconnect_title, - firstPopupContent = R.string.disconnect_content, - secondPopupTitle = R.string.disconnect_complete_title, - secondPopupContent = R.string.disconnect_complete_content, - ), -} From 52f1b243279e9df79218b1964a6fa0ff2f266cb9 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Sat, 1 Feb 2025 00:45:08 +0900 Subject: [PATCH 19/21] =?UTF-8?q?[TNT-185]=20feat:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=99=94=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 - TraineeMyPageMainPage๋กœ ์ฝ”๋“œ ์ด๋™ --- .../trainee/mypage/TraineeMyPageContract.kt | 2 + .../trainee/mypage/TraineeMyPageMainPage.kt | 251 ++++++++++++++++++ .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 247 ++--------------- .../trainee/mypage/TraineeMyPageViewModel.kt | 14 +- ...wScreen.kt => TraineeMyPageWebViewPage.kt} | 2 +- 5 files changed, 287 insertions(+), 229 deletions(-) create mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt rename feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/{TraineeMyPageWebViewScreen.kt => TraineeMyPageWebViewPage.kt} (95%) diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index 75227a9f..d9eca23c 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -1,5 +1,6 @@ package co.kr.tnt.trainee.mypage +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPagePage.MyPage import co.kr.tnt.trainee.mypage.model.DialogState import co.kr.tnt.ui.base.UiEvent import co.kr.tnt.ui.base.UiSideEffect @@ -7,6 +8,7 @@ import co.kr.tnt.ui.base.UiState internal class TraineeMyPageContract { data class TraineeMyPageUiState( + val page: TraineeMyPagePage = MyPage, val image: String? = "", val name: String = "", val trainerName: String = "", diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt new file mode 100644 index 00000000..4158b5ec --- /dev/null +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt @@ -0,0 +1,251 @@ +package co.kr.tnt.trainee.mypage + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import co.kr.tnt.core.ui.R +import co.kr.tnt.designsystem.component.TnTIconPopupDialog +import co.kr.tnt.designsystem.component.TnTProfileImage +import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog +import co.kr.tnt.designsystem.component.TnTSwitch +import co.kr.tnt.designsystem.component.button.TnTTextButton +import co.kr.tnt.designsystem.component.button.model.ButtonSize +import co.kr.tnt.designsystem.component.button.model.ButtonType +import co.kr.tnt.designsystem.theme.TnTTheme +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState +import co.kr.tnt.trainee.mypage.model.DialogState +import co.kr.tnt.ui.component.TnTMyPageButton +import co.kr.tnt.ui.model.DefaultUserProfile + +@Composable +internal fun TraineeMyPageMainPage( + state: TraineeMyPageUiState, + onBackClick: () -> Unit, + onEditButtonClick: () -> Unit, + onConnectButtonClick: () -> Unit, + onDisconnectButtonClick: () -> Unit, + onPushNotificationToggle: () -> Unit, + onServiceTermClick: () -> Unit, + onPrivacyClick: () -> Unit, + onOpenSourceClick: () -> Unit, + onLogoutClick: () -> Unit, + onDeleteAccountClick: () -> Unit, + onConfirmFirstPopup: () -> Unit, + onConfirmSecondPopup: () -> Unit, + onDismissPopup: () -> Unit, +) { + BackHandler { onBackClick() } + + Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding(innerPadding) + .verticalScroll(rememberScrollState()), + ) { + TnTProfileImage( + defaultImage = painterResource(DefaultUserProfile.Trainee.image), + imageSize = 132.dp, + showEditButton = false, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + ) + Text( + text = state.name, + color = TnTTheme.colors.neutralColors.Neutral950, + style = TnTTheme.typography.h2, + ) + Spacer(Modifier.height(8.dp)) + TnTTextButton( + text = stringResource(R.string.modifying_personal_info), + size = ButtonSize.Small, + type = ButtonType.Gray, + onClick = onEditButtonClick, + ) + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + ) { + if (state.isConnected.not()) { + TnTMyPageButton( + text = stringResource(co.kr.tnt.feature.trainee.mypage.R.string.connect_with_trainer), + onClick = onConnectButtonClick, + verticalPadding = 14.dp, + ) + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(horizontal = 20.dp, vertical = 12.dp), + ) { + Text( + text = stringResource(R.string.app_push_notification), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + TnTSwitch( + checked = state.isPushEnabled, + onCheckedChange = onPushNotificationToggle, + ) + } + Column( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + TnTMyPageButton( + text = stringResource(R.string.terms_of_service), + onClick = onServiceTermClick, + verticalPadding = 8.dp, + ) + TnTMyPageButton( + text = stringResource(R.string.privacy_policy), + onClick = onPrivacyClick, + verticalPadding = 8.dp, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 8.dp), + ) { + Text( + text = stringResource(R.string.app_version), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + Text( + text = state.appVersion, + color = TnTTheme.colors.neutralColors.Neutral400, + style = TnTTheme.typography.body2Medium, + ) + } + TnTMyPageButton( + text = stringResource(R.string.open_source_license), + onClick = onOpenSourceClick, + verticalPadding = 8.dp, + ) + } + if (state.isConnected) { + TnTMyPageButton( + text = stringResource(co.kr.tnt.feature.trainee.mypage.R.string.disconnect_with_trainer), + onClick = onDisconnectButtonClick, + verticalPadding = 14.dp, + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + Text( + text = stringResource(R.string.logout), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 8.dp) + .clickable(onClick = onLogoutClick), + ) + Text( + text = stringResource(R.string.delete_account), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 8.dp) + .clickable(onClick = onDeleteAccountClick), + ) + } + } + } + } + + if (state.showWarningDialog) { + TnTIconPopupDialog( + title = if (state.dialogState == DialogState.DISCONNECT) { + stringResource(state.dialogState.warningDialogTitle, state.trainerName) + } else { + stringResource(state.dialogState.warningDialogTitle) + }, + content = stringResource(state.dialogState.warningDialogContent), + leftButtonText = stringResource(R.string.cancel), + rightButtonText = stringResource(R.string.ok), + onLeftButtonClick = onDismissPopup, + onRightButtonClick = onConfirmFirstPopup, + onDismiss = onDismissPopup, + ) + } + + if (state.showCompleteDialog) { + TnTSingleButtonPopupDialog( + title = if (state.dialogState == DialogState.DISCONNECT) { + stringResource(state.dialogState.completeDialogTitle, state.trainerName) + } else { + stringResource(state.dialogState.completeDialogTitle) + }, + content = stringResource(state.dialogState.completeDialogContent), + buttonText = stringResource(R.string.ok), + onButtonClick = onConfirmSecondPopup, + onDismiss = onConfirmSecondPopup, + ) + } +} + +@Preview +@Composable +private fun TraineeMyPageMainPagePreview() { + TnTTheme { + TraineeMyPageMainPage( + state = TraineeMyPageUiState( + image = null, + name = "๊น€ํšŒ์›", + isConnected = false, + isPushEnabled = true, + appVersion = "0.0.0", + ), + onEditButtonClick = {}, + onConnectButtonClick = {}, + onPushNotificationToggle = {}, + onServiceTermClick = {}, + onPrivacyClick = {}, + onOpenSourceClick = {}, + onLogoutClick = {}, + onDeleteAccountClick = {}, + onDisconnectButtonClick = {}, + onBackClick = {}, + onConfirmFirstPopup = {}, + onDismissPopup = {}, + onConfirmSecondPopup = {}, + ) + } +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index 1ed03c8a..dcbe05ff 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -1,50 +1,15 @@ package co.kr.tnt.trainee.mypage import android.widget.Toast -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -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.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import co.kr.tnt.designsystem.component.TnTIconPopupDialog -import co.kr.tnt.designsystem.component.TnTProfileImage -import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog -import co.kr.tnt.designsystem.component.TnTSwitch -import co.kr.tnt.designsystem.component.button.TnTTextButton -import co.kr.tnt.designsystem.component.button.model.ButtonSize -import co.kr.tnt.designsystem.component.button.model.ButtonType -import co.kr.tnt.designsystem.theme.TnTTheme -import co.kr.tnt.feature.trainee.mypage.R import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState -import co.kr.tnt.trainee.mypage.model.DialogState -import co.kr.tnt.ui.component.TnTMyPageButton -import co.kr.tnt.ui.model.DefaultUserProfile -import co.kr.tnt.core.ui.R as uiResource @Composable internal fun TraineeMyPageRoute( @@ -65,6 +30,7 @@ internal fun TraineeMyPageRoute( onPushNotificationToggle = { viewModel.setEvent(TraineeMyPageUiEvent.ToggleNotification) }, onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick) }, onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick) }, + onDismissWebView = { viewModel.setEvent(TraineeMyPageUiEvent.OnWebViewBackClick) }, onOpenSourceClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnOpenSourceClick) }, onLogoutClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnLogoutClick) }, onDeleteAccountClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDeleteAccountClick) }, @@ -103,199 +69,28 @@ private fun TraineeMyPageScreen( onConfirmFirstPopup: () -> Unit, onConfirmSecondPopup: () -> Unit, onDismissPopup: () -> Unit, + onDismissWebView: () -> Unit, ) { - BackHandler { onBackClick() } - - Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .padding(innerPadding) - .verticalScroll(rememberScrollState()), - ) { - TnTProfileImage( - defaultImage = painterResource(DefaultUserProfile.Trainee.image), - imageSize = 132.dp, - showEditButton = false, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - ) - Text( - text = state.name, - color = TnTTheme.colors.neutralColors.Neutral950, - style = TnTTheme.typography.h2, - ) - Spacer(Modifier.height(8.dp)) - TnTTextButton( - text = stringResource(uiResource.string.modifying_personal_info), - size = ButtonSize.Small, - type = ButtonType.Gray, - onClick = onEditButtonClick, - ) - Column( - verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier - .fillMaxWidth() - .padding(20.dp), - ) { - if (state.isConnected.not()) { - TnTMyPageButton( - text = stringResource(R.string.connect_with_trainer), - onClick = onConnectButtonClick, - verticalPadding = 14.dp, - ) - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(horizontal = 20.dp, vertical = 12.dp), - ) { - Text( - text = stringResource(uiResource.string.app_push_notification), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - TnTSwitch( - checked = state.isPushEnabled, - onCheckedChange = onPushNotificationToggle, - ) - } - Column( - modifier = Modifier - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), - ) { - TnTMyPageButton( - text = stringResource(uiResource.string.terms_of_service), - onClick = onServiceTermClick, - verticalPadding = 8.dp, - ) - TnTMyPageButton( - text = stringResource(uiResource.string.privacy_policy), - onClick = onPrivacyClick, - verticalPadding = 8.dp, - ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 8.dp), - ) { - Text( - text = stringResource(uiResource.string.app_version), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - Text( - text = state.appVersion, - color = TnTTheme.colors.neutralColors.Neutral400, - style = TnTTheme.typography.body2Medium, - ) - } - TnTMyPageButton( - text = stringResource(uiResource.string.open_source_license), - onClick = onOpenSourceClick, - verticalPadding = 8.dp, - ) - } - if (state.isConnected) { - TnTMyPageButton( - text = stringResource(R.string.disconnect_with_trainer), - onClick = onDisconnectButtonClick, - verticalPadding = 14.dp, - ) - } - Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), - ) { - Text( - text = stringResource(uiResource.string.logout), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 8.dp) - .clickable(onClick = onLogoutClick), - ) - Text( - text = stringResource(uiResource.string.delete_account), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 8.dp) - .clickable(onClick = onDeleteAccountClick), - ) - } - } - } - } - - if (state.showWarningDialog) { - TnTIconPopupDialog( - title = if (state.dialogState == DialogState.DISCONNECT) { - stringResource(state.dialogState.warningDialogTitle, state.trainerName) - } else { - stringResource(state.dialogState.warningDialogTitle) - }, - content = stringResource(state.dialogState.warningDialogContent), - leftButtonText = stringResource(uiResource.string.cancel), - rightButtonText = stringResource(uiResource.string.ok), - onLeftButtonClick = onDismissPopup, - onRightButtonClick = onConfirmFirstPopup, - onDismiss = onDismissPopup, - ) - } - - if (state.showCompleteDialog) { - TnTSingleButtonPopupDialog( - title = if (state.dialogState == DialogState.DISCONNECT) { - stringResource(state.dialogState.completeDialogTitle, state.trainerName) - } else { - stringResource(state.dialogState.completeDialogTitle) - }, - content = stringResource(state.dialogState.completeDialogContent), - buttonText = stringResource(uiResource.string.ok), - onButtonClick = onConfirmSecondPopup, - onDismiss = onConfirmSecondPopup, + when (state.page) { + TraineeMyPageContract.TraineeMyPagePage.MyPage -> TraineeMyPageMainPage( + state = state, + onBackClick = onBackClick, + onEditButtonClick = onEditButtonClick, + onConnectButtonClick = onConnectButtonClick, + onDisconnectButtonClick = onDisconnectButtonClick, + onPushNotificationToggle = onPushNotificationToggle, + onServiceTermClick = onServiceTermClick, + onPrivacyClick = onPrivacyClick, + onOpenSourceClick = onOpenSourceClick, + onLogoutClick = onLogoutClick, + onDeleteAccountClick = onDeleteAccountClick, + onConfirmFirstPopup = onConfirmFirstPopup, + onConfirmSecondPopup = onConfirmSecondPopup, + onDismissPopup = onDismissPopup, ) - } -} - -@Preview -@Composable -private fun TraineeMyPagePreview() { - TnTTheme { - TraineeMyPageScreen( - state = TraineeMyPageUiState( - image = null, - name = "๊น€ํšŒ์›", - isConnected = false, - isPushEnabled = true, - appVersion = "0.0.0", - ), - onEditButtonClick = {}, - onConnectButtonClick = {}, - onPushNotificationToggle = {}, - onServiceTermClick = {}, - onPrivacyClick = {}, - onOpenSourceClick = {}, - onLogoutClick = {}, - onDeleteAccountClick = {}, - onDisconnectButtonClick = {}, - onBackClick = {}, - onConfirmFirstPopup = {}, - onDismissPopup = {}, - onConfirmSecondPopup = {}, + TraineeMyPageContract.TraineeMyPagePage.WebView -> TraineeMyPageWebViewPage( + url = state.url, + onBackPress = onDismissWebView, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index 4ee6214b..d4be39eb 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -2,6 +2,7 @@ package co.kr.tnt.trainee.mypage import androidx.lifecycle.viewModelScope import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect +import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPagePage import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState import co.kr.tnt.trainee.mypage.model.DialogState @@ -80,11 +81,20 @@ internal class TraineeMyPageViewModel @Inject constructor() : private fun openWebView() { // TODO Url API ํ˜ธ์ถœ? val url = "https://www.naver.com" - updateState { copy(showWebView = !showWebView, url = url) } + updateState { + copy( + showWebView = !showWebView, + url = url, + ) + } + + val nextPage = TraineeMyPagePage.getNextPage(currentState.page) + updateState { copy(page = nextPage) } } private fun dismissWebView() { - updateState { copy(showWebView = !showWebView) } + val previousPage = TraineeMyPagePage.getPreviousPage(currentState.page) + updateState { copy(page = previousPage) } } private fun navigateToOpenSource() { diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt similarity index 95% rename from feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt rename to feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt index a11292f3..4300ac93 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView @Composable -fun TraineeMyPageWebViewScreen(url: String, onBackPress: () -> Unit) { +fun TraineeMyPageWebViewPage(url: String, onBackPress: () -> Unit) { var webView: WebView? = null BackHandler { From d93e336a0974074fb8455bde9252297a939fa11b Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Sat, 1 Feb 2025 03:24:20 +0900 Subject: [PATCH 20/21] =?UTF-8?q?[TNT-185]=20feat:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20mypage=20=EB=AA=A8=EB=93=88=20=EC=B5=9C?= =?UTF-8?q?=EC=B4=88=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/co/kr/tnt/navigation/RouteModel.kt | 3 ++ feature/main/build.gradle.kts | 1 + feature/webview/.gitignore | 1 + feature/webview/build.gradle.kts | 13 +++++++++ feature/webview/src/main/AndroidManifest.xml | 4 +++ .../tnt/feature/webview/WebViewNavigation.kt | 29 +++++++++++++++++++ .../kr/tnt/feature/webview/WebViewScreen.kt} | 9 ++++-- settings.gradle.kts | 1 + 8 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 feature/webview/.gitignore create mode 100644 feature/webview/build.gradle.kts create mode 100644 feature/webview/src/main/AndroidManifest.xml create mode 100644 feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewNavigation.kt rename feature/{trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt => webview/src/main/java/co/kr/tnt/feature/webview/WebViewScreen.kt} (90%) diff --git a/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt b/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt index 86c589b5..7e6abc96 100644 --- a/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt +++ b/core/navigation/src/main/java/co/kr/tnt/navigation/RouteModel.kt @@ -41,4 +41,7 @@ sealed interface Route { @Serializable data object TraineeMyPage : Route + + @Serializable + data class WebView(val url: String) : Route } diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index 6544e409..a89ffbe9 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -10,6 +10,7 @@ android { dependencies { implementation(projects.feature.home) + implementation(projects.feature.webview) implementation(projects.feature.login) implementation(projects.feature.roleselect) implementation(projects.feature.trainee.signup) diff --git a/feature/webview/.gitignore b/feature/webview/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/webview/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/webview/build.gradle.kts b/feature/webview/build.gradle.kts new file mode 100644 index 00000000..56c3c69e --- /dev/null +++ b/feature/webview/build.gradle.kts @@ -0,0 +1,13 @@ +import co.kr.tnt.setNamespace + +plugins { + id("tnt.android.feature") +} + +android { + setNamespace("feature.trainer.webview") +} + +dependencies { + implementation(libs.kotlinx.immutable) +} diff --git a/feature/webview/src/main/AndroidManifest.xml b/feature/webview/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8bdb7e14 --- /dev/null +++ b/feature/webview/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewNavigation.kt b/feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewNavigation.kt new file mode 100644 index 00000000..830f4318 --- /dev/null +++ b/feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewNavigation.kt @@ -0,0 +1,29 @@ +package co.kr.tnt.feature.webview + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptionsBuilder +import androidx.navigation.compose.composable +import androidx.navigation.toRoute +import co.kr.tnt.navigation.Route + +fun NavController.navigateToWebView( + url: String, + navOptions: NavOptionsBuilder.() -> Unit = {}, +) = navigate( + route = Route.WebView(url), + builder = navOptions, +) + +fun NavGraphBuilder.webViewScreen( + navigateToPrevious: () -> Unit, +) { + composable { backstackEntry -> + backstackEntry.toRoute().apply { + WebViewScreen( + url = url, + onBackClick = navigateToPrevious, + ) + } + } +} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt b/feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewScreen.kt similarity index 90% rename from feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt rename to feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewScreen.kt index 4300ac93..18454793 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageWebViewPage.kt +++ b/feature/webview/src/main/java/co/kr/tnt/feature/webview/WebViewScreen.kt @@ -1,4 +1,4 @@ -package co.kr.tnt.trainee.mypage +package co.kr.tnt.feature.webview import android.webkit.WebSettings import android.webkit.WebView @@ -12,14 +12,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView @Composable -fun TraineeMyPageWebViewPage(url: String, onBackPress: () -> Unit) { +internal fun WebViewScreen( + url: String, + onBackClick: () -> Unit, +) { var webView: WebView? = null BackHandler { if (webView?.canGoBack() == true) { webView?.goBack() } else { - onBackPress() + onBackClick() } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 2f0cf3c6..5f15ee62 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,4 +51,5 @@ include( ":feature:trainee:signup", ":feature:trainee:connect", ":feature:trainee:mypage", + ":feature:webview", ) From f1f70a6154a86fd275f6dcf0fa47840f628536d1 Mon Sep 17 00:00:00 2001 From: SeonJeongk Date: Sat, 1 Feb 2025 03:41:04 +0900 Subject: [PATCH 21/21] =?UTF-8?q?[TNT-185]=20feat:=20=ED=8A=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=8B=88=20mypage=EB=9E=91=20webview=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/co/kr/tnt/main/ui/TnTAppState.kt | 2 +- .../main/java/co/kr/tnt/main/ui/TnTNavHost.kt | 8 + .../trainee/mypage/TraineeMyPageContract.kt | 31 +-- .../trainee/mypage/TraineeMyPageMainPage.kt | 251 ----------------- .../tnt/trainee/mypage/TraineeMyPageScreen.kt | 255 ++++++++++++++++-- .../trainee/mypage/TraineeMyPageViewModel.kt | 65 +++-- .../navigation/TraineeMyPageNavigation.kt | 2 + 7 files changed, 280 insertions(+), 334 deletions(-) delete mode 100644 feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt diff --git a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTAppState.kt b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTAppState.kt index 8bfbc3f7..6f563f8b 100644 --- a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTAppState.kt +++ b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTAppState.kt @@ -33,5 +33,5 @@ class TnTAppState( @Composable get() = navController .currentBackStackEntryAsState().value?.destination - val startDestination = Route.Login + val startDestination = Route.TraineeMyPage } diff --git a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt index 4853b6e7..cbb86b62 100644 --- a/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt +++ b/feature/main/src/main/java/co/kr/tnt/main/ui/TnTNavHost.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.compose.NavHost +import co.kr.tnt.feature.webview.navigateToWebView +import co.kr.tnt.feature.webview.webViewScreen import co.kr.tnt.home.navigation.homeNavGraph import co.kr.tnt.home.navigation.navigateToHome import co.kr.tnt.login.navigation.loginScreen @@ -94,6 +96,12 @@ fun TnTNavHost( navigateToPrevious = { navController.popBackStack() }, navigateToTraineeConnect = { navController.navigateToTraineeConnect(isFromMyPage = true) }, navigateToLogin = { navController.navigateToLogin(clearBackStack = true) }, + navigateToWebView = { url -> + navController.navigateToWebView(url = url) + }, + ) + webViewScreen( + navigateToPrevious = { navController.popBackStack() }, ) homeNavGraph() } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt index d9eca23c..2495fed6 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageContract.kt @@ -1,6 +1,5 @@ package co.kr.tnt.trainee.mypage -import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPagePage.MyPage import co.kr.tnt.trainee.mypage.model.DialogState import co.kr.tnt.ui.base.UiEvent import co.kr.tnt.ui.base.UiSideEffect @@ -8,7 +7,6 @@ import co.kr.tnt.ui.base.UiState internal class TraineeMyPageContract { data class TraineeMyPageUiState( - val page: TraineeMyPagePage = MyPage, val image: String? = "", val name: String = "", val trainerName: String = "", @@ -32,11 +30,9 @@ internal class TraineeMyPageContract { data object OnOpenSourceClick : TraineeMyPageUiEvent data object OnLogoutClick : TraineeMyPageUiEvent data object OnDeleteAccountClick : TraineeMyPageUiEvent - data object OnConfirmFirstPopup : TraineeMyPageUiEvent + data object OnConfirmWarningDialog : TraineeMyPageUiEvent + data object OnConfirmCompleteDialog : TraineeMyPageUiEvent data object OnDismissPopup : TraineeMyPageUiEvent - data object OnBackClick : TraineeMyPageUiEvent - data object OnConfirmSecondPopup : TraineeMyPageUiEvent - data object OnWebViewBackClick : TraineeMyPageUiEvent } sealed interface TraineeMyPageEffect : UiSideEffect { @@ -44,27 +40,6 @@ internal class TraineeMyPageContract { data object NavigateToConnect : TraineeMyPageEffect data object NavigateToPrevious : TraineeMyPageEffect data object NavigateToLogin : TraineeMyPageEffect - } - - enum class TraineeMyPagePage { - MyPage, - WebView, - ; - - companion object { - fun getPreviousPage(currentPage: TraineeMyPagePage): TraineeMyPagePage { - return when (currentPage) { - WebView -> MyPage - else -> error("No previous page defined for $currentPage") - } - } - - fun getNextPage(currentPage: TraineeMyPagePage): TraineeMyPagePage { - return when (currentPage) { - MyPage -> WebView - else -> error("No next page defined for $currentPage") - } - } - } + data class NavigateToWebView(val url: String) : TraineeMyPageEffect } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt deleted file mode 100644 index 4158b5ec..00000000 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageMainPage.kt +++ /dev/null @@ -1,251 +0,0 @@ -package co.kr.tnt.trainee.mypage - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -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.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import co.kr.tnt.core.ui.R -import co.kr.tnt.designsystem.component.TnTIconPopupDialog -import co.kr.tnt.designsystem.component.TnTProfileImage -import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog -import co.kr.tnt.designsystem.component.TnTSwitch -import co.kr.tnt.designsystem.component.button.TnTTextButton -import co.kr.tnt.designsystem.component.button.model.ButtonSize -import co.kr.tnt.designsystem.component.button.model.ButtonType -import co.kr.tnt.designsystem.theme.TnTTheme -import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState -import co.kr.tnt.trainee.mypage.model.DialogState -import co.kr.tnt.ui.component.TnTMyPageButton -import co.kr.tnt.ui.model.DefaultUserProfile - -@Composable -internal fun TraineeMyPageMainPage( - state: TraineeMyPageUiState, - onBackClick: () -> Unit, - onEditButtonClick: () -> Unit, - onConnectButtonClick: () -> Unit, - onDisconnectButtonClick: () -> Unit, - onPushNotificationToggle: () -> Unit, - onServiceTermClick: () -> Unit, - onPrivacyClick: () -> Unit, - onOpenSourceClick: () -> Unit, - onLogoutClick: () -> Unit, - onDeleteAccountClick: () -> Unit, - onConfirmFirstPopup: () -> Unit, - onConfirmSecondPopup: () -> Unit, - onDismissPopup: () -> Unit, -) { - BackHandler { onBackClick() } - - Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .padding(innerPadding) - .verticalScroll(rememberScrollState()), - ) { - TnTProfileImage( - defaultImage = painterResource(DefaultUserProfile.Trainee.image), - imageSize = 132.dp, - showEditButton = false, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - ) - Text( - text = state.name, - color = TnTTheme.colors.neutralColors.Neutral950, - style = TnTTheme.typography.h2, - ) - Spacer(Modifier.height(8.dp)) - TnTTextButton( - text = stringResource(R.string.modifying_personal_info), - size = ButtonSize.Small, - type = ButtonType.Gray, - onClick = onEditButtonClick, - ) - Column( - verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier - .fillMaxWidth() - .padding(20.dp), - ) { - if (state.isConnected.not()) { - TnTMyPageButton( - text = stringResource(co.kr.tnt.feature.trainee.mypage.R.string.connect_with_trainer), - onClick = onConnectButtonClick, - verticalPadding = 14.dp, - ) - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(horizontal = 20.dp, vertical = 12.dp), - ) { - Text( - text = stringResource(R.string.app_push_notification), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - TnTSwitch( - checked = state.isPushEnabled, - onCheckedChange = onPushNotificationToggle, - ) - } - Column( - modifier = Modifier - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), - ) { - TnTMyPageButton( - text = stringResource(R.string.terms_of_service), - onClick = onServiceTermClick, - verticalPadding = 8.dp, - ) - TnTMyPageButton( - text = stringResource(R.string.privacy_policy), - onClick = onPrivacyClick, - verticalPadding = 8.dp, - ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 8.dp), - ) { - Text( - text = stringResource(R.string.app_version), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - ) - Text( - text = state.appVersion, - color = TnTTheme.colors.neutralColors.Neutral400, - style = TnTTheme.typography.body2Medium, - ) - } - TnTMyPageButton( - text = stringResource(R.string.open_source_license), - onClick = onOpenSourceClick, - verticalPadding = 8.dp, - ) - } - if (state.isConnected) { - TnTMyPageButton( - text = stringResource(co.kr.tnt.feature.trainee.mypage.R.string.disconnect_with_trainer), - onClick = onDisconnectButtonClick, - verticalPadding = 14.dp, - ) - } - Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(TnTTheme.colors.commonColors.Common0) - .padding(vertical = 12.dp), - ) { - Text( - text = stringResource(R.string.logout), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 8.dp) - .clickable(onClick = onLogoutClick), - ) - Text( - text = stringResource(R.string.delete_account), - color = TnTTheme.colors.neutralColors.Neutral700, - style = TnTTheme.typography.body2Medium, - modifier = Modifier - .padding(horizontal = 20.dp, vertical = 8.dp) - .clickable(onClick = onDeleteAccountClick), - ) - } - } - } - } - - if (state.showWarningDialog) { - TnTIconPopupDialog( - title = if (state.dialogState == DialogState.DISCONNECT) { - stringResource(state.dialogState.warningDialogTitle, state.trainerName) - } else { - stringResource(state.dialogState.warningDialogTitle) - }, - content = stringResource(state.dialogState.warningDialogContent), - leftButtonText = stringResource(R.string.cancel), - rightButtonText = stringResource(R.string.ok), - onLeftButtonClick = onDismissPopup, - onRightButtonClick = onConfirmFirstPopup, - onDismiss = onDismissPopup, - ) - } - - if (state.showCompleteDialog) { - TnTSingleButtonPopupDialog( - title = if (state.dialogState == DialogState.DISCONNECT) { - stringResource(state.dialogState.completeDialogTitle, state.trainerName) - } else { - stringResource(state.dialogState.completeDialogTitle) - }, - content = stringResource(state.dialogState.completeDialogContent), - buttonText = stringResource(R.string.ok), - onButtonClick = onConfirmSecondPopup, - onDismiss = onConfirmSecondPopup, - ) - } -} - -@Preview -@Composable -private fun TraineeMyPageMainPagePreview() { - TnTTheme { - TraineeMyPageMainPage( - state = TraineeMyPageUiState( - image = null, - name = "๊น€ํšŒ์›", - isConnected = false, - isPushEnabled = true, - appVersion = "0.0.0", - ), - onEditButtonClick = {}, - onConnectButtonClick = {}, - onPushNotificationToggle = {}, - onServiceTermClick = {}, - onPrivacyClick = {}, - onOpenSourceClick = {}, - onLogoutClick = {}, - onDeleteAccountClick = {}, - onDisconnectButtonClick = {}, - onBackClick = {}, - onConfirmFirstPopup = {}, - onDismissPopup = {}, - onConfirmSecondPopup = {}, - ) - } -} diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt index dcbe05ff..ff783ed1 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageScreen.kt @@ -1,21 +1,55 @@ package co.kr.tnt.trainee.mypage import android.widget.Toast +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import co.kr.tnt.core.ui.R +import co.kr.tnt.designsystem.component.TnTIconPopupDialog +import co.kr.tnt.designsystem.component.TnTProfileImage +import co.kr.tnt.designsystem.component.TnTSingleButtonPopupDialog +import co.kr.tnt.designsystem.component.TnTSwitch +import co.kr.tnt.designsystem.component.button.TnTTextButton +import co.kr.tnt.designsystem.component.button.model.ButtonSize +import co.kr.tnt.designsystem.component.button.model.ButtonType +import co.kr.tnt.designsystem.theme.TnTTheme import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState +import co.kr.tnt.trainee.mypage.model.DialogState +import co.kr.tnt.ui.component.TnTMyPageButton +import co.kr.tnt.ui.model.DefaultUserProfile @Composable internal fun TraineeMyPageRoute( navigateToPrevious: () -> Unit, navigateToConnect: () -> Unit, navigateToLogin: () -> Unit, + navigateToWebView: (String) -> Unit, viewModel: TraineeMyPageViewModel = hiltViewModel(), ) { val context = LocalContext.current @@ -23,20 +57,18 @@ internal fun TraineeMyPageRoute( TraineeMyPageScreen( state = uiState, - onBackClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnBackClick) }, onEditButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnEditButtonClick) }, onConnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnConnectButtonClick) }, onDisconnectButtonClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDisconnectButtonClick) }, onPushNotificationToggle = { viewModel.setEvent(TraineeMyPageUiEvent.ToggleNotification) }, onServiceTermClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnServiceTermClick) }, onPrivacyClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnPrivacyClick) }, - onDismissWebView = { viewModel.setEvent(TraineeMyPageUiEvent.OnWebViewBackClick) }, onOpenSourceClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnOpenSourceClick) }, onLogoutClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnLogoutClick) }, onDeleteAccountClick = { viewModel.setEvent(TraineeMyPageUiEvent.OnDeleteAccountClick) }, onDismissPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnDismissPopup) }, - onConfirmFirstPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmFirstPopup) }, - onConfirmSecondPopup = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmSecondPopup) }, + onConfirmWarningDialog = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmWarningDialog) }, + onConfirmCompleteDialog = { viewModel.setEvent(TraineeMyPageUiEvent.OnConfirmCompleteDialog) }, ) LaunchedEffect(viewModel.effect) { @@ -48,6 +80,8 @@ internal fun TraineeMyPageRoute( is TraineeMyPageEffect.ShowToast -> { Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show() } + + is TraineeMyPageEffect.NavigateToWebView -> navigateToWebView(effect.url) } } } @@ -56,7 +90,6 @@ internal fun TraineeMyPageRoute( @Composable private fun TraineeMyPageScreen( state: TraineeMyPageUiState, - onBackClick: () -> Unit, onEditButtonClick: () -> Unit, onConnectButtonClick: () -> Unit, onDisconnectButtonClick: () -> Unit, @@ -66,31 +99,199 @@ private fun TraineeMyPageScreen( onOpenSourceClick: () -> Unit, onLogoutClick: () -> Unit, onDeleteAccountClick: () -> Unit, - onConfirmFirstPopup: () -> Unit, - onConfirmSecondPopup: () -> Unit, + onConfirmWarningDialog: () -> Unit, + onConfirmCompleteDialog: () -> Unit, onDismissPopup: () -> Unit, - onDismissWebView: () -> Unit, ) { - when (state.page) { - TraineeMyPageContract.TraineeMyPagePage.MyPage -> TraineeMyPageMainPage( - state = state, - onBackClick = onBackClick, - onEditButtonClick = onEditButtonClick, - onConnectButtonClick = onConnectButtonClick, - onDisconnectButtonClick = onDisconnectButtonClick, - onPushNotificationToggle = onPushNotificationToggle, - onServiceTermClick = onServiceTermClick, - onPrivacyClick = onPrivacyClick, - onOpenSourceClick = onOpenSourceClick, - onLogoutClick = onLogoutClick, - onDeleteAccountClick = onDeleteAccountClick, - onConfirmFirstPopup = onConfirmFirstPopup, - onConfirmSecondPopup = onConfirmSecondPopup, - onDismissPopup = onDismissPopup, + Scaffold(containerColor = TnTTheme.colors.neutralColors.Neutral50) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding(innerPadding) + .verticalScroll(rememberScrollState()), + ) { + TnTProfileImage( + defaultImage = painterResource(DefaultUserProfile.Trainee.image), + imageSize = 132.dp, + showEditButton = false, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + ) + Text( + text = state.name, + color = TnTTheme.colors.neutralColors.Neutral950, + style = TnTTheme.typography.h2, + ) + Spacer(Modifier.height(8.dp)) + TnTTextButton( + text = stringResource(R.string.modifying_personal_info), + size = ButtonSize.Small, + type = ButtonType.Gray, + onClick = onEditButtonClick, + ) + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + ) { + if (state.isConnected.not()) { + TnTMyPageButton( + text = stringResource(co.kr.tnt.feature.trainee.mypage.R.string.connect_with_trainer), + onClick = onConnectButtonClick, + verticalPadding = 14.dp, + ) + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(horizontal = 20.dp, vertical = 12.dp), + ) { + Text( + text = stringResource(R.string.app_push_notification), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + TnTSwitch( + checked = state.isPushEnabled, + onCheckedChange = onPushNotificationToggle, + ) + } + Column( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + TnTMyPageButton( + text = stringResource(R.string.terms_of_service), + onClick = onServiceTermClick, + verticalPadding = 8.dp, + ) + TnTMyPageButton( + text = stringResource(R.string.privacy_policy), + onClick = onPrivacyClick, + verticalPadding = 8.dp, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 8.dp), + ) { + Text( + text = stringResource(R.string.app_version), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + ) + Text( + text = state.appVersion, + color = TnTTheme.colors.neutralColors.Neutral400, + style = TnTTheme.typography.body2Medium, + ) + } + TnTMyPageButton( + text = stringResource(R.string.open_source_license), + onClick = onOpenSourceClick, + verticalPadding = 8.dp, + ) + } + if (state.isConnected) { + TnTMyPageButton( + text = stringResource(co.kr.tnt.feature.trainee.mypage.R.string.disconnect_with_trainer), + onClick = onDisconnectButtonClick, + verticalPadding = 14.dp, + ) + } + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(TnTTheme.colors.commonColors.Common0) + .padding(vertical = 12.dp), + ) { + Text( + text = stringResource(R.string.logout), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 8.dp) + .clickable(onClick = onLogoutClick), + ) + Text( + text = stringResource(R.string.delete_account), + color = TnTTheme.colors.neutralColors.Neutral700, + style = TnTTheme.typography.body2Medium, + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 8.dp) + .clickable(onClick = onDeleteAccountClick), + ) + } + } + } + } + + if (state.showWarningDialog) { + TnTIconPopupDialog( + title = if (state.dialogState == DialogState.DISCONNECT) { + stringResource(state.dialogState.warningDialogTitle, state.trainerName) + } else { + stringResource(state.dialogState.warningDialogTitle) + }, + content = stringResource(state.dialogState.warningDialogContent), + leftButtonText = stringResource(R.string.cancel), + rightButtonText = stringResource(R.string.ok), + onLeftButtonClick = onDismissPopup, + onRightButtonClick = onConfirmWarningDialog, + onDismiss = onDismissPopup, + ) + } + + if (state.showCompleteDialog) { + TnTSingleButtonPopupDialog( + title = if (state.dialogState == DialogState.DISCONNECT) { + stringResource(state.dialogState.completeDialogTitle, state.trainerName) + } else { + stringResource(state.dialogState.completeDialogTitle) + }, + content = stringResource(state.dialogState.completeDialogContent), + buttonText = stringResource(R.string.ok), + onButtonClick = onConfirmCompleteDialog, + onDismiss = onConfirmCompleteDialog, ) - TraineeMyPageContract.TraineeMyPagePage.WebView -> TraineeMyPageWebViewPage( - url = state.url, - onBackPress = onDismissWebView, + } +} + +@Preview +@Composable +private fun TraineeMyPageScreenPreview() { + TnTTheme { + TraineeMyPageScreen( + state = TraineeMyPageUiState( + image = null, + name = "๊น€ํšŒ์›", + isConnected = false, + isPushEnabled = true, + appVersion = "0.0.0", + ), + onEditButtonClick = {}, + onConnectButtonClick = {}, + onPushNotificationToggle = {}, + onServiceTermClick = {}, + onPrivacyClick = {}, + onOpenSourceClick = {}, + onLogoutClick = {}, + onDeleteAccountClick = {}, + onDisconnectButtonClick = {}, + onDismissPopup = {}, + onConfirmWarningDialog = {}, + onConfirmCompleteDialog = {}, ) } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt index d4be39eb..2f6ce3f7 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/TraineeMyPageViewModel.kt @@ -2,7 +2,6 @@ package co.kr.tnt.trainee.mypage import androidx.lifecycle.viewModelScope import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect -import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPagePage import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState import co.kr.tnt.trainee.mypage.model.DialogState @@ -24,13 +23,11 @@ internal class TraineeMyPageViewModel @Inject constructor() : TraineeMyPageUiEvent.ToggleNotification -> togglePushNotification() TraineeMyPageUiEvent.OnServiceTermClick -> openWebView() TraineeMyPageUiEvent.OnPrivacyClick -> openWebView() - TraineeMyPageUiEvent.OnWebViewBackClick -> dismissWebView() TraineeMyPageUiEvent.OnOpenSourceClick -> navigateToOpenSource() TraineeMyPageUiEvent.OnLogoutClick -> logout() TraineeMyPageUiEvent.OnDeleteAccountClick -> deleteAccount() - TraineeMyPageUiEvent.OnBackClick -> navigateBack() - TraineeMyPageUiEvent.OnConfirmFirstPopup -> confirmFirstPopup() - TraineeMyPageUiEvent.OnConfirmSecondPopup -> handleSecondPopupConfirm() + TraineeMyPageUiEvent.OnConfirmWarningDialog -> confirmWarningDialog() + TraineeMyPageUiEvent.OnConfirmCompleteDialog -> handleCompleteDialogConfirm() TraineeMyPageUiEvent.OnDismissPopup -> dismissPopup() } } @@ -87,14 +84,7 @@ internal class TraineeMyPageViewModel @Inject constructor() : url = url, ) } - - val nextPage = TraineeMyPagePage.getNextPage(currentState.page) - updateState { copy(page = nextPage) } - } - - private fun dismissWebView() { - val previousPage = TraineeMyPagePage.getPreviousPage(currentState.page) - updateState { copy(page = previousPage) } + sendEffect(TraineeMyPageEffect.NavigateToWebView(url)) } private fun navigateToOpenSource() { @@ -102,26 +92,33 @@ internal class TraineeMyPageViewModel @Inject constructor() : } private fun logout() { - updateState { copy(showWarningDialog = true, dialogState = DialogState.LOGOUT) } + updateState { + copy( + showWarningDialog = true, + dialogState = DialogState.LOGOUT, + ) + } } private fun deleteAccount() { - updateState { copy(showWarningDialog = true, dialogState = DialogState.DELETE_ACCOUNT) } - } - - private fun navigateBack() { - sendEffect(TraineeMyPageEffect.NavigateToPrevious) - } - - private fun confirmFirstPopup() { - updateState { copy(showWarningDialog = false, showCompleteDialog = true) } + updateState { + copy( + showWarningDialog = true, + dialogState = DialogState.DELETE_ACCOUNT, + ) + } } - private fun dismissPopup() { - updateState { copy(showWarningDialog = false, showCompleteDialog = false) } + private fun confirmWarningDialog() { + updateState { + copy( + showWarningDialog = false, + showCompleteDialog = true, + ) + } } - private fun handleSecondPopupConfirm() { + private fun handleCompleteDialogConfirm() { when (currentState.dialogState) { DialogState.LOGOUT -> performLogout() DialogState.DELETE_ACCOUNT -> performAccountDeletion() @@ -129,6 +126,15 @@ internal class TraineeMyPageViewModel @Inject constructor() : } } + private fun dismissPopup() { + updateState { + copy( + showWarningDialog = false, + showCompleteDialog = false, + ) + } + } + private fun performLogout() { viewModelScope.launch { // TODO ๋กœ๊ทธ์•„์›ƒ API ํ˜ธ์ถœ @@ -148,7 +154,12 @@ internal class TraineeMyPageViewModel @Inject constructor() : private fun performDisconnect() { viewModelScope.launch { // TODO ์—ฐ๊ฒฐ ํ•ด์ œ API ํ˜ธ์ถœ - updateState { copy(isConnected = false, showCompleteDialog = false) } + updateState { + copy( + isConnected = false, + showCompleteDialog = false, + ) + } } } } diff --git a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt index 267a739f..de5ab6f8 100644 --- a/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt +++ b/feature/trainee/mypage/src/main/java/co/kr/tnt/trainee/mypage/navigation/TraineeMyPageNavigation.kt @@ -18,12 +18,14 @@ fun NavGraphBuilder.traineeMyPageScreen( navigateToPrevious: () -> Unit, navigateToTraineeConnect: () -> Unit, navigateToLogin: () -> Unit, + navigateToWebView: (String) -> Unit, ) { composable { TraineeMyPageRoute( navigateToPrevious = navigateToPrevious, navigateToConnect = navigateToTraineeConnect, navigateToLogin = navigateToLogin, + navigateToWebView = navigateToWebView, ) } }