diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7bc34188..03a2ac03 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ android:name=".MainActivity" android:exported="true" android:label="@string/app_name" + android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.Pokit"> diff --git a/app/src/main/java/pokitmons/pokit/MainActivity.kt b/app/src/main/java/pokitmons/pokit/MainActivity.kt index 1d21a571..73948740 100644 --- a/app/src/main/java/pokitmons/pokit/MainActivity.kt +++ b/app/src/main/java/pokitmons/pokit/MainActivity.kt @@ -4,14 +4,14 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import pokitmons.pokit.core.ui.theme.PokitTheme -import pokitmons.pokit.login.LoginScreen +import pokitmons.pokit.navigation.LoginNavHost class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PokitTheme { - LoginScreen() + LoginNavHost() } } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/attributes/ButtonAttributes.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/attributes/ButtonAttributes.kt index b08bfa1d..6e97beee 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/attributes/ButtonAttributes.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/attributes/ButtonAttributes.kt @@ -1,5 +1,7 @@ package pokitmons.pokit.core.ui.components.atom.button.attributes +import androidx.compose.ui.graphics.Color + enum class PokitButtonShape { ROUND, RECTANGLE, } @@ -28,3 +30,15 @@ data class PokitButtonIcon( enum class PokitButtonIconPosition { RIGHT, LEFT, } + +enum class PokitLoginButtonType { + GOOGLE, APPLE +} + +data class PokitLoginResource( + val iconResourceId: Int, + val iconTintColor: Color = Color.Unspecified, + val textColor: Color, + val backgroundColor: Color, + val borderColor: Color, +) diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/checkbox/PokitCheckbox.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/checkbox/PokitCheckbox.kt index ed7772e7..c366b9c8 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/checkbox/PokitCheckbox.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/checkbox/PokitCheckbox.kt @@ -109,7 +109,7 @@ private fun getStrokeColor( Color.Unspecified } - !checked -> { + !checked && (style != PokitCheckboxStyle.ICON_ONLY) -> { PokitTheme.colors.borderSecondary } @@ -134,7 +134,7 @@ private fun getBackgroundColor( PokitTheme.colors.backgroundDisable } - !checked -> { + !checked && (style != PokitCheckboxStyle.ICON_ONLY) -> { PokitTheme.colors.backgroundBase } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/loginbutton/PokitLoginButton.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/loginbutton/PokitLoginButton.kt new file mode 100644 index 00000000..a42618f9 --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/loginbutton/PokitLoginButton.kt @@ -0,0 +1,86 @@ +package pokitmons.pokit.core.ui.components.atom.loginbutton + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +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.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +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.unit.dp +import pokitmons.pokit.core.ui.R +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitLoginButtonType +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitLoginResource +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +fun PokitLoginButton( + modifier: Modifier = Modifier, + onClick: () -> Unit, + loginType: PokitLoginButtonType, + text: String, +) { + val loginResource: PokitLoginResource = getLoginResource(loginType) + + Surface( + modifier = Modifier + .height(50.dp) + .border( + shape = RoundedCornerShape(8.dp), + width = 1.dp, + color = loginResource.borderColor + ), + shape = RoundedCornerShape(8.dp), + color = loginResource.backgroundColor, + onClick = onClick + ) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + modifier = Modifier + .size(24.dp), + painter = painterResource(id = loginResource.iconResourceId), + tint = loginResource.iconTintColor, + contentDescription = null + ) + Text( + modifier = Modifier + .padding(start = 12.dp), + text = text, + color = loginResource.textColor, + style = PokitTheme.typography.label1Regular + ) + } + } +} + +@Composable +private fun getLoginResource(loginType: PokitLoginButtonType): PokitLoginResource { + return when (loginType) { + PokitLoginButtonType.APPLE -> PokitLoginResource( + iconResourceId = R.drawable.icon_24_apple, + iconTintColor = PokitTheme.colors.inverseWh, + textColor = PokitTheme.colors.inverseWh, + backgroundColor = PokitTheme.colors.backgroundTertiary, + borderColor = PokitTheme.colors.backgroundTertiary + ) + + PokitLoginButtonType.GOOGLE -> PokitLoginResource( + iconResourceId = R.drawable.icon_24_google, + textColor = PokitTheme.colors.textPrimary, + backgroundColor = PokitTheme.colors.backgroundBase, + borderColor = PokitTheme.colors.borderSecondary + ) + } +} diff --git a/feature/login/build.gradle.kts b/feature/login/build.gradle.kts index f3ffc1a0..4188ee20 100644 --- a/feature/login/build.gradle.kts +++ b/feature/login/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) + implementation(libs.androidx.navigation.compose) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -81,4 +82,6 @@ dependencies { implementation(libs.androidx.credentials) implementation(libs.androidx.credentials.play.services.auth) implementation(libs.googleid) + + implementation(project(":core:ui")) } diff --git a/feature/login/src/main/java/pokitmons/pokit/keyword/KeywordScreen.kt b/feature/login/src/main/java/pokitmons/pokit/keyword/KeywordScreen.kt new file mode 100644 index 00000000..3a8f2731 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/keyword/KeywordScreen.kt @@ -0,0 +1,161 @@ +package pokitmons.pokit.keyword + +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +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.unit.dp +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.components.atom.chip.PokitChip +import pokitmons.pokit.core.ui.components.atom.chip.attributes.PokitChipSize +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.core.ui.R as Ui +import pokitmons.pokit.login.R as Login + +@Composable +fun KeywordScreen( + onNavigateToSignUpScreen: () -> Unit, + popBackStack: () -> Unit, +) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(all = 20.dp) + .padding(bottom = 8.dp) + ) { + Column { + Icon(painter = painterResource(id = Ui.drawable.icon_24_arrow_left), contentDescription = null) + Spacer(modifier = Modifier.height(32.dp)) + Text( + text = stringResource(id = Login.string.keyword_title), + style = PokitTheme.typography.title1 + ) + Spacer(modifier = Modifier.height(12.dp)) + Text( + text = stringResource(id = Login.string.select_keyword), + style = PokitTheme.typography.title3 + ) + Spacer(modifier = Modifier.height(36.dp)) + + // TODO FlowRow도 사용해보기 + Column { + val categories: List> = listOf( + stringResource(id = Login.string.sports_and_leisure), + stringResource(id = Login.string.phrases_and_office), + stringResource(id = Login.string.fashion), + stringResource(id = Login.string.travel), + stringResource(id = Login.string.economy_and_politics), + stringResource(id = Login.string.movies_and_dramas), + stringResource(id = Login.string.restaurants), + stringResource(id = Login.string.interior), + stringResource(id = Login.string.it), + stringResource(id = Login.string.design), + stringResource(id = Login.string.self_development), + stringResource(id = Login.string.humor), + stringResource(id = Login.string.music), + stringResource(id = Login.string.job_info) + ).chunked(3) + + Row { + categories[0].forEach { category -> + PokitChip( + data = null, + size = PokitChipSize.MEDIUM, + text = category, + removeIconPosition = null, + onClickRemove = { }, + onClickItem = { } + ) + Spacer(modifier = Modifier.padding(start = 12.dp)) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Row { + categories[1].forEach { category -> + PokitChip( + data = null, + size = PokitChipSize.MEDIUM, + text = category, + removeIconPosition = null, + onClickRemove = { }, + onClickItem = { } + ) + Spacer(modifier = Modifier.padding(start = 12.dp)) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Row { + categories[2].forEach { category -> + PokitChip( + data = null, + size = PokitChipSize.MEDIUM, + text = category, + removeIconPosition = null, + onClickRemove = { }, + onClickItem = { } + ) + Spacer(modifier = Modifier.padding(start = 12.dp)) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Row { + categories[3].forEach { category -> + PokitChip( + data = null, + size = PokitChipSize.MEDIUM, + text = category, + removeIconPosition = null, + onClickRemove = { }, + onClickItem = { } + ) + Spacer(modifier = Modifier.padding(start = 12.dp)) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Row { + categories[4].forEach { category -> + PokitChip( + data = null, + size = PokitChipSize.MEDIUM, + text = category, + removeIconPosition = null, + onClickRemove = { }, + onClickItem = { } + ) + Spacer(modifier = Modifier.padding(start = 12.dp)) + } + } + } + } + + PokitButton( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter), + text = stringResource(id = pokitmons.pokit.login.R.string.next), + icon = null, + size = PokitButtonSize.LARGE, + onClick = { onNavigateToSignUpScreen() } + ) + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/login/LoginScreen.kt b/feature/login/src/main/java/pokitmons/pokit/login/LoginScreen.kt index 6473035b..f869ed43 100644 --- a/feature/login/src/main/java/pokitmons/pokit/login/LoginScreen.kt +++ b/feature/login/src/main/java/pokitmons/pokit/login/LoginScreen.kt @@ -2,22 +2,62 @@ package pokitmons.pokit.login import android.annotation.SuppressLint import android.util.Log -import androidx.compose.material3.Text +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.height +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.credentials.CredentialManager import androidx.credentials.GetCredentialRequest import com.google.android.libraries.identity.googleid.GetGoogleIdOption import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import kotlinx.coroutines.launch +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitLoginButtonType +import pokitmons.pokit.core.ui.components.atom.loginbutton.PokitLoginButton -@SuppressLint("CoroutineCreationDuringComposition") @Composable -fun LoginScreen() { +fun LoginScreen( + onNavigateToTermsOfServiceScreen: () -> Unit, + onNavigateToMainScreen: () -> Unit, +) { // TODO 서버 api 개발완료 후 viewmodel 연동 및 아키텍처 구축 + Box( + modifier = Modifier + .fillMaxSize() + ) { + Column( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(start = 20.dp, end = 20.dp, bottom = 32.dp) + ) { + PokitLoginButton( + loginType = PokitLoginButtonType.APPLE, + text = stringResource(id = R.string.apple_login), + onClick = { onNavigateToMainScreen() } + ) + + Spacer(modifier = Modifier.height(8.dp)) + + PokitLoginButton( + loginType = PokitLoginButtonType.GOOGLE, + text = stringResource(id = R.string.google_login), + onClick = { onNavigateToTermsOfServiceScreen() } + ) + } + } +} - Text(text = "로그인 테스트 화면") +@SuppressLint("CoroutineCreationDuringComposition") +@Composable +private fun googleLogin() { val coroutineScope = rememberCoroutineScope() val context = LocalContext.current diff --git a/feature/login/src/main/java/pokitmons/pokit/navigation/LoginNavHost.kt b/feature/login/src/main/java/pokitmons/pokit/navigation/LoginNavHost.kt new file mode 100644 index 00000000..a6285297 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/navigation/LoginNavHost.kt @@ -0,0 +1,54 @@ +package pokitmons.pokit.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import pokitmons.pokit.keyword.KeywordScreen +import pokitmons.pokit.login.LoginScreen +import pokitmons.pokit.nickname.InputNicknameScreen +import pokitmons.pokit.success.SignUpSuccessScreen +import pokitmons.pokit.terms.TermsOfServiceScreen + +@Composable +fun LoginNavHost() { + val navController = rememberNavController() + NavHost( + navController = navController, + startDestination = LoginRoute.LoginScreen.name + ) { + composable(route = LoginRoute.LoginScreen.name) { + LoginScreen( + onNavigateToTermsOfServiceScreen = { navController.navigate(route = LoginRoute.TermsOfServiceScreen.name) }, + onNavigateToMainScreen = { } // TODO 메인 화면 구현후 수정 + ) + } + + composable(route = LoginRoute.TermsOfServiceScreen.name) { + TermsOfServiceScreen( + onNavigateToInputNicknameScreen = { navController.navigate(route = LoginRoute.InputNicknameScreen.name) }, + popBackStack = { navController.popBackStack() } + ) + } + + composable(route = LoginRoute.InputNicknameScreen.name) { + InputNicknameScreen( + onNavigateToKeywordScreen = { navController.navigate(route = LoginRoute.KeywordScreen.name) }, + popBackStack = { navController.popBackStack() } + ) + } + + composable(route = LoginRoute.KeywordScreen.name) { + KeywordScreen( + onNavigateToSignUpScreen = { navController.navigate(route = LoginRoute.SignUpSuccessScreen.name) }, + popBackStack = { navController.popBackStack() } + ) + } + + composable(route = LoginRoute.SignUpSuccessScreen.name) { + SignUpSuccessScreen( + onNavigateToMainScreen = { } // TODO 메인 화면 구현후 수정 + ) + } + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/navigation/LoginRoute.kt b/feature/login/src/main/java/pokitmons/pokit/navigation/LoginRoute.kt new file mode 100644 index 00000000..497b8e4e --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/navigation/LoginRoute.kt @@ -0,0 +1,9 @@ +package pokitmons.pokit.navigation + +sealed class LoginRoute(val name: String) { + data object LoginScreen : LoginRoute("LoginScreen") + data object TermsOfServiceScreen : LoginRoute("TermsOfServiceScreen") + data object InputNicknameScreen : LoginRoute("InputNicknameScreen") + data object KeywordScreen : LoginRoute("KeywordScreen") + data object SignUpSuccessScreen : LoginRoute("SignUpSuccessScreen") +} diff --git a/feature/login/src/main/java/pokitmons/pokit/nickname/InputNicknameScreen.kt b/feature/login/src/main/java/pokitmons/pokit/nickname/InputNicknameScreen.kt new file mode 100644 index 00000000..0ae25982 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/nickname/InputNicknameScreen.kt @@ -0,0 +1,94 @@ +package pokitmons.pokit.nickname + +import androidx.compose.foundation.clickable +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.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +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.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.components.block.labeledinput.LabeledInput +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.core.ui.R as UI +import pokitmons.pokit.login.R as Login + +private const val NICKNAME_MAX_LENGTH = 10 // TODO 매직넘버를 포함하는 모듈화 추가 후 마이그레이션 예정 +private const val NICKNAME_MIN_LENGTH = 1 // TODO 매직넘버를 포함하는 모듈화 추가 후 마이그레이션 예정 + +@Composable +fun InputNicknameScreen( + onNavigateToKeywordScreen: () -> Unit, + popBackStack: () -> Unit, +) { + val inputNicknameViewModel: InputNicknameViewModel = viewModel() // TODO hiltViewModel 마이그레이션 예정 + val inputNicknameState by inputNicknameViewModel.inputNicknameState.collectAsState() + + Box( + modifier = Modifier + .padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 28.dp) + .fillMaxSize() + ) { + Column() { + Icon( + modifier = Modifier.clickable { popBackStack() }, + painter = painterResource(id = UI.drawable.icon_24_arrow_left), + contentDescription = "뒤로가기" + ) + + Spacer(modifier = Modifier.padding(top = 32.dp)) + + Text( + style = PokitTheme.typography.title1, + text = stringResource(id = Login.string.input_nickname_title) + ) + + Spacer(modifier = Modifier.padding(top = 28.dp)) + + LabeledInput( + modifier = Modifier + .fillMaxWidth() + .imePadding(), + label = "", + inputText = inputNicknameState, + maxLength = NICKNAME_MAX_LENGTH, + sub = if (inputNicknameState.length < NICKNAME_MAX_LENGTH) { + stringResource(id = Login.string.input_restriction_message) + } else { + stringResource(id = Login.string.input_max_length) + }, + isError = inputNicknameState.length > NICKNAME_MAX_LENGTH, + hintText = stringResource(id = Login.string.input_nickname_hint), + onChangeText = { text -> + if (text.length <= NICKNAME_MAX_LENGTH) { + inputNicknameViewModel.inputText(text) + } + } + ) + } + + PokitButton( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter), + text = stringResource(id = pokitmons.pokit.login.R.string.next), + icon = null, + size = PokitButtonSize.LARGE, + enable = inputNicknameState.length >= NICKNAME_MIN_LENGTH, + onClick = { onNavigateToKeywordScreen() } + ) + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/nickname/InputNicknameViewModel.kt b/feature/login/src/main/java/pokitmons/pokit/nickname/InputNicknameViewModel.kt new file mode 100644 index 00000000..0b7678e8 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/nickname/InputNicknameViewModel.kt @@ -0,0 +1,15 @@ +package pokitmons.pokit.nickname + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class InputNicknameViewModel : ViewModel() { + private val _inputNicknameState = MutableStateFlow("") + val inputNicknameState: StateFlow + get() = _inputNicknameState + + fun inputText(text: String) { + _inputNicknameState.value = text + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/success/SignUpSuccessScreen.kt b/feature/login/src/main/java/pokitmons/pokit/success/SignUpSuccessScreen.kt new file mode 100644 index 00000000..dd195ddc --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/success/SignUpSuccessScreen.kt @@ -0,0 +1,78 @@ +package pokitmons.pokit.success + +import androidx.compose.foundation.Image +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.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +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.text.style.TextAlign +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.login.R + +@Composable +fun SignUpSuccessScreen( + onNavigateToMainScreen: () -> Unit, +) { + Box( + modifier = Modifier + .padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 28.dp) + ) { + Icon( + painter = painterResource(id = pokitmons.pokit.core.ui.R.drawable.icon_24_arrow_left), + contentDescription = null + ) + + Column( + modifier = Modifier + .fillMaxSize() + .offset(y = (-110).dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(id = R.drawable.ic_launcher_foreground), + contentDescription = "회원가입 완료 이미지" + ) + + Spacer(modifier = Modifier.height(28.dp)) + + Text( + style = PokitTheme.typography.title1, + text = stringResource(id = R.string.sign_up_success) + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Text( + textAlign = TextAlign.Center, + style = PokitTheme.typography.body1Bold, + text = stringResource(id = R.string.manage_links) + ) + } + + PokitButton( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter), + text = stringResource(id = R.string.next), + icon = null, + size = PokitButtonSize.LARGE, + onClick = { onNavigateToMainScreen() } + ) + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/terms/TermsCheckBoxItem.kt b/feature/login/src/main/java/pokitmons/pokit/terms/TermsCheckBoxItem.kt new file mode 100644 index 00000000..fdc1b2ec --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/terms/TermsCheckBoxItem.kt @@ -0,0 +1,44 @@ +package pokitmons.pokit.terms + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +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.unit.dp +import pokitmons.pokit.core.ui.components.atom.checkbox.PokitCheckbox +import pokitmons.pokit.core.ui.components.atom.checkbox.attributes.PokitCheckboxStyle +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +fun TermsCheckBoxItem( + text: String, + isChecked: Boolean, + click: () -> Unit, +) { + Box(modifier = Modifier.fillMaxWidth()) { + Row(verticalAlignment = Alignment.CenterVertically) { + PokitCheckbox( + checked = isChecked, + style = PokitCheckboxStyle.ICON_ONLY, + onClick = { click() } + ) + + Text( + modifier = Modifier.padding(start = 4.dp), + text = text, + style = PokitTheme.typography.body2Medium + ) + } + Icon( + modifier = Modifier.align(Alignment.CenterEnd), + painter = painterResource(id = pokitmons.pokit.core.ui.R.drawable.icon_24_arrow_right), + contentDescription = "화살표" + ) + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/terms/TermsOfServiceScreen.kt b/feature/login/src/main/java/pokitmons/pokit/terms/TermsOfServiceScreen.kt new file mode 100644 index 00000000..d950a515 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/terms/TermsOfServiceScreen.kt @@ -0,0 +1,127 @@ +package pokitmons.pokit.terms + +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +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.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.components.atom.checkbox.PokitCheckbox +import pokitmons.pokit.core.ui.components.atom.checkbox.attributes.PokitCheckboxStyle +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.core.ui.R as UI +import pokitmons.pokit.login.R as Login + +@Composable +fun TermsOfServiceScreen( + onNavigateToInputNicknameScreen: () -> Unit, + popBackStack: () -> Unit, +) { + val termsViewModel: TermsViewModel = viewModel() // TODO hiltViewModel 마이그레이션 예정 + val termsState by termsViewModel.termsState.collectAsState() + + Box( + modifier = Modifier + .fillMaxSize() + .padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 28.dp) + ) { + Column { + Icon( + painter = painterResource(id = UI.drawable.icon_24_arrow_left), + contentDescription = "뒤로가기", + modifier = Modifier.clickable { popBackStack() } + ) + + Spacer(modifier = Modifier.height(32.dp)) + + Text( + text = stringResource(id = Login.string.service_privacy_title), + style = PokitTheme.typography.title1 + ) + + Spacer(modifier = Modifier.height(32.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .height(64.dp) + .border( + shape = RoundedCornerShape(8.dp), + width = 1.dp, + color = PokitTheme.colors.brand + ), + verticalAlignment = Alignment.CenterVertically + ) { + Spacer(modifier = Modifier.padding(start = 16.dp)) + + PokitCheckbox( + style = PokitCheckboxStyle.STROKE, + checked = termsState.isAllChecked, + onClick = { termsViewModel.checkAllTerms() } + ) + Text( + modifier = Modifier.padding(start = 16.dp), + text = stringResource(id = Login.string.privacy_all_agree), + style = PokitTheme.typography.body1Bold + ) + } + + Spacer(modifier = Modifier.padding(top = 20.dp)) + + Column( + modifier = Modifier + .padding(start = 20.dp, end = 20.dp) + .fillMaxWidth() + ) { + TermsCheckBoxItem( + text = stringResource(id = Login.string.personal_data_agree), + isChecked = termsState.isPersonalDataChecked, + click = { termsViewModel.checkPersonalData() } + ) + Spacer(modifier = Modifier.padding(top = 16.dp)) + + TermsCheckBoxItem( + text = stringResource(id = Login.string.service_agree), + isChecked = termsState.isServiceChecked, + click = { termsViewModel.checkServiceTerm() } + ) + Spacer(modifier = Modifier.padding(top = 16.dp)) + + TermsCheckBoxItem( + text = stringResource(id = Login.string.marketing_agree), + isChecked = termsState.isMarketingChecked, + click = { termsViewModel.checkMarketing() } + ) + } + } + + PokitButton( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter), + text = stringResource(id = Login.string.next), + icon = null, + size = PokitButtonSize.LARGE, + enable = termsState.isPersonalDataChecked && termsState.isServiceChecked, + onClick = { onNavigateToInputNicknameScreen() } + ) + } +} diff --git a/feature/login/src/main/java/pokitmons/pokit/terms/TermsState.kt b/feature/login/src/main/java/pokitmons/pokit/terms/TermsState.kt new file mode 100644 index 00000000..342655c5 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/terms/TermsState.kt @@ -0,0 +1,8 @@ +package pokitmons.pokit.terms + +data class TermsState( + val isAllChecked: Boolean = false, + val isPersonalDataChecked: Boolean = false, + val isServiceChecked: Boolean = false, + val isMarketingChecked: Boolean = false, +) diff --git a/feature/login/src/main/java/pokitmons/pokit/terms/TermsViewModel.kt b/feature/login/src/main/java/pokitmons/pokit/terms/TermsViewModel.kt new file mode 100644 index 00000000..4cae62a1 --- /dev/null +++ b/feature/login/src/main/java/pokitmons/pokit/terms/TermsViewModel.kt @@ -0,0 +1,42 @@ +package pokitmons.pokit.terms + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class TermsViewModel() : ViewModel() { + private val _termsState = MutableStateFlow(TermsState()) + val termsState: StateFlow + get() = _termsState + + fun checkAllTerms() { + when (_termsState.value.isAllChecked) { + true -> _termsState.value = TermsState() + false -> _termsState.value = _termsState.value.copy( + isAllChecked = true, + isPersonalDataChecked = true, + isServiceChecked = true, + isMarketingChecked = true + ) + } + } + + fun checkPersonalData() { + _termsState.value = _termsState.value.copy(isPersonalDataChecked = !_termsState.value.isPersonalDataChecked) + isAllCheck() + } + + fun checkServiceTerm() { + _termsState.value = _termsState.value.copy(isServiceChecked = !_termsState.value.isServiceChecked) + isAllCheck() + } + + fun checkMarketing() { + _termsState.value = _termsState.value.copy(isMarketingChecked = !_termsState.value.isMarketingChecked) + isAllCheck() + } + + private fun isAllCheck() = with(_termsState.value) { + _termsState.value = copy(isAllChecked = isMarketingChecked && isServiceChecked && isPersonalDataChecked) + } +} diff --git a/feature/login/src/main/res/values/strings.xml b/feature/login/src/main/res/values/strings.xml index 20aee43a..cc30b3cd 100644 --- a/feature/login/src/main/res/values/strings.xml +++ b/feature/login/src/main/res/values/strings.xml @@ -1,3 +1,32 @@ - login + Google로 시작하기 + Apple로 시작하기 + 서비스 이용을 위해\n이용약 동의가 필요합니다 + 약관 전체동의 + (필수)개인정보 수집 및 이용 동의 + (필수)서비스 이용약관 + (선택)마케팅 정보 수신 + Pokit에 사용할 닉네임을\n입력해주세요 + 내용을 입력해주세요. + 한글, 영어, 숫자로만 입력이 가능합니다. + 최대 20자까지 입력 가능합니다. + 어떤 분야에 관심이 있으세요? + 최대 3개를 골라주시면,\n관련 콘텐츠를 추천해드릴게요! + 스포츠/레저 + 문구/오피스 + 패션 + 여행 + 경제/시사 + 영화/드라마 + 맛집 + 인테리어 + IT + 디자인 + 자기계발 + 유머 + 음악 + 취업정보 + 회원가입이 완료되었습니다! + POKIT을 통해 많은 링크를\n간편하게 관리하세요 + 다음 \ No newline at end of file