From 6339a75439a35c8ab98f718e1e95e450cb92f5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 21 Aug 2024 22:15:54 +0900 Subject: [PATCH 01/23] =?UTF-8?q?Step1:=20LazyColumn=20Test=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/payments/LazyColumnTest.kt | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 app/src/androidTest/java/nextstep/payments/LazyColumnTest.kt diff --git a/app/src/androidTest/java/nextstep/payments/LazyColumnTest.kt b/app/src/androidTest/java/nextstep/payments/LazyColumnTest.kt new file mode 100644 index 00000000..270c99a5 --- /dev/null +++ b/app/src/androidTest/java/nextstep/payments/LazyColumnTest.kt @@ -0,0 +1,99 @@ +package nextstep.payments + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Text +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.assertCountEquals +import androidx.compose.ui.test.junit4.ComposeTestRule +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithTag +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performScrollToIndex +import org.junit.Rule +import org.junit.Test + +class LazyColumnTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun column() { + composeTestRule.setContent { + Column { + items.forEach { + Text( + text = it, + modifier = Modifier.testTag("text") + ) + } + } + } + + composeTestRule + .onAllNodesWithTag("text") + .assertCountEquals(10000) + } + + @Test + fun lazyColumn() { + composeTestRule.setContent { + LazyColumn(modifier = Modifier.testTag("lazyColumn")) { + items(items.size) { + Text( + text = items[it], + modifier = Modifier.testTag("text") + ) + } + } + } + val nodeSize = composeTestRule.nodeSize("text") + + composeTestRule + .onNodeWithTag("lazyColumn") + .performScrollToIndex(9999) + + val nodeSizeAfterScroll = composeTestRule.nodeSize("text") + assert(nodeSize == nodeSizeAfterScroll) + assert(nodeSizeAfterScroll < 10000) + } + + @Test + fun lazyColumn2() { + composeTestRule.setContent { + LazyColumn(modifier = Modifier.testTag("lazyColumn")) { + items(items.size) { + Text( + text = items[it], + modifier = Modifier.testTag("text $it") + ) + } + } + } + composeTestRule + .onNodeWithTag("text 0") + .assertExists() + + composeTestRule + .onNodeWithTag("lazyColumn") + .performScrollToIndex(9999) + + composeTestRule + .onNodeWithTag("text 0") + .assertDoesNotExist() + + composeTestRule + .onNodeWithTag("text 9999") + .assertExists() + } + + private fun ComposeTestRule.nodeSize(textTag: String): Int = onAllNodesWithTag(textTag) + .fetchSemanticsNodes() + .size + + companion object { + private val items = List(10000) { "Item $it" } + } +} From 756cf597fd4f64c78d52d087ff2d8c8ad436c95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 21 Aug 2024 22:17:13 +0900 Subject: [PATCH 02/23] =?UTF-8?q?Step1:=20ViewModel=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/NewCardViewModel.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt diff --git a/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt new file mode 100644 index 00000000..d8bb4f7c --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt @@ -0,0 +1,37 @@ +package nextstep.payments.ui.screen + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class NewCardViewModel : ViewModel() { + + private val _cardNumber = MutableStateFlow("") + val cardNumber: StateFlow = _cardNumber.asStateFlow() + + private val _expiredDate = MutableStateFlow("") + val expiredDate: StateFlow = _expiredDate.asStateFlow() + + private val _ownerName = MutableStateFlow("") + val ownerName: StateFlow = _ownerName.asStateFlow() + + private val _password = MutableStateFlow("") + val password: StateFlow = _password.asStateFlow() + + fun setCardNumber(cardNumber: String) { + _cardNumber.value = cardNumber + } + + fun setExpiredDate(expiredDate: String) { + _expiredDate.value = expiredDate + } + + fun setOwnerName(ownerName: String) { + _ownerName.value = ownerName + } + + fun setPassword(password: String) { + _password.value = password + } +} From 2ffea4697060940e02a17e251b3200456ffed312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 21 Aug 2024 22:18:30 +0900 Subject: [PATCH 03/23] =?UTF-8?q?Step1:=20Card=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/component/PaymentCard.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt diff --git a/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt new file mode 100644 index 00000000..52690c30 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt @@ -0,0 +1,39 @@ +package nextstep.payments.ui.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun PaymentCard( + modifier: Modifier = Modifier, +) { + Box( + contentAlignment = Alignment.CenterStart, + modifier = modifier + .shadow(8.dp) + .size(width = 208.dp, height = 124.dp) + .background( + color = Color(0xFF333333), + shape = RoundedCornerShape(5.dp), + ) + ) { + Box( + modifier = Modifier + .padding(start = 14.dp, bottom = 10.dp) + .size(width = 40.dp, height = 26.dp) + .background( + color = Color(0xFFCBBA64), + shape = RoundedCornerShape(4.dp), + ) + ) + } +} From f4fb31d62ee5050a4a5b53452e2a41fe4643376d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 21 Aug 2024 22:18:46 +0900 Subject: [PATCH 04/23] =?UTF-8?q?Step1:=20TopBar=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/component/NewCardTopBar.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/component/NewCardTopBar.kt diff --git a/app/src/main/java/nextstep/payments/ui/component/NewCardTopBar.kt b/app/src/main/java/nextstep/payments/ui/component/NewCardTopBar.kt new file mode 100644 index 00000000..13b6bcb3 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/component/NewCardTopBar.kt @@ -0,0 +1,41 @@ +package nextstep.payments.ui.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun NewCardTopBar( + onBackClick: () -> Unit, + onSaveClick: () -> Unit, + modifier: Modifier = Modifier, +) { + TopAppBar( + title = { Text("카드 추가") }, + navigationIcon = { + IconButton(onClick = { onBackClick() }) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "뒤로 가기", + ) + } + }, + actions = { + IconButton(onClick = { onSaveClick() }) { + Icon( + imageVector = Icons.Filled.Check, + contentDescription = "완료", + ) + } + }, + modifier = modifier + ) +} From 9859a80aa6f88ee6d52fb727373bc5e0b3232b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 21 Aug 2024 22:19:02 +0900 Subject: [PATCH 05/23] =?UTF-8?q?Step1:=20NewCardScreen=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/NewCardScreen.kt | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt diff --git a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt new file mode 100644 index 00000000..9ad4ac18 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt @@ -0,0 +1,149 @@ +package nextstep.payments.ui.screen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import nextstep.payments.ui.component.NewCardTopBar +import nextstep.payments.ui.component.PaymentCard +import nextstep.payments.ui.theme.PaymentsTheme + + +//Stateful한 NewCardScreen +@Composable +internal fun NewCardScreen( + modifier: Modifier = Modifier, + viewModel: NewCardViewModel = viewModel(), +) { + val cardNumber by viewModel.cardNumber.collectAsStateWithLifecycle() + val expiredDate by viewModel.expiredDate.collectAsStateWithLifecycle() + val ownerName by viewModel.ownerName.collectAsStateWithLifecycle() + val password by viewModel.password.collectAsStateWithLifecycle() + + NewCardScreen( + cardNumber = cardNumber, + expiredDate = expiredDate, + ownerName = ownerName, + password = password, + setCardNumber = viewModel::setCardNumber, + setExpiredDate = viewModel::setExpiredDate, + setOwnerName = viewModel::setOwnerName, + setPassword = viewModel::setPassword, + modifier = modifier, + ) +} + +//Stateless한 NewCardScreen +@Composable +private fun NewCardScreen( + cardNumber: String, + expiredDate: String, + ownerName: String, + password: String, + setCardNumber: (String) -> Unit, + setExpiredDate: (String) -> Unit, + setOwnerName: (String) -> Unit, + setPassword: (String) -> Unit, + modifier: Modifier = Modifier, +) { + Scaffold( + topBar = { NewCardTopBar(onBackClick = { TODO() }, onSaveClick = { TODO() }) }, + modifier = modifier + ) { innerPadding -> + Column( + verticalArrangement = Arrangement.spacedBy(18.dp), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding(innerPadding) + .padding(horizontal = 24.dp) + ) { + Spacer(modifier = Modifier.height(14.dp)) + + PaymentCard() + + Spacer(modifier = Modifier.height(10.dp)) + + OutlinedTextField( + value = cardNumber, + onValueChange = { setCardNumber(it) }, + label = { Text("카드 번호") }, + placeholder = { Text("0000 - 0000 - 0000 - 0000") }, + modifier = Modifier.fillMaxWidth(), + ) + + OutlinedTextField( + value = expiredDate, + onValueChange = { setExpiredDate(it) }, + label = { Text("만료일") }, + placeholder = { Text("MM / YY") }, + modifier = Modifier.fillMaxWidth(), + ) + + OutlinedTextField( + value = ownerName, + onValueChange = { setOwnerName(it) }, + label = { Text("카드 소유자 이름(선택)") }, + placeholder = { Text("카드에 표시된 이름을 입력하세요.") }, + modifier = Modifier.fillMaxWidth(), + ) + + OutlinedTextField( + value = password, + onValueChange = { setPassword(it) }, + label = { Text("비밀번호") }, + placeholder = { Text("0000") }, + modifier = Modifier.fillMaxWidth(), + visualTransformation = PasswordVisualTransformation(), + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun NewCardScreenPreview() { + PaymentsTheme { + NewCardScreen( + viewModel = NewCardViewModel().apply { + setCardNumber( + "1234 - 5678 - 1234 - 5678" + ) + setExpiredDate("00 / 00") + setOwnerName("홍길동") + setPassword("password") + } + ) + } +} + + +@Preview(showBackground = true) +@Composable +fun StatelessNewCardScreenPreview() { + PaymentsTheme { + NewCardScreen( + cardNumber = "1234 - 5678 - 1234 - 5678", + expiredDate = "00 / 00", + ownerName = "홍길동", + password = "password", + setCardNumber = { }, + setExpiredDate = { }, + setOwnerName = { }, + setPassword = { }, + ) + } +} From 45aa975ec6af01f867627a673586f74ec7f1a1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 21 Aug 2024 22:19:15 +0900 Subject: [PATCH 06/23] =?UTF-8?q?Step1:=20MainActivity=EC=97=90=20Screen?= =?UTF-8?q?=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/payments/MainActivity.kt | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nextstep/payments/MainActivity.kt b/app/src/main/java/nextstep/payments/MainActivity.kt index d6d9bca1..39aa3ada 100644 --- a/app/src/main/java/nextstep/payments/MainActivity.kt +++ b/app/src/main/java/nextstep/payments/MainActivity.kt @@ -10,6 +10,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import nextstep.payments.ui.screen.NewCardScreen +import nextstep.payments.ui.screen.NewCardViewModel import nextstep.payments.ui.theme.PaymentsTheme class MainActivity : ComponentActivity() { @@ -22,25 +24,10 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - Greeting("Android") + NewCardScreen() } } } } } -@Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { - Text( - text = "Hello $name!", - modifier = modifier - ) -} - -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - PaymentsTheme { - Greeting("Android") - } -} From 254b053969d1cd710ebbdc9a0c07280d35d5b74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 18:18:53 +0900 Subject: [PATCH 07/23] =?UTF-8?q?Step2:=20Navigation=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9996218f..fb434ec6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.lifecycle.runtime.compose) + implementation(libs.androidx.navigation.compose) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ee0e55c2..425522a0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,9 +8,11 @@ espressoCore = "3.6.1" lifecycleRuntimeKtx = "2.8.4" activityCompose = "1.9.1" composeBom = "2024.06.00" +navigationCompose = "2.7.7" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } From c00bc0e297698e59e9c83ca88fb5e68e64fc6d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 18:20:31 +0900 Subject: [PATCH 08/23] =?UTF-8?q?Step2:=20=EA=B8=B0=EB=B3=B8=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C,=20=EC=B9=B4=EB=93=9C=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EB=8D=94=EB=AF=B8=EC=B9=B4=EB=93=9C=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 카드 추가시 보여지는 카드 컴포저블 이름 변경 - 카드 추가를 위한 디자인 추가 - 카드가 추가 되면 보여주기 위한 카드 더미 컴포저블 추가 --- .../payments/ui/component/PaymentCard.kt | 135 ++++++++++++++++-- .../payments/ui/screen/NewCardScreen.kt | 4 +- 2 files changed, 126 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt index 52690c30..73698599 100644 --- a/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt @@ -1,20 +1,109 @@ package nextstep.payments.ui.component import androidx.compose.foundation.background +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +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.draw.shadow import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp @Composable -fun PaymentCard( +fun AddPaymentCard( modifier: Modifier = Modifier, +) { + DefaultCard( + modifier = modifier, + color =Color(0xFFE5E5E5), + content = { + Box( + modifier = Modifier + .fillMaxSize() + .clickable { /* Click Action */ }, + contentAlignment = Alignment.Center + ) { + Icon(imageVector = Icons.Default.Add, contentDescription = "Add") + } + } + ) +} + +@Composable +fun DefaultPaymentCard( + modifier: Modifier = Modifier, +) { + DefaultCard( + modifier = modifier, + content = { + CardIcChip(Modifier.padding(start = 14.dp, bottom = 10.dp)) + } + ) +} + +@Composable +fun RegisteredPaymentCard( + modifier: Modifier = Modifier, +) { + DefaultCard( + modifier = modifier, + content = { + + Column( + Modifier + .fillMaxSize() + .padding(start = 14.dp, end = 14.dp, top = 44.dp, bottom = 16.dp), + verticalArrangement = Arrangement.SpaceBetween + ) { + CardIcChip() + Text( + modifier = modifier.fillMaxWidth(), + text = "1234-5678-1234-5678", + fontSize = 12.sp, + color = Color.White, + maxLines = 1 + ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "JINHYUK JANG", + color = Color.White, + fontSize = 12.sp + ) + Text( + modifier = modifier, + text = "00/00", + fontSize = 12.sp, + color = Color.White, + ) + } + } + } + ) +} + +@Composable +fun DefaultCard( + modifier: Modifier = Modifier, + color:Color = Color(0xFF333333), + content: @Composable () -> Unit = {} ) { Box( contentAlignment = Alignment.CenterStart, @@ -22,18 +111,42 @@ fun PaymentCard( .shadow(8.dp) .size(width = 208.dp, height = 124.dp) .background( - color = Color(0xFF333333), + color = color, shape = RoundedCornerShape(5.dp), ) ) { - Box( - modifier = Modifier - .padding(start = 14.dp, bottom = 10.dp) - .size(width = 40.dp, height = 26.dp) - .background( - color = Color(0xFFCBBA64), - shape = RoundedCornerShape(4.dp), - ) - ) + content() } } + +@Composable +fun CardIcChip( + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .size(width = 40.dp, height = 26.dp) + .background( + color = Color(0xFFCBBA64), + shape = RoundedCornerShape(4.dp), + ) + ) +} + +@Preview(showBackground = true) +@Composable +private fun AddPaymentCardPreview() { + AddPaymentCard() +} + +@Preview(showBackground = true) +@Composable +private fun DefaultPaymentCardPreview() { + DefaultPaymentCard() +} + +@Preview(showBackground = true) +@Composable +private fun RegisteredPaymentCardPreview() { + RegisteredPaymentCard() +} diff --git a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt index 9ad4ac18..3c606acd 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import nextstep.payments.ui.component.NewCardTopBar -import nextstep.payments.ui.component.PaymentCard +import nextstep.payments.ui.component.DefaultPaymentCard import nextstep.payments.ui.theme.PaymentsTheme @@ -73,7 +73,7 @@ private fun NewCardScreen( ) { Spacer(modifier = Modifier.height(14.dp)) - PaymentCard() + DefaultPaymentCard() Spacer(modifier = Modifier.height(10.dp)) From 09d11d86224b77585ba6fa1834bed31be8d8a17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 18:32:40 +0900 Subject: [PATCH 09/23] =?UTF-8?q?Step2:=20=ED=8E=98=EC=9D=B4=EB=A8=BC?= =?UTF-8?q?=EC=B8=A0=20=EC=B9=B4=EB=93=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EB=94=94=EC=9E=90=EC=9D=B8=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 - 0,1,2 에 따라서 카드 리스트 및 추가 화면이 보이도록 구성 --- .../payments/ui/screen/PaymentCardsScreen.kt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt new file mode 100644 index 00000000..1ea95f1b --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt @@ -0,0 +1,115 @@ +package nextstep.payments.ui.screen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import nextstep.payments.ui.component.AddPaymentCard +import nextstep.payments.ui.component.RegisteredPaymentCard + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PaymentCardsScreen( + onAddCardClick: () -> Unit +) { + val visible = false + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { + Text( + text = "Payments", + fontSize = 22.sp + ) + }, + actions = { + if (visible) { + TextButton(onClick = { onAddCardClick() }) { + Text( + text = "추가", + color = Color.Black, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + ) + } + } + }, + ) + } + ) { paddingModifier -> + PaymentCardList( + modifier = Modifier.padding(paddingModifier), + onAddCardClick = onAddCardClick, + ) + } +} + + +@Composable +fun PaymentCardList( + modifier: Modifier = Modifier, + onAddCardClick: () -> Unit, +) { + LazyColumn( + modifier = modifier + .fillMaxWidth() + .padding(top = 12.dp), + verticalArrangement = Arrangement.spacedBy(30.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val testValue = 2 + when (testValue) { + 0 -> { + item { + Text( + modifier = modifier.fillMaxWidth(), + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + text = "새로운 카드를 등록해주세요", + textAlign = TextAlign.Center + ) + } + item { + AddPaymentCard() + } + } + + 1 -> { + item { + RegisteredPaymentCard() + } + item { + AddPaymentCard() + } + } + + 2 -> { + items(4) { + RegisteredPaymentCard() + } + } + } + } +} + +@Preview +@Composable +fun PaymentCardsScreenPreview() { + PaymentCardsScreen( + onAddCardClick = {} + ) +} From 928c4f521d5ead35482c73142bb01f5da5c1acd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 18:49:07 +0900 Subject: [PATCH 10/23] =?UTF-8?q?Step2:=20Card=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/payments/ui/model/PaymentCardModel.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt diff --git a/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt b/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt new file mode 100644 index 00000000..016ea0f0 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt @@ -0,0 +1,9 @@ +package nextstep.payments.ui.model + +data class PaymentCardModel( + val cardNumber: String, + val ownerName: String, + val expiredDate: String, + val cvcNumber: String, + val password: String +) From 7c77fd62d1af096832d5f42b87aca67eb40697be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 18:49:22 +0900 Subject: [PATCH 11/23] =?UTF-8?q?Step2:=20=EC=B9=B4=EB=93=9C=20UI=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/payments/ui/state/PaymentCardUiState.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/state/PaymentCardUiState.kt diff --git a/app/src/main/java/nextstep/payments/ui/state/PaymentCardUiState.kt b/app/src/main/java/nextstep/payments/ui/state/PaymentCardUiState.kt new file mode 100644 index 00000000..1e1c79f8 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/state/PaymentCardUiState.kt @@ -0,0 +1,9 @@ +package nextstep.payments.ui.state + +import nextstep.payments.ui.model.PaymentCardModel + +sealed interface PaymentCardUiState { + data object Empty : PaymentCardUiState + data class One(val card: PaymentCardModel) : PaymentCardUiState + data class Many(val cards: List): PaymentCardUiState +} From ed6884a1ee68b66032efcb0acc4a932549035791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:07:29 +0900 Subject: [PATCH 12/23] =?UTF-8?q?Step2:=20CardViewModel=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/PaymentCardsViewModel.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt new file mode 100644 index 00000000..03a06e9d --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt @@ -0,0 +1,12 @@ +package nextstep.payments.ui.screen + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import nextstep.payments.ui.state.PaymentCardUiState + +class PaymentCardsViewModel : ViewModel() { + private val _cardsScreenState = MutableStateFlow(PaymentCardUiState.Empty) + val cardsScreenState: StateFlow = _cardsScreenState.asStateFlow() +} From 2e4141375ba0b3431666331a06afc32bf6b89a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:07:41 +0900 Subject: [PATCH 13/23] =?UTF-8?q?Step2:=20UiState=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/PaymentCardsScreen.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt index 1ea95f1b..c0fa9ebd 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt @@ -10,6 +10,8 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton 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.graphics.Color @@ -18,15 +20,19 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel import nextstep.payments.ui.component.AddPaymentCard import nextstep.payments.ui.component.RegisteredPaymentCard +import nextstep.payments.ui.state.PaymentCardUiState @OptIn(ExperimentalMaterial3Api::class) @Composable fun PaymentCardsScreen( + viewModel: PaymentCardsViewModel = viewModel(), onAddCardClick: () -> Unit ) { val visible = false + val uiState by viewModel.cardsScreenState.collectAsState() Scaffold( topBar = { CenterAlignedTopAppBar( @@ -53,6 +59,7 @@ fun PaymentCardsScreen( ) { paddingModifier -> PaymentCardList( modifier = Modifier.padding(paddingModifier), + uiState = uiState, onAddCardClick = onAddCardClick, ) } @@ -62,6 +69,7 @@ fun PaymentCardsScreen( @Composable fun PaymentCardList( modifier: Modifier = Modifier, + uiState: PaymentCardUiState, onAddCardClick: () -> Unit, ) { LazyColumn( @@ -71,9 +79,8 @@ fun PaymentCardList( verticalArrangement = Arrangement.spacedBy(30.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - val testValue = 2 - when (testValue) { - 0 -> { + when (uiState) { + PaymentCardUiState.Empty -> { item { Text( modifier = modifier.fillMaxWidth(), @@ -88,7 +95,7 @@ fun PaymentCardList( } } - 1 -> { + is PaymentCardUiState.One -> { item { RegisteredPaymentCard() } @@ -97,8 +104,8 @@ fun PaymentCardList( } } - 2 -> { - items(4) { + is PaymentCardUiState.Many -> { + items(uiState.cards.size) { RegisteredPaymentCard() } } From 17223cef04c5c23acfcb52369af3094b77175474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:25:19 +0900 Subject: [PATCH 14/23] =?UTF-8?q?Step2:=20=EC=B9=B4=EB=93=9C=20=EB=A0=88?= =?UTF-8?q?=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/domain/PaymentCardsRepository.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/domain/PaymentCardsRepository.kt diff --git a/app/src/main/java/nextstep/payments/domain/PaymentCardsRepository.kt b/app/src/main/java/nextstep/payments/domain/PaymentCardsRepository.kt new file mode 100644 index 00000000..e3442c14 --- /dev/null +++ b/app/src/main/java/nextstep/payments/domain/PaymentCardsRepository.kt @@ -0,0 +1,13 @@ +package nextstep.payments.domain + +import nextstep.payments.ui.model.PaymentCardModel + +object PaymentCardsRepository { + + private val _cards = mutableListOf() + val cards: List get() = _cards.toList() + + fun addCard(card: PaymentCardModel) { + _cards.add(card) + } +} From 9dcd6246e8efa04f739636ab9449a625468d0d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:25:44 +0900 Subject: [PATCH 15/23] =?UTF-8?q?Step2:=20NewCardViewModel=EC=97=90=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EC=B6=94=EA=B0=80=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/NewCardViewModel.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt index d8bb4f7c..5e9a4969 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/NewCardViewModel.kt @@ -4,8 +4,15 @@ import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import nextstep.payments.domain.PaymentCardsRepository +import nextstep.payments.ui.model.PaymentCardModel -class NewCardViewModel : ViewModel() { +class NewCardViewModel( + private val repository: PaymentCardsRepository = PaymentCardsRepository +) : ViewModel() { + + private val _cardAdded = MutableStateFlow(false) + val cardAdded: StateFlow = _cardAdded.asStateFlow() private val _cardNumber = MutableStateFlow("") val cardNumber: StateFlow = _cardNumber.asStateFlow() @@ -34,4 +41,16 @@ class NewCardViewModel : ViewModel() { fun setPassword(password: String) { _password.value = password } + + fun addCard() { + repository.addCard( + PaymentCardModel( + cardNumber = cardNumber.value, + expiredDate = expiredDate.value, + ownerName = ownerName.value, + password = password.value + ) + ) + _cardAdded.value = true + } } From 64e0c2c42a31d279998f376c5952e2ac627a6792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:27:01 +0900 Subject: [PATCH 16/23] =?UTF-8?q?Step2:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=B9=B4=EB=93=9C=20=EB=AA=A8=EB=8D=B8=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt b/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt index 016ea0f0..442e5c76 100644 --- a/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt +++ b/app/src/main/java/nextstep/payments/ui/model/PaymentCardModel.kt @@ -4,6 +4,5 @@ data class PaymentCardModel( val cardNumber: String, val ownerName: String, val expiredDate: String, - val cvcNumber: String, val password: String ) From 6c434fc9ed412c1ce835f27ba3cae9e231467954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:39:13 +0900 Subject: [PATCH 17/23] =?UTF-8?q?Step2:=20Navigation=20Model=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/payments/ui/navigation/NavigationModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/navigation/NavigationModel.kt diff --git a/app/src/main/java/nextstep/payments/ui/navigation/NavigationModel.kt b/app/src/main/java/nextstep/payments/ui/navigation/NavigationModel.kt new file mode 100644 index 00000000..4340d106 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/navigation/NavigationModel.kt @@ -0,0 +1,6 @@ +package nextstep.payments.ui.navigation + +sealed class NavigationModel(val route: String) { + data object AddPaymentCard : NavigationModel("AddCard") + data object PaymentCards : NavigationModel("PaymentCards") +} From 14d7ddb8a01e812ccdd2f7ea12007ee26256b34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:42:18 +0900 Subject: [PATCH 18/23] =?UTF-8?q?Step2:=20PaymentCardScreen=EC=97=90=20?= =?UTF-8?q?=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/PaymentCardsScreen.kt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt index c0fa9ebd..e90cb002 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt @@ -25,14 +25,26 @@ import nextstep.payments.ui.component.AddPaymentCard import nextstep.payments.ui.component.RegisteredPaymentCard import nextstep.payments.ui.state.PaymentCardUiState +@Composable +fun PaymentCardsScreenRoute( + onAddCardClick: () -> Unit, + viewModel: PaymentCardsViewModel = viewModel(), +) { + val uiState by viewModel.cardsScreenState.collectAsState() + PaymentCardsScreen( + uiState = uiState, + onAddCardClick = onAddCardClick + ) +} + + @OptIn(ExperimentalMaterial3Api::class) @Composable fun PaymentCardsScreen( - viewModel: PaymentCardsViewModel = viewModel(), + uiState: PaymentCardUiState, onAddCardClick: () -> Unit ) { val visible = false - val uiState by viewModel.cardsScreenState.collectAsState() Scaffold( topBar = { CenterAlignedTopAppBar( @@ -117,6 +129,7 @@ fun PaymentCardList( @Composable fun PaymentCardsScreenPreview() { PaymentCardsScreen( + uiState = PaymentCardUiState.Empty, onAddCardClick = {} ) } From b6b58320767b7c3468318a0bd1e451e86f09f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:42:35 +0900 Subject: [PATCH 19/23] =?UTF-8?q?Step2:=20NewCardScreen=EC=97=90=20?= =?UTF-8?q?=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/screen/NewCardScreen.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt index 3c606acd..f258ef2d 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt @@ -18,15 +18,26 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel -import nextstep.payments.ui.component.NewCardTopBar import nextstep.payments.ui.component.DefaultPaymentCard +import nextstep.payments.ui.component.NewCardTopBar import nextstep.payments.ui.theme.PaymentsTheme +@Composable +fun NewCardScreenRoute( + onBackClick: () -> Unit, + viewModel: NewCardViewModel = viewModel(), +) { + NewCardScreen( + onBackClick = onBackClick, + viewModel = viewModel, + ) +} //Stateful한 NewCardScreen @Composable internal fun NewCardScreen( modifier: Modifier = Modifier, + onBackClick: () -> Unit, viewModel: NewCardViewModel = viewModel(), ) { val cardNumber by viewModel.cardNumber.collectAsStateWithLifecycle() @@ -44,6 +55,7 @@ internal fun NewCardScreen( setOwnerName = viewModel::setOwnerName, setPassword = viewModel::setPassword, modifier = modifier, + onBackClick = onBackClick, ) } @@ -59,9 +71,10 @@ private fun NewCardScreen( setOwnerName: (String) -> Unit, setPassword: (String) -> Unit, modifier: Modifier = Modifier, + onBackClick: () -> Unit, ) { Scaffold( - topBar = { NewCardTopBar(onBackClick = { TODO() }, onSaveClick = { TODO() }) }, + topBar = { NewCardTopBar(onBackClick = { onBackClick() }, onSaveClick = { TODO() }) }, modifier = modifier ) { innerPadding -> Column( @@ -125,7 +138,8 @@ fun NewCardScreenPreview() { setExpiredDate("00 / 00") setOwnerName("홍길동") setPassword("password") - } + }, + onBackClick = { } ) } } @@ -144,6 +158,7 @@ fun StatelessNewCardScreenPreview() { setExpiredDate = { }, setOwnerName = { }, setPassword = { }, + onBackClick = { } ) } } From 489bc428236277782828e18a837288ab88facc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:42:53 +0900 Subject: [PATCH 20/23] =?UTF-8?q?Step2:=20=EB=82=B4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=ED=98=B8=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/navigation/PaymentsNavigationHost.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt diff --git a/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt b/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt new file mode 100644 index 00000000..bb5404b6 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt @@ -0,0 +1,29 @@ +package nextstep.payments.ui.navigation + +import androidx.compose.runtime.Composable +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import nextstep.payments.ui.screen.NewCardScreenRoute +import nextstep.payments.ui.screen.PaymentCardsScreenRoute + +@Composable +fun PaymentsNavigationHost( + navHostController: NavHostController +) { + NavHost( + navController = navHostController, + startDestination = NavigationModel.PaymentCards.route + ) { + composable(NavigationModel.PaymentCards.route) { navBackResult -> + PaymentCardsScreenRoute( + onAddCardClick = { navHostController.navigate(NavigationModel.AddPaymentCard.route) }, + ) + } + composable(NavigationModel.AddPaymentCard.route) { + NewCardScreenRoute( + onBackClick = { navHostController.popBackStack() }, + ) + } + } +} From 1bf9263c6a49b442a475421be200baec699bd5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Tue, 27 Aug 2024 19:43:08 +0900 Subject: [PATCH 21/23] =?UTF-8?q?Step2:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/nextstep/payments/MainActivity.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nextstep/payments/MainActivity.kt b/app/src/main/java/nextstep/payments/MainActivity.kt index 39aa3ada..88493a13 100644 --- a/app/src/main/java/nextstep/payments/MainActivity.kt +++ b/app/src/main/java/nextstep/payments/MainActivity.kt @@ -10,6 +10,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.navigation.compose.rememberNavController +import nextstep.payments.ui.navigation.PaymentsNavigationHost import nextstep.payments.ui.screen.NewCardScreen import nextstep.payments.ui.screen.NewCardViewModel import nextstep.payments.ui.theme.PaymentsTheme @@ -24,7 +26,7 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - NewCardScreen() + PaymentsNavigationHost(navHostController = rememberNavController()) } } } From ab7abe25cd50e5ceba1ff8234d9e24585212aaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 28 Aug 2024 10:15:44 +0900 Subject: [PATCH 22/23] =?UTF-8?q?Step2:=20=EC=B9=B4=EB=93=9C=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B9=B4=EB=93=9C=20=EB=93=B1=EB=A1=9D=EC=8B=9C=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=83=89=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payments/ui/component/PaymentCard.kt | 9 +++++-- .../payments/ui/screen/PaymentCardsScreen.kt | 4 ++- .../java/nextstep/payments/ui/theme/Theme.kt | 25 ++++++++++++------- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt index 73698599..6faf921b 100644 --- a/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/component/PaymentCard.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.unit.sp @Composable fun AddPaymentCard( modifier: Modifier = Modifier, + onClick: () -> Unit = {} ) { DefaultCard( modifier = modifier, @@ -35,10 +36,14 @@ fun AddPaymentCard( Box( modifier = Modifier .fillMaxSize() - .clickable { /* Click Action */ }, + .clickable { onClick() }, contentAlignment = Alignment.Center ) { - Icon(imageVector = Icons.Default.Add, contentDescription = "Add") + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add", + tint = Color.Black, + ) } } ) diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt index e90cb002..0bd3eaa7 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt @@ -103,7 +103,9 @@ fun PaymentCardList( ) } item { - AddPaymentCard() + AddPaymentCard( + onClick = onAddCardClick + ) } } diff --git a/app/src/main/java/nextstep/payments/ui/theme/Theme.kt b/app/src/main/java/nextstep/payments/ui/theme/Theme.kt index cdc30f51..f80dc11f 100644 --- a/app/src/main/java/nextstep/payments/ui/theme/Theme.kt +++ b/app/src/main/java/nextstep/payments/ui/theme/Theme.kt @@ -2,6 +2,7 @@ package nextstep.payments.ui.theme import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme @@ -39,15 +40,7 @@ fun PaymentsTheme( dynamicColor: Boolean = true, content: @Composable () -> Unit, ) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColorScheme - else -> LightColorScheme - } + val colorScheme = getColorScheme(darkTheme, dynamicColor) MaterialTheme( colorScheme = colorScheme, @@ -55,3 +48,17 @@ fun PaymentsTheme( content = content ) } + +@Composable +fun getColorScheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, +): ColorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme +} From dde2a8b5e30c4505dbc7ec9153d81df9ea455d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9E=A5=EC=A7=84=ED=98=81/StoreClient=ED=8C=80/=EC=9B=90?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4?= Date: Wed, 28 Aug 2024 10:43:10 +0900 Subject: [PATCH 23/23] =?UTF-8?q?Step2:=20=EC=B9=B4=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/navigation/PaymentsNavigationHost.kt | 1 + .../payments/ui/screen/NewCardScreen.kt | 16 +++++++++++++--- .../payments/ui/screen/PaymentCardsScreen.kt | 18 +++++++++++++++--- .../ui/screen/PaymentCardsViewModel.kt | 13 ++++++++++++- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt b/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt index bb5404b6..91091cb4 100644 --- a/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt +++ b/app/src/main/java/nextstep/payments/ui/navigation/PaymentsNavigationHost.kt @@ -23,6 +23,7 @@ fun PaymentsNavigationHost( composable(NavigationModel.AddPaymentCard.route) { NewCardScreenRoute( onBackClick = { navHostController.popBackStack() }, + onAddComplete = { navHostController.popBackStack() } ) } } diff --git a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt index f258ef2d..c396f124 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/NewCardScreen.kt @@ -25,10 +25,15 @@ import nextstep.payments.ui.theme.PaymentsTheme @Composable fun NewCardScreenRoute( onBackClick: () -> Unit, + onAddComplete: () -> Unit, viewModel: NewCardViewModel = viewModel(), ) { NewCardScreen( onBackClick = onBackClick, + onSaveClick = { + viewModel.addCard() + onAddComplete() + }, viewModel = viewModel, ) } @@ -38,6 +43,7 @@ fun NewCardScreenRoute( internal fun NewCardScreen( modifier: Modifier = Modifier, onBackClick: () -> Unit, + onSaveClick: () -> Unit, viewModel: NewCardViewModel = viewModel(), ) { val cardNumber by viewModel.cardNumber.collectAsStateWithLifecycle() @@ -56,6 +62,7 @@ internal fun NewCardScreen( setPassword = viewModel::setPassword, modifier = modifier, onBackClick = onBackClick, + onSaveClick = onSaveClick, ) } @@ -72,9 +79,10 @@ private fun NewCardScreen( setPassword: (String) -> Unit, modifier: Modifier = Modifier, onBackClick: () -> Unit, + onSaveClick: () -> Unit, ) { Scaffold( - topBar = { NewCardTopBar(onBackClick = { onBackClick() }, onSaveClick = { TODO() }) }, + topBar = { NewCardTopBar(onBackClick = { onBackClick() }, onSaveClick = { onSaveClick() }) }, modifier = modifier ) { innerPadding -> Column( @@ -139,7 +147,8 @@ fun NewCardScreenPreview() { setOwnerName("홍길동") setPassword("password") }, - onBackClick = { } + onBackClick = { }, + onSaveClick = { } ) } } @@ -158,7 +167,8 @@ fun StatelessNewCardScreenPreview() { setExpiredDate = { }, setOwnerName = { }, setPassword = { }, - onBackClick = { } + onBackClick = { }, + onSaveClick = { } ) } } diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt index 0bd3eaa7..facb2332 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsScreen.kt @@ -1,5 +1,6 @@ package nextstep.payments.ui.screen +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -10,6 +11,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.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -30,6 +32,9 @@ fun PaymentCardsScreenRoute( onAddCardClick: () -> Unit, viewModel: PaymentCardsViewModel = viewModel(), ) { + LaunchedEffect(key1 = Unit, block = { + viewModel.loadCardPayments() + }) val uiState by viewModel.cardsScreenState.collectAsState() PaymentCardsScreen( uiState = uiState, @@ -44,7 +49,7 @@ fun PaymentCardsScreen( uiState: PaymentCardUiState, onAddCardClick: () -> Unit ) { - val visible = false + val visible = uiState is PaymentCardUiState.Many Scaffold( topBar = { CenterAlignedTopAppBar( @@ -59,7 +64,7 @@ fun PaymentCardsScreen( TextButton(onClick = { onAddCardClick() }) { Text( text = "추가", - color = Color.Black, + color = if(isSystemInDarkTheme()) Color.White else Color.Black, fontSize = 18.sp, fontWeight = FontWeight.Bold, ) @@ -114,7 +119,9 @@ fun PaymentCardList( RegisteredPaymentCard() } item { - AddPaymentCard() + AddPaymentCard( + onClick = onAddCardClick + ) } } @@ -122,6 +129,11 @@ fun PaymentCardList( items(uiState.cards.size) { RegisteredPaymentCard() } + item { + AddPaymentCard( + onClick = onAddCardClick + ) + } } } } diff --git a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt index 03a06e9d..9e223d7b 100644 --- a/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/screen/PaymentCardsViewModel.kt @@ -4,9 +4,20 @@ import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import nextstep.payments.domain.PaymentCardsRepository import nextstep.payments.ui.state.PaymentCardUiState -class PaymentCardsViewModel : ViewModel() { +class PaymentCardsViewModel( + private val paymentCardsRepository: PaymentCardsRepository = PaymentCardsRepository +) : ViewModel() { private val _cardsScreenState = MutableStateFlow(PaymentCardUiState.Empty) val cardsScreenState: StateFlow = _cardsScreenState.asStateFlow() + + fun loadCardPayments() { + when { + paymentCardsRepository.cards.isEmpty() -> _cardsScreenState.value = PaymentCardUiState.Empty + paymentCardsRepository.cards.count() == 1 -> _cardsScreenState.value = PaymentCardUiState.One(paymentCardsRepository.cards.first()) + else -> _cardsScreenState.value = PaymentCardUiState.Many(paymentCardsRepository.cards) + } + } }