Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ fun MainScreen(
) {
splashNavGraph(
navigateToOnboarding = appState::navigateToOnboarding,
navigateToHome = appState::navigateToHome,
paddingValues = innerPadding
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
package com.cherrish.android.presentation.onboarding.information

import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.shape.RoundedCornerShape
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.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.ImeAction
Expand All @@ -33,7 +49,6 @@ 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 com.cherrish.android.core.common.extension.addFocusCleaner
import com.cherrish.android.core.common.extension.collectLatestSideEffect
import com.cherrish.android.core.designsystem.component.button.CherrishButton
import com.cherrish.android.core.designsystem.component.textfield.CherrishTextField
Expand Down Expand Up @@ -69,6 +84,7 @@ fun OnboardingInformationRoute(
)
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun OnboardingInformationScreen(
paddingValues: PaddingValues,
Expand All @@ -84,82 +100,144 @@ private fun OnboardingInformationScreen(
) {
val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
val density = LocalDensity.current

val nameFocusRequester = remember { FocusRequester() }
val ageFocusRequester = remember { FocusRequester() }
var isNameFocused by remember { mutableStateOf(false) }
var isAgeFocused by remember { mutableStateOf(false) }

Scaffold(
bottomBar = {
CherrishButton(
text = "다음",
onClick = onNextClick,
enabled = enabled,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 30.dp)
.background(CherrishTheme.colors.gray0)
.navigationBarsPadding()
val coroutineScope = rememberCoroutineScope()

val bringIntoViewRequester = remember { BringIntoViewRequester() }

)
LaunchedEffect(isNameFocused, isAgeFocused) {
if (isNameFocused || isAgeFocused) {
delay(300)
bringIntoViewRequester.bringIntoView()
}
) { innerPadding ->
Column(
modifier = modifier
.fillMaxSize()
.background(color = CherrishTheme.colors.gray0)
.addFocusCleaner(focusManager)
.padding(paddingValues = paddingValues)
.imePadding()
}

val listState = rememberLazyListState()
val imeBottom = WindowInsets.ime.getBottom(density)
val imeBottomDp = with(density) { imeBottom.toDp() }
val targetBottomInset = if (imeBottomDp > 0.dp) 0.dp else paddingValues.calculateBottomPadding()
val bottomInset by animateDpAsState(targetValue = targetBottomInset, label = "bottomInset")

Column(
modifier = modifier
.fillMaxSize()
.background(color = CherrishTheme.colors.gray0)
) {
Comment on lines +127 to +131
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the target file
cat -n app/src/main/java/com/cherrish/android/presentation/onboarding/information/OnboardingInformationScreen.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 15266


루트 Column에 paddingValues 적용 필수

현재 코드는 루트 Column에 .padding(paddingValues)를 적용하지 않고 있습니다. 대신 paddingValues에서 하단 패딩만 추출하여 버튼에만 적용되고 있어, 상태바와 내비게이션 바의 패딩이 제대로 처리되지 않을 수 있습니다. 루트 Column 수정자에 .padding(paddingValues)를 먼저 적용하세요.

✅ 적용 예시
     Column(
         modifier = modifier
             .fillMaxSize()
             .background(color = CherrishTheme.colors.gray0)
+            .padding(paddingValues)
     ) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Column(
modifier = modifier
.fillMaxSize()
.background(color = CherrishTheme.colors.gray0)
) {
Column(
modifier = modifier
.fillMaxSize()
.background(color = CherrishTheme.colors.gray0)
.padding(paddingValues)
) {
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/onboarding/information/OnboardingInformationScreen.kt`
around lines 127 - 131, The root Column in OnboardingInformationScreen.kt is
missing application of the system padding; apply paddingValues to the Column
modifier first (i.e., include .padding(paddingValues) on the Column's modifier
before .fillMaxSize()/.background()) so window insets (status/nav bars) are
respected rather than only applying bottom inset to the button; update the
Column modifier where Column(...) is declared to use .padding(paddingValues)
then chain the existing .fillMaxSize() and .background().

LazyColumn(
state = listState,
modifier = Modifier
.fillMaxWidth()
.weight(1f),
contentPadding = PaddingValues(bottom = 24.dp)
) {
Spacer(modifier = Modifier.weight(135f))
UserInfoHeader()
Spacer(modifier = Modifier.weight(70f))
item {
Spacer(modifier = Modifier.height(157.dp))
}

stickyHeader {
Column(
modifier = Modifier
.fillMaxWidth()
.background(CherrishTheme.colors.gray0)
) {
UserInfoHeader()
}
}

UserInfoTextField(
textFieldName = "이름",
value = username,
onValueChange = onNameChange,
placeholder = "김체리",
keyboardImeAction = ImeAction.Next,
onNextAction = { ageFocusRequester.requestFocus() },
keyboardType = KeyboardType.Text,
errorText = "이름은 최대 7자까지 입력 가능합니다.",
errorCase = nameErrorCase
)
item {
Spacer(modifier = Modifier.height(70.dp))
}

Spacer(modifier = Modifier.weight(30f))
item {
Column(
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequester)
.windowInsetsPadding(WindowInsets.ime.only(WindowInsetsSides.Bottom))
) {
UserInfoTextField(
textFieldName = "이름",
value = username,
onValueChange = onNameChange,
placeholder = "김체리",
keyboardImeAction = ImeAction.Next,
onNextAction = { ageFocusRequester.requestFocus() },
keyboardType = KeyboardType.Text,
errorText = "이름은 최대 7자까지 입력 가능합니다.",
errorCase = nameErrorCase,
textFieldModifier = Modifier
.focusRequester(nameFocusRequester)
.pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown(pass = PointerEventPass.Initial)
nameFocusRequester.requestFocus()
waitForUpOrCancellation()
}
}
.onFocusChanged { state ->
isNameFocused = state.isFocused
}
)

UserInfoTextField(
textFieldName = "나이",
value = age,
onValueChange = onAgeChange,
placeholder = "20",
keyboardImeAction = ImeAction.Done,
onDoneAction = {
keyboardController?.hide()
kotlinx.coroutines.MainScope().launch {
delay(100)
focusManager.clearFocus()
}
},
keyboardType = KeyboardType.Number,
visualTransformation = if (isAgeFocused) {
VisualTransformation.None
} else {
AgeSuffixTransformation(" 세")
},
errorText = "입력 가능한 최대 나이 100세를 초과했습니다.",
errorCase = ageErrorCase,
modifier = Modifier
.focusRequester(ageFocusRequester)
.onFocusChanged { state ->
isAgeFocused = state.isFocused
}
)
Spacer(modifier = Modifier.height(30.dp))

Spacer(modifier = Modifier.weight(200f))
UserInfoTextField(
textFieldName = "나이",
value = age,
onValueChange = onAgeChange,
placeholder = "20",
keyboardImeAction = ImeAction.Done,
onDoneAction = {
keyboardController?.hide()
coroutineScope.launch {
delay(100)
focusManager.clearFocus()
}
},
keyboardType = KeyboardType.Number,
visualTransformation = if (isAgeFocused) {
VisualTransformation.None
} else {
AgeSuffixTransformation(
" 세"
)
},
errorText = "입력 가능한 최대 나이 100세를 초과했습니다.",
errorCase = ageErrorCase,
textFieldModifier = Modifier
.focusRequester(ageFocusRequester)
.pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown(pass = PointerEventPass.Initial)
ageFocusRequester.requestFocus()
waitForUpOrCancellation()
}
}
.onFocusChanged { state ->
isAgeFocused = state.isFocused
}
)
}

Spacer(modifier = Modifier.padding(innerPadding.calculateBottomPadding()))
Spacer(modifier = Modifier.height(24.dp))
}
}

CherrishButton(
text = "다음",
onClick = onNextClick,
enabled = enabled,
modifier = Modifier
.fillMaxWidth()
.background(CherrishTheme.colors.gray0)
.padding(horizontal = 24.dp)
.padding(top = 30.dp, bottom = 30.dp + bottomInset)
)
}
}

Expand All @@ -168,7 +246,7 @@ private fun UserInfoHeader() {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 26.dp),
.padding(horizontal = 15.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
Expand All @@ -195,6 +273,7 @@ private fun UserInfoTextField(
keyboardType: KeyboardType,
errorText: String,
modifier: Modifier = Modifier,
textFieldModifier: Modifier = Modifier,
onNextAction: () -> Unit = {},
onDoneAction: () -> Unit = {},
visualTransformation: VisualTransformation = VisualTransformation.None,
Expand Down Expand Up @@ -227,7 +306,7 @@ private fun UserInfoTextField(
onDoneAction = onDoneAction,
keyboardType = keyboardType,
visualTransformation = visualTransformation,
modifier = Modifier.fillMaxWidth()
modifier = textFieldModifier.fillMaxWidth()
)

Spacer(modifier = Modifier.height(4.dp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.cherrish.android.presentation.onboarding.information
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.cherrish.android.core.common.extension.onLogFailure
import com.cherrish.android.core.local.TokenManager
import com.cherrish.android.data.model.OnboardingProfileRequestModel
import com.cherrish.android.data.repository.OnboardingProfileRepository
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -19,8 +18,7 @@ import kotlinx.coroutines.launch

@HiltViewModel
class OnboardingInformationViewModel @Inject constructor(
private val onboardingProfileRepository: OnboardingProfileRepository,
private val tokenManager: TokenManager
private val onboardingProfileRepository: OnboardingProfileRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(InformationUiState())
val uiState: StateFlow<InformationUiState> = _uiState.asStateFlow()
Expand All @@ -43,7 +41,7 @@ class OnboardingInformationViewModel @Inject constructor(
_uiState.update { it.copy(age = filtered) }
}

fun onAgeErrorCase(age: String): Boolean = age.toIntOrNull() ?.let { it > 100 } ?: false
fun onAgeErrorCase(age: String): Boolean = age.toIntOrNull()?.let { it > 100 } ?: false

fun onNextClicked() {
val age = uiState.value.age.toIntOrNull() ?: return
Expand All @@ -55,7 +53,6 @@ class OnboardingInformationViewModel @Inject constructor(
age = age
)
).onSuccess { response ->
tokenManager.saveId(response.id)
_sideEffect.emit(InformationSideEffect.NavigateToHome)
}.onLogFailure {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
Expand All @@ -19,8 +20,6 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
Expand All @@ -35,23 +34,21 @@ import kotlinx.coroutines.launch
@Composable
fun SplashRoute(
navigateToOnboarding: () -> Unit,
navigateToHome: () -> Unit,
paddingValues: PaddingValues,
viewModel: SplashViewModel = hiltViewModel()
) {
val scope = rememberCoroutineScope()

LifecycleEventEffect(Lifecycle.Event.ON_START) {
LaunchedEffect(Unit) {
scope.launch {
delay(3000)
viewModel.isAutoLoginCheck()
viewModel.navigateToOnboarding()
}
}
Comment on lines 40 to 47
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

LaunchedEffect 내부의 불필요한 scope.launch 제거 필요

LaunchedEffect는 이미 코루틴 스코프를 제공하므로, 내부에서 rememberCoroutineScope()를 통한 scope.launch는 불필요합니다. 또한 이 패턴은 컴포저블이 컴포지션을 벗어날 때 delay가 제대로 취소되지 않을 수 있습니다.

♻️ 수정 제안
-    val scope = rememberCoroutineScope()
-
-    LaunchedEffect(Unit) {
-        scope.launch {
-            delay(3000)
-            viewModel.navigateToOnboarding()
-        }
-    }
+    LaunchedEffect(Unit) {
+        delay(3000)
+        viewModel.navigateToOnboarding()
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val scope = rememberCoroutineScope()
LifecycleEventEffect(Lifecycle.Event.ON_START) {
LaunchedEffect(Unit) {
scope.launch {
delay(3000)
viewModel.isAutoLoginCheck()
viewModel.navigateToOnboarding()
}
}
LaunchedEffect(Unit) {
delay(3000)
viewModel.navigateToOnboarding()
}
🤖 Prompt for AI Agents
In `@app/src/main/java/com/cherrish/android/presentation/splash/SplashScreen.kt`
around lines 40 - 47, Remove the unnecessary rememberCoroutineScope() and the
internal scope.launch inside the LaunchedEffect in SplashScreen; LaunchedEffect
already provides a coroutine scope and cancellation, so call delay(3000)
directly inside LaunchedEffect and then invoke viewModel.navigateToOnboarding(),
and delete references to scope and rememberCoroutineScope to ensure proper
cancellation when the composable leaves composition.


viewModel.sideEffect.collectLatestSideEffect { sideEffect ->
when (sideEffect) {
SplashSideEffect.NavigateToOnboarding -> navigateToOnboarding()
SplashSideEffect.NavigateToHome -> navigateToHome()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ package com.cherrish.android.presentation.splash

sealed interface SplashSideEffect {
data object NavigateToOnboarding : SplashSideEffect
data object NavigateToHome : SplashSideEffect
}
Loading