diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
index 73688362..9d7b269d 100644
Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
index 886c2d43..dd2f183d 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
index d0cb1031..3f60bf43 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
index bdc0577a..969a6694 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
index 34cfdd1f..b3b640a5 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
index 63b4cf71..b98c7de9 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
index 21c3fc6b..9e0257c8 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
index 202a4e35..4c10f834 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
index 8300957c..ae18f6d6 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
index d3cdbe82..90347123 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
index 390317b9..5826d74a 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
index 7758c027..79db8199 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
index f72a2000..4480f255 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
index 3eb90f67..257ad0d6 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
index fc398678..c7e4f3b7 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
index ef3fee5c..6be407b7 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
index 60bbf7bf..173e0cac 100644
--- a/app/src/main/res/values/ic_launcher_background.xml
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -1,4 +1,4 @@
- #FF2B0F
+ #FF472F
\ No newline at end of file
diff --git a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/card/Card.kt b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/card/Card.kt
index 4fdac04c..12b87f37 100644
--- a/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/card/Card.kt
+++ b/core/designsystem/src/main/java/co/kr/tnt/designsystem/component/card/Card.kt
@@ -283,6 +283,7 @@ fun TnTSessionRecordCard(
}
@Composable
+@Suppress("UnusedParameter")
fun TnTMemberProfileCard(
name: String,
profileImage: Painter,
@@ -293,12 +294,12 @@ fun TnTMemberProfileCard(
modifier: Modifier = Modifier,
onClick: () -> Unit,
) {
+ // TODO: clickable 추가
Column(
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(TnTTheme.colors.commonColors.Common0)
- .clickable(onClick = onClick)
.padding(12.dp),
) {
Row(
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 88453a1d..68ce7151 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
@@ -1,5 +1,6 @@
package co.kr.tnt.navigation
+import co.kr.tnt.navigation.model.ScreenMode
import kotlinx.serialization.Serializable
// TODO Route 정리
@@ -35,7 +36,7 @@ sealed interface Route {
) : Route
@Serializable
- data class TrainerInvite(val isSkippable: Boolean) : Route
+ data class TrainerInvite(val screenMode: ScreenMode) : Route
@Serializable
data class TrainerConnect(
@@ -44,7 +45,7 @@ sealed interface Route {
) : Route
@Serializable
- data class TraineeConnect(val isSkippable: Boolean) : Route
+ data class TraineeConnect(val screenMode: ScreenMode) : Route
@Serializable
data object TrainerMain : Route
diff --git a/core/navigation/src/main/java/co/kr/tnt/navigation/model/ScreenMode.kt b/core/navigation/src/main/java/co/kr/tnt/navigation/model/ScreenMode.kt
new file mode 100644
index 00000000..84319672
--- /dev/null
+++ b/core/navigation/src/main/java/co/kr/tnt/navigation/model/ScreenMode.kt
@@ -0,0 +1,7 @@
+package co.kr.tnt.navigation.model
+
+enum class ScreenMode {
+ BACK,
+ SKIP,
+ CLOSE,
+}
diff --git a/data/repository/src/main/java/co/kr/data/repository/TraineeRepositoryImpl.kt b/data/repository/src/main/java/co/kr/data/repository/TraineeRepositoryImpl.kt
index 4b293194..87a39421 100644
--- a/data/repository/src/main/java/co/kr/data/repository/TraineeRepositoryImpl.kt
+++ b/data/repository/src/main/java/co/kr/data/repository/TraineeRepositoryImpl.kt
@@ -20,7 +20,6 @@ import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.time.LocalDate
-import java.time.LocalDateTime
import javax.inject.Inject
import javax.inject.Singleton
@@ -54,7 +53,7 @@ internal class TraineeRepositoryImpl @Inject constructor(
override suspend fun postMealRecord(
mealImage: File?,
- date: LocalDateTime,
+ date: String,
mealType: String,
memo: String,
) {
@@ -64,7 +63,7 @@ internal class TraineeRepositoryImpl @Inject constructor(
}
val mealRecordRequest = MealRecordRequest(
- date = date.toString(),
+ date = date,
dietType = mealType,
memo = memo,
)
diff --git a/domain/src/main/java/co/kr/tnt/domain/model/User.kt b/domain/src/main/java/co/kr/tnt/domain/model/User.kt
index a2183f93..125fc00f 100644
--- a/domain/src/main/java/co/kr/tnt/domain/model/User.kt
+++ b/domain/src/main/java/co/kr/tnt/domain/model/User.kt
@@ -32,7 +32,7 @@ sealed class User {
val birthday: LocalDate?,
val weight: Double?,
val height: Int?,
- val ptPurpose: List,
+ val ptPurpose: List?,
val caution: String?,
val isConnected: Boolean,
) : User() {
diff --git a/domain/src/main/java/co/kr/tnt/domain/repository/TraineeRepository.kt b/domain/src/main/java/co/kr/tnt/domain/repository/TraineeRepository.kt
index a0413d23..44693fea 100644
--- a/domain/src/main/java/co/kr/tnt/domain/repository/TraineeRepository.kt
+++ b/domain/src/main/java/co/kr/tnt/domain/repository/TraineeRepository.kt
@@ -6,13 +6,12 @@ import co.kr.tnt.domain.model.trainee.TraineeDailyRecordStatus
import co.kr.tnt.domain.model.trainee.TraineeMealRecordDetail
import java.io.File
import java.time.LocalDate
-import java.time.LocalDateTime
interface TraineeRepository {
suspend fun getMyInfo(): User.Trainee
suspend fun postMealRecord(
mealImage: File?,
- date: LocalDateTime,
+ date: String,
mealType: String,
memo: String,
)
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 16548c8f..015f6e29 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.domain.model.UserType
import co.kr.tnt.login.navigation.loginScreen
import co.kr.tnt.login.navigation.navigateToLogin
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.roleselect.navigateToRoleSelection
import co.kr.tnt.roleselect.roleSelectionScreen
import co.kr.tnt.trainee.connect.navigation.navigateToTraineeConnect
@@ -72,7 +73,7 @@ fun TnTNavHost(
traineeSignUpScreen(
navigateToPrevious = navController::safePopBackStack,
navigateToConnect = {
- navController.navigateToTraineeConnect(isSkippable = true)
+ navController.navigateToTraineeConnect(screenMode = ScreenMode.SKIP)
},
)
trainerInviteScreen(
diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt
index 55d6507f..554ae053 100644
--- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt
+++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/CodeEntryPage.kt
@@ -7,11 +7,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -23,16 +26,18 @@ import co.kr.tnt.designsystem.component.button.TnTTextButton
import co.kr.tnt.designsystem.component.button.model.ButtonSize
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.feature.trainee.connect.R
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.connect.component.CodeTextField
import co.kr.tnt.trainee.connect.model.InputState
-import co.kr.tnt.core.ui.R as uiResource
+import co.kr.tnt.core.designsystem.R as uiResource
+import co.kr.tnt.core.ui.R as coreR
@Composable
internal fun CodeEntryPage(
showDialog: Boolean,
inviteCode: String,
inputState: InputState,
- isSkippable: Boolean,
+ screenMode: ScreenMode,
onSkipClick: () -> Unit,
onBackClick: () -> Unit,
onNextClick: () -> Unit,
@@ -42,34 +47,52 @@ internal fun CodeEntryPage(
onDismissPopup: () -> Unit,
) {
BackHandler {
- if (isSkippable) {
- onSkipClick()
- } else {
- onBackClick()
+ when (screenMode) {
+ ScreenMode.BACK -> onBackClick()
+ ScreenMode.SKIP -> onSkipClick()
+ ScreenMode.CLOSE -> onBackClick()
}
}
Scaffold(
topBar = {
- if (isSkippable) {
- TnTTopBar(
- title = stringResource(uiResource.string.connect),
- trailingComponent = {
- Text(
- text = stringResource(uiResource.string.skip),
- color = TnTTheme.colors.neutralColors.Neutral400,
- style = TnTTheme.typography.body2Medium,
- modifier = Modifier.clickable {
- onSkipClick()
- },
- )
- },
- )
- } else {
- TnTTopBarWithBackButton(
- title = stringResource(uiResource.string.connect),
- onBackClick = onBackClick,
- )
+ when (screenMode) {
+ ScreenMode.BACK -> {
+ TnTTopBarWithBackButton(
+ title = stringResource(coreR.string.connect),
+ onBackClick = onBackClick,
+ )
+ }
+ ScreenMode.SKIP -> {
+ TnTTopBar(
+ title = stringResource(coreR.string.connect),
+ trailingComponent = {
+ Text(
+ text = stringResource(coreR.string.skip),
+ color = TnTTheme.colors.neutralColors.Neutral400,
+ style = TnTTheme.typography.body2Medium,
+ modifier = Modifier.clickable {
+ onSkipClick()
+ },
+ )
+ },
+ )
+ }
+ ScreenMode.CLOSE -> {
+ TnTTopBar(
+ title = stringResource(coreR.string.connect),
+ trailingComponent = {
+ IconButton(
+ onClick = onBackClick,
+ ) {
+ Icon(
+ painter = painterResource(uiResource.drawable.ic_delete),
+ contentDescription = null,
+ )
+ }
+ },
+ )
+ }
}
},
containerColor = TnTTheme.colors.commonColors.Common0,
@@ -100,7 +123,7 @@ internal fun CodeEntryPage(
)
}
TnTBottomButton(
- text = stringResource(uiResource.string.next),
+ text = stringResource(coreR.string.next),
enabled = inputState.isValid,
onClick = onNextClick,
modifier = Modifier.align(Alignment.BottomCenter),
@@ -126,10 +149,10 @@ internal fun CodeEntryPage(
private fun CodeEntryPagePreview() {
TnTTheme {
CodeEntryPage(
- showDialog = true,
+ showDialog = false,
inputState = InputState.FOCUS,
inviteCode = "23A4SDA31",
- isSkippable = false,
+ screenMode = ScreenMode.CLOSE,
onSkipClick = {},
onNextClick = {},
onValidateClick = {},
diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt
index 7a384b25..020f0bcb 100644
--- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt
+++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/TraineeConnectScreen.kt
@@ -6,6 +6,7 @@ import androidx.compose.runtime.getValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.kr.tnt.designsystem.snackbar.LocalSnackbar
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.connect.TraineeConnectContract.TraineeConnectPage
import co.kr.tnt.trainee.connect.TraineeConnectContract.TraineeConnectSideEffect
import co.kr.tnt.trainee.connect.TraineeConnectContract.TraineeConnectUiEvent
@@ -14,7 +15,7 @@ import java.time.LocalDate
@Composable
internal fun TraineeConnectRoute(
- isSkippable: Boolean,
+ screenMode: ScreenMode,
navigateToPrevious: () -> Unit,
navigateToHome: (Boolean) -> Unit,
viewModel: TraineeConnectViewModel = hiltViewModel(),
@@ -24,7 +25,7 @@ internal fun TraineeConnectRoute(
TraineeConnectScreen(
state = state,
- isSkippable = isSkippable,
+ screenMode = screenMode,
onBackClick = { viewModel.setEvent(TraineeConnectUiEvent.OnChangeDialogState) },
onNextClick = { viewModel.setEvent(TraineeConnectUiEvent.OnNextClick) },
onSkipClick = { viewModel.setEvent(TraineeConnectUiEvent.OnSkipClick) },
@@ -61,7 +62,7 @@ internal fun TraineeConnectRoute(
@Composable
private fun TraineeConnectScreen(
state: TraineeConnectUiState,
- isSkippable: Boolean,
+ screenMode: ScreenMode,
onChangeInviteCode: (code: String) -> Unit,
onCodeValidationClick: (code: String) -> Unit,
onCancelConnectClick: () -> Unit,
@@ -78,7 +79,7 @@ private fun TraineeConnectScreen(
showDialog = state.showDialog,
inputState = state.inviteCodeInputState,
inviteCode = state.inviteCode,
- isSkippable = isSkippable,
+ screenMode = screenMode,
onNextClick = onNextClick,
onBackClick = onBackClick,
onSkipClick = onSkipClick,
diff --git a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/navigation/TraineeConnectNavigation.kt b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/navigation/TraineeConnectNavigation.kt
index 7f6d007b..be47b143 100644
--- a/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/navigation/TraineeConnectNavigation.kt
+++ b/feature/trainee/connect/src/main/java/co/kr/tnt/trainee/connect/navigation/TraineeConnectNavigation.kt
@@ -6,13 +6,14 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.connect.TraineeConnectRoute
fun NavController.navigateToTraineeConnect(
- isSkippable: Boolean,
+ screenMode: ScreenMode,
navOptions: NavOptionsBuilder.() -> Unit = {},
) = navigate(
- route = Route.TraineeConnect(isSkippable),
+ route = Route.TraineeConnect(screenMode),
builder = navOptions,
)
@@ -23,7 +24,7 @@ fun NavGraphBuilder.traineeConnectScreen(
composable { backstackEntry ->
backstackEntry.toRoute().apply {
TraineeConnectRoute(
- isSkippable = isSkippable,
+ screenMode = screenMode,
navigateToPrevious = navigateToPrevious,
navigateToHome = { navigateToHome(false) },
)
diff --git a/feature/trainee/home/src/main/java/co/kr/tnt/trainee/home/TraineeHomeScreen.kt b/feature/trainee/home/src/main/java/co/kr/tnt/trainee/home/TraineeHomeScreen.kt
index fe82c3ba..73d38481 100644
--- a/feature/trainee/home/src/main/java/co/kr/tnt/trainee/home/TraineeHomeScreen.kt
+++ b/feature/trainee/home/src/main/java/co/kr/tnt/trainee/home/TraineeHomeScreen.kt
@@ -121,7 +121,6 @@ internal fun TraineeHomeRoute(
},
content = {
RecordBottomSheetContent(
- onClickExercise = { viewModel.setEvent(TraineeHomeUiEvent.OnClickExerciseRecord) },
onClickDiet = { viewModel.setEvent(TraineeHomeUiEvent.OnClickMealRecord) },
)
},
@@ -331,6 +330,7 @@ private fun Calendar(
}
@Composable
+@Suppress("UnusedParameter")
private fun DailyPtSession(
session: TraineePtSession,
context: Context,
@@ -357,7 +357,7 @@ private fun DailyPtSession(
profileImage = session.trainerImage?.let { painter },
showSessionRecordCreation = false,
showSessionRecordDetails = session.hasRecord,
- onClick = { onClickPtSessionCard(session.ptSessionId) },
+ onClick = { },
modifier = Modifier.padding(
start = 20.dp,
end = 20.dp,
@@ -442,7 +442,6 @@ private fun EmptyDailyRecords() {
@Composable
private fun RecordBottomSheetContent(
- onClickExercise: () -> Unit,
onClickDiet: () -> Unit,
) {
Column(
@@ -454,12 +453,6 @@ private fun RecordBottomSheetContent(
style = TnTTheme.typography.h3,
modifier = Modifier.padding(vertical = 20.dp),
)
- RecordItem(
- icon = "\uD83C\uDFCB\uD83C\uDFFB\u200D♀\uFE0F",
- text = "개인 운동",
- modifier = Modifier.clickable(onClick = onClickExercise),
- )
- Spacer(Modifier.height(12.dp))
RecordItem(
icon = "\uD83E\uDD57",
text = "식단",
@@ -550,7 +543,6 @@ private fun TraineeHomeScreenPreview() {
private fun RecordBottomSheetContentPreview() {
TnTTheme {
RecordBottomSheetContent(
- onClickExercise = { },
onClickDiet = { },
)
}
diff --git a/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/TraineeMainScreen.kt b/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/TraineeMainScreen.kt
index 80982e6f..ab656feb 100644
--- a/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/TraineeMainScreen.kt
+++ b/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/TraineeMainScreen.kt
@@ -3,13 +3,13 @@ package co.kr.tnt.trainee.main
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import co.kr.tnt.designsystem.component.bottombar.TnTBottomBar
import co.kr.tnt.designsystem.theme.TnTTheme
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.home.navigation.traineeHomeNavGraph
import co.kr.tnt.trainee.mypage.navigation.traineeMyPageNavGraph
import co.kr.tnt.trainee.notification.navigation.navigateToTraineeNotification
@@ -18,7 +18,7 @@ import co.kr.tnt.ui.extensions.safePopBackStack
@Composable
internal fun TraineeMainRoute(
- navigateToConnect: (Boolean) -> Unit,
+ navigateToConnect: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
navigateToMealRecord: () -> Unit,
@@ -42,7 +42,7 @@ internal fun TraineeMainRoute(
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
private fun TraineeMainScreen(
state: TraineeMainState,
- navigateToConnect: (Boolean) -> Unit,
+ navigateToConnect: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
navigateToMealRecord: () -> Unit,
@@ -72,7 +72,7 @@ private fun TraineeMainScreen(
navigateToNotification = navController::navigateToTraineeNotification,
navigateToMealRecord = navigateToMealRecord,
navigateToMealDetail = navigateToMealDetail,
- navigateToConnect = { navigateToConnect(false) },
+ navigateToConnect = { navigateToConnect(ScreenMode.CLOSE) },
) {
traineeNotification(
navigateToPrevious = navController::safePopBackStack,
diff --git a/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/navigation/TraineeMainNavigation.kt b/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/navigation/TraineeMainNavigation.kt
index 6afa763c..1f2d4ec6 100644
--- a/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/navigation/TraineeMainNavigation.kt
+++ b/feature/trainee/main/src/main/java/co/kr/tnt/trainee/main/navigation/TraineeMainNavigation.kt
@@ -6,6 +6,7 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navOptions
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.main.TraineeMainRoute
fun NavController.navigateToTraineeMain(
@@ -22,7 +23,7 @@ fun NavController.navigateToTraineeMain(
)
fun NavGraphBuilder.traineeMainScreen(
- navigateToConnect: (Boolean) -> Unit,
+ navigateToConnect: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
navigateToMealRecord: () -> Unit,
diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/detail/TraineeMealRecordDetailScreen.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/detail/TraineeMealRecordDetailScreen.kt
index e68a0cae..c1ac6b69 100644
--- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/detail/TraineeMealRecordDetailScreen.kt
+++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/detail/TraineeMealRecordDetailScreen.kt
@@ -3,7 +3,6 @@ package co.kr.tnt.trainee.mealrecord.detail
import android.content.Context
import androidx.compose.foundation.Image
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
@@ -83,6 +82,7 @@ internal fun TraineeMealRecordDetailRoute(
}
@Composable
+@Suppress("UnusedParameter")
private fun TraineeMealRecordDetailScreen(
state: TraineeMealRecordDetailUiState,
context: Context,
@@ -109,7 +109,6 @@ private fun TraineeMealRecordDetailScreen(
Icon(
painter = painterResource(R.drawable.ic_more),
contentDescription = null,
- modifier = Modifier.clickable(onClick = onClickMore),
)
},
)
diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordContract.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordContract.kt
index 1a696cf4..c77cafe2 100644
--- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordContract.kt
+++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordContract.kt
@@ -29,13 +29,12 @@ internal class TraineeMealRecordContract {
EXIT,
}
- val mealDateTime: LocalDateTime?
- get() = time?.let { LocalDateTime.of(date, it) }
-
fun validateMealRecord(): TraineeMealRecordUiState {
+ val now = LocalDateTime.now()
+ val selectedDateTime = time?.let { LocalDateTime.of(date, it) }
+
return copy(
- isMealRecordValid = date <= LocalDate.now() &&
- time != null &&
+ isMealRecordValid = selectedDateTime != null && selectedDateTime <= now &&
mealType.isNotBlank() &&
memo.isNotBlank(),
)
diff --git a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordViewModel.kt b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordViewModel.kt
index 7434d235..9581e126 100644
--- a/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordViewModel.kt
+++ b/feature/trainee/mealrecord/src/main/java/co/kr/tnt/trainee/mealrecord/record/TraineeMealRecordViewModel.kt
@@ -4,6 +4,7 @@ import android.content.Context
import androidx.core.net.toUri
import androidx.lifecycle.viewModelScope
import co.kr.tnt.domain.repository.TraineeRepository
+import co.kr.tnt.domain.utils.DateFormatter
import co.kr.tnt.trainee.mealrecord.record.TraineeMealRecordContract.TraineeMealRecordSideEffect
import co.kr.tnt.trainee.mealrecord.record.TraineeMealRecordContract.TraineeMealRecordUiEvent
import co.kr.tnt.trainee.mealrecord.record.TraineeMealRecordContract.TraineeMealRecordUiState
@@ -21,6 +22,7 @@ import javax.inject.Inject
@HiltViewModel
internal class TraineeMealRecordViewModel @Inject constructor(
private val traineeRepository: TraineeRepository,
+ private val dateFormatter: DateFormatter,
) :
BaseViewModel(
TraineeMealRecordUiState(),
@@ -94,6 +96,11 @@ internal class TraineeMealRecordViewModel @Inject constructor(
private fun postMealRecord(context: Context) {
updateState { copy(isLoading = true) }
+ val mealDateTime = dateFormatter.format(
+ LocalDateTime.of(currentState.date, currentState.time),
+ "yyyy-MM-dd'T'HH:mm:ss",
+ )
+
viewModelScope.launch {
val state = currentState
val imageFile: File? = state.image?.toFile(context)?.let { file ->
@@ -106,7 +113,7 @@ internal class TraineeMealRecordViewModel @Inject constructor(
runCatching {
traineeRepository.postMealRecord(
mealImage = imageFile,
- date = state.mealDateTime ?: LocalDateTime.now(),
+ date = mealDateTime,
mealType = state.mealType,
memo = state.memo,
)
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 610c58c6..8948c4ff 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
@@ -35,6 +35,7 @@ import co.kr.tnt.designsystem.snackbar.LocalSnackbar
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.domain.model.User
import co.kr.tnt.feature.trainee.mypage.R
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageEffect
import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiEvent
import co.kr.tnt.trainee.mypage.TraineeMyPageContract.TraineeMyPageUiState
@@ -58,7 +59,7 @@ import co.kr.tnt.core.ui.R as coreR
@Composable
internal fun TraineeMyPageRoute(
padding: PaddingValues,
- navigateToConnect: (Boolean) -> Unit,
+ navigateToConnect: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
viewModel: TraineeMyPageViewModel = hiltViewModel(),
@@ -98,7 +99,7 @@ internal fun TraineeMyPageRoute(
LaunchedEffect(viewModel.effect) {
viewModel.effect.collect { effect ->
when (effect) {
- TraineeMyPageEffect.NavigateToConnect -> navigateToConnect(false)
+ TraineeMyPageEffect.NavigateToConnect -> navigateToConnect(ScreenMode.BACK)
TraineeMyPageEffect.NavigateToLogin -> navigateToLogin()
is TraineeMyPageEffect.ShowToast -> snackbar.show(effect.message)
is TraineeMyPageEffect.NavigateToWebView -> navigateToWebView(effect.url)
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 4f277450..db318bab 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
@@ -7,6 +7,7 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navigation
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainee.mypage.TraineeMyPageRoute
fun NavController.navigateToTraineeMyPage(
@@ -18,7 +19,7 @@ fun NavController.navigateToTraineeMyPage(
fun NavGraphBuilder.traineeMyPageNavGraph(
padding: PaddingValues,
- navigateToTraineeConnect: (Boolean) -> Unit,
+ navigateToTraineeConnect: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
) {
diff --git a/feature/trainee/notification/src/main/java/co/kr/tnt/trainee/notification/TraineeNotificationViewModel.kt b/feature/trainee/notification/src/main/java/co/kr/tnt/trainee/notification/TraineeNotificationViewModel.kt
index b83cea53..ba0d5de3 100644
--- a/feature/trainee/notification/src/main/java/co/kr/tnt/trainee/notification/TraineeNotificationViewModel.kt
+++ b/feature/trainee/notification/src/main/java/co/kr/tnt/trainee/notification/TraineeNotificationViewModel.kt
@@ -1,15 +1,12 @@
package co.kr.tnt.trainee.notification
import co.kr.tnt.domain.model.NotificationInfo
-import co.kr.tnt.domain.model.NotificationType
import co.kr.tnt.trainee.notification.TraineeNotificationContract.TraineeNotificationEffect
import co.kr.tnt.trainee.notification.TraineeNotificationContract.TraineeNotificationUiEvent
import co.kr.tnt.trainee.notification.TraineeNotificationContract.TraineeNotificationUiState
import co.kr.tnt.ui.base.BaseViewModel
import co.kr.tnt.ui.model.NotificationState
import dagger.hilt.android.lifecycle.HiltViewModel
-import java.time.LocalDateTime
-import java.time.format.DateTimeFormatter
import javax.inject.Inject
@HiltViewModel
@@ -29,31 +26,7 @@ internal class TraineeNotificationViewModel @Inject constructor() :
private fun getNotification() {
// TODO 알림 불러오기
- val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
-
- val sampleNotifications = listOf(
- NotificationInfo(
- type = NotificationType.DISCONNECT,
- title = "트레이너 연결 해제",
- contents = "박헬린 트레이너가 연결을 끊었어요",
- time = LocalDateTime.parse("2025-02-03T23:12:00", formatter),
- isChecked = false,
- ),
- NotificationInfo(
- type = NotificationType.DISCONNECT,
- title = "트레이너 연결 해제",
- contents = "김헬스 트레이너가 연결을 끊었어요",
- time = LocalDateTime.parse("2025-02-03T23:03:00", formatter),
- isChecked = true,
- ),
- NotificationInfo(
- type = NotificationType.DISCONNECT,
- title = "트레이너 연결 해제",
- contents = "김피티 트레이너가 연결을 끊었어요",
- time = LocalDateTime.parse("2025-02-02T22:29:00", formatter),
- isChecked = true,
- ),
- )
+ val sampleNotifications = emptyList()
updateState { copy(notifications = sampleNotifications.map(NotificationState::fromDomain)) }
}
diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineePTPurposePage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineePTPurposePage.kt
index 7b4fe4cf..f4f1a889 100644
--- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineePTPurposePage.kt
+++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineePTPurposePage.kt
@@ -67,7 +67,7 @@ internal fun TraineePTPurposePage(
val purposeText = stringResource(purpose.textResId)
PurposeButton(
text = purposeText,
- isSelected = purposeText in state.ptPurpose,
+ isSelected = state.ptPurpose?.contains(purposeText) == true,
onClick = { onPurposeSelected(purposeText) },
modifier = Modifier.weight(1f),
)
@@ -78,7 +78,6 @@ internal fun TraineePTPurposePage(
TnTBottomButton(
text = stringResource(uiResource.string.next),
onClick = onNextClick,
- enabled = state.ptPurpose.isNotEmpty(),
modifier = Modifier.align(Alignment.BottomCenter),
)
}
diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt
index 98f89573..fa907d9b 100644
--- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt
+++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeProfileSetupPage.kt
@@ -99,8 +99,7 @@ internal fun TraineeProfileSetupPage(
title = stringResource(coreR.string.name),
value = state.name,
onValueChange = { newValue ->
- val filteredText = validateInput(newValue)
- onNameChange(filteredText)
+ onNameChange(newValue)
},
modifier = Modifier.padding(horizontal = 20.dp),
placeholder = stringResource(R.string.enter_your_name),
@@ -121,13 +120,6 @@ internal fun TraineeProfileSetupPage(
}
}
-/**
- * 입력 값을 검사해 한글/영어/공백만 허용하고 특수문자는 제거
- */
-private fun validateInput(input: String): String {
- return input.filter { it.isLetter() || it.isWhitespace() }
-}
-
@Preview(showBackground = true)
@Composable
private fun TraineeProfileSetupPagePreview() {
diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpContract.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpContract.kt
index da2c66f6..0a978836 100644
--- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpContract.kt
+++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpContract.kt
@@ -19,11 +19,15 @@ internal class TraineeSignUpContract {
val birthday: LocalDate? = null,
val height: String? = null,
val weight: String? = null,
- val ptPurpose: List = emptyList(),
+ val ptPurpose: List? = emptyList(),
val caution: String? = "",
val isLoading: Boolean = false,
) : UiState {
- val isNameValid get() = name.length <= MAX_NAME_LENGTH
+ /**
+ * 입력 값을 검사해 한글/영어/공백만 허용하고 특수문자는 제거
+ */
+ private val nameRegex = Regex("^[a-zA-Zㄱ-ㅎㅏ-ㅣ가-힣 ]+\$")
+ val isNameValid get() = name.isBlank() || name.matches(nameRegex) && name.length <= MAX_NAME_LENGTH
/**
* 키가 유효한 입력값인지 검사
diff --git a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpViewModel.kt b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpViewModel.kt
index 0ef6aa85..2e42986e 100644
--- a/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpViewModel.kt
+++ b/feature/trainee/signup/src/main/java/co/kr/tnt/trainee/signup/TraineeSignUpViewModel.kt
@@ -115,7 +115,7 @@ internal class TraineeSignUpViewModel @Inject constructor(
}
private fun updateSelectedPurposes(purpose: String) {
- val updatedPurposes = currentState.ptPurpose.toMutableList().apply {
+ val updatedPurposes = currentState.ptPurpose.orEmpty().toMutableList().apply {
if (contains(purpose)) {
remove(purpose)
} else {
diff --git a/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TraineeProfilePage.kt b/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TraineeProfilePage.kt
index 7785722d..ed2154c2 100644
--- a/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TraineeProfilePage.kt
+++ b/feature/trainer/connect/src/main/java/co/kr/tnt/trainer/connect/TraineeProfilePage.kt
@@ -145,7 +145,7 @@ internal fun TraineeProfilePage(
}
TextWithBackground(
label = stringResource(R.string.purpose_of_pt),
- text = trainee.ptPurpose.joinToString(", "),
+ text = trainee.ptPurpose?.joinToString(", ") ?: "",
)
Spacer(Modifier.height(32.dp))
if (!trainee.caution.isNullOrEmpty()) {
diff --git a/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/TrainerHomeScreen.kt b/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/TrainerHomeScreen.kt
index 2bb50cda..983a68b5 100644
--- a/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/TrainerHomeScreen.kt
+++ b/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/TrainerHomeScreen.kt
@@ -50,6 +50,7 @@ import co.kr.tnt.designsystem.snackbar.LocalSnackbar
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.domain.model.PtSession
import co.kr.tnt.domain.utils.DateFormatter
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeSideEffect
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiEvent
import co.kr.tnt.trainer.home.TrainerHomeContract.TrainerHomeUiState
@@ -71,7 +72,7 @@ internal fun TrainerHomeRoute(
padding: PaddingValues,
navigateToNotification: () -> Unit,
navigateToAddPtSession: () -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
) {
val toast = LocalSnackbar.current
val state by viewModel.uiState.collectAsStateWithLifecycle()
@@ -91,7 +92,7 @@ internal fun TrainerHomeRoute(
when (effect) {
TrainerHomeSideEffect.NavigateToNotification -> navigateToNotification()
TrainerHomeSideEffect.NavigateToAddPtSession -> navigateToAddPtSession()
- TrainerHomeSideEffect.NavigateToInvite -> navigateToInvite(false)
+ TrainerHomeSideEffect.NavigateToInvite -> navigateToInvite(ScreenMode.CLOSE)
is TrainerHomeSideEffect.ShowToast -> toast.show(effect.message)
}
}
diff --git a/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/navigation/TrainerHomeNavigation.kt b/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/navigation/TrainerHomeNavigation.kt
index 0dc71dd2..9c693fc1 100644
--- a/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/navigation/TrainerHomeNavigation.kt
+++ b/feature/trainer/home/src/main/java/co/kr/tnt/trainer/home/navigation/TrainerHomeNavigation.kt
@@ -8,6 +8,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import androidx.navigation.navOptions
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.home.TrainerHomeRoute
fun NavController.navigateToTrainerHome(
@@ -27,7 +28,7 @@ fun NavGraphBuilder.trainerHomeNavGraph(
padding: PaddingValues,
navigateToNotification: () -> Unit,
navigateToAddPtSession: () -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
homeDestination: NavGraphBuilder.() -> Unit = { },
) {
navigation(startDestination = Route.TrainerHome) {
diff --git a/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt b/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt
index 9ca9583f..eb1963f2 100644
--- a/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt
+++ b/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/TrainerInviteScreen.kt
@@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -24,6 +26,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -37,14 +40,15 @@ import co.kr.tnt.designsystem.component.button.model.ButtonType
import co.kr.tnt.designsystem.snackbar.LocalSnackbar
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.feature.trainer.invite.R
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.invite.TrainerInviteContract.TrainerInviteSideEffect
import co.kr.tnt.trainer.invite.TrainerInviteContract.TrainerInviteUiEvent
import co.kr.tnt.trainer.invite.TrainerInviteContract.TrainerInviteUiState
-import co.kr.tnt.core.ui.R as uiResource
+import co.kr.tnt.core.ui.R as coreR
@Composable
internal fun TrainerInviteRoute(
- isSkippable: Boolean,
+ screenMode: ScreenMode,
navigateToPrevious: () -> Unit,
navigateToHome: (Boolean) -> Unit,
viewModel: TrainerInviteViewModel = hiltViewModel(),
@@ -56,7 +60,7 @@ internal fun TrainerInviteRoute(
TrainerInviteScreen(
state = state,
- isSkippable = isSkippable,
+ screenMode = screenMode,
onRegenerateClick = { viewModel.setEvent(TrainerInviteUiEvent.OnRegenerateClick) },
onCodeClick = { code -> viewModel.setEvent(TrainerInviteUiEvent.OnCodeClick(code)) },
onBackClick = { viewModel.setEvent(TrainerInviteUiEvent.OnBackClick) },
@@ -78,41 +82,61 @@ internal fun TrainerInviteRoute(
@Composable
internal fun TrainerInviteScreen(
state: TrainerInviteUiState,
- isSkippable: Boolean,
+ screenMode: ScreenMode,
onCodeClick: (code: String) -> Unit,
onRegenerateClick: () -> Unit,
onBackClick: () -> Unit,
onSkipClick: () -> Unit,
) {
BackHandler {
- if (isSkippable) {
- onSkipClick()
- } else {
- onBackClick()
+ when (screenMode) {
+ ScreenMode.BACK -> onBackClick()
+ ScreenMode.SKIP -> onSkipClick()
+ ScreenMode.CLOSE -> onBackClick()
}
}
Scaffold(
topBar = {
- if (isSkippable) {
- TnTTopBar(
- title = stringResource(uiResource.string.connect),
- trailingComponent = {
- Text(
- text = stringResource(uiResource.string.skip),
- color = TnTTheme.colors.neutralColors.Neutral400,
- style = TnTTheme.typography.body2Medium,
- modifier = Modifier.clickable {
- onSkipClick()
- },
- )
- },
- )
- } else {
- TnTTopBarWithBackButton(
- title = stringResource(R.string.add_member),
- onBackClick = onBackClick,
- )
+ when (screenMode) {
+ ScreenMode.BACK -> {
+ TnTTopBarWithBackButton(
+ title = stringResource(R.string.add_member),
+ onBackClick = onBackClick,
+ )
+ }
+
+ ScreenMode.SKIP -> {
+ TnTTopBar(
+ title = stringResource(coreR.string.connect),
+ trailingComponent = {
+ Text(
+ text = stringResource(coreR.string.skip),
+ color = TnTTheme.colors.neutralColors.Neutral400,
+ style = TnTTheme.typography.body2Medium,
+ modifier = Modifier.clickable {
+ onSkipClick()
+ },
+ )
+ },
+ )
+ }
+
+ ScreenMode.CLOSE -> {
+ TnTTopBar(
+ title = stringResource(R.string.add_member),
+ trailingComponent = {
+ IconButton(
+ onClick = onBackClick,
+ ) {
+ Icon(
+ painter = painterResource(co.kr.tnt.core.designsystem.R.drawable.ic_delete),
+ contentDescription = null,
+ )
+ }
+ },
+ )
+ }
}
},
containerColor = TnTTheme.colors.commonColors.Common0,
@@ -198,7 +222,7 @@ private fun CodeGenerationPagePreview() {
onBackClick = {},
onSkipClick = {},
onRegenerateClick = {},
- isSkippable = false,
+ screenMode = ScreenMode.SKIP,
)
}
}
diff --git a/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/navigation/TrainerInviteNavigation.kt b/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/navigation/TrainerInviteNavigation.kt
index cebaf1f2..b10019c1 100644
--- a/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/navigation/TrainerInviteNavigation.kt
+++ b/feature/trainer/invite/src/main/java/co/kr/tnt/trainer/invite/navigation/TrainerInviteNavigation.kt
@@ -6,13 +6,14 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.invite.TrainerInviteRoute
fun NavController.navigateToTrainerInvite(
- isSkippable: Boolean,
+ screenMode: ScreenMode,
navOptions: NavOptionsBuilder.() -> Unit = {},
) = navigate(
- route = Route.TrainerInvite(isSkippable),
+ route = Route.TrainerInvite(screenMode),
builder = navOptions,
)
@@ -23,7 +24,7 @@ fun NavGraphBuilder.trainerInviteScreen(
composable { backstackEntry ->
backstackEntry.toRoute().apply {
TrainerInviteRoute(
- isSkippable = isSkippable,
+ screenMode = screenMode,
navigateToPrevious = navigateToPrevious,
navigateToHome = { navigateToHome(true) },
)
diff --git a/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/TrainerMainScreen.kt b/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/TrainerMainScreen.kt
index e9ce587f..259a26a8 100644
--- a/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/TrainerMainScreen.kt
+++ b/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/TrainerMainScreen.kt
@@ -2,13 +2,13 @@ package co.kr.tnt.trainer.main
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import co.kr.tnt.designsystem.component.bottombar.TnTBottomBar
import co.kr.tnt.designsystem.theme.TnTTheme
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.addptsession.navigation.addPtSession
import co.kr.tnt.trainer.addptsession.navigation.navigateToAddPtSession
import co.kr.tnt.trainer.feedback.navigation.trainerFeedbackNavGraph
@@ -22,7 +22,7 @@ import co.kr.tnt.ui.extensions.safePopBackStack
@Composable
internal fun TrainerMainRoute(
navigateToConnect: (trainerId: String, traineeId: String) -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
) {
@@ -43,7 +43,7 @@ internal fun TrainerMainRoute(
private fun TrainerMainScreen(
state: TrainerMainState,
navigateToConnect: (trainerId: String, traineeId: String) -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
) {
diff --git a/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/navigation/TrainerMainNavigation.kt b/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/navigation/TrainerMainNavigation.kt
index 9717b57d..e3c598cc 100644
--- a/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/navigation/TrainerMainNavigation.kt
+++ b/feature/trainer/main/src/main/java/co/kr/tnt/trainer/main/navigation/TrainerMainNavigation.kt
@@ -6,6 +6,7 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navOptions
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.main.TrainerMainRoute
fun NavController.navigateToTrainerMain(
@@ -23,7 +24,7 @@ fun NavController.navigateToTrainerMain(
fun NavGraphBuilder.trainerMainScreen(
navigateToConnect: (trainerId: String, traineeId: String) -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
navigateToLogin: () -> Unit,
navigateToWebView: (url: String) -> Unit,
) {
diff --git a/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/TrainerMembersScreen.kt b/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/TrainerMembersScreen.kt
index ad5ef625..9298a7a3 100644
--- a/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/TrainerMembersScreen.kt
+++ b/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/TrainerMembersScreen.kt
@@ -31,6 +31,7 @@ import co.kr.tnt.core.designsystem.R
import co.kr.tnt.designsystem.component.card.TnTMemberProfileCard
import co.kr.tnt.designsystem.theme.TnTTheme
import co.kr.tnt.domain.model.MemberInfo
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.members.TrainerMemberContract.TrainerMemberUiState
import co.kr.tnt.ui.component.TnTCountTopBar
import coil.compose.rememberAsyncImagePainter
@@ -39,7 +40,7 @@ import coil.request.ImageRequest
@Composable
internal fun TrainerMembersRoute(
padding: PaddingValues,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
viewModel: TrainerMembersViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
@@ -47,7 +48,7 @@ internal fun TrainerMembersRoute(
TrainerMembersScreen(
state = uiState,
padding = padding,
- onClickInviteButton = { navigateToInvite(false) },
+ onClickInviteButton = { navigateToInvite(ScreenMode.BACK) },
)
}
@@ -158,7 +159,7 @@ private fun MemberList(member: MemberInfo) {
Spacer(modifier = Modifier.height(16.dp))
}
-@Preview(widthDp = 300)
+@Preview(showBackground = true, widthDp = 300)
@Composable
private fun TrainerMembersScreenPreview() {
TnTTheme {
diff --git a/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/navigation/TrainerMembersNavigation.kt b/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/navigation/TrainerMembersNavigation.kt
index 03063e85..5960fd80 100644
--- a/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/navigation/TrainerMembersNavigation.kt
+++ b/feature/trainer/members/src/main/java/co/kr/tnt/trainer/members/navigation/TrainerMembersNavigation.kt
@@ -7,6 +7,7 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.members.TrainerMembersRoute
fun NavController.navigateToTrainerMembers(
@@ -18,7 +19,7 @@ fun NavController.navigateToTrainerMembers(
fun NavGraphBuilder.trainerMembersNavGraph(
padding: PaddingValues,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
membersDestination: NavGraphBuilder.() -> Unit = { },
) {
navigation(startDestination = Route.TrainerMembers) {
diff --git a/feature/trainer/notification/src/main/java/co/kr/tnt/trainer/notification/TrainerNotificationViewModel.kt b/feature/trainer/notification/src/main/java/co/kr/tnt/trainer/notification/TrainerNotificationViewModel.kt
index 5ec719ca..199965a3 100644
--- a/feature/trainer/notification/src/main/java/co/kr/tnt/trainer/notification/TrainerNotificationViewModel.kt
+++ b/feature/trainer/notification/src/main/java/co/kr/tnt/trainer/notification/TrainerNotificationViewModel.kt
@@ -1,15 +1,12 @@
package co.kr.tnt.trainer.notification
import co.kr.tnt.domain.model.NotificationInfo
-import co.kr.tnt.domain.model.NotificationType
import co.kr.tnt.trainer.notification.TrainerNotificationContract.TrainerNotificationEffect
import co.kr.tnt.trainer.notification.TrainerNotificationContract.TrainerNotificationUiEvent
import co.kr.tnt.trainer.notification.TrainerNotificationContract.TrainerNotificationUiState
import co.kr.tnt.ui.base.BaseViewModel
import co.kr.tnt.ui.model.NotificationState
import dagger.hilt.android.lifecycle.HiltViewModel
-import java.time.LocalDateTime
-import java.time.format.DateTimeFormatter
import javax.inject.Inject
@HiltViewModel
@@ -30,31 +27,7 @@ internal class TrainerNotificationViewModel @Inject constructor() :
private fun getNotification() {
// TODO 알림 불러오기
- val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
-
- val sampleNotifications = listOf(
- NotificationInfo(
- type = NotificationType.CONNECT_COMPLETE,
- title = "트레이니 연결 완료",
- contents = "김회원 회원과 연결되었어요",
- time = LocalDateTime.parse("2025-02-03T23:59:00", formatter),
- isChecked = false,
- ),
- NotificationInfo(
- type = NotificationType.CONNECT_COMPLETE,
- title = "트레이니 연결 완료",
- contents = "김돌돌 회원과 연결되었어요",
- time = LocalDateTime.parse("2025-02-03T11:12:00", formatter),
- isChecked = false,
- ),
- NotificationInfo(
- type = NotificationType.DISCONNECT,
- title = "트레이니 연결 해제",
- contents = "박헬린 회원이 연결을 끊었어요",
- time = LocalDateTime.parse("2025-02-03T23:12:00", formatter),
- isChecked = true,
- ),
- )
+ val sampleNotifications = emptyList()
updateState { copy(notifications = sampleNotifications.map(NotificationState::fromDomain)) }
}
diff --git a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt
index a3d42c2c..65d3b764 100644
--- a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt
+++ b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerProfileSetupPage.kt
@@ -17,9 +17,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -55,10 +53,6 @@ internal fun TrainerProfileSetupPage(
val context = LocalContext.current
- val isWarning by remember(state.name) {
- derivedStateOf { state.name.length > MAX_LENGTH }
- }
-
val pickMediaLauncher = rememberLauncherForActivityResult(PickVisualMedia()) { uri ->
if (uri != null) {
onProfileImageSelect(uri)
@@ -108,14 +102,13 @@ internal fun TrainerProfileSetupPage(
title = stringResource(coreR.string.name),
value = state.name,
onValueChange = { newValue ->
- val filteredText = validateInput(newValue)
- onNameChange(filteredText)
+ onNameChange(newValue)
},
modifier = Modifier.padding(horizontal = 20.dp),
placeholder = stringResource(R.string.name_placeholder),
maxLength = MAX_LENGTH,
isSingleLine = true,
- showWarning = isWarning,
+ showWarning = state.isNameValid.not(),
isRequired = true,
warningMessage = stringResource(coreR.string.text_length_and_format_warning, MAX_LENGTH),
)
@@ -123,20 +116,13 @@ internal fun TrainerProfileSetupPage(
TnTBottomButton(
text = stringResource(coreR.string.next),
modifier = Modifier.align(Alignment.BottomCenter),
- enabled = state.name.isNotBlank() && !isWarning,
+ enabled = state.name.isNotBlank() && state.isNameValid,
onClick = onNextClick,
)
}
}
}
-/**
- * 입력 값을 검사해 한글/영어/공백만 허용하고 특수문자는 제거
- */
-private fun validateInput(input: String): String {
- return input.filter { it.isLetter() || it.isWhitespace() }
-}
-
@Preview(showBackground = true)
@Composable
private fun TrainerProfileSetupPagePreview() {
diff --git a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpContract.kt b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpContract.kt
index cde2c903..4a854edb 100644
--- a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpContract.kt
+++ b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpContract.kt
@@ -6,13 +6,21 @@ import co.kr.tnt.ui.base.UiEvent
import co.kr.tnt.ui.base.UiSideEffect
import co.kr.tnt.ui.base.UiState
+private const val MAX_LENGTH = 15
+
internal class TrainerSignUpContract {
data class TrainerSignUpUiState(
val page: TrainerSignUpPage = TrainerSignUpPage.ProfileSetUp,
val name: String = "",
val image: Uri? = null,
val isLoading: Boolean = false,
- ) : UiState
+ ) : UiState {
+ /**
+ * 입력 값을 검사해 한글/영어/공백만 허용하고 특수문자는 제거
+ */
+ private val nameRegex = Regex("^[a-zA-Zㄱ-ㅎㅏ-ㅣ가-힣 ]+\$")
+ val isNameValid get() = name.isBlank() || name.matches(nameRegex) && name.length <= MAX_LENGTH
+ }
sealed interface TrainerSignUpUiEvent : UiEvent {
data class OnImageChange(val imageUri: Uri) : TrainerSignUpUiEvent
diff --git a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpScreen.kt b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpScreen.kt
index 73f1be7a..99159da3 100644
--- a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpScreen.kt
+++ b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/TrainerSignUpScreen.kt
@@ -8,6 +8,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.kr.tnt.designsystem.snackbar.LocalSnackbar
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.signup.TrainerSignUpContract.TrainerSignUpUiEvent
import co.kr.tnt.trainer.signup.TrainerSignUpContract.TrainerSignUpUiState
@@ -17,7 +18,7 @@ internal fun TrainerSignUpRoute(
authType: String,
email: String,
navigateToPrevious: () -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
viewModel: TrainerSignUpViewModel = hiltViewModel(),
) {
val context = LocalContext.current
@@ -47,7 +48,7 @@ internal fun TrainerSignUpRoute(
viewModel.effect.collect { effect ->
when (effect) {
TrainerSignUpContract.TrainerSignUpEffect.NavigateToBack -> navigateToPrevious()
- TrainerSignUpContract.TrainerSignUpEffect.NavigateToConnect -> navigateToInvite(true)
+ TrainerSignUpContract.TrainerSignUpEffect.NavigateToConnect -> navigateToInvite(ScreenMode.SKIP)
is TrainerSignUpContract.TrainerSignUpEffect.ShowToast -> snackbar.show(effect.message)
}
}
diff --git a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/navigation/TrainerSignUpNavigation.kt b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/navigation/TrainerSignUpNavigation.kt
index d4919034..813985d6 100644
--- a/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/navigation/TrainerSignUpNavigation.kt
+++ b/feature/trainer/signup/src/main/java/co/kr/tnt/trainer/signup/navigation/TrainerSignUpNavigation.kt
@@ -6,6 +6,7 @@ import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import co.kr.tnt.navigation.Route
+import co.kr.tnt.navigation.model.ScreenMode
import co.kr.tnt.trainer.signup.TrainerSignUpRoute
fun NavController.navigateToTrainerSignUp(
@@ -24,7 +25,7 @@ fun NavController.navigateToTrainerSignUp(
fun NavGraphBuilder.trainerSignUpScreen(
navigateToPrevious: () -> Unit,
- navigateToInvite: (Boolean) -> Unit,
+ navigateToInvite: (ScreenMode) -> Unit,
) {
composable { backstackEntry ->
backstackEntry.toRoute().apply {