From 4037bfc4ecb49a94a60b5042a9971fa6a0a9d1ee Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 21 Jul 2024 18:41:40 +0900 Subject: [PATCH 1/7] =?UTF-8?q?Boolti-264=20refactor:=20CopyButton=20->=20?= =?UTF-8?q?SmallButton=20=EA=B3=B5=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/component/CopyButton.kt | 47 ----------- .../presentation/component/SmallButton.kt | 84 +++++++++++++++++++ .../screen/showdetail/ShowDetailScreen.kt | 5 +- presentation/src/main/res/values/strings.xml | 8 +- 4 files changed, 93 insertions(+), 51 deletions(-) delete mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/component/CopyButton.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/component/SmallButton.kt diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/CopyButton.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/CopyButton.kt deleted file mode 100644 index 71a58bd8..00000000 --- a/presentation/src/main/java/com/nexters/boolti/presentation/component/CopyButton.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.nexters.boolti.presentation.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -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.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.nexters.boolti.presentation.R -import com.nexters.boolti.presentation.theme.Grey85 - -@Composable -fun CopyButton( - label: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Row( - modifier = modifier - .clip(shape = RoundedCornerShape(4.dp)) - .clickable(onClick = onClick) - .height(30.dp) - .background(color = Grey85) - .padding(horizontal = 12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_copy), - contentDescription = label - ) - Text( - modifier = Modifier.padding(start = 6.dp), - text = label, - style = MaterialTheme.typography.labelMedium - ) - } -} \ No newline at end of file diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/component/SmallButton.kt b/presentation/src/main/java/com/nexters/boolti/presentation/component/SmallButton.kt new file mode 100644 index 00000000..57fd02fe --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/component/SmallButton.kt @@ -0,0 +1,84 @@ +package com.nexters.boolti.presentation.component + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +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.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.nexters.boolti.presentation.R +import com.nexters.boolti.presentation.theme.BooltiTheme +import com.nexters.boolti.presentation.theme.Grey05 +import com.nexters.boolti.presentation.theme.Grey50 +import com.nexters.boolti.presentation.theme.Grey80 +import com.nexters.boolti.presentation.theme.Grey85 + +@Composable +fun SmallButton( + label: String, + modifier: Modifier = Modifier, + @DrawableRes iconRes: Int? = null, + backgroundColor: Color = Grey85, + iconTint: Color = Grey50, + labelStyle: TextStyle = MaterialTheme.typography.labelMedium.copy(color = Grey05), + onClick: () -> Unit, +) { + Row( + modifier = modifier + .clip(shape = RoundedCornerShape(4.dp)) + .clickable(onClick = onClick) + .height(30.dp) + .background(color = backgroundColor) + .padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + iconRes?.let { id -> + Icon( + modifier = modifier.padding(end = 6.dp), + painter = painterResource(id = id), + tint = iconTint, + contentDescription = label, + ) + } + Text( + text = label, + style = labelStyle, + ) + } +} + +@Preview +@Composable +private fun CopyButtonPreview() { + BooltiTheme { + SmallButton( + label = stringResource(R.string.ticketing_copy_address), + iconRes = R.drawable.ic_copy, + ) { } + } +} + +@Preview +@Composable +private fun LoginButtonPreview() { + BooltiTheme { + SmallButton( + label = stringResource(R.string.login), + backgroundColor = Grey80, + ) { } + } +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt index 0ba869a8..9430c4b3 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/showdetail/ShowDetailScreen.kt @@ -64,10 +64,10 @@ import com.nexters.boolti.domain.model.ShowState import com.nexters.boolti.presentation.R import com.nexters.boolti.presentation.component.BtAppBar import com.nexters.boolti.presentation.component.BtAppBarDefaults -import com.nexters.boolti.presentation.component.CopyButton import com.nexters.boolti.presentation.component.MainButton import com.nexters.boolti.presentation.component.MainButtonDefaults import com.nexters.boolti.presentation.component.ShowInquiry +import com.nexters.boolti.presentation.component.SmallButton import com.nexters.boolti.presentation.extension.requireActivity import com.nexters.boolti.presentation.screen.LocalSnackbarController import com.nexters.boolti.presentation.screen.ticketing.ChooseTicketBottomSheet @@ -360,7 +360,8 @@ private fun ContentScaffold( val clipboardManager = LocalClipboardManager.current val copiedMessage = stringResource(id = R.string.ticketing_address_copied_message) - CopyButton( + SmallButton( + iconRes = R.drawable.ic_copy, label = stringResource(id = R.string.ticketing_copy_address), onClick = { clipboardManager.setText(AnnotatedString(showDetail.streetAddress)) diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 66b43c7b..31f60a43 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -50,6 +50,7 @@ 확인 로그인 하러 가기 + 로그인 알 수 없는 에러가 발생했습니다 복사 다음 @@ -211,13 +212,16 @@ 예매 진행 중 오류가 발생하였습니다.\n다시 시도해 주세요 - 불티 로그인 하러가기 + 로그인하고 이용해보세요 원하는 공연 티켓을 예매해보세요! 로그아웃 결제 내역 - QR 스캔 + 입장 확인 정말 로그아웃 하시겠어요? 공연 등록 + 프로필 보기 + 계정 설정 + 내 공연 QR 스캔 From 276a0df9d2d7329960918185c692b84fd8a88472 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 21 Jul 2024 19:56:46 +0900 Subject: [PATCH 2/7] =?UTF-8?q?Boolti-264=20style:=20=EB=A7=88=EC=9D=B4=20?= =?UTF-8?q?=ED=83=AD=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/build.gradle.kts | 1 + .../presentation/screen/home/HomeScreen.kt | 1 - .../boolti/presentation/screen/my/MyScreen.kt | 360 +++++++++++------- .../src/main/res/drawable/ic_list.xml | 38 ++ .../src/main/res/drawable/ic_plus_ticket.xml | 31 ++ .../src/main/res/drawable/ic_profile.xml | 31 ++ .../src/main/res/drawable/ic_qr_simple.xml | 41 ++ 7 files changed, 373 insertions(+), 130 deletions(-) create mode 100644 presentation/src/main/res/drawable/ic_list.xml create mode 100644 presentation/src/main/res/drawable/ic_plus_ticket.xml create mode 100644 presentation/src/main/res/drawable/ic_profile.xml create mode 100644 presentation/src/main/res/drawable/ic_qr_simple.xml diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index ac7568e4..12722d5c 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -18,6 +18,7 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") buildConfigField("String", "PACKAGE_NAME", "\"${libs.versions.packageName.get()}\"") + buildConfigField("String", "VERSION_NAME", "\"${libs.versions.versionName.get()}\"") } buildTypes { diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 071d80f9..77174133 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -125,7 +125,6 @@ fun HomeScreen( requireLogin = requireLogin, navigateToReservations = navigateToReservations, onClickQrScan = onClickQrScan, - onClickSignout = onClickSignout, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index 9b31bcfa..927db8ee 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -1,206 +1,308 @@ package com.nexters.boolti.presentation.screen.my +import androidx.annotation.DrawableRes import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable -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.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface 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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +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 coil.compose.AsyncImage import com.nexters.boolti.domain.model.User +import com.nexters.boolti.presentation.BuildConfig import com.nexters.boolti.presentation.R -import com.nexters.boolti.presentation.component.BTDialog -import com.nexters.boolti.presentation.theme.Grey10 +import com.nexters.boolti.presentation.component.SmallButton +import com.nexters.boolti.presentation.theme.BooltiTheme import com.nexters.boolti.presentation.theme.Grey30 -import com.nexters.boolti.presentation.theme.Grey50 +import com.nexters.boolti.presentation.theme.Grey80 import com.nexters.boolti.presentation.theme.marginHorizontal +import com.nexters.boolti.presentation.theme.point3 @Composable fun MyScreen( + modifier: Modifier = Modifier, + viewModel: MyViewModel = hiltViewModel(), requireLogin: () -> Unit, navigateToReservations: () -> Unit, onClickQrScan: () -> Unit, - onClickSignout: () -> Unit, - modifier: Modifier = Modifier, - viewModel: MyViewModel = hiltViewModel(), ) { val user by viewModel.user.collectAsStateWithLifecycle() - var openLogoutDialog by remember { mutableStateOf(false) } val uriHandler = LocalUriHandler.current LaunchedEffect(Unit) { viewModel.fetchMyInfo() } - Column( - modifier = modifier.verticalScroll(state = rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - MyHeader(user = user, requireLogin = requireLogin) - MyButton( - modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.my_ticketing_history), - onClick = if (user == null) requireLogin else navigateToReservations, - ) - MyButton( - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp), - text = stringResource(R.string.my_register_show), - onClick = { - if (user != null) { - uriHandler.openUri("https://boolti.in/home") // 웹에서 로그인되지 않은 상태라면 login 페이지로 리다이렉션 시킴 - } else { - uriHandler.openUri("https://boolti.in/login") - } - } - ) - MyButton( - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp), - text = stringResource(id = R.string.my_scan_qr), - onClick = onClickQrScan, - ) + MyScreen( + modifier = modifier, + user = user, + onClickHeaderButton = if (user != null) requireLogin else requireLogin, // TODO 프로필 구현 후 프로필 화면 이동 연결 + onClickReservations = if (user != null) navigateToReservations else requireLogin, + onClickRegisterShow = { + val url = if (user != null) "https://boolti.in/home" else "https://boolti.in/login" + uriHandler.openUri(url) + }, + onClickQrScan = if (user != null) onClickQrScan else requireLogin, + ) +} - if (user != null) { - MyButton( - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp), - text = stringResource(id = R.string.my_logout), - onClick = { openLogoutDialog = true }, - ) - } +@Composable +fun MyScreen( + modifier: Modifier = Modifier, + user: User? = null, + onClickHeaderButton: () -> Unit = {}, + onClickAccountSetting: () -> Unit = {}, + onClickReservations: () -> Unit = {}, + onClickRegisterShow: () -> Unit = { }, + onClickQrScan: () -> Unit = {}, +) { + Surface { + Box( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background), + ) { + Column( + modifier = modifier, + ) { + MyHeader( + user = user, + onClickButton = onClickHeaderButton, + ) + MyMenu( + modifier = Modifier.padding(top = 20.dp), + iconRes = R.drawable.ic_profile, + label = stringResource(R.string.account_setting), + onClick = onClickAccountSetting, + ) + MyMenu( + iconRes = R.drawable.ic_list, + label = stringResource(R.string.my_ticketing_history), + onClick = onClickReservations + ) - Spacer(modifier = Modifier.weight(1.0f)) - if (user != null) SignoutButton(onClick = onClickSignout) - } + HorizontalDivider( + modifier = Modifier.padding(vertical = 32.dp, horizontal = marginHorizontal), + color = MaterialTheme.colorScheme.surfaceTint, + ) - if (openLogoutDialog) { - BTDialog( - positiveButtonLabel = stringResource(id = R.string.my_logout), - onClickPositiveButton = { - openLogoutDialog = false - viewModel.logout() - }, - onDismiss = { openLogoutDialog = false } - ) { - Text( - modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.my_logout_popup), - textAlign = TextAlign.Center, - ) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = marginHorizontal), + text = stringResource(R.string.my_show), + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onBackground, + ) + + MyMenu( + modifier = Modifier.padding(top = 8.dp), + iconRes = R.drawable.ic_plus_ticket, + label = stringResource(R.string.my_register_show), + onClick = onClickRegisterShow, + ) + MyMenu( + iconRes = R.drawable.ic_qr_simple, + label = stringResource(R.string.my_scan_qr), + onClick = onClickQrScan, + ) + } + Column( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 36.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + modifier = Modifier.size(width = 79.dp, height = 28.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_logo_boolti), + tint = Grey80, + contentDescription = stringResource(R.string.description_app_logo), + ) + Text( + modifier = Modifier.padding(top = 8.dp), + text = "Version ${BuildConfig.VERSION_NAME}", + style = MaterialTheme.typography.bodySmall, + color = Grey80, + ) + } } } } @Composable -fun MyHeader(modifier: Modifier = Modifier, user: User?, requireLogin: () -> Unit) { - val headerModifier = if (user == null) modifier.clickable(onClick = requireLogin) else modifier - +private fun MyHeader( + modifier: Modifier = Modifier, + user: User? = null, + onClickButton: () -> Unit, +) { + val shape = RoundedCornerShape( + bottomStart = 12.dp, + bottomEnd = 12.dp, + ) Row( - modifier = headerModifier + modifier = modifier .fillMaxWidth() - .padding(horizontal = marginHorizontal) - .padding(top = 40.dp, bottom = 32.dp), + .clip(shape) + .background(MaterialTheme.colorScheme.surface) + .padding(vertical = 28.dp, horizontal = marginHorizontal), verticalAlignment = Alignment.CenterVertically, ) { - AsyncImage( - modifier = Modifier - .size(70.dp) - .clip(shape = RoundedCornerShape(100.dp)), - model = user?.photo, - contentDescription = null, - fallback = painterResource(id = R.drawable.ic_fallback_profile) - ) - Column( - modifier = Modifier.padding(start = 12.dp), - verticalArrangement = Arrangement.Center, - ) { - Text( - text = user?.nickname ?: stringResource(id = R.string.my_login), - style = MaterialTheme.typography.titleLarge - ) - Text( - text = user?.email ?: stringResource(id = R.string.my_login_sub), - style = MaterialTheme.typography.bodyLarge.copy(color = Grey30), + user?.let { + AsyncImage( + modifier = Modifier + .padding(end = 12.dp) + .size(36.dp) + .clip(shape = CircleShape) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outline, + shape = CircleShape, + ), + model = user.photo, + contentDescription = null, + placeholder = painterResource(id = R.drawable.ic_fallback_profile), + fallback = painterResource(id = R.drawable.ic_fallback_profile), ) } - Spacer(modifier = modifier.weight(1.0f)) - if (user == null) { - Icon( - modifier = Modifier.padding(start = 12.dp), - painter = painterResource(id = R.drawable.ic_arrow_right), - contentDescription = null, - tint = Grey50, + Text( + modifier = Modifier.weight(1f), + text = user?.nickname ?: stringResource(R.string.my_login), + style = point3, + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + if (user == null) { // TODO 프로필 기능 추가 후 조건문 제거 + SmallButton( + modifier = Modifier.padding(start = 16.dp), + label = if (user != null) { + stringResource(R.string.show_profile_button) + } else { + stringResource(R.string.login) + }, + backgroundColor = Grey80, + onClick = onClickButton, ) } } } @Composable -private fun MyButton( - text: String, - onClick: () -> Unit, +private fun MyMenu( + @DrawableRes iconRes: Int, + label: String, modifier: Modifier = Modifier, + onClick: () -> Unit, ) { Row( modifier = modifier - .clickable(onClick = onClick) - .background(color = MaterialTheme.colorScheme.surface) - .padding(horizontal = marginHorizontal, vertical = 20.dp), - horizontalArrangement = Arrangement.SpaceBetween, + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + .clickable( + role = Role.Button, + onClick = onClick, + ) + .padding(vertical = 12.dp, horizontal = marginHorizontal), ) { - Text( - text = text, - style = MaterialTheme.typography.titleLarge.copy(color = Grey10), - ) Icon( - painter = painterResource(id = R.drawable.ic_arrow_right), - contentDescription = null, - tint = Grey50, + modifier = Modifier.size(24.dp), + imageVector = ImageVector.vectorResource(iconRes), + tint = Grey30, + contentDescription = label, + ) + Text( + modifier = Modifier.padding(start = 12.dp), + text = label, + style = MaterialTheme.typography.bodyLarge, + color = Grey30, ) } } +@Preview("로그인 한 유저 헤더") @Composable -fun SignoutButton( - modifier: Modifier = Modifier, - onClick: () -> Unit, -) { - Text( - modifier = modifier - .padding(bottom = 40.dp) - .clickable(onClick = onClick), - text = stringResource(id = R.string.signout), - style = MaterialTheme.typography.bodySmall.copy(color = Grey50), - textDecoration = TextDecoration.Underline, +private fun MyHeaderUserPreview() { + val user = User( + id = "", + nickname = "일이삼사오육칠팔구십", + email = "boolti@gmail.com", + photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" ) + BooltiTheme { + MyHeader(user = user) {} + } } + +@Preview("게스트 헤더") +@Composable +private fun MyHeaderGuestPreview() { + BooltiTheme { + MyHeader(user = null) {} + } +} + +@Preview +@Composable +private fun MyScreenPreview() { + val user = User( + id = "", + nickname = "불티유저", + email = "boolti@gmail.com", + photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + ) + BooltiTheme { + MyScreen( + user = user, + ) + } +} + +/* +* if (openLogoutDialog) { + BTDialog( + positiveButtonLabel = stringResource(id = R.string.my_logout), + onClickPositiveButton = { + openLogoutDialog = false + viewModel.logout() + }, + onDismiss = { openLogoutDialog = false } + ) { + Text( + modifier = Modifier.fillMaxWidth(), + text = stringResource(id = R.string.my_logout_popup), + textAlign = TextAlign.Center, + ) + } + } +* */ diff --git a/presentation/src/main/res/drawable/ic_list.xml b/presentation/src/main/res/drawable/ic_list.xml new file mode 100644 index 00000000..e80a2637 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_list.xml @@ -0,0 +1,38 @@ + + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_plus_ticket.xml b/presentation/src/main/res/drawable/ic_plus_ticket.xml new file mode 100644 index 00000000..5ed2f83f --- /dev/null +++ b/presentation/src/main/res/drawable/ic_plus_ticket.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_profile.xml b/presentation/src/main/res/drawable/ic_profile.xml new file mode 100644 index 00000000..5d0362f1 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_profile.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/presentation/src/main/res/drawable/ic_qr_simple.xml b/presentation/src/main/res/drawable/ic_qr_simple.xml new file mode 100644 index 00000000..7b113d09 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_qr_simple.xml @@ -0,0 +1,41 @@ + + + + + + + From 45d7ffa5e4e0cb480a1c4bc3f3f952804851e58c Mon Sep 17 00:00:00 2001 From: mangbaam Date: Sun, 21 Jul 2024 23:47:17 +0900 Subject: [PATCH 3/7] =?UTF-8?q?Boolti-264=20style:=20=EA=B3=84=EC=A0=95=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=ED=99=94=EB=A9=B4=20ui=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/network/response/UserResponse.kt | 2 + .../com/nexters/boolti/domain/model/User.kt | 1 + .../screen/my/AccountSettingScreen.kt | 260 ++++++++++++++++++ .../boolti/presentation/screen/my/MyScreen.kt | 25 +- presentation/src/main/res/values/strings.xml | 6 +- 5 files changed, 272 insertions(+), 22 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt diff --git a/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt b/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt index d5182e35..3ff9fdcc 100644 --- a/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt +++ b/data/src/main/java/com/nexters/boolti/data/network/response/UserResponse.kt @@ -9,6 +9,7 @@ internal data class UserResponse( val nickname: String? = null, val email: String? = null, val imgPath: String? = null, + val userCode: String? = null, ) { fun toDomain(): User { return User( @@ -16,6 +17,7 @@ internal data class UserResponse( nickname = nickname ?: "", email = email ?: "", photo = imgPath, + userCode = userCode ?: "", ) } } diff --git a/domain/src/main/java/com/nexters/boolti/domain/model/User.kt b/domain/src/main/java/com/nexters/boolti/domain/model/User.kt index c67b6dc9..790dfb77 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/model/User.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/model/User.kt @@ -5,4 +5,5 @@ data class User( val nickname: String = "", val email: String = "", val photo: String? = null, + val userCode: String, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt new file mode 100644 index 00000000..fce33805 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt @@ -0,0 +1,260 @@ +package com.nexters.boolti.presentation.screen.my + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +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.nexters.boolti.presentation.R +import com.nexters.boolti.presentation.component.BTDialog +import com.nexters.boolti.presentation.component.BtBackAppBar +import com.nexters.boolti.presentation.theme.BooltiTheme +import com.nexters.boolti.presentation.theme.Grey30 +import com.nexters.boolti.presentation.theme.Grey50 +import com.nexters.boolti.presentation.theme.KakaoYellow +import com.nexters.boolti.presentation.theme.marginHorizontal + +@Composable +fun AccountSettingScreen( + modifier: Modifier = Modifier, + viewModel: MyViewModel = hiltViewModel(), + navigateBack: () -> Unit, + requireLogout: () -> Unit, + onClickResign: () -> Unit, +) { + val user by viewModel.user.collectAsStateWithLifecycle() + + AccountSettingScreen( + modifier = modifier, + userCode = user?.userCode ?: "", + onClickBack = navigateBack, + requireLogout = requireLogout, + onClickResign = onClickResign, + ) +} + +@Composable +fun AccountSettingScreen( + modifier: Modifier = Modifier, + userCode: String, + onClickBack: () -> Unit = {}, + requireLogout: () -> Unit = {}, + onClickResign: () -> Unit = {}, +) { + var showLogoutDialog by remember { mutableStateOf(false) } + + Scaffold( + modifier = modifier, + topBar = { + BtBackAppBar( + title = stringResource(R.string.account_setting), + onClickBack = onClickBack, + ) + } + ) { innerPadding -> + Box( + modifier = modifier + .fillMaxSize() + .padding(innerPadding) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + + Section( + modifier = Modifier.padding(top = 20.dp), + ) { + Title(stringResource(R.string.user_code)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + text = "#$userCode", + color = Grey30, + ) + } + + Section { + Title(stringResource(R.string.sns_provider)) + KakaoChip(modifier = Modifier.padding(top = 16.dp)) + } + + Section( + modifier = Modifier.clickable { showLogoutDialog = true }, + ) { + Row( + modifier = Modifier.fillMaxWidth(), + ) { + Title( + modifier = Modifier.weight(1f), + title = stringResource(R.string.my_logout), + ) + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_arrow_right), + contentDescription = stringResource(R.string.my_logout), + tint = Grey50, + ) + } + } + } + + TextButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 30.dp), + onClick = onClickResign, + ) { + Text( + style = MaterialTheme.typography.bodyLarge, + text = stringResource(R.string.signout_button), + textDecoration = TextDecoration.Underline, + color = Grey50, + ) + } + + if (showLogoutDialog) { + BTDialog( + positiveButtonLabel = stringResource(id = R.string.my_logout), + onClickPositiveButton = { + showLogoutDialog = false + requireLogout() + }, + onDismiss = { showLogoutDialog = false } + ) { + Text( + modifier = Modifier.fillMaxWidth(), + text = stringResource(id = R.string.my_logout_popup), + textAlign = TextAlign.Center, + ) + } + } + } + } +} + +@Composable +private fun Section( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + modifier = modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surface) + .padding(vertical = 16.dp, horizontal = marginHorizontal), + content = content, + ) +} + +@Composable +private fun Title( + title: String, + modifier: Modifier = Modifier, +) { + Text( + modifier = modifier, + text = title, + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onBackground, + ) +} + +@Composable +private fun KakaoChip(modifier: Modifier = Modifier) { + SnsProvider( + modifier = modifier, + iconRes = R.drawable.ic_kakaotalk, + iconBackgroundColor = KakaoYellow, + label = stringResource(R.string.kakao), + ) +} + +@Composable +private fun SnsProvider( + @DrawableRes iconRes: Int, + iconBackgroundColor: Color, + label: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .border(width = 1.dp, color = MaterialTheme.colorScheme.outline, shape = CircleShape) + .padding(top = 6.dp, bottom = 6.dp, start = 6.dp, end = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .size(32.dp) + .clip(CircleShape) + .background(iconBackgroundColor), + contentAlignment = Alignment.Center, + ) { + Image( + modifier = Modifier + .size(20.dp) + .clip(CircleShape), + imageVector = ImageVector.vectorResource(iconRes), + contentDescription = label, + ) + } + Text( + modifier = Modifier.padding(start = 12.dp), + text = label, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } +} + +@Preview +@Composable +private fun KakaoChipPreview() { + BooltiTheme { + KakaoChip() + } +} + +@Preview +@Composable +private fun AccountSettingScreenPreview() { + BooltiTheme { + AccountSettingScreen( + userCode = "AB1800028", + onClickBack = {}, + requireLogout = {}, + ) { } + } +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index 927db8ee..d393e504 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -257,7 +257,8 @@ private fun MyHeaderUserPreview() { id = "", nickname = "일이삼사오육칠팔구십", email = "boolti@gmail.com", - photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + userCode = "AB1800028", ) BooltiTheme { MyHeader(user = user) {} @@ -279,7 +280,8 @@ private fun MyScreenPreview() { id = "", nickname = "불티유저", email = "boolti@gmail.com", - photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + userCode = "AB1800028", ) BooltiTheme { MyScreen( @@ -287,22 +289,3 @@ private fun MyScreenPreview() { ) } } - -/* -* if (openLogoutDialog) { - BTDialog( - positiveButtonLabel = stringResource(id = R.string.my_logout), - onClickPositiveButton = { - openLogoutDialog = false - viewModel.logout() - }, - onDismiss = { openLogoutDialog = false } - ) { - Text( - modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.my_logout_popup), - textAlign = TextAlign.Center, - ) - } - } -* */ diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 31f60a43..faf1cc42 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -20,6 +20,8 @@ 이 공연의 티켓이 아니에요 존재하지 않는 티켓이에요 + 카카오 + 이름을 올바르게 입력해 주세요 연락처를 올바르게 입력해 주세요 @@ -74,10 +76,11 @@ 불티를 찾아주셔서 감사합니다 불티 유저 탈퇴 후 30일 이내에 로그인하여, 계정 삭제가 취소되었어요\n불티를 다시 찾아주셔서 감사해요! + 식별 코드 회원 탈퇴 - 탈퇴하기 + 계정 삭제 탈퇴하시겠어요? 탈퇴일로부터 30일 이내로 로그인 시 계정 삭제를 취소할 수 있습니다. 30일이 지나면 계정 및 정보가 영구 삭제됩니다. 탈퇴 전, 꼭 읽어보세요! @@ -222,6 +225,7 @@ 프로필 보기 계정 설정 내 공연 + 연결 서비스 QR 스캔 From 57dcfa4ef02cf4921add04c14b576be94bb8c797 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Mon, 22 Jul 2024 01:35:27 +0900 Subject: [PATCH 4/7] =?UTF-8?q?Boolti-264=20feat:=20=EA=B3=84=EC=A0=95=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boolti/data/datasource/AuthDataSource.kt | 3 +++ .../com/nexters/boolti/data/db/AppSettings.kt | 1 + .../com/nexters/boolti/domain/model/User.kt | 2 +- .../boolti/presentation/extension/Flow.kt | 11 ++++++++ .../boolti/presentation/screen/Main.kt | 5 ++++ .../presentation/screen/MainDestination.kt | 4 ++- .../AccountSettingNavigation.kt | 19 ++++++++++++++ .../AccountSettingScreen.kt | 13 +++++++--- .../accountsetting/AccountSettingViewModel.kt | 26 +++++++++++++++++++ .../screen/home/HomeNavigation.kt | 4 +-- .../presentation/screen/home/HomeScreen.kt | 3 ++- .../boolti/presentation/screen/my/MyScreen.kt | 2 ++ 12 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/extension/Flow.kt create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt rename presentation/src/main/java/com/nexters/boolti/presentation/screen/{my => accountsetting}/AccountSettingScreen.kt (95%) create mode 100644 presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingViewModel.kt diff --git a/data/src/main/java/com/nexters/boolti/data/datasource/AuthDataSource.kt b/data/src/main/java/com/nexters/boolti/data/datasource/AuthDataSource.kt index aaa135a3..3c8a2c45 100644 --- a/data/src/main/java/com/nexters/boolti/data/datasource/AuthDataSource.kt +++ b/data/src/main/java/com/nexters/boolti/data/datasource/AuthDataSource.kt @@ -35,6 +35,7 @@ internal class AuthDataSource @Inject constructor( nickname = it.nickname ?: "", email = it.email ?: "", imgPath = it.photo, + userCode = it.userCode, ) } } @@ -61,6 +62,7 @@ internal class AuthDataSource @Inject constructor( email = null, phoneNumber = null, photo = null, + userCode = null, accessToken = "", refreshToken = "", ) @@ -80,6 +82,7 @@ internal class AuthDataSource @Inject constructor( nickname = user.nickname, email = user.email, photo = user.imgPath, + userCode = user.userCode, ) } } diff --git a/data/src/main/java/com/nexters/boolti/data/db/AppSettings.kt b/data/src/main/java/com/nexters/boolti/data/db/AppSettings.kt index 149ff4fd..1f488af7 100644 --- a/data/src/main/java/com/nexters/boolti/data/db/AppSettings.kt +++ b/data/src/main/java/com/nexters/boolti/data/db/AppSettings.kt @@ -15,6 +15,7 @@ internal data class AppSettings( val email: String? = null, val phoneNumber: String? = null, val photo: String? = null, + val userCode: String? = null, val accessToken: String = "", val refreshToken: String = "", val refundPolicy: List = emptyList(), diff --git a/domain/src/main/java/com/nexters/boolti/domain/model/User.kt b/domain/src/main/java/com/nexters/boolti/domain/model/User.kt index 790dfb77..a1868408 100644 --- a/domain/src/main/java/com/nexters/boolti/domain/model/User.kt +++ b/domain/src/main/java/com/nexters/boolti/domain/model/User.kt @@ -5,5 +5,5 @@ data class User( val nickname: String = "", val email: String = "", val photo: String? = null, - val userCode: String, + val userCode: String = "", ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/extension/Flow.kt b/presentation/src/main/java/com/nexters/boolti/presentation/extension/Flow.kt new file mode 100644 index 00000000..7855661c --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/extension/Flow.kt @@ -0,0 +1,11 @@ +package com.nexters.boolti.presentation.extension + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn + +fun Flow.stateInUi( + scope: CoroutineScope, + initialValue: T +) = stateIn(scope, SharingStarted.WhileSubscribed(5000), initialValue) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt index 17c3374d..097f8b6a 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/Main.kt @@ -25,6 +25,7 @@ import com.nexters.boolti.presentation.component.ToastSnackbarHost import com.nexters.boolti.presentation.extension.navigateToHome import com.nexters.boolti.presentation.screen.MainDestination.Home import com.nexters.boolti.presentation.screen.MainDestination.ShowDetail +import com.nexters.boolti.presentation.screen.accountsetting.AccountSettingScreen import com.nexters.boolti.presentation.screen.business.BusinessScreen import com.nexters.boolti.presentation.screen.gift.addGiftScreen import com.nexters.boolti.presentation.screen.giftcomplete.addGiftCompleteScreen @@ -192,6 +193,10 @@ fun MainNavigation(modifier: Modifier, onClickQrScan: (showId: String, showName: popBackStack = { navController.popBackStack(MainDestination.Gift.route, true)} ) BusinessScreen(popBackStack = navController::popBackStack) + AccountSettingScreen( + navigateTo = navController::navigateTo, + popBackStack = navController::popBackStack, + ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt index 4fbfab36..c2f17412 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/MainDestination.kt @@ -18,7 +18,8 @@ sealed class MainDestination(val route: String) { ) } - data object Gift : MainDestination(route = "gift/{$showId}?salesTicketId={$salesTicketId}&ticketCount={$ticketCount}") { + data object Gift : + MainDestination(route = "gift/{$showId}?salesTicketId={$salesTicketId}&ticketCount={$ticketCount}") { val arguments = listOf( navArgument(showId) { type = NavType.StringType }, navArgument(salesTicketId) { type = NavType.StringType }, @@ -70,6 +71,7 @@ sealed class MainDestination(val route: String) { data object SignOut : MainDestination(route = "signout") data object Login : MainDestination(route = "login") data object Business : MainDestination(route = "business") + data object AccountSetting : MainDestination(route = "accountSetting") } /** diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt new file mode 100644 index 00000000..846722b3 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingNavigation.kt @@ -0,0 +1,19 @@ +package com.nexters.boolti.presentation.screen.accountsetting + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.nexters.boolti.presentation.screen.MainDestination + +fun NavGraphBuilder.AccountSettingScreen( + navigateTo: (String) -> Unit, + popBackStack: () -> Unit, +) { + composable( + route = MainDestination.AccountSetting.route, + ) { + AccountSettingScreen( + navigateBack = popBackStack, + onClickResign = { navigateTo(MainDestination.SignOut.route) }, + ) + } +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt similarity index 95% rename from presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt rename to presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt index fce33805..11396d9d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/AccountSettingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt @@ -1,4 +1,4 @@ -package com.nexters.boolti.presentation.screen.my +package com.nexters.boolti.presentation.screen.accountsetting import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -21,6 +21,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton 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 @@ -50,18 +51,22 @@ import com.nexters.boolti.presentation.theme.marginHorizontal @Composable fun AccountSettingScreen( modifier: Modifier = Modifier, - viewModel: MyViewModel = hiltViewModel(), + viewModel: AccountSettingViewModel = hiltViewModel(), navigateBack: () -> Unit, - requireLogout: () -> Unit, onClickResign: () -> Unit, ) { val user by viewModel.user.collectAsStateWithLifecycle() + val loggedIn by viewModel.loggedIn.collectAsStateWithLifecycle() + + LaunchedEffect(loggedIn) { + if (loggedIn == false) navigateBack() + } AccountSettingScreen( modifier = modifier, userCode = user?.userCode ?: "", onClickBack = navigateBack, - requireLogout = requireLogout, + requireLogout = viewModel::logout, onClickResign = onClickResign, ) } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingViewModel.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingViewModel.kt new file mode 100644 index 00000000..cfcd36a0 --- /dev/null +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingViewModel.kt @@ -0,0 +1,26 @@ +package com.nexters.boolti.presentation.screen.accountsetting + +import androidx.lifecycle.viewModelScope +import com.nexters.boolti.domain.repository.AuthRepository +import com.nexters.boolti.presentation.base.BaseViewModel +import com.nexters.boolti.presentation.extension.stateInUi +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AccountSettingViewModel @Inject constructor( + private val repository: AuthRepository, +) : BaseViewModel() { + val user = repository.cachedUser + .stateInUi(viewModelScope, null) + + val loggedIn = repository.loggedIn + .stateInUi(viewModelScope, null) + + fun logout() { + viewModelScope.launch { + repository.logout() + } + } +} diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt index 15ef653d..f74ddc66 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeNavigation.kt @@ -17,10 +17,10 @@ fun NavGraphBuilder.HomeScreen( onClickShowItem = { navigateTo("${MainDestination.ShowDetail.route}/$it") }, onClickTicket = { navigateTo("${MainDestination.TicketDetail.route}/$it") }, onClickQrScan = { navigateTo(MainDestination.HostedShows.route) }, - onClickSignout = { navigateTo(MainDestination.SignOut.route) }, + onClickAccountSetting = { navigateTo(MainDestination.AccountSetting.route) }, navigateToReservations = { navigateTo(MainDestination.Reservations.route) }, navigateToBusiness = { navigateTo(MainDestination.Business.route) }, - requireLogin = { navigateTo(MainDestination.Login.route) } + requireLogin = { navigateTo(MainDestination.Login.route) }, ) } } diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt index 77174133..87eb1034 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/home/HomeScreen.kt @@ -46,7 +46,7 @@ fun HomeScreen( onClickShowItem: (showId: String) -> Unit, onClickTicket: (ticketId: String) -> Unit, onClickQrScan: () -> Unit, - onClickSignout: () -> Unit, + onClickAccountSetting: () -> Unit, navigateToReservations: () -> Unit, navigateToBusiness: () -> Unit, requireLogin: () -> Unit, @@ -123,6 +123,7 @@ fun HomeScreen( MyScreen( modifier = modifier.padding(innerPadding), requireLogin = requireLogin, + onClickAccountSetting = onClickAccountSetting, navigateToReservations = navigateToReservations, onClickQrScan = onClickQrScan, ) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index d393e504..435c8740 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -52,6 +52,7 @@ fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = hiltViewModel(), requireLogin: () -> Unit, + onClickAccountSetting: () -> Unit, navigateToReservations: () -> Unit, onClickQrScan: () -> Unit, ) { @@ -66,6 +67,7 @@ fun MyScreen( modifier = modifier, user = user, onClickHeaderButton = if (user != null) requireLogin else requireLogin, // TODO 프로필 구현 후 프로필 화면 이동 연결 + onClickAccountSetting = if (user != null) onClickAccountSetting else requireLogin, onClickReservations = if (user != null) navigateToReservations else requireLogin, onClickRegisterShow = { val url = if (user != null) "https://boolti.in/home" else "https://boolti.in/login" From 329e5c7ed7e9ebb3822ab2be19365c600b5e2b6a Mon Sep 17 00:00:00 2001 From: mangbaam Date: Mon, 22 Jul 2024 01:45:21 +0900 Subject: [PATCH 5/7] =?UTF-8?q?Boolti-264=20style:=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(=EA=B3=84=EC=A0=95=20=ED=83=88=ED=87=B4?= =?UTF-8?q?=20->=20=EA=B3=84=EC=A0=95=20=EC=82=AD=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../screen/accountsetting/AccountSettingScreen.kt | 2 +- presentation/src/main/res/values/strings.xml | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt index 11396d9d..afe8ce97 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt @@ -144,7 +144,7 @@ fun AccountSettingScreen( ) { Text( style = MaterialTheme.typography.bodyLarge, - text = stringResource(R.string.signout_button), + text = stringResource(R.string.signout), textDecoration = TextDecoration.Underline, color = Grey50, ) diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index faf1cc42..97739817 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -79,17 +79,15 @@ 식별 코드 - 회원 탈퇴 - 계정 삭제 - 탈퇴하시겠어요? - 탈퇴일로부터 30일 이내로 로그인 시 계정 삭제를 취소할 수 있습니다. 30일이 지나면 계정 및 정보가 영구 삭제됩니다. - 탈퇴 전, 꼭 읽어보세요! + 계정 삭제 + 삭제하기 + 삭제 전, 꼭 읽어보세요! 주최한 공연 정보는 사라지지 않아요 예매한 티켓은 전부 사라지며 복구할 수 없어요 탈퇴 일로부터 30일 이내 재 로그인 시 계정 삭제를 취소할 수 있어요 - 탈퇴 이유를 입력해주세요 + 삭제 이유를 입력해주세요 예) 계정 탈퇴 후 재가입할게요 %,d원 From 6b5c022c6736dd4b5cbca2575948ca6b7273f948 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Mon, 22 Jul 2024 23:51:48 +0900 Subject: [PATCH 6/7] =?UTF-8?q?Boolti-264=20style:=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(=EA=B3=84=EC=A0=95=20=ED=83=88=ED=87=B4?= =?UTF-8?q?=20->=20=EA=B3=84=EC=A0=95=20=EC=82=AD=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/src/main/res/values/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 97739817..94107dbc 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -75,7 +75,7 @@ 약관 동의하고 시작하기 불티를 찾아주셔서 감사합니다 불티 유저 - 탈퇴 후 30일 이내에 로그인하여, 계정 삭제가 취소되었어요\n불티를 다시 찾아주셔서 감사해요! + 30일 내에 로그인하여 계정 삭제가 취소되었어요.\n불티를 다시 찾아주셔서 감사해요! 식별 코드 @@ -85,10 +85,10 @@ 주최한 공연 정보는 사라지지 않아요 예매한 티켓은 전부 사라지며 복구할 수 없어요 - 탈퇴 일로부터 30일 이내 재 로그인 시 계정 삭제를 취소할 수 있어요 + 삭제일로부터 30일 이내 재 로그인 시 계정 삭제를 취소할 수 있어요 삭제 이유를 입력해주세요 - 예) 계정 탈퇴 후 재가입할게요 + 예) 계정 삭제 후 재가입할게요 %,d원 총 %,d원 From b90fa87be6ae85238a9edc3d8868b3159b173fc1 Mon Sep 17 00:00:00 2001 From: mangbaam Date: Tue, 23 Jul 2024 00:17:47 +0900 Subject: [PATCH 7/7] =?UTF-8?q?Boolti-264=20style:=20=EB=A7=88=EC=9D=B4,?= =?UTF-8?q?=20=EA=B3=84=EC=A0=95=20=EC=84=A4=EC=A0=95=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 2 +- .../accountsetting/AccountSettingScreen.kt | 9 +- .../boolti/presentation/screen/my/MyScreen.kt | 102 +++++++++++------- 3 files changed, 73 insertions(+), 40 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 54738dcd..3b416e10 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ targetJvm = "17" kotlin = "1.9.22" android = "8.2.2" ksp = "1.9.22-1.0.16" -composeBom = "2024.04.00" +composeBom = "2024.06.00" activity-ktx = "1.8.2" lifecycle = "2.7.0" diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt index afe8ce97..d682d09d 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/accountsetting/AccountSettingScreen.kt @@ -14,7 +14,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -80,6 +82,7 @@ fun AccountSettingScreen( onClickResign: () -> Unit = {}, ) { var showLogoutDialog by remember { mutableStateOf(false) } + val scrollState = rememberScrollState() Scaffold( modifier = modifier, @@ -96,9 +99,9 @@ fun AccountSettingScreen( .padding(innerPadding) ) { Column( + modifier = Modifier.verticalScroll(scrollState), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - Section( modifier = Modifier.padding(top = 20.dp), ) { @@ -118,7 +121,9 @@ fun AccountSettingScreen( } Section( - modifier = Modifier.clickable { showLogoutDialog = true }, + modifier = Modifier + .padding(bottom = 100.dp) + .clickable { showLogoutDialog = true }, ) { Row( modifier = Modifier.fillMaxWidth(), diff --git a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt index 435c8740..e5c6adeb 100644 --- a/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt +++ b/presentation/src/main/java/com/nexters/boolti/presentation/screen/my/MyScreen.kt @@ -9,10 +9,13 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -34,6 +37,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage @@ -87,56 +91,63 @@ fun MyScreen( onClickRegisterShow: () -> Unit = { }, onClickQrScan: () -> Unit = {}, ) { + val scrollState = rememberScrollState() + Surface { Box( modifier = modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background), ) { - Column( - modifier = modifier, - ) { + Column { MyHeader( + modifier = Modifier.zIndex(1f), user = user, onClickButton = onClickHeaderButton, ) - MyMenu( - modifier = Modifier.padding(top = 20.dp), - iconRes = R.drawable.ic_profile, - label = stringResource(R.string.account_setting), - onClick = onClickAccountSetting, - ) - MyMenu( - iconRes = R.drawable.ic_list, - label = stringResource(R.string.my_ticketing_history), - onClick = onClickReservations - ) + Column( + modifier = Modifier + .offset(y = (-12).dp) + .verticalScroll(scrollState), + ) { + MyMenu( + modifier = Modifier.padding(top = 32.dp), + iconRes = R.drawable.ic_profile, + label = stringResource(R.string.account_setting), + onClick = onClickAccountSetting, + ) + MyMenu( + iconRes = R.drawable.ic_list, + label = stringResource(R.string.my_ticketing_history), + onClick = onClickReservations + ) - HorizontalDivider( - modifier = Modifier.padding(vertical = 32.dp, horizontal = marginHorizontal), - color = MaterialTheme.colorScheme.surfaceTint, - ) + HorizontalDivider( + modifier = Modifier.padding(vertical = 32.dp, horizontal = marginHorizontal), + color = MaterialTheme.colorScheme.surfaceTint, + ) - Text( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = marginHorizontal), - text = stringResource(R.string.my_show), - style = MaterialTheme.typography.titleLarge, - color = MaterialTheme.colorScheme.onBackground, - ) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = marginHorizontal), + text = stringResource(R.string.my_show), + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onBackground, + ) - MyMenu( - modifier = Modifier.padding(top = 8.dp), - iconRes = R.drawable.ic_plus_ticket, - label = stringResource(R.string.my_register_show), - onClick = onClickRegisterShow, - ) - MyMenu( - iconRes = R.drawable.ic_qr_simple, - label = stringResource(R.string.my_scan_qr), - onClick = onClickQrScan, - ) + MyMenu( + modifier = Modifier.padding(top = 8.dp), + iconRes = R.drawable.ic_plus_ticket, + label = stringResource(R.string.my_register_show), + onClick = onClickRegisterShow, + ) + MyMenu( + iconRes = R.drawable.ic_qr_simple, + label = stringResource(R.string.my_scan_qr), + onClick = onClickQrScan, + ) + } } Column( modifier = Modifier @@ -291,3 +302,20 @@ private fun MyScreenPreview() { ) } } + +@Preview(device = "spec:parent=pixel_5,orientation=landscape") +@Composable +private fun MyScreenLandscapePreview() { + val user = User( + id = "", + nickname = "불티유저", + email = "boolti@gmail.com", + photo = "https://images.unsplash.com/photo-1721497684662-cf36f0ee232e?q=80&w=4965&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + userCode = "AB1800028", + ) + BooltiTheme { + MyScreen( + user = user, + ) + } +}