Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
499916e
[MOD/#260] ClodyApp -> ClodyApplication으로 이름 변경
MoonsuKang May 6, 2025
35f94e7
[ADD/#260] 앱 내비게이션 경로를 sealed interface로 정의하여 type-safe한 인자 전달 구조 도입
MoonsuKang May 6, 2025
00be128
[ADD/#260] safePopBackStack 헬퍼 함수 추가
MoonsuKang May 6, 2025
4fdf85b
[ADD/#260] ClodyNavHost 컴포저블 추가(앱 전역 내비게이션 구성)
MoonsuKang May 6, 2025
5618202
[ADD/#260] ClodyAppState 클래스 및 rememberClodyAppState 함수 추가(앱의 전역 상태 관리)
MoonsuKang May 6, 2025
38808ca
[ADD/#260] ClodyApp 루트 Composable 함수 추가(앱 테마 및 네비게이션 관리)
MoonsuKang May 6, 2025
1a85489
[REFACTOR/#260] MainActivity에서 ClodyApp으로 전환하여 앱 상태 관리 및 내비게이션 통합
MoonsuKang May 6, 2025
2c7732c
[DEL/#260] 기존 네비호스트 제거
MoonsuKang May 6, 2025
ccda88c
[ADD/#260] 일기답장에 대한 상태 enum class 추가
MoonsuKang May 6, 2025
de54c49
[ADD/#260] 로그인 화면 네비게이션 구성 추가
MoonsuKang May 6, 2025
ea13105
[REFACTOR/#260] SignUpScreen -> LoginScreen으로 변경 및 패키지 분리
MoonsuKang May 6, 2025
7057285
[ADD/#260] SignUpNavigation 구성(약관 및 닉네임 화면 내비게이션)
MoonsuKang May 6, 2025
ac64cac
[REFACTOR/#260] 네비게이션 관련 패키지명 변경
MoonsuKang May 6, 2025
2007896
[ADD/#260] 알림 화면 네비게이션 구성 추가
MoonsuKang May 6, 2025
9792601
[ADD/#260] 가이드 화면 네비게이션 구성 추가 및 패키지 경로 변경
MoonsuKang May 6, 2025
ed1d672
[DEL/#260] 기존 네비게이션 구조 제거(Auth부분)
MoonsuKang May 6, 2025
3fd2b8c
[ADD/#260] 스플래시 화면 네비게이션 로직 구성 및 SplashRoute에 전달되는 인자를 최신 구조에 맞게 수정
MoonsuKang May 6, 2025
12f384e
[ADD/#260] 설정 화면 네비게이션 구성 추가
MoonsuKang May 6, 2025
9a83ecd
[DEL/#260] 기존 설정 네비게이션 구조 제거
MoonsuKang May 6, 2025
bd36712
[REFACTOR/#260] 세팅화면의 각 라우트에 전달되는 인자 수정(네비 구조 변경에 따른)
MoonsuKang May 6, 2025
ec256f9
[ADD/#260] 일기작성 네비게이션 로직 추가
MoonsuKang May 6, 2025
85fd18b
[DEL/#260] 기존 일기작성 네비게이션 구조 제거
MoonsuKang May 6, 2025
158c521
[REFACTOR/#260] 네비 구조 변경에 따른 일기작성 라우트 인자 수정
MoonsuKang May 6, 2025
7354894
[ADD/#260] 일기응답로딩 네비게이션 구조 추가
MoonsuKang May 6, 2025
e12a556
[DEL/#260] 기존 일기응답로딩 네비게이션 구조 제거
MoonsuKang May 6, 2025
6f045a4
[ADD/#260] 네비 구조 변경에 따른 일기응답로딩 화면 라우트 인자 수정
MoonsuKang May 6, 2025
d30643b
[REFACTOR/#260] 일기응답 네비게이션 구조 수정
MoonsuKang May 6, 2025
6fa98c9
[REFACTOR/#260] 네비게이션 구조 변경에 따른 라우트 인자 수정
MoonsuKang May 6, 2025
f470601
[REFACTOR/#260] 홈 네비게이션 구조 수정
MoonsuKang May 6, 2025
799bbd8
[REFACTOR/#260] 홈 화면 네비게이션 인자 수정 및 응답 상태 추가
MoonsuKang May 6, 2025
633ad43
[REFACTOR/#260] 일기 리스트 네비게이션 구조 수정
MoonsuKang May 6, 2025
a565415
[REFACTOR/#260] 네비게이션 구조 변경에 따른 일기리스트 라우트 인자 수정
MoonsuKang May 6, 2025
4e9df39
[MOVE/#260] 네비게이션 패키지 구조 변경에 따른 가이드 화면 임포트 경로 변경
MoonsuKang May 6, 2025
7045a8e
[REFACTOR/#260] 로그인 화면 내 약관 네비게이션 인자 이름 변경
MoonsuKang May 23, 2025
af48056
[REFACTOR/#260] SignUpNavigation에서 NavHostController를 NavController로 변경
MoonsuKang May 23, 2025
1166d54
[REFACTOR/#260] 내비게이션 매개변수 이름 업데이트
MoonsuKang May 23, 2025
308a9bb
[ADD/#260] Clover Dialog를 띄우는 상태 추가
MoonsuKang May 23, 2025
9adfea2
[REFACTOR/#260] DailyDiary의 replyStatus 타입을 ReplyStatus enum으로 변경
MoonsuKang May 25, 2025
cb09b1e
[REFACTOR/#260] diary 컴포넌트에서 replyStatus 타입을 ReplyStatus enum으로 리팩터링
MoonsuKang May 25, 2025
aebfde9
[REFACTOR/#260] ReplyStatus -> Domain Layer
MoonsuKang May 28, 2025
2b8d331
[FIX/#260] Ktlint formatting
MoonsuKang May 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<application
android:name=".ClodyApp"
android:name=".ClodyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

@HiltAndroidApp
class ClodyApp : Application() {
class ClodyApplication : Application() {
override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sopt.clody.data.remote.dto.response

import com.sopt.clody.domain.model.ReplyStatus
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -11,7 +12,7 @@ data class MonthlyDiaryResponseDto(
@Serializable
data class DailyDiary(
@SerialName("diaryCount") val diaryCount: Int,
@SerialName("replyStatus") val replyStatus: String,
@SerialName("replyStatus") val replyStatus: ReplyStatus,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify ReplyStatus enum values match server contract.

Changing from String to ReplyStatus enum improves type safety but could cause deserialization failures if the server sends values not defined in the enum.

Please run the following script to verify that all possible server replyStatus values are covered by the ReplyStatus enum:


🏁 Script executed:

#!/bin/bash
# Description: Check ReplyStatus enum constants and verify they match server usage
# Expected: Find enum constants and check against codebase usage

# Find the ReplyStatus enum definition
ast-grep --pattern 'enum class ReplyStatus {
  $$$
}'

# Search for any hardcoded replyStatus strings that might indicate server values
rg -i "replyStatus.*=.*\"" -A 2 -B 2

# Look for ReplyStatus usage in tests that might show expected values
rg "ReplyStatus\." -A 2 -B 2 --type kt

Length of output: 3172


🏁 Script executed:

#!/bin/bash
# Locate and inspect the ReplyStatus enum definition
echo "Searching for ReplyStatus enum file..."
enum_files=$(rg -l "enum class ReplyStatus" -n .)

if [ -z "$enum_files" ]; then
  echo "⚠️ No ReplyStatus enum definition found."
  exit 1
fi

echo "Found ReplyStatus enum in:"
echo "$enum_files"
echo

for file in $enum_files; do
  echo "=== $file ==="
  # Print the first 200 lines to capture the enum constants and annotations
  sed -n '1,200p' "$file"
  echo
  echo "SerialName annotations in $file:"
  rg "@SerialName" -n "$file" || echo "  (none found)"
  echo
  echo "Checking for expected constants UNREADY and READY_NOT_READ:"
  grep -E "UNREADY|READY_NOT_READ" "$file" || echo "  (one or both constants missing)"
  echo
done

Length of output: 1704


ReplyStatus enum covers current server values – update usages and add fallback

The ReplyStatus enum defines UNREADY, READY_READ, and READY_NOT_READ, which matches the JSON values the server currently returns (kotlinx.serialization will use the constant names by default since there are no @SerialName annotations).

However, to ensure robustness and consistency across the app:

  • Guard against unknown values
    • Consider adding @SerialName on each enum constant for explicit mapping, or introduce a fallback (e.g., an UNKNOWN constant with @SerialName annotations) to avoid runtime failures if the server contract evolves.
  • Migrate remaining String-based comparisons to the enum
    • app/src/main/java/com/sopt/clody/presentation/ui/type/DiaryCloverType.kt – replace diaryData.replyStatus == "…" with diaryData.replyStatus == ReplyStatus.…
    • app/src/main/java/com/sopt/clody/presentation/ui/home/screen/HomeViewModel.kt – change _replyStatus: MutableStateFlow<String> to MutableStateFlow<ReplyStatus> (and its public StateFlow)
    • app/src/main/java/com/sopt/clody/presentation/ui/home/calendar/component/DayItem.kt – update the if (diaryData.replyStatus == "…") checks to use ReplyStatus

These changes will preserve type safety, prevent silent deserialization errors, and keep the UI logic in sync with the new enum-based model.

🤖 Prompt for AI Agents
In
app/src/main/java/com/sopt/clody/data/remote/dto/response/MonthlyDiaryResponseDto.kt
at line 15, the ReplyStatus enum is used for replyStatus but lacks @SerialName
annotations and a fallback for unknown values. Add @SerialName annotations to
each enum constant to explicitly map JSON values, and introduce an UNKNOWN enum
constant with a matching @SerialName to handle unexpected server values safely.
Then, update all code using replyStatus string comparisons in
DiaryCloverType.kt, HomeViewModel.kt, and DayItem.kt to use the ReplyStatus enum
instead, including changing MutableStateFlow<String> to
MutableStateFlow<ReplyStatus> where applicable, ensuring consistent type-safe
usage across the app.

@SerialName("date") val date: String,
@SerialName("diary") val diary: List<DailyDiaryContent>,
@SerialName("isDeleted") val isDeleted: Boolean,
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/com/sopt/clody/domain/model/ReplyStatus.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sopt.clody.domain.model

import kotlinx.serialization.Serializable

@Serializable
enum class ReplyStatus {
UNREADY, READY_READ, READY_NOT_READ
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sopt.clody.presentation.ui.auth.screen
package com.sopt.clody.presentation.ui.auth.guide

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
Expand Down Expand Up @@ -38,7 +38,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sopt.clody.R
import com.sopt.clody.presentation.ui.auth.navigation.AuthNavigator
import com.sopt.clody.presentation.ui.component.button.ClodyButton
import com.sopt.clody.presentation.utils.extension.heightForScreenPercentage
import com.sopt.clody.ui.theme.ClodyTheme
Expand All @@ -47,9 +46,9 @@ import kotlinx.coroutines.launch

@Composable
fun GuideRoute(
navigator: AuthNavigator,
navigateToHome: () -> Unit,
) {
GuideScreen(onNextButtonClick = { navigator.navigateHome() })
GuideScreen(onNextButtonClick = navigateToHome)
}

@OptIn(ExperimentalFoundationApi::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.sopt.clody.presentation.ui.auth.guide.navigation

import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import com.sopt.clody.presentation.ui.auth.guide.GuideRoute
import com.sopt.clody.presentation.utils.navigation.Route

fun NavGraphBuilder.guideScreen(
navigateToHome: () -> Unit,
) {
composable<Route.Guide> {
GuideRoute(
navigateToHome = navigateToHome,
)
}
}

fun NavController.navigateToGuide(
navOptions: NavOptionsBuilder.() -> Unit = {},
) {
navigate(Route.Guide, navOptions)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sopt.clody.presentation.ui.auth.signup
package com.sopt.clody.presentation.ui.auth.login

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
Expand All @@ -25,42 +25,37 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.sopt.clody.R
import com.sopt.clody.presentation.ui.auth.component.button.KaKaoButton
import com.sopt.clody.presentation.ui.auth.navigation.AuthNavigator
import com.sopt.clody.presentation.ui.auth.signup.SignUpViewModel
import com.sopt.clody.presentation.ui.component.LoadingScreen
import com.sopt.clody.presentation.utils.base.UiState
import com.sopt.clody.presentation.utils.extension.heightForScreenPercentage
import com.sopt.clody.ui.theme.ClodyTheme

@Composable
fun SignUpRoute(
authNavigator: AuthNavigator,
fun LoginRoute(
navigateToTermsOfService: () -> Unit,
navigateToHome: () -> Unit,
) {
val viewModel: SignUpViewModel = hiltViewModel()
val signInState by viewModel.signInState.collectAsState()
val context = LocalContext.current

LaunchedEffect(signInState) {
LaunchedEffect(signInState.uiState) {
when (signInState.uiState) {
is UiState.Success -> {
authNavigator.navigateHome()
}

is UiState.Failure -> {
authNavigator.navigateTermsOfService()
}

else -> {}
is UiState.Success -> navigateToHome()
is UiState.Failure -> navigateToTermsOfService()
else -> Unit
}
}

SignUpScreen(
LoginScreen(
isLoading = signInState.uiState is UiState.Loading,
onSignInClick = { viewModel.signInWithKakao(context) },
)
}

@Composable
fun SignUpScreen(
fun LoginScreen(
isLoading: Boolean,
onSignInClick: () -> Unit,
) {
Expand All @@ -85,34 +80,33 @@ fun SignUpScreen(
.padding(bottom = 40.dp),
)
},
content = { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.background(color = backgroundColor)
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.heightForScreenPercentage(0.38f))
Image(
painter = painterResource(id = R.drawable.ic_signup_logo),
contentDescription = null,
contentScale = ContentScale.Crop,
)
Spacer(modifier = Modifier.heightForScreenPercentage(0.02f))
Image(
painter = painterResource(id = R.drawable.ic__signup_title),
contentDescription = null,
)
Spacer(modifier = Modifier.heightForScreenPercentage(0.01f))
Image(
painter = painterResource(id = R.drawable.ic_signup_logotitle),
contentDescription = null,
contentScale = ContentScale.Crop,
)
}
},
)
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.background(color = backgroundColor)
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.heightForScreenPercentage(0.38f))
Image(
painter = painterResource(id = R.drawable.ic_signup_logo),
contentDescription = null,
contentScale = ContentScale.Crop,
)
Spacer(modifier = Modifier.heightForScreenPercentage(0.02f))
Image(
painter = painterResource(id = R.drawable.ic__signup_title),
contentDescription = null,
)
Spacer(modifier = Modifier.heightForScreenPercentage(0.01f))
Image(
painter = painterResource(id = R.drawable.ic_signup_logotitle),
contentDescription = null,
contentScale = ContentScale.Crop,
)
}
}

if (isLoading) {
LoadingScreen()
Expand All @@ -121,8 +115,8 @@ fun SignUpScreen(

@Preview(showBackground = true)
@Composable
fun RegisterScreenPreview() {
SignUpScreen(
fun LoginScreenPreview() {
LoginScreen(
isLoading = false,
onSignInClick = {},
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sopt.clody.presentation.ui.auth.signup
package com.sopt.clody.presentation.ui.auth.login

import com.sopt.clody.presentation.utils.base.UiState

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sopt.clody.presentation.ui.auth.login.navigation

import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.compose.composable
import com.sopt.clody.presentation.ui.auth.login.LoginRoute
import com.sopt.clody.presentation.utils.navigation.Route

fun NavGraphBuilder.loginScreen(
navigateToTermsOfService: () -> Unit,
navigateToHome: () -> Unit,
) {
composable<Route.Login> {
LoginRoute(
navigateToTermsOfService = navigateToTermsOfService,
navigateToHome = navigateToHome,
)
}
}

fun NavController.navigateToLogin(
navOptions: NavOptionsBuilder.() -> Unit = {},
) {
navigate(Route.Login, navOptions)
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sopt.clody.presentation.ui.auth.screen
package com.sopt.clody.presentation.ui.auth.signup

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
Expand Down Expand Up @@ -41,8 +41,6 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.sopt.clody.R
import com.sopt.clody.presentation.ui.auth.component.textfield.NickNameTextField
import com.sopt.clody.presentation.ui.auth.navigation.AuthNavigator
import com.sopt.clody.presentation.ui.auth.signup.SignUpViewModel
import com.sopt.clody.presentation.ui.component.LoadingScreen
import com.sopt.clody.presentation.ui.component.button.ClodyButton
import com.sopt.clody.presentation.ui.component.dialog.FailureDialog
Expand All @@ -52,7 +50,8 @@ import com.sopt.clody.ui.theme.ClodyTheme

@Composable
fun NicknameRoute(
navigator: AuthNavigator,
navigateToReminder: () -> Unit,
navigateToPrevious: () -> Unit,
viewModel: SignUpViewModel = hiltViewModel(),
) {
val nickname by viewModel.nickname.collectAsState()
Expand All @@ -67,7 +66,7 @@ fun NicknameRoute(
nickname = nickname,
onNicknameChange = viewModel::setNickname,
onCompleteClick = { viewModel.proceedWithSignUp(context) },
onBackClick = { navigator.navigateBack() },
onBackClick = navigateToPrevious,
isLoading = signUpState.uiState is UiState.Loading,
isValidNickname = isValidNickname,
nicknameMessage = nicknameMessage,
Expand All @@ -76,7 +75,7 @@ fun NicknameRoute(
LaunchedEffect(signUpState) {
when (val result = signUpState.uiState) {
is UiState.Success -> {
navigator.navigateTimeReminder()
navigateToReminder()
}

is UiState.Failure -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.sopt.clody.data.remote.dto.request.SignUpRequestDto
import com.sopt.clody.data.remote.util.NetworkUtil
import com.sopt.clody.domain.repository.AuthRepository
import com.sopt.clody.domain.repository.TokenRepository
import com.sopt.clody.presentation.ui.auth.login.SignInState
import com.sopt.clody.presentation.utils.base.UiState
import com.sopt.clody.presentation.utils.network.ErrorMessages.FAILURE_NETWORK_MESSAGE
import com.sopt.clody.presentation.utils.network.ErrorMessages.FAILURE_TEMPORARY_MESSAGE
Expand Down
Loading