diff --git a/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysNextButton.kt b/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysNextButton.kt index 5b468aa..3535057 100644 --- a/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysNextButton.kt +++ b/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysNextButton.kt @@ -22,7 +22,8 @@ fun DaysNextButton( message: String = "다음", type: BtnType = BtnType.Tall, isEnabled: Boolean = false, - onClick: () -> Unit + onDisabledClick: () -> Unit = {}, + onEnabledClick: () -> Unit ) { val buttonHeight = when (type) { BtnType.Tall -> 90.dp @@ -30,12 +31,14 @@ fun DaysNextButton( } Button( - onClick = onClick, - enabled = isEnabled, + onClick = { + if (isEnabled) onEnabledClick() else onDisabledClick() + }, + enabled = true, shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp), colors = ButtonColors( - containerColor = DaysTheme.colors.grey500, - contentColor = DaysTheme.colors.white, + containerColor = if (isEnabled) DaysTheme.colors.grey500 else DaysTheme.colors.grey100, + contentColor = if (isEnabled) DaysTheme.colors.white else DaysTheme.colors.white, disabledContainerColor = DaysTheme.colors.grey100, disabledContentColor = DaysTheme.colors.white, ), diff --git a/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysOnlyBackAppbar.kt b/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysOnlyBackAppbar.kt index 99e2890..779ada7 100644 --- a/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysOnlyBackAppbar.kt +++ b/3days/core/design-system/src/main/java/com/weave/design_system/component/DaysOnlyBackAppbar.kt @@ -3,7 +3,7 @@ package com.weave.design_system.component import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api @@ -31,24 +31,20 @@ fun DaysOnlyBackAppbar( TopAppBar( modifier = Modifier .fillMaxWidth() - .height(54.dp), + .heightIn(min = 54.dp), title = { Text(text = "") }, navigationIcon = { - Box(modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.CenterStart) - { - Box( - modifier = Modifier - .size(48.dp, 48.dp) - .padding(start = 6.dp, top = 8.dp) - .noRippleClickable(onBackPressed), - ) { - Icon( - modifier = Modifier.size(24.dp, 24.dp), - imageVector = ImageVector.vectorResource(id = R.drawable.ic_round_arrow_back), - contentDescription = "Back" - ) - } + Box( + modifier = Modifier + .size(48.dp, 48.dp) + .noRippleClickable(onBackPressed), + contentAlignment = Alignment.Center + ) { + Icon( + modifier = Modifier.size(24.dp, 24.dp), + imageVector = ImageVector.vectorResource(id = R.drawable.ic_round_arrow_back), + contentDescription = "Back" + ) } }, colors = TopAppBarDefaults.topAppBarColors().copy( diff --git a/3days/core/design-system/src/main/res/drawable/ic_question_mark.xml b/3days/core/design-system/src/main/res/drawable/ic_question_mark.xml new file mode 100644 index 0000000..58b47a3 --- /dev/null +++ b/3days/core/design-system/src/main/res/drawable/ic_question_mark.xml @@ -0,0 +1,12 @@ + + + + diff --git a/3days/core/design-system/src/main/res/drawable/ic_reading_glasses.png b/3days/core/design-system/src/main/res/drawable/ic_reading_glasses.png new file mode 100644 index 0000000..c45028b Binary files /dev/null and b/3days/core/design-system/src/main/res/drawable/ic_reading_glasses.png differ diff --git a/3days/core/design-system/src/main/res/values/strings.xml b/3days/core/design-system/src/main/res/values/strings.xml index 8d84561..ee76104 100644 --- a/3days/core/design-system/src/main/res/values/strings.xml +++ b/3days/core/design-system/src/main/res/values/strings.xml @@ -69,4 +69,13 @@ 만나서 반가워요! 당신의 성별을 무엇인가요? + 성별을 선택해 주세요 + + 좋은 %s분 소개시켜 드릴께요! + 당신의 나이는 무엇인가요? + 년생 + 가입 연령 확인하기 + %s년 기준으로 %s년생(만 20살)부터 %s년생(만 35살)까지 가입할 수 있어요 + 나이를 입력해 주세요 + 가입이 불가한 연령이예요 diff --git a/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt b/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt index e5936da..254d7e4 100644 --- a/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt +++ b/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt @@ -101,7 +101,7 @@ fun MobileSendAuthScreen( message = stringResource(id = R.string.next_button_message), type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, isEnabled = isEnabled, - onClick = { onNextBtnClicked(phoneNumber.text) }, + onEnabledClick = { onNextBtnClicked(phoneNumber.text) }, modifier = Modifier .align(Alignment.BottomCenter) .padding( diff --git a/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt b/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt index cf55147..6ad4d3c 100644 --- a/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt +++ b/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt @@ -73,7 +73,7 @@ fun TermsAgreementScreen( message = stringResource(id = R.string.next_button_message), type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, isEnabled = true, - onClick = { onNextBtnClicked() }, + onEnabledClick = { onNextBtnClicked() }, modifier = Modifier .align(Alignment.BottomCenter) .padding( diff --git a/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt b/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt index d33379f..4f09b2f 100644 --- a/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt +++ b/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.weave.my_profile -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/3days/feat/my-profile/src/main/AndroidManifest.xml b/3days/feat/my-profile/src/main/AndroidManifest.xml index a5918e6..44008a4 100644 --- a/3days/feat/my-profile/src/main/AndroidManifest.xml +++ b/3days/feat/my-profile/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileBirthYearScreen.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileBirthYearScreen.kt new file mode 100644 index 0000000..249a1ad --- /dev/null +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileBirthYearScreen.kt @@ -0,0 +1,379 @@ +package com.weave.my_profile + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TooltipState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusManager +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.key.Key +import androidx.compose.ui.input.key.KeyEvent +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.weave.design_system.DaysTheme +import com.weave.design_system.R +import com.weave.design_system.component.BtnType +import com.weave.design_system.component.DaysNextButton +import com.weave.design_system.component.DaysOnlyBackAppbar +import com.weave.design_system.component.DaysSnackBar +import com.weave.design_system.component.DaysStepIndicator +import com.weave.design_system.component.SnackBarType +import com.weave.design_system.component.tooltip.DaysTooltip +import com.weave.design_system.component.tooltip.TooltipDirection +import com.weave.design_system.extension.addFocusCleaner +import com.weave.design_system.extension.noRippleClickable +import com.weave.utils.Keyboard +import com.weave.utils.keyboardAsState +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.time.Year + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MyProfileBirthYearScreen( + sharedViewModel: MyProfileSharedViewModel = hiltViewModel(), + onBackBtnClicked: () -> Unit, + onNextBtnClicked: () -> Unit +) { + val focusManager = LocalFocusManager.current + val isKeyboardVisible by keyboardAsState() + var isEnabled by remember { mutableStateOf(sharedViewModel.birthYear.all { it.isNotEmpty() }) } + val snackState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val noInputMessage = stringResource(id = R.string.my_profile_birth_year_empty_input_message) + val tooltipState = remember { TooltipState() } + + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + DaysOnlyBackAppbar(onBackPressed = onBackBtnClicked) + }, + snackbarHost = { + SnackbarHost( + modifier = Modifier + .padding(bottom = 110.dp) + .imePadding(), + hostState = snackState, + ) { snackData -> + DaysSnackBar( + message = snackData.visuals.message, + type = if (snackData.visuals.actionLabel == SnackBarType.DEFAULT.toString()) SnackBarType.DEFAULT else SnackBarType.ERROR + ) + } + } + ) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .addFocusCleaner(focusManager) + .padding( + top = innerPadding.calculateTopPadding(), + ) + ) { + Image( + modifier = Modifier + .fillMaxSize() + .background(DaysTheme.colors.bgDefault), + painter = painterResource(id = R.drawable.texture_bg), + contentDescription = stringResource(id = R.string.background_description) + ) + + Column( + modifier = Modifier + .matchParentSize() + .padding(horizontal = 26.dp) + ) { + Spacer(modifier = Modifier.height(12.dp)) + + DaysStepIndicator(currentStep = 2, totalStep = 5) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = stringResource(id = R.string.my_profile_birth_year_sub_title, if(sharedViewModel.genderState == "남성") "여성" else "남성"), + style = DaysTheme.typography.regular14.toTextStyle(), + color = DaysTheme.colors.grey200 + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = stringResource(id = R.string.my_profile_birth_year_title), + style = DaysTheme.typography.semiBold24.toTextStyle(), + color = DaysTheme.colors.grey500 + ) + + Spacer(modifier = Modifier.height(40.dp)) + + BirthYearInputRow( + focusManager = focusManager, + birthYear = sharedViewModel.birthYear, + onNumChanged = { index, newValue -> + sharedViewModel.birthYear[index] = newValue + isEnabled = sharedViewModel.birthYear.all { it.isNotEmpty() } + }, + unSupportedYear = false + ) + + Spacer(modifier = Modifier.height(24.dp)) + + DaysTooltip( + tooltipState = tooltipState, + direction = TooltipDirection.Top, + tooltipText = boldBirthYearMessage(), + content = { + Row( + modifier = Modifier + .fillMaxWidth() + .noRippleClickable { + scope.launch { tooltipState.show() } + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + modifier = Modifier.size(18.dp), + painter = painterResource(id = R.drawable.ic_question_mark), + tint = DaysTheme.colors.grey200, + contentDescription = "" + ) + + Spacer(modifier = Modifier.width(4.dp)) + + Text( + text = stringResource(id = R.string.my_profile_birth_year_description), + style = DaysTheme.typography.regular14.toTextStyle(), + color = DaysTheme.colors.grey200 + ) + } + + } + ) + } + + DaysNextButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding( + bottom = if (isKeyboardVisible == Keyboard.Closed) innerPadding.calculateBottomPadding() else 0.dp + ), + message = stringResource(id = R.string.next_button_message), + type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, + isEnabled = isEnabled, + onEnabledClick = onNextBtnClicked, + onDisabledClick = { + scope.launch { + val job = launch { + snackState.showSnackbar( + message = noInputMessage, + actionLabel = SnackBarType.ERROR.toString(), + duration = SnackbarDuration.Indefinite + ) + } + delay(3000L) + job.cancel() + } + } + ) + } + } +} + +fun boldBirthYearMessage(): AnnotatedString { + val currentYear = Year.now().value + val twentyYearsOld = currentYear - 20 + val thirtyFiveYearsOld = currentYear - 35 + + return buildAnnotatedString { + append("${currentYear}년 기준으로 ") + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + append("${twentyYearsOld}년생(만 20살)부터 ${thirtyFiveYearsOld}년생(만 35살) ") + } + append("까지 가입할 수 있어요") + } +} + +@Composable +fun BirthYearInputRow( + modifier: Modifier = Modifier, + focusManager: FocusManager, + birthYear: SnapshotStateList, + onNumChanged: (Int, String) -> Unit, + unSupportedYear: Boolean +) { + val focusRequesters = List(4) { FocusRequester() } + val isFocused = remember { mutableIntStateOf(-1) } + val showHint = birthYear.all { it.isEmpty() } + + Row( + modifier = modifier + .fillMaxWidth() + .aspectRatio(314f / 92f), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.Bottom + ) { + birthYear.forEachIndexed { index, _ -> + BasicTextField( + modifier = Modifier + .weight(1f) + .fillMaxSize() + .background( + color = if (unSupportedYear) DaysTheme.colors.pink50 else DaysTheme.colors.yellow50, + shape = RoundedCornerShape(20.dp) + ) + .border( + width = 4.dp, + color = if (unSupportedYear) { + DaysTheme.colors.red300 + } else { + if (isFocused.intValue == index) Color(0xFFDFDBA5) else DaysTheme.colors.white + }, + shape = RoundedCornerShape(20.dp) + ) + .focusRequester(focusRequesters[index]) + .onFocusChanged { focusState -> + if (focusState.isFocused) { + isFocused.intValue = index + onNumChanged(index, "") + } else if (isFocused.intValue == index) { + isFocused.intValue = -1 + } + } + .onKeyEvent { event: KeyEvent -> + if (event.key == Key.Backspace) { + if (birthYear[index].isEmpty()) { + if (index > 0) { + focusRequesters[index - 1].requestFocus() + } + } + return@onKeyEvent true + } + false + }, + value = birthYear[index], + onValueChange = { newValue -> + if (newValue.length == 1 && newValue.all { it.isDigit() }) { + onNumChanged(index, newValue) + + val nextIdx = index + 1 + if (nextIdx < birthYear.size) { + focusRequesters[nextIdx].requestFocus() + } else if (birthYear.all { it.isNotBlank() }) { + focusManager.clearFocus() + } + } + }, + textStyle = DaysTheme.typography.semiBold28.copy( + fontSize = 40.dp, lineHeight = 60.dp, textAlign = TextAlign.Center + ).toTextStyle(), + singleLine = true, + maxLines = 1, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + decorationBox = { innerTextField -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + if (showHint) { + Text( + modifier = Modifier.fillMaxWidth(), + text = "2000"[index].toString(), + style = DaysTheme.typography.semiBold28.copy( + color = Color(0xFFF1EFCC), + fontSize = 40.dp, + lineHeight = 60.dp, + textAlign = TextAlign.Center + ).toTextStyle() + ) + } + innerTextField() + } + } + ) + } + + Text( + modifier = Modifier.padding(bottom = 4.dp), + text = "년생", + style = DaysTheme.typography.semiBold18.toTextStyle(), + color = DaysTheme.colors.grey300 + ) + } +} + +@Preview(showBackground = true) +@Composable +fun BirthYearInputRowPreview() { + val focusManager = LocalFocusManager.current + val birthYear = remember { mutableStateListOf("", "", "", "") } + + val onNumChanged: (Int, String) -> Unit = { index, newValue -> + birthYear[index] = newValue + } + + BirthYearInputRow( + focusManager = focusManager, + birthYear = birthYear, + onNumChanged = onNumChanged, + unSupportedYear = false + ) +} + +@Preview +@Composable +fun MyProfileBirthYearScreenPreview() { + MyProfileBirthYearScreen( + onNextBtnClicked = {}, + onBackBtnClicked = {} + ) +} \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt index acb17b3..139d4af 100644 --- a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt @@ -7,13 +7,18 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,31 +26,54 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import com.weave.design_system.DaysTheme import com.weave.design_system.R import com.weave.design_system.component.BtnType import com.weave.design_system.component.DaysGenderSelector import com.weave.design_system.component.DaysNextButton import com.weave.design_system.component.DaysOnlyBackAppbar +import com.weave.design_system.component.DaysSnackBar import com.weave.design_system.component.DaysStepIndicator +import com.weave.design_system.component.SnackBarType import com.weave.utils.Keyboard import com.weave.utils.keyboardAsState +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable fun MyProfileGenderScreen( + sharedViewModel: MyProfileSharedViewModel = hiltViewModel(), onBackBtnClicked: () -> Unit, onNextBtnClicked: () -> Unit ) { val isKeyboardVisible by keyboardAsState() - var isEnabled by remember { mutableStateOf(false) } - var genderState by remember { mutableStateOf("") } + var isEnabled by remember { mutableStateOf(sharedViewModel.genderState.isNotEmpty()) } + val snackState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val noSelectedMessage = stringResource(id = R.string.my_profile_gender_no_selected_message) Scaffold( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .imePadding(), topBar = { DaysOnlyBackAppbar(onBackPressed = onBackBtnClicked) + }, + snackbarHost = { + SnackbarHost( + modifier = Modifier + .padding(bottom = 110.dp) + .imePadding(), + hostState = snackState, + ) { snackData -> + DaysSnackBar( + message = snackData.visuals.message, + type = if (snackData.visuals.actionLabel == SnackBarType.DEFAULT.toString()) SnackBarType.DEFAULT else SnackBarType.ERROR + ) + } } - ) { innerPadding -> + ) { innerPadding -> Box( modifier = Modifier .fillMaxSize() @@ -89,24 +117,37 @@ fun MyProfileGenderScreen( Spacer(modifier = Modifier.height(40.dp)) DaysGenderSelector( - genderState = genderState, + genderState = sharedViewModel.genderState, onChangedGender = { newValue -> - genderState = newValue - isEnabled = genderState.isNotEmpty() + sharedViewModel.genderState = newValue + isEnabled = sharedViewModel.genderState.isNotEmpty() } ) } DaysNextButton( - message = stringResource(id = R.string.next_button_message), - type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, - isEnabled = isEnabled, - onClick = { onNextBtnClicked() }, modifier = Modifier .align(Alignment.BottomCenter) .padding( bottom = if (isKeyboardVisible == Keyboard.Closed) innerPadding.calculateBottomPadding() else 0.dp - ) + ), + message = stringResource(id = R.string.next_button_message), + type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, + isEnabled = isEnabled, + onEnabledClick = onNextBtnClicked, + onDisabledClick = { + scope.launch { + val job = launch { + snackState.showSnackbar( + message = noSelectedMessage, + actionLabel = SnackBarType.ERROR.toString(), + duration = SnackbarDuration.Indefinite + ) + } + delay(3000L) + job.cancel() + } + } ) } } @@ -114,7 +155,7 @@ fun MyProfileGenderScreen( @Preview @Composable -fun MyProfileGenderScreenPreview(){ +fun MyProfileGenderScreenPreview() { MyProfileGenderScreen( onNextBtnClicked = {}, onBackBtnClicked = {} diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt index 183fc93..8f50585 100644 --- a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt @@ -2,18 +2,24 @@ package com.weave.my_profile import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -21,31 +27,26 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.weave.design_system.DaysTheme import com.weave.design_system.R -import com.weave.design_system.component.BtnType -import com.weave.design_system.component.DaysNextButton -import com.weave.design_system.component.DaysOnlyBackAppbar -import com.weave.utils.Keyboard -import com.weave.utils.keyboardAsState +import kotlinx.coroutines.delay @Composable fun MyProfileInitScreen( - onBackBtnClicked: () -> Unit, onNextBtnClicked: () -> Unit ) { - val isKeyboardVisible by keyboardAsState() + val screenHeight = + LocalDensity.current.run { androidx.compose.ui.platform.LocalContext.current.resources.displayMetrics.heightPixels.toDp() } + + LaunchedEffect(Unit) { + delay(3000) + onNextBtnClicked() + } Scaffold( modifier = Modifier.fillMaxSize(), - topBar = { - DaysOnlyBackAppbar(onBackPressed = onBackBtnClicked) - } ) { innerPadding -> Box( modifier = Modifier .fillMaxSize() - .padding( - top = innerPadding.calculateTopPadding(), - ) ) { Image( modifier = Modifier @@ -55,30 +56,39 @@ fun MyProfileInitScreen( contentDescription = stringResource(id = R.string.background_description) ) + + Box( + modifier = Modifier + .fillMaxWidth() + .height(screenHeight / 2) + .align(Alignment.BottomCenter) + .background( + brush = Brush.verticalGradient( + colors = listOf(Color(0x00F3DDE5), Color(0xFFF3DDE5)) + ) + ) + ) + Column( - modifier = Modifier.matchParentSize(), - horizontalAlignment = Alignment.CenterHorizontally + modifier = Modifier + .matchParentSize() + .padding(bottom = innerPadding.calculateBottomPadding()), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center ) { - Spacer(modifier = Modifier.height(54.dp)) + Image( + modifier = Modifier.size(48.dp), + painter = painterResource(id = R.drawable.ic_reading_glasses), + contentDescription = "" + ) + Spacer(modifier = Modifier.height(24.dp)) Text( text = stringResource(id = R.string.my_profile_init_title), - style = DaysTheme.typography.semiBold24.toTextStyle(), + style = DaysTheme.typography.semiBold20.toTextStyle(), color = DaysTheme.colors.grey500, textAlign = TextAlign.Center ) } - - DaysNextButton( - message = stringResource(id = R.string.next_button_message), - type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, - isEnabled = true, - onClick = { onNextBtnClicked() }, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding( - bottom = if (isKeyboardVisible == Keyboard.Closed) innerPadding.calculateBottomPadding() else 0.dp - ) - ) } } } @@ -87,7 +97,6 @@ fun MyProfileInitScreen( @Composable fun MyProfileInitScreenPreview() { MyProfileInitScreen( - onBackBtnClicked = {}, onNextBtnClicked = {} ) } \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt index bba2e16..3ae67c5 100644 --- a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt @@ -1,5 +1,10 @@ package com.weave.my_profile +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.ViewModel +import androidx.navigation.NavBackStackEntry import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -9,15 +14,35 @@ fun NavGraphBuilder.navGraphMyProfile(navController: NavController) { navigation(startDestination = "my_profile_init", route = "my_profile") { composable("my_profile_init") { MyProfileInitScreen( - onBackBtnClicked = { navController.popBackStack() }, onNextBtnClicked = { navController.navigate("my_profile_gender") }, ) } composable("my_profile_gender") { + val sharedViewModel = it.sharedViewModel(navController = navController) + MyProfileGenderScreen( + sharedViewModel = sharedViewModel, + onBackBtnClicked = { navController.popBackStack() }, + onNextBtnClicked = { navController.navigate("my_profile_birth") } + ) + } + composable("my_profile_birth") { + val sharedViewModel = it.sharedViewModel(navController = navController) + + MyProfileBirthYearScreen( + sharedViewModel = sharedViewModel, onBackBtnClicked = { navController.popBackStack() }, - onNextBtnClicked = { navController.navigate("my_profile_age") } + onNextBtnClicked = { navController.navigate("next_screen") } ) } } +} + +@Composable +inline fun NavBackStackEntry.sharedViewModel(navController: NavController): T { + val navGraphRoute = destination.parent?.route ?: return hiltViewModel() + val parentEntry = remember(this) { + navController.getBackStackEntry(navGraphRoute) + } + return hiltViewModel(parentEntry) } \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileSharedViewModel.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileSharedViewModel.kt new file mode 100644 index 0000000..c5e7197 --- /dev/null +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileSharedViewModel.kt @@ -0,0 +1,18 @@ +package com.weave.my_profile + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class MyProfileSharedViewModel @Inject constructor( + +): ViewModel() { + + var genderState by mutableStateOf("") + val birthYear = mutableStateListOf("", "", "", "") +} \ No newline at end of file diff --git a/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt b/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt index 07019af..d0b6243 100644 --- a/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt +++ b/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.weave.my_profile +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). *