From 9ef856586cf27a4067c23f1367005a1b242164a2 Mon Sep 17 00:00:00 2001 From: OYJ Date: Sat, 31 Aug 2024 13:56:01 +0900 Subject: [PATCH 01/18] =?UTF-8?q?Refactor=20:=20=EB=A1=9C=EA=B3=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=88=98=EC=8B=9D=EC=9E=90=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B5=AC=EC=84=B1=EC=9A=94=EC=86=8C=20?= =?UTF-8?q?=EC=9C=A0=EC=97=B0=EC=84=B1=20=ED=96=A5=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 로고 프로퍼티 @DrawableRes 추가 2. modifier 프로퍼티 사용 --- app/src/main/java/nextstep/payments/data/BankType.kt | 3 ++- app/src/main/java/nextstep/payments/ui/PaymentCard.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nextstep/payments/data/BankType.kt b/app/src/main/java/nextstep/payments/data/BankType.kt index 012fcabb..033e970a 100644 --- a/app/src/main/java/nextstep/payments/data/BankType.kt +++ b/app/src/main/java/nextstep/payments/data/BankType.kt @@ -1,12 +1,13 @@ package nextstep.payments.data import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes import androidx.annotation.StringRes import nextstep.payments.R enum class BankType( @StringRes val companyName: Int, - val logo: Int, + @DrawableRes val logo: Int, @ColorRes val brandColor: Int, ) { NOT_SELECTED( diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index 9f9efa16..288ccde8 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -59,7 +59,7 @@ fun PaymentCard( content: @Composable () -> Unit = {} ) { Box( - modifier = Modifier.padding(top = 10.dp) + modifier = modifier.padding(top = 10.dp) ) { content() From 2343f5c1708d0db6e18c9343cced2d90d8a637cf Mon Sep 17 00:00:00 2001 From: OYJ Date: Sat, 31 Aug 2024 16:45:44 +0900 Subject: [PATCH 02/18] =?UTF-8?q?Refactor=20:=20Card=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8D=BC=ED=8B=B0=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EB=93=B1=EB=A1=9D=20=EB=A1=9C=EC=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. brandColor: Color -> bankType: BankType 교체 2. selectedBankType를 NewCardViewModel에서 관리 3. addCard시 NewCardViewModel 내부의 값으로 사용함 --- .../list/RegisteredCreditCardsScreenTest.kt | 9 +++--- .../nextstep/payments/NewCardViewModel.kt | 20 ++++++++++-- .../main/java/nextstep/payments/data/Card.kt | 2 +- .../java/nextstep/payments/ui/PaymentCard.kt | 4 +-- .../payments/ui/card/list/CardListScreen.kt | 6 ++-- .../list/component/card/CardLazyColumn.kt | 5 +-- .../ui/card/registration/NewCardScreen.kt | 31 +++++++------------ .../registration/component/BankSelectRow.kt | 4 +-- 8 files changed, 45 insertions(+), 36 deletions(-) diff --git a/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt b/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt index a0f45e00..7f4e57ef 100644 --- a/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt +++ b/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.test.core.app.ApplicationProvider import nextstep.payments.R +import nextstep.payments.data.BankType import nextstep.payments.data.Card import nextstep.payments.data.PaymentCardsRepository import nextstep.payments.data.RegisteredCreditCards @@ -54,14 +55,14 @@ class RegisteredCreditCardsScreenTest { ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = Color(context.getColor(R.color.bc_card)) + bankType = BankType.BC ), Card( cardNumber = "1234-5678-1234-5628", ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = Color(context.getColor(R.color.bc_card)) + bankType = BankType.BC ) ) ) @@ -123,7 +124,7 @@ class RegisteredCreditCardsScreenTest { ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = Color(context.getColor(R.color.bc_card)) + bankType = BankType.BC ) ) val registeredCreditCards = RegisteredCreditCards( @@ -133,7 +134,7 @@ class RegisteredCreditCardsScreenTest { ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = Color(context.getColor(R.color.bc_card)) + bankType = BankType.BC ) ) ) diff --git a/app/src/main/java/nextstep/payments/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/NewCardViewModel.kt index 4b928d55..8d7f0913 100644 --- a/app/src/main/java/nextstep/payments/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/NewCardViewModel.kt @@ -24,9 +24,11 @@ class NewCardViewModel( private val _password = MutableStateFlow("") val password: StateFlow = _password.asStateFlow() - private val _cardAdded = MutableStateFlow(false) + private val _cardAdded = MutableStateFlow(false) val cardAdded: StateFlow = _cardAdded.asStateFlow() + private val _selectedBankType = MutableStateFlow(BankType.NOT_SELECTED) + val selectedBankType: StateFlow = _selectedBankType.asStateFlow() fun setCardNumber(cardNumber: String) { _cardNumber.value = cardNumber @@ -44,8 +46,20 @@ class NewCardViewModel( _password.value = password } - fun addCard(card: Card) { - repository.addCard(card) + fun setSelectBankType(bankType: BankType) { + _selectedBankType.value = bankType + } + + fun addCard() { + repository.addCard( + Card( + cardNumber = cardNumber.value, + expiredDate = expiredDate.value, + ownerName = ownerName.value, + password = password.value, + bankType = selectedBankType.value + ) + ) _cardAdded.value = true } } diff --git a/app/src/main/java/nextstep/payments/data/Card.kt b/app/src/main/java/nextstep/payments/data/Card.kt index 997d5d0f..9c38de5e 100644 --- a/app/src/main/java/nextstep/payments/data/Card.kt +++ b/app/src/main/java/nextstep/payments/data/Card.kt @@ -7,5 +7,5 @@ data class Card( val expiredDate: String, val ownerName: String, val password: String, - val brandColor: Color + val bankType: BankType ) diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index 288ccde8..49e43fc3 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -92,7 +92,7 @@ fun PaymentCard( @Composable private fun PaymentCardPreview() { PaymentCard( - brandColor = colorResource(id = BankType.BC.brandColor) + brandColor = colorResource(id = BankType.KAKAO.brandColor) ) } @@ -105,7 +105,7 @@ private fun NewPaymentCardPreview() { ownerName = "홍길동", expiredDate = "12/34", password = "123", - brandColor = colorResource(id = BankType.BC.brandColor) + bankType = BankType.BC ), modifier = Modifier.size(width = 208.dp, height = 124.dp), content = { PaymentCard() } diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt index b3ffcb92..a303e5e8 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt @@ -179,7 +179,7 @@ private fun CardListScreenOnePreview() { ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = colorResource(id = BankType.BC.brandColor) + bankType = BankType.BC ) ) ), @@ -198,14 +198,14 @@ private fun CardListScreenManyPreview() { ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = colorResource(id = BankType.BC.brandColor) + bankType = BankType.BC ), Card( cardNumber = "1234-5678-1234-1234", ownerName = "홍길동", expiredDate = "12/24", password = "123", - brandColor = colorResource(id = BankType.KAKAO.brandColor) + bankType = BankType.BC ) ) ) diff --git a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt index 0eeec93a..4070ac36 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt @@ -3,6 +3,7 @@ package nextstep.payments.ui.card.list.component.card import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.tooling.preview.Preview import nextstep.payments.data.BankType @@ -23,7 +24,7 @@ fun CardLazyColumn(cards: List, modifier: Modifier = Modifier) { modifier = Modifier, content = { PaymentCard( - brandColor = cards[it].brandColor + brandColor = colorResource(cards[it].bankType.brandColor) ) } ) @@ -41,7 +42,7 @@ private fun CardLazyColumnPreview() { ownerName = "홍길동", expiredDate = "12/34", password = "123", - brandColor = colorResource(id = BankType.KAKAO.brandColor) + bankType = BankType.KAKAO ) ) ) diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index 02f9a8fa..e7399894 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -1,5 +1,6 @@ package nextstep.payments.ui.card.registration +import android.annotation.SuppressLint import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -16,7 +17,6 @@ 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.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -52,9 +52,9 @@ fun NewCardScreen( val ownerName by viewModel.ownerName.collectAsStateWithLifecycle() val password by viewModel.password.collectAsStateWithLifecycle() val cardAdded by viewModel.cardAdded.collectAsStateWithLifecycle() - val modalBottomSheetState = rememberModalBottomSheetState(confirmValueChange = { false }) - var selectedBankType by remember { mutableStateOf(BankType.NOT_SELECTED) } + val selectedBankType by viewModel.selectedBankType.collectAsStateWithLifecycle() var showCardCompanyBottomSheet by rememberSaveable { mutableStateOf(true) } + val modalBottomSheetState = rememberModalBottomSheetState(confirmValueChange = { false }) LaunchedEffect(cardAdded) { if (cardAdded) navigateToCardList() @@ -72,9 +72,9 @@ fun NewCardScreen( onDismissRequest = { } ) { BankSelectRow( - onClick = { + onClickBankType = { showCardCompanyBottomSheet = false - selectedBankType = it + viewModel.setSelectBankType(it) } ) } @@ -86,7 +86,7 @@ fun NewCardScreen( expiredDate = expiredDate, ownerName = ownerName, password = password, - brandColor = colorResource(selectedBankType.brandColor), + bankType = selectedBankType, setCardNumber = viewModel::setCardNumber, setExpiredDatedNumber = viewModel::setExpiredDate, setOwnerNamedNumber = viewModel::setOwnerName, @@ -97,6 +97,7 @@ fun NewCardScreen( } // Stateless +@SuppressLint("ResourceAsColor") @Composable private fun NewCardScreen( modifier: Modifier = Modifier, @@ -104,26 +105,18 @@ private fun NewCardScreen( expiredDate: String, ownerName: String, password: String, - brandColor: Color, + bankType: BankType, setCardNumber: (String) -> Unit, setExpiredDatedNumber: (String) -> Unit, setOwnerNamedNumber: (String) -> Unit, setPasswordNumber: (String) -> Unit, onBackClick: () -> Unit = {}, - onSaveClick: (Card) -> Unit = {}, + onSaveClick: () -> Unit = {}, ) { Scaffold( topBar = { NewCardTopBar(onBackClick = onBackClick, onSaveClick = { - onSaveClick( - Card( - cardNumber = cardNumber, - expiredDate = expiredDate, - ownerName = ownerName, - password = password, - brandColor = brandColor - ) - ) + onSaveClick() }) }, modifier = modifier ) { innerPadding -> @@ -137,7 +130,7 @@ private fun NewCardScreen( Spacer(modifier = Modifier.height(14.dp)) PaymentCard( - brandColor = brandColor + brandColor = colorResource(bankType.brandColor) ) Spacer(modifier = Modifier.height(10.dp)) @@ -201,7 +194,7 @@ private fun StatelessNewCardScreenPreview() { expiredDate = "02/26", ownerName = "김수현", password = "1234", - brandColor = colorResource(id = BankType.NOT_SELECTED.brandColor), + bankType = BankType.NOT_SELECTED, setCardNumber = {}, setExpiredDatedNumber = {}, setOwnerNamedNumber = {}, diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt b/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt index 8f195c4a..c2a363b6 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt @@ -26,7 +26,7 @@ private const val COLUMN_COUNT = 4 @OptIn(ExperimentalLayoutApi::class) @Composable fun BankSelectRow( - onClick: (BankType) -> Unit = {}, + onClickBankType: (BankType) -> Unit = {}, ) { FlowRow( modifier = Modifier @@ -40,7 +40,7 @@ fun BankSelectRow( if (bankType == BankType.NOT_SELECTED) return@forEach CardSelector( bankType = bankType, - onClick = onClick + onClick = { onClickBankType(bankType) } ) } } From 3cbdfb541a3337a330ebca13f5f9a669e1b097a1 Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 14:16:09 +0900 Subject: [PATCH 03/18] =?UTF-8?q?Refactor=20:=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 재사용 가능 로직 캡슐화 --- .../java/nextstep/payments/ui/PaymentCard.kt | 42 ++++++++----------- .../list/component/card/CardLazyColumn.kt | 10 ++--- .../ui/card/registration/NewCardScreen.kt | 3 +- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index 49e43fc3..b6777484 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -26,12 +26,14 @@ import nextstep.payments.ui.card.list.component.card.CardOwnerName @Composable fun PaymentCard( + brandColor: Color, modifier: Modifier = Modifier, - brandColor: Color = Color(0xFF333333) + content: @Composable () -> Unit = {} ) { Box( contentAlignment = Alignment.CenterStart, modifier = modifier + .padding(top = 10.dp) .shadow(8.dp) .size(width = 208.dp, height = 124.dp) .background( @@ -49,20 +51,15 @@ fun PaymentCard( shape = RoundedCornerShape(4.dp), ) ) + content() } } @Composable -fun PaymentCard( +fun PaymentCardContents( card: Card, - modifier: Modifier = Modifier, - content: @Composable () -> Unit = {} ) { - Box( - modifier = modifier.padding(top = 10.dp) - ) { - content() - + Box { CardNumber( cardNumber = card.cardNumber, modifier = Modifier @@ -90,24 +87,19 @@ fun PaymentCard( @Preview @Composable -private fun PaymentCardPreview() { - PaymentCard( - brandColor = colorResource(id = BankType.KAKAO.brandColor) +private fun NewPaymentCardPreview() { + val card = Card( + cardNumber = "1234-5678-1234-5678", + ownerName = "홍길동", + expiredDate = "12/34", + password = "123", + bankType = BankType.BC ) -} -@Preview -@Composable -private fun NewPaymentCardPreview() { PaymentCard( - card = Card( - cardNumber = "1234-5678-1234-5678", - ownerName = "홍길동", - expiredDate = "12/34", - password = "123", - bankType = BankType.BC - ), - modifier = Modifier.size(width = 208.dp, height = 124.dp), - content = { PaymentCard() } + brandColor = colorResource(id = BankType.BC.brandColor), + content = { + PaymentCardContents(card = card) + } ) } diff --git a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt index 4070ac36..8232210c 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt @@ -3,12 +3,12 @@ package nextstep.payments.ui.card.list.component.card import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.tooling.preview.Preview import nextstep.payments.data.BankType import nextstep.payments.data.Card import nextstep.payments.ui.PaymentCard +import nextstep.payments.ui.PaymentCardContents @Composable fun CardLazyColumn(cards: List, modifier: Modifier = Modifier) { @@ -20,13 +20,9 @@ fun CardLazyColumn(cards: List, modifier: Modifier = Modifier) { key = { index -> cards[index].cardNumber } ) { PaymentCard( - card = cards[it], + brandColor = colorResource(id = cards[it].bankType.brandColor), modifier = Modifier, - content = { - PaymentCard( - brandColor = colorResource(cards[it].bankType.brandColor) - ) - } + content = { PaymentCardContents(card = cards[it]) } ) } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index e7399894..d2f42e30 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -21,7 +21,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -32,7 +31,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel import nextstep.payments.NewCardViewModel import nextstep.payments.R import nextstep.payments.data.BankType -import nextstep.payments.data.Card import nextstep.payments.ui.PaymentCard import nextstep.payments.ui.card.registration.component.BankSelectRow import nextstep.payments.ui.card.registration.component.NewCardTopBar @@ -133,6 +131,7 @@ private fun NewCardScreen( brandColor = colorResource(bankType.brandColor) ) + Spacer(modifier = Modifier.height(10.dp)) OutlinedTextField( From af3e538828234b71c3ea28234cb1c2fc5670bcf8 Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 14:20:04 +0900 Subject: [PATCH 04/18] =?UTF-8?q?Refactor=20:=20=EB=8B=A8=EC=9D=BC=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EB=85=B8=EC=B6=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. CardLazyColumn 컴포넌트 대신 직접 PaymentCard 컴포넌트 호출 --- .../nextstep/payments/ui/card/list/CardListScreen.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt index a303e5e8..651c6fd1 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt @@ -22,6 +22,8 @@ import nextstep.payments.R import nextstep.payments.data.BankType import nextstep.payments.data.Card import nextstep.payments.data.RegisteredCreditCards +import nextstep.payments.ui.PaymentCard +import nextstep.payments.ui.PaymentCardContents import nextstep.payments.ui.card.CreditCardUiState import nextstep.payments.ui.card.list.component.card.CardLazyColumn import nextstep.payments.ui.card.list.component.card.CardListTopBar @@ -114,9 +116,10 @@ fun CardListScreenOne( .padding(paddingValues) .fillMaxWidth() ) { - CardLazyColumn( - cards = cards, - modifier = Modifier.align(CenterHorizontally) + PaymentCard( + brandColor = colorResource(id = cards.first().bankType.brandColor), + modifier = Modifier.align(CenterHorizontally), + content = { PaymentCardContents(card = cards.first()) } ) EmptyCardImage( @@ -198,7 +201,7 @@ private fun CardListScreenManyPreview() { ownerName = "홍길동", expiredDate = "12/24", password = "123", - bankType = BankType.BC + bankType = BankType.KAKAO ), Card( cardNumber = "1234-5678-1234-1234", From 366b597a69b2df41eb9c5b8e4e737ef6cd580f9c Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 14:27:04 +0900 Subject: [PATCH 05/18] =?UTF-8?q?Refactor=20=20:=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=ED=9A=8C=EC=82=AC=EB=AA=85=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 1. Text 컴포넌트를 사용하여 카드 회사명 노출 --- app/src/main/java/nextstep/payments/ui/PaymentCard.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index b6777484..9b0b5629 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -60,6 +61,13 @@ fun PaymentCardContents( card: Card, ) { Box { + Text( + text = card.bankType.name, + color = Color.White, + modifier = Modifier + .padding(start = 14.dp, top = 10.dp) + ) + CardNumber( cardNumber = card.cardNumber, modifier = Modifier From 9a1042017757842c800c5d23122b853b9e0dd005 Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 14:37:55 +0900 Subject: [PATCH 06/18] =?UTF-8?q?Chore=20:=20README.md=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 3단계 기능 목록 체크 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c0a14fb..c60a238f 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,6 @@ ## 기능 목록 - [O] 카드사 목록 화면 구현 - [O] 선택한 카드사에 따라 카드 미리보기가 바뀌어야 한다. -- [] 카드사를 선택할 때 적절한 카드사 아이콘을 노출한다. +- [O] 카드사를 선택할 때 적절한 카드사 아이콘을 노출한다. +- [O] 카드 등록 시 카드에 카드사 이름이 표시된다. + From 9e3eaa0e53f1052d5128d5596d152e9ef6a33dab Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 14:51:07 +0900 Subject: [PATCH 07/18] =?UTF-8?q?Chore=20:=20Step4=20(=EC=B9=B4=EB=93=9C?= =?UTF-8?q?=EC=88=98=EC=A0=95)=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c60a238f..c98e2c41 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,9 @@ - [O] 카드사를 선택할 때 적절한 카드사 아이콘을 노출한다. - [O] 카드 등록 시 카드에 카드사 이름이 표시된다. +# Step 4 - 페이먼츠(카드 수정) + +## 기능 목록 +- [] 등록된 카드 선택시 카드 수정 화면으로 이동한다. +- [] 카드 수정 시 변경이 일어나지 않으면 수정이 불가능하다. +- [] 카드가 수정되면 카드 목록 화면에 변경사항이 반영된다. From d319fbbbaafec4a03e0ca2abbf894f338ac0e945 Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 16:18:07 +0900 Subject: [PATCH 08/18] =?UTF-8?q?Feat=20:=20=EB=93=B1=EB=A1=9D=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20=EC=84=A0=ED=83=9D=20=EC=8B=9C=20=EC=B9=B4=EB=93=9C?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Card데이터 클래스에 Parcelize 활용하여 데이터 전송 2. 등록된 카드 선택시 카드 등록 화면으로 이동 --- app/build.gradle.kts | 2 ++ .../main/java/nextstep/payments/MainActivity.kt | 6 ++++++ app/src/main/java/nextstep/payments/data/Card.kt | 6 ++++-- .../java/nextstep/payments/ui/PaymentCard.kt | 7 ++++++- .../payments/ui/card/list/CardListScreen.kt | 16 +++++++++++++--- .../ui/card/registration/NewCardActivity.kt | 9 +++++++-- .../ui/card/registration/NewCardScreen.kt | 2 ++ 7 files changed, 40 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9996218f..a9035317 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) + id("kotlin-parcelize") } android { @@ -68,4 +69,5 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) + implementation(kotlin("script-runtime")) } diff --git a/app/src/main/java/nextstep/payments/MainActivity.kt b/app/src/main/java/nextstep/payments/MainActivity.kt index 6656f923..88906905 100644 --- a/app/src/main/java/nextstep/payments/MainActivity.kt +++ b/app/src/main/java/nextstep/payments/MainActivity.kt @@ -40,6 +40,12 @@ class MainActivity : ComponentActivity() { onAddCard = { val intent = Intent(this, NewCardActivity::class.java) launcher.launch(intent) + }, + onCardClick = { card -> + val intent = Intent(this, NewCardActivity::class.java).apply { + putExtra("card", card) + } + launcher.launch(intent) } ) } diff --git a/app/src/main/java/nextstep/payments/data/Card.kt b/app/src/main/java/nextstep/payments/data/Card.kt index 9c38de5e..4b57a42e 100644 --- a/app/src/main/java/nextstep/payments/data/Card.kt +++ b/app/src/main/java/nextstep/payments/data/Card.kt @@ -1,11 +1,13 @@ package nextstep.payments.data -import androidx.compose.ui.graphics.Color +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +@Parcelize data class Card( val cardNumber: String, val expiredDate: String, val ownerName: String, val password: String, val bankType: BankType -) +) : Parcelable diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index 9b0b5629..e0fd3736 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -1,6 +1,7 @@ package nextstep.payments.ui import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -59,8 +60,12 @@ fun PaymentCard( @Composable fun PaymentCardContents( card: Card, + onClick: (Card) -> Unit = {} ) { - Box { + Box( + modifier = Modifier + .clickable { onClick(card) } + ) { Text( text = card.bankType.name, color = Color.White, diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt index 651c6fd1..b9e1eebe 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt @@ -35,19 +35,22 @@ import nextstep.payments.ui.theme.PaymentsTheme fun CardListScreen( viewModel: CardListViewModel = viewModel(), onAddCard: () -> Unit = {}, + onCardClick: (Card) -> Unit = {} ) { val cards by viewModel.registeredCreditCards.collectAsStateWithLifecycle() CardListScreen( registeredCreditCards = cards, onAddCard = onAddCard, + onCardClick = onCardClick ) } @Composable fun CardListScreen( registeredCreditCards: RegisteredCreditCards = RegisteredCreditCards(mutableListOf()), - onAddCard: () -> Unit = {} + onAddCard: () -> Unit = {}, + onCardClick: (Card) -> Unit = {} ) { when (registeredCreditCards.getState()) { @@ -62,6 +65,7 @@ fun CardListScreen( CardListScreenOne( cards = registeredCreditCards.cardList, onAddCard = onAddCard, + onCardClick = onCardClick ) } @@ -108,7 +112,8 @@ fun CardListScreenEmpty( @Composable fun CardListScreenOne( cards: List, - onAddCard: () -> Unit = {} + onAddCard: () -> Unit = {}, + onCardClick: (Card) -> Unit = {} ) { Scaffold(topBar = { CardListTopBar() }) { paddingValues -> Column( @@ -119,7 +124,12 @@ fun CardListScreenOne( PaymentCard( brandColor = colorResource(id = cards.first().bankType.brandColor), modifier = Modifier.align(CenterHorizontally), - content = { PaymentCardContents(card = cards.first()) } + content = { + PaymentCardContents( + card = cards.first(), + onClick = onCardClick + ) + } ) EmptyCardImage( diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt index 00013407..3738227f 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt @@ -5,14 +5,19 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels import nextstep.payments.NewCardViewModel +import nextstep.payments.data.Card import nextstep.payments.ui.theme.PaymentsTheme class NewCardActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - val viewModel by viewModels() + private var card: Card? = null + override fun onCreate(savedInstanceState: Bundle?) { + val viewModel by viewModels() super.onCreate(savedInstanceState) + + card = intent?.getParcelableExtra("card") + setContent { PaymentsTheme { NewCardScreen( diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index d2f42e30..b5de8cc7 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -170,6 +170,8 @@ private fun NewCardScreen( } } + + @Preview @Composable private fun NewCardScreenPreview() { From c577a00a63d78ba7aaa4f33e1c4efa00d16d6de7 Mon Sep 17 00:00:00 2001 From: OYJ Date: Sun, 1 Sep 2024 16:20:11 +0900 Subject: [PATCH 09/18] =?UTF-8?q?Chore=20:=20README.md=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 등록된 카드 선택시, 카드 수정 화면으로 이동 기능 체크 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c98e2c41..6773cf3c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,6 @@ # Step 4 - 페이먼츠(카드 수정) ## 기능 목록 -- [] 등록된 카드 선택시 카드 수정 화면으로 이동한다. +- [O] 등록된 카드 선택시 카드 수정 화면으로 이동한다. - [] 카드 수정 시 변경이 일어나지 않으면 수정이 불가능하다. - [] 카드가 수정되면 카드 목록 화면에 변경사항이 반영된다. From 73c0d77d0c79a1062566b5424978b91316bded46 Mon Sep 17 00:00:00 2001 From: OYJ Date: Mon, 2 Sep 2024 15:24:53 +0900 Subject: [PATCH 10/18] =?UTF-8?q?Fix=20:=20=ED=95=9C=EC=9E=A5=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81=EC=9D=98=20=EC=B9=B4=EB=93=9C=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=B9=B4=EB=93=9C=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=8B=9C=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=EC=95=88?= =?UTF-8?q?=ED=95=98=EB=8D=98=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 한 장 이상의 카드 화면에 클릭이벤트 등록 --- .../payments/ui/card/list/CardListScreen.kt | 9 ++++++--- .../ui/card/list/component/card/CardLazyColumn.kt | 13 +++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt index b9e1eebe..7eef2958 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt @@ -24,7 +24,7 @@ import nextstep.payments.data.Card import nextstep.payments.data.RegisteredCreditCards import nextstep.payments.ui.PaymentCard import nextstep.payments.ui.PaymentCardContents -import nextstep.payments.ui.card.CreditCardUiState +import nextstep.payments.ui.card.state.CreditCardUiState import nextstep.payments.ui.card.list.component.card.CardLazyColumn import nextstep.payments.ui.card.list.component.card.CardListTopBar import nextstep.payments.ui.card.list.component.card.CardListTopBarWithAdd @@ -73,6 +73,7 @@ fun CardListScreen( CardListScreenMany( cards = registeredCreditCards.cardList, onAddCard = onAddCard, + onCardClick = onCardClick ) } } @@ -149,7 +150,8 @@ fun CardListScreenOne( @Composable fun CardListScreenMany( cards: List, - onAddCard: () -> Unit = {} + onAddCard: () -> Unit = {}, + onCardClick: (Card) -> Unit = {} ) { Scaffold(topBar = { CardListTopBarWithAdd( @@ -163,7 +165,8 @@ fun CardListScreenMany( ) { CardLazyColumn( cards = cards, - modifier = Modifier.align(CenterHorizontally) + modifier = Modifier.align(CenterHorizontally), + onCardClick = onCardClick ) } } diff --git a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt index 8232210c..d0efd919 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt @@ -11,7 +11,11 @@ import nextstep.payments.ui.PaymentCard import nextstep.payments.ui.PaymentCardContents @Composable -fun CardLazyColumn(cards: List, modifier: Modifier = Modifier) { +fun CardLazyColumn( + cards: List, + modifier: Modifier = Modifier, + onCardClick: (Card) -> Unit = {} +) { LazyColumn( modifier = modifier ) { @@ -22,7 +26,12 @@ fun CardLazyColumn(cards: List, modifier: Modifier = Modifier) { PaymentCard( brandColor = colorResource(id = cards[it].bankType.brandColor), modifier = Modifier, - content = { PaymentCardContents(card = cards[it]) } + content = { + PaymentCardContents( + card = cards[it], + onClick = onCardClick + ) + } ) } } From b328592767238184c271e8f55322e50eed810850 Mon Sep 17 00:00:00 2001 From: OYJ Date: Mon, 2 Sep 2024 16:55:21 +0900 Subject: [PATCH 11/18] =?UTF-8?q?Feat=20:=20=EB=93=B1=EB=A1=9D=EB=90=9C=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 등록된 카드 구분을 위해 Card 데이터 클래스에 id속성 추가 2. 카드 수정 및 Id생성 함수 구현(CardRepository) 3. 등록된 카드 데이터가 newCardActivity로 전달될 상황에 대한 로직 구현 4. 카드 등록 UI상태 값 추가 및 관리 --- .../main/java/nextstep/payments/data/Card.kt | 1 + .../payments/data/PaymentCardsRepository.kt | 9 +++++ .../java/nextstep/payments/ui/PaymentCard.kt | 1 + .../payments/ui/card/list/CardListScreen.kt | 5 ++- .../ui/card/list/CardListViewModel.kt | 5 ++- .../list/component/card/CardLazyColumn.kt | 1 + .../list/component/card/EmptyCardImage.kt | 1 - .../ui/card/registration/NewCardActivity.kt | 6 ++- .../ui/card/registration/NewCardScreen.kt | 24 +++++++++--- .../card/registration}/NewCardViewModel.kt | 38 ++++++++++++++++++- .../card/registration/RegistrationUiState.kt | 6 +++ 11 files changed, 84 insertions(+), 13 deletions(-) rename app/src/main/java/nextstep/payments/{ => ui/card/registration}/NewCardViewModel.kt (63%) create mode 100644 app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt diff --git a/app/src/main/java/nextstep/payments/data/Card.kt b/app/src/main/java/nextstep/payments/data/Card.kt index 4b57a42e..b24766cc 100644 --- a/app/src/main/java/nextstep/payments/data/Card.kt +++ b/app/src/main/java/nextstep/payments/data/Card.kt @@ -5,6 +5,7 @@ import kotlinx.parcelize.Parcelize @Parcelize data class Card( + val id: Int, val cardNumber: String, val expiredDate: String, val ownerName: String, diff --git a/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt b/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt index a0e8a0e9..1e704e45 100644 --- a/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt +++ b/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt @@ -12,4 +12,13 @@ object PaymentCardsRepository { fun removeAllCard() { _cards.clear() } + + fun editCard(oldCard: Card?, newCard: Card) { + val index = _cards.indexOfFirst { it.id == oldCard!!.id } + _cards[index] = newCard + } + + fun createId(): Int { + return _cards.maxOfOrNull { it.id }?.plus(1) ?: 1 + } } diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index e0fd3736..94522741 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -102,6 +102,7 @@ fun PaymentCardContents( @Composable private fun NewPaymentCardPreview() { val card = Card( + id = 1, cardNumber = "1234-5678-1234-5678", ownerName = "홍길동", expiredDate = "12/34", diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt index 7eef2958..60ec6df1 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListScreen.kt @@ -24,7 +24,7 @@ import nextstep.payments.data.Card import nextstep.payments.data.RegisteredCreditCards import nextstep.payments.ui.PaymentCard import nextstep.payments.ui.PaymentCardContents -import nextstep.payments.ui.card.state.CreditCardUiState +import nextstep.payments.ui.card.CreditCardUiState import nextstep.payments.ui.card.list.component.card.CardLazyColumn import nextstep.payments.ui.card.list.component.card.CardListTopBar import nextstep.payments.ui.card.list.component.card.CardListTopBarWithAdd @@ -191,6 +191,7 @@ private fun CardListScreenOnePreview() { registeredCreditCards = RegisteredCreditCards( cardList = listOf( Card( + id = 1, cardNumber = "1234-5678-1234-6654", ownerName = "홍길동", expiredDate = "12/24", @@ -210,6 +211,7 @@ private fun CardListScreenManyPreview() { val registeredCreditCards = RegisteredCreditCards( cardList = listOf( Card( + id = 1, cardNumber = "1234-5678-1234-6654", ownerName = "홍길동", expiredDate = "12/24", @@ -217,6 +219,7 @@ private fun CardListScreenManyPreview() { bankType = BankType.KAKAO ), Card( + id = 1, cardNumber = "1234-5678-1234-1234", ownerName = "홍길동", expiredDate = "12/24", diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt index 7ff8404d..c1dc0fa6 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt @@ -4,13 +4,14 @@ import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import nextstep.payments.data.RegisteredCreditCards import nextstep.payments.data.PaymentCardsRepository +import nextstep.payments.data.RegisteredCreditCards class CardListViewModel : ViewModel() { private val _registeredCreditCards = MutableStateFlow(RegisteredCreditCards(emptyList())) - val registeredCreditCards: StateFlow = _registeredCreditCards.asStateFlow() + val registeredCreditCards: StateFlow = + _registeredCreditCards.asStateFlow() fun fetchCards() { _registeredCreditCards.value = RegisteredCreditCards(PaymentCardsRepository.cards) diff --git a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt index d0efd919..a4bc2e4a 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/component/card/CardLazyColumn.kt @@ -43,6 +43,7 @@ private fun CardLazyColumnPreview() { CardLazyColumn( cards = listOf( Card( + id = 1, cardNumber = "1234-5678-1234-5678", ownerName = "홍길동", expiredDate = "12/34", diff --git a/app/src/main/java/nextstep/payments/ui/card/list/component/card/EmptyCardImage.kt b/app/src/main/java/nextstep/payments/ui/card/list/component/card/EmptyCardImage.kt index cff42059..b4ac1fa7 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/component/card/EmptyCardImage.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/component/card/EmptyCardImage.kt @@ -9,7 +9,6 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt index 3738227f..301cff18 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt @@ -4,7 +4,6 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels -import nextstep.payments.NewCardViewModel import nextstep.payments.data.Card import nextstep.payments.ui.theme.PaymentsTheme @@ -16,7 +15,10 @@ class NewCardActivity : ComponentActivity() { val viewModel by viewModels() super.onCreate(savedInstanceState) - card = intent?.getParcelableExtra("card") + (intent?.getParcelableExtra("card") as Card?)?.let { + viewModel.setOldCard(it) + viewModel.setUiState(RegistrationUiState.EditCard) + } setContent { PaymentsTheme { diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index b5de8cc7..6ca6efd9 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -1,6 +1,5 @@ package nextstep.payments.ui.card.registration -import android.annotation.SuppressLint import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -28,13 +27,13 @@ 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.NewCardViewModel import nextstep.payments.R import nextstep.payments.data.BankType import nextstep.payments.ui.PaymentCard import nextstep.payments.ui.card.registration.component.BankSelectRow import nextstep.payments.ui.card.registration.component.NewCardTopBar import nextstep.payments.ui.theme.PaymentsTheme +import kotlin.reflect.KFunction0 // Stateful @OptIn(ExperimentalMaterial3Api::class) @@ -53,6 +52,7 @@ fun NewCardScreen( val selectedBankType by viewModel.selectedBankType.collectAsStateWithLifecycle() var showCardCompanyBottomSheet by rememberSaveable { mutableStateOf(true) } val modalBottomSheetState = rememberModalBottomSheetState(confirmValueChange = { false }) + val uiState by viewModel.uiState.collectAsStateWithLifecycle() LaunchedEffect(cardAdded) { if (cardAdded) navigateToCardList() @@ -64,6 +64,12 @@ fun NewCardScreen( } } + LaunchedEffect(key1 = uiState) { + if (uiState == RegistrationUiState.EditCard) { + showCardCompanyBottomSheet = false + } + } + if (showCardCompanyBottomSheet) { ModalBottomSheet( sheetState = modalBottomSheetState, @@ -77,6 +83,15 @@ fun NewCardScreen( ) } } + val saveFunction: KFunction0 = when (uiState) { + RegistrationUiState.NewCard -> { + viewModel::addCard + } + + RegistrationUiState.EditCard -> { + viewModel::editCard + } + } NewCardScreen( modifier = modifier, @@ -90,12 +105,11 @@ fun NewCardScreen( setOwnerNamedNumber = viewModel::setOwnerName, setPasswordNumber = viewModel::setPassword, onBackClick = onBackClick, - onSaveClick = viewModel::addCard, + onSaveClick = saveFunction, ) } // Stateless -@SuppressLint("ResourceAsColor") @Composable private fun NewCardScreen( modifier: Modifier = Modifier, @@ -170,8 +184,6 @@ private fun NewCardScreen( } } - - @Preview @Composable private fun NewCardScreenPreview() { diff --git a/app/src/main/java/nextstep/payments/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt similarity index 63% rename from app/src/main/java/nextstep/payments/NewCardViewModel.kt rename to app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt index 8d7f0913..a23735bd 100644 --- a/app/src/main/java/nextstep/payments/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt @@ -1,4 +1,4 @@ -package nextstep.payments +package nextstep.payments.ui.card.registration import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -30,6 +30,13 @@ class NewCardViewModel( private val _selectedBankType = MutableStateFlow(BankType.NOT_SELECTED) val selectedBankType: StateFlow = _selectedBankType.asStateFlow() + var oldCard: Card? = null + private set + + private val _uiState = MutableStateFlow(RegistrationUiState.NewCard) + val uiState: StateFlow = _uiState.asStateFlow() + + fun setCardNumber(cardNumber: String) { _cardNumber.value = cardNumber } @@ -53,6 +60,7 @@ class NewCardViewModel( fun addCard() { repository.addCard( Card( + id = repository.createId(), cardNumber = cardNumber.value, expiredDate = expiredDate.value, ownerName = ownerName.value, @@ -62,4 +70,32 @@ class NewCardViewModel( ) _cardAdded.value = true } + + fun editCard() { + repository.editCard( + oldCard = oldCard, + newCard = Card( + id = oldCard!!.id, + cardNumber = cardNumber.value, + expiredDate = expiredDate.value, + ownerName = ownerName.value, + password = password.value, + bankType = selectedBankType.value + ) + ) + _cardAdded.value = true + } + + fun setOldCard(card: Card) { + oldCard = card + _cardNumber.value = card.cardNumber + _expiredDate.value = card.expiredDate + _ownerName.value = card.ownerName + _password.value = card.password + _selectedBankType.value = card.bankType + } + + fun setUiState(newUiState: RegistrationUiState) { + _uiState.value = newUiState + } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt b/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt new file mode 100644 index 00000000..bd12cad3 --- /dev/null +++ b/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt @@ -0,0 +1,6 @@ +package nextstep.payments.ui.card.registration + +sealed interface RegistrationUiState { + data object NewCard : RegistrationUiState + data object EditCard : RegistrationUiState +} From 9fcbb3d7cfbff257036f6ec9326ba90ffc5b3287 Mon Sep 17 00:00:00 2001 From: OYJ Date: Mon, 2 Sep 2024 17:09:52 +0900 Subject: [PATCH 12/18] =?UTF-8?q?Refactor=20:=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=EB=90=9C=20=EC=B9=B4=EB=93=9C=20=EC=88=98=EC=A0=95=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 카드 데이터 변경사항 유효성 검사 로직 추가 --- .../ui/card/registration/NewCardScreen.kt | 6 ---- .../ui/card/registration/NewCardViewModel.kt | 34 +++++++++++++------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index 6ca6efd9..4966a5c1 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -64,12 +64,6 @@ fun NewCardScreen( } } - LaunchedEffect(key1 = uiState) { - if (uiState == RegistrationUiState.EditCard) { - showCardCompanyBottomSheet = false - } - } - if (showCardCompanyBottomSheet) { ModalBottomSheet( sheetState = modalBottomSheetState, diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt index a23735bd..065b16a3 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt @@ -72,18 +72,30 @@ class NewCardViewModel( } fun editCard() { - repository.editCard( - oldCard = oldCard, - newCard = Card( - id = oldCard!!.id, - cardNumber = cardNumber.value, - expiredDate = expiredDate.value, - ownerName = ownerName.value, - password = password.value, - bankType = selectedBankType.value + if (isCardDataChange()) { + repository.editCard( + oldCard = oldCard, + newCard = Card( + id = oldCard!!.id, + cardNumber = cardNumber.value, + expiredDate = expiredDate.value, + ownerName = ownerName.value, + password = password.value, + bankType = selectedBankType.value + ) ) - ) - _cardAdded.value = true + _cardAdded.value = true + } + } + + private fun isCardDataChange(): Boolean { + oldCard?.let { + return it.cardNumber != cardNumber.value || + it.expiredDate != expiredDate.value || + it.ownerName != ownerName.value || + it.password != password.value || + it.bankType != selectedBankType.value + } ?: return false } fun setOldCard(card: Card) { From 8a81524f84ccb959e97c2304a7221ef99cb6897d Mon Sep 17 00:00:00 2001 From: OYJ Date: Mon, 2 Sep 2024 17:11:37 +0900 Subject: [PATCH 13/18] =?UTF-8?q?Chore=20:=20README.me=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 구현된 기능 목록 체크 - 카드 수정 시 변경이 일어나지 않으면 수정이 불가능하다. - 카드가 수정되면 카드 목록 화면에 변경사항이 반영된다. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6773cf3c..6a17e90f 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,5 @@ ## 기능 목록 - [O] 등록된 카드 선택시 카드 수정 화면으로 이동한다. -- [] 카드 수정 시 변경이 일어나지 않으면 수정이 불가능하다. -- [] 카드가 수정되면 카드 목록 화면에 변경사항이 반영된다. +- [O] 카드 수정 시 변경이 일어나지 않으면 수정이 불가능하다. +- [O] 카드가 수정되면 카드 목록 화면에 변경사항이 반영된다. From aa8201059022c05f254b735dac72656695d9829b Mon Sep 17 00:00:00 2001 From: OYJ Date: Tue, 10 Sep 2024 18:08:08 +0900 Subject: [PATCH 14/18] =?UTF-8?q?Chore=20:=20=EB=A6=AC=EB=B7=B0=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 1. 불필요한 의존성 삭제 2. 테스트 코드 수정 3. PaymentCardContents 컴포넌트 파라미터에 modifier추가 --- app/build.gradle.kts | 1 - .../screen/card/list/RegisteredCreditCardsScreenTest.kt | 4 ++++ app/src/main/java/nextstep/payments/ui/PaymentCard.kt | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a9035317..a229b1ba 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,5 +69,4 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) - implementation(kotlin("script-runtime")) } diff --git a/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt b/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt index 7f4e57ef..e1f4da93 100644 --- a/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt +++ b/app/src/androidTest/java/nextstep/payments/screen/card/list/RegisteredCreditCardsScreenTest.kt @@ -51,6 +51,7 @@ class RegisteredCreditCardsScreenTest { val registeredCreditCards = RegisteredCreditCards( mutableListOf( Card( + id = 1, cardNumber = "1234-5678-1234-5678", ownerName = "홍길동", expiredDate = "12/24", @@ -58,6 +59,7 @@ class RegisteredCreditCardsScreenTest { bankType = BankType.BC ), Card( + id = 2, cardNumber = "1234-5678-1234-5628", ownerName = "홍길동", expiredDate = "12/24", @@ -120,6 +122,7 @@ class RegisteredCreditCardsScreenTest { // given : 카드 등록이 되어있다. PaymentCardsRepository.addCard( Card( + id = 13, cardNumber = "1234-5678-1234-5628", ownerName = "홍길동", expiredDate = "12/24", @@ -130,6 +133,7 @@ class RegisteredCreditCardsScreenTest { val registeredCreditCards = RegisteredCreditCards( mutableListOf( Card( + id = 4, cardNumber = "1234-5678-1234-5628", ownerName = "홍길동", expiredDate = "12/24", diff --git a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt index 94522741..4b0e1de0 100644 --- a/app/src/main/java/nextstep/payments/ui/PaymentCard.kt +++ b/app/src/main/java/nextstep/payments/ui/PaymentCard.kt @@ -60,6 +60,7 @@ fun PaymentCard( @Composable fun PaymentCardContents( card: Card, + modifier: Modifier = Modifier, onClick: (Card) -> Unit = {} ) { Box( From 1b91e7f5dbaf0bee2623af6a054a8e0f9b24e8d3 Mon Sep 17 00:00:00 2001 From: OYJ Date: Tue, 10 Sep 2024 19:52:10 +0900 Subject: [PATCH 15/18] =?UTF-8?q?Refactor=20:=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. UiState에 따른 카드 등록 분기 로직 이동 (NewCardScreen -> NewCardViewModel) 2. 관련 함수 수정 --- .../payments/ui/card/registration/NewCardScreen.kt | 13 +------------ .../ui/card/registration/NewCardViewModel.kt | 14 ++++++++++---- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index 4966a5c1..044337ba 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -33,7 +33,6 @@ import nextstep.payments.ui.PaymentCard import nextstep.payments.ui.card.registration.component.BankSelectRow import nextstep.payments.ui.card.registration.component.NewCardTopBar import nextstep.payments.ui.theme.PaymentsTheme -import kotlin.reflect.KFunction0 // Stateful @OptIn(ExperimentalMaterial3Api::class) @@ -52,7 +51,6 @@ fun NewCardScreen( val selectedBankType by viewModel.selectedBankType.collectAsStateWithLifecycle() var showCardCompanyBottomSheet by rememberSaveable { mutableStateOf(true) } val modalBottomSheetState = rememberModalBottomSheetState(confirmValueChange = { false }) - val uiState by viewModel.uiState.collectAsStateWithLifecycle() LaunchedEffect(cardAdded) { if (cardAdded) navigateToCardList() @@ -77,15 +75,6 @@ fun NewCardScreen( ) } } - val saveFunction: KFunction0 = when (uiState) { - RegistrationUiState.NewCard -> { - viewModel::addCard - } - - RegistrationUiState.EditCard -> { - viewModel::editCard - } - } NewCardScreen( modifier = modifier, @@ -99,7 +88,7 @@ fun NewCardScreen( setOwnerNamedNumber = viewModel::setOwnerName, setPasswordNumber = viewModel::setPassword, onBackClick = onBackClick, - onSaveClick = saveFunction, + onSaveClick = viewModel::saveCard, ) } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt index 065b16a3..cb8f0bf2 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt @@ -30,8 +30,7 @@ class NewCardViewModel( private val _selectedBankType = MutableStateFlow(BankType.NOT_SELECTED) val selectedBankType: StateFlow = _selectedBankType.asStateFlow() - var oldCard: Card? = null - private set + private var oldCard: Card? = null private val _uiState = MutableStateFlow(RegistrationUiState.NewCard) val uiState: StateFlow = _uiState.asStateFlow() @@ -57,7 +56,14 @@ class NewCardViewModel( _selectedBankType.value = bankType } - fun addCard() { + fun saveCard() { + when (uiState.value) { + RegistrationUiState.NewCard -> addCard() + RegistrationUiState.EditCard -> editCard() + } + } + + private fun addCard() { repository.addCard( Card( id = repository.createId(), @@ -71,7 +77,7 @@ class NewCardViewModel( _cardAdded.value = true } - fun editCard() { + private fun editCard() { if (isCardDataChange()) { repository.editCard( oldCard = oldCard, From 12b22a6d66b1c981b182f8cd0e0013c3cfa49012 Mon Sep 17 00:00:00 2001 From: OYJ Date: Wed, 11 Sep 2024 00:36:28 +0900 Subject: [PATCH 16/18] =?UTF-8?q?Refactor=20:=20=EB=A6=AC=EB=B7=B0=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 1. editCard() oldCard 파라미터 제거, oldCard의 id 불필요 2. Card의 유니크값인 id 디폴트값 0으로 변경 3. Card추가 시 Card의 id값이 0이라면 신규 Id 부여, 해당 로직을 저장소에 처리함. 4. let을 사용하여 !! 표시 제거 --- .../main/java/nextstep/payments/data/Card.kt | 2 +- .../payments/data/PaymentCardsRepository.kt | 9 ++++--- .../ui/card/registration/NewCardViewModel.kt | 26 +++++++++---------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/nextstep/payments/data/Card.kt b/app/src/main/java/nextstep/payments/data/Card.kt index b24766cc..815356a8 100644 --- a/app/src/main/java/nextstep/payments/data/Card.kt +++ b/app/src/main/java/nextstep/payments/data/Card.kt @@ -5,7 +5,7 @@ import kotlinx.parcelize.Parcelize @Parcelize data class Card( - val id: Int, + var id: Int = 0, val cardNumber: String, val expiredDate: String, val ownerName: String, diff --git a/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt b/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt index 1e704e45..01fe9e8f 100644 --- a/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt +++ b/app/src/main/java/nextstep/payments/data/PaymentCardsRepository.kt @@ -3,9 +3,10 @@ package nextstep.payments.data object PaymentCardsRepository { private val _cards = mutableListOf() - val cards: List get() = _cards.toList() + val cards: List get() = _cards fun addCard(card: Card) { + if (card.id == 0) card.id = createId() _cards.add(card) } @@ -13,12 +14,12 @@ object PaymentCardsRepository { _cards.clear() } - fun editCard(oldCard: Card?, newCard: Card) { - val index = _cards.indexOfFirst { it.id == oldCard!!.id } + fun editCard(newCard: Card) { + val index = _cards.indexOfFirst { it.id == newCard.id } _cards[index] = newCard } - fun createId(): Int { + private fun createId(): Int { return _cards.maxOfOrNull { it.id }?.plus(1) ?: 1 } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt index cb8f0bf2..5eeb03b4 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt @@ -66,7 +66,6 @@ class NewCardViewModel( private fun addCard() { repository.addCard( Card( - id = repository.createId(), cardNumber = cardNumber.value, expiredDate = expiredDate.value, ownerName = ownerName.value, @@ -78,19 +77,20 @@ class NewCardViewModel( } private fun editCard() { - if (isCardDataChange()) { - repository.editCard( - oldCard = oldCard, - newCard = Card( - id = oldCard!!.id, - cardNumber = cardNumber.value, - expiredDate = expiredDate.value, - ownerName = ownerName.value, - password = password.value, - bankType = selectedBankType.value + oldCard?.let { + if (isCardDataChange()) { + repository.editCard( + newCard = Card( + id = it.id, + cardNumber = cardNumber.value, + expiredDate = expiredDate.value, + ownerName = ownerName.value, + password = password.value, + bankType = selectedBankType.value + ) ) - ) - _cardAdded.value = true + _cardAdded.value = true + } } } From 4a398822a3964faa7e78749c0e09f069b1281b27 Mon Sep 17 00:00:00 2001 From: OYJ Date: Mon, 16 Sep 2024 21:55:40 +0900 Subject: [PATCH 17/18] =?UTF-8?q?Refactor=20:=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D,=20=EC=B9=B4=EB=93=9C=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=B4=EC=A1=B4=20=EA=B8=B0?= =?UTF-8?q?=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 1. SavedStateHandle를 활용하여 라이프사이클에 따른 데이터 소멸 방지 2. 카드 목록, 카드 등록 데이터 보존 --- .../java/nextstep/payments/MainActivity.kt | 16 +++- .../payments/data/RegisteredCreditCards.kt | 5 +- .../ui/card/list/CardListViewModel.kt | 30 ++++++- .../ui/card/registration/NewCardActivity.kt | 14 ++- .../ui/card/registration/NewCardScreen.kt | 9 +- .../ui/card/registration/NewCardViewModel.kt | 85 +++++++++++++++++-- .../card/registration/RegistrationUiState.kt | 8 +- .../registration/component/BankSelectRow.kt | 6 +- 8 files changed, 156 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/nextstep/payments/MainActivity.kt b/app/src/main/java/nextstep/payments/MainActivity.kt index 88906905..64935c8a 100644 --- a/app/src/main/java/nextstep/payments/MainActivity.kt +++ b/app/src/main/java/nextstep/payments/MainActivity.kt @@ -17,11 +17,11 @@ import nextstep.payments.ui.card.registration.NewCardActivity import nextstep.payments.ui.theme.PaymentsTheme class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val viewModel by viewModels() + private val viewModel: CardListViewModel by viewModels { CardListViewModel.Factory } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) setContent { val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { @@ -52,6 +52,16 @@ class MainActivity : ComponentActivity() { } } } + + override fun onPause() { + super.onPause() + viewModel.saveCard() + } + + override fun onResume() { + super.onResume() + viewModel.openCard() + } } diff --git a/app/src/main/java/nextstep/payments/data/RegisteredCreditCards.kt b/app/src/main/java/nextstep/payments/data/RegisteredCreditCards.kt index fb8588ef..cfa51384 100644 --- a/app/src/main/java/nextstep/payments/data/RegisteredCreditCards.kt +++ b/app/src/main/java/nextstep/payments/data/RegisteredCreditCards.kt @@ -1,8 +1,11 @@ package nextstep.payments.data +import android.os.Parcelable +import kotlinx.parcelize.Parcelize import nextstep.payments.ui.card.CreditCardUiState -data class RegisteredCreditCards(val cardList: List) { +@Parcelize +data class RegisteredCreditCards(val cardList: List) : Parcelable { fun getState(): CreditCardUiState { return when (cardList.size) { diff --git a/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt index c1dc0fa6..d369cebf 100644 --- a/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/list/CardListViewModel.kt @@ -1,13 +1,20 @@ package nextstep.payments.ui.card.list +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import nextstep.payments.data.PaymentCardsRepository import nextstep.payments.data.RegisteredCreditCards -class CardListViewModel : ViewModel() { +class CardListViewModel( + val savedStateHandle: SavedStateHandle, +) : ViewModel() { private val _registeredCreditCards = MutableStateFlow(RegisteredCreditCards(emptyList())) val registeredCreditCards: StateFlow = @@ -16,4 +23,25 @@ class CardListViewModel : ViewModel() { fun fetchCards() { _registeredCreditCards.value = RegisteredCreditCards(PaymentCardsRepository.cards) } + + fun saveCard() { + savedStateHandle[KEY_REGISTERED_CREDIT_CARDS] = _registeredCreditCards.value + } + + fun openCard() { + _registeredCreditCards.value = + savedStateHandle[KEY_REGISTERED_CREDIT_CARDS] ?: RegisteredCreditCards(emptyList()) + } + + companion object { + private const val KEY_REGISTERED_CREDIT_CARDS = "registeredCreditCards" + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val savedStateHandle = createSavedStateHandle() + CardListViewModel( + savedStateHandle = savedStateHandle + ) + } + } + } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt index 301cff18..68d9f639 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt @@ -9,17 +9,15 @@ import nextstep.payments.ui.theme.PaymentsTheme class NewCardActivity : ComponentActivity() { - private var card: Card? = null + private val viewModel: NewCardViewModel by viewModels { NewCardViewModel.Factory } override fun onCreate(savedInstanceState: Bundle?) { - val viewModel by viewModels() super.onCreate(savedInstanceState) (intent?.getParcelableExtra("card") as Card?)?.let { viewModel.setOldCard(it) viewModel.setUiState(RegistrationUiState.EditCard) } - setContent { PaymentsTheme { NewCardScreen( @@ -35,6 +33,16 @@ class NewCardActivity : ComponentActivity() { } } } + + override fun onResume() { + super.onResume() + viewModel.setData() + } + + override fun onPause() { + super.onPause() + viewModel.saveData() + } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt index 044337ba..a63b9158 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardScreen.kt @@ -1,5 +1,6 @@ package nextstep.payments.ui.card.registration +import android.annotation.SuppressLint import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -25,6 +26,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import nextstep.payments.R @@ -35,6 +37,7 @@ import nextstep.payments.ui.card.registration.component.NewCardTopBar import nextstep.payments.ui.theme.PaymentsTheme // Stateful +@SuppressLint("StateFlowValueCalledInComposition") @OptIn(ExperimentalMaterial3Api::class) @Composable fun NewCardScreen( @@ -52,6 +55,7 @@ fun NewCardScreen( var showCardCompanyBottomSheet by rememberSaveable { mutableStateOf(true) } val modalBottomSheetState = rememberModalBottomSheetState(confirmValueChange = { false }) + LaunchedEffect(cardAdded) { if (cardAdded) navigateToCardList() } @@ -170,10 +174,13 @@ private fun NewCardScreen( @Preview @Composable private fun NewCardScreenPreview() { + val savedState = SavedStateHandle(mapOf("someIdArg" to 1)) PaymentsTheme { NewCardScreen(modifier = Modifier, navigateToCardList = {}, - viewModel = NewCardViewModel().apply { + viewModel = NewCardViewModel( + savedStateHandle = savedState + ).apply { setCardNumber("0000 - 0000 - 0000 -0000") setExpiredDate("02/26") setOwnerName("김수현") diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt index 5eeb03b4..7f4cbe92 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt @@ -1,6 +1,12 @@ package nextstep.payments.ui.card.registration +import android.util.Log +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -9,7 +15,8 @@ import nextstep.payments.data.Card import nextstep.payments.data.PaymentCardsRepository class NewCardViewModel( - private val repository: PaymentCardsRepository = PaymentCardsRepository + private val repository: PaymentCardsRepository = PaymentCardsRepository, + private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val _cardNumber = MutableStateFlow("") @@ -35,6 +42,49 @@ class NewCardViewModel( private val _uiState = MutableStateFlow(RegistrationUiState.NewCard) val uiState: StateFlow = _uiState.asStateFlow() + init { + setData() + } + + fun setData() { + Log.e(TAG, "setData") + savedStateHandle.get(KEY_CARD_NUMBER)?.let { + _cardNumber.value = it + } + Log.e(TAG, "setData: ${_cardNumber.value}") + savedStateHandle.get(KEY_EXPIRED)?.let { + _expiredDate.value = it + } + + savedStateHandle.get(KEY_OWNER_NAME)?.let { + _ownerName.value = it + } + + savedStateHandle.get(KEY_PASSWORD)?.let { + _password.value = it + } + + savedStateHandle.get(KEY_BANK_TYPE)?.let { + _selectedBankType.value = it + } + + savedStateHandle.get(KEY_UI_STATE)?.let { + _uiState.value = it + } + savedStateHandle.get(KEY_OLD_CARD)?.let { + oldCard = it + } + } + + fun saveData() { + savedStateHandle[KEY_CARD_NUMBER] = cardNumber.value + savedStateHandle[KEY_EXPIRED] = expiredDate.value + savedStateHandle[KEY_OWNER_NAME] = ownerName.value + savedStateHandle[KEY_PASSWORD] = password.value + savedStateHandle[KEY_BANK_TYPE] = selectedBankType.value + savedStateHandle[KEY_UI_STATE] = uiState.value + savedStateHandle[KEY_OLD_CARD] = oldCard + } fun setCardNumber(cardNumber: String) { _cardNumber.value = cardNumber @@ -105,15 +155,38 @@ class NewCardViewModel( } fun setOldCard(card: Card) { + if (savedStateHandle.get(KEY_OLD_CARD) == null) { + _cardNumber.value = card.cardNumber + _expiredDate.value = card.expiredDate + _ownerName.value = card.ownerName + _password.value = card.password + _selectedBankType.value = card.bankType + } oldCard = card - _cardNumber.value = card.cardNumber - _expiredDate.value = card.expiredDate - _ownerName.value = card.ownerName - _password.value = card.password - _selectedBankType.value = card.bankType } fun setUiState(newUiState: RegistrationUiState) { _uiState.value = newUiState } + + companion object { + private const val TAG = "NewCardViewModel" + private const val KEY_CARD_NUMBER = "cardNumber" + private const val KEY_EXPIRED = "expired" + private const val KEY_OWNER_NAME = "ownerName" + private const val KEY_PASSWORD = "password" + private const val KEY_BANK_TYPE = "bankType" + private const val KEY_UI_STATE = "uiState" + private const val KEY_OLD_CARD = "oldCard" + + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val savedStateHandle = createSavedStateHandle() + NewCardViewModel( + repository = PaymentCardsRepository, + savedStateHandle = savedStateHandle + ) + } + } + } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt b/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt index bd12cad3..a1f213a9 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/RegistrationUiState.kt @@ -1,6 +1,12 @@ package nextstep.payments.ui.card.registration -sealed interface RegistrationUiState { +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +sealed interface RegistrationUiState : Parcelable { + @Parcelize data object NewCard : RegistrationUiState + + @Parcelize data object EditCard : RegistrationUiState } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt b/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt index c2a363b6..fe72617b 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/component/BankSelectRow.kt @@ -1,5 +1,6 @@ package nextstep.payments.ui.card.registration.component +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -54,7 +55,10 @@ fun CardSelector( Column( modifier = Modifier .size(80.dp) - .clickable { onClick(bankType) }, + .clickable { + Log.e("CardSelector", "CardSelector: $bankType") + onClick(bankType) + }, ) { Image( painter = painterResource(id = bankType.logo), From 656f36a00fa1161d8fca1ea6a009acadf87a8184 Mon Sep 17 00:00:00 2001 From: OYJ Date: Mon, 16 Sep 2024 22:01:59 +0900 Subject: [PATCH 18/18] =?UTF-8?q?Refactor=20:=20=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20uiState=20=EC=83=81=ED=83=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 카드 저장 uiState 상태 저장 로직 변경 2. 카드 데이터 저장 및 열기 네이밍 변경 --- .../ui/card/registration/NewCardActivity.kt | 4 ++-- .../ui/card/registration/NewCardViewModel.kt | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt index 68d9f639..e6fb70b2 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardActivity.kt @@ -36,12 +36,12 @@ class NewCardActivity : ComponentActivity() { override fun onResume() { super.onResume() - viewModel.setData() + viewModel.openCardData() } override fun onPause() { super.onPause() - viewModel.saveData() + viewModel.saveCardData() } } diff --git a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt index 7f4cbe92..b5774bdc 100644 --- a/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt +++ b/app/src/main/java/nextstep/payments/ui/card/registration/NewCardViewModel.kt @@ -1,6 +1,5 @@ package nextstep.payments.ui.card.registration -import android.util.Log import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -43,15 +42,13 @@ class NewCardViewModel( val uiState: StateFlow = _uiState.asStateFlow() init { - setData() + openCardData() } - fun setData() { - Log.e(TAG, "setData") + fun openCardData() { savedStateHandle.get(KEY_CARD_NUMBER)?.let { _cardNumber.value = it } - Log.e(TAG, "setData: ${_cardNumber.value}") savedStateHandle.get(KEY_EXPIRED)?.let { _expiredDate.value = it } @@ -76,7 +73,7 @@ class NewCardViewModel( } } - fun saveData() { + fun saveCardData() { savedStateHandle[KEY_CARD_NUMBER] = cardNumber.value savedStateHandle[KEY_EXPIRED] = expiredDate.value savedStateHandle[KEY_OWNER_NAME] = ownerName.value @@ -166,7 +163,9 @@ class NewCardViewModel( } fun setUiState(newUiState: RegistrationUiState) { - _uiState.value = newUiState + if(savedStateHandle.get(KEY_UI_STATE) == null) { + _uiState.value = newUiState + } } companion object {