From e40274428fe9a59715b6d3e7fe5013230ca62426 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Mon, 28 Oct 2024 14:29:58 +0900 Subject: [PATCH 01/42] =?UTF-8?q?docs:=20step1=20=EC=9A=94=EA=B5=AC=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=AA=A9=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b0d9ac6..4a15df8 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# android-shopping-cart \ No newline at end of file +# android-shopping-cart + +# step1 + +## 기능 요구 사항 +- [ ] 상품 목록 화면을 구현한다. + +## 프로그래밍 요구 사항 +- [ ] viewModel, Hilt 등은 장바구니 미션에서 활용하지 않는다. +- [ ] 상품 목록 화면을 구현할 때 Lazy 컴포넌트를 활용한다. +- [ ] 컴포저블 함수가 너무 많은 일을 하지 않도록 분리하기 위해 노력해 본다. + - [ ] 의미있는 단위의 함수를 모아 별도의 파일로 분리해본다. + \ No newline at end of file From 82ed69c1a239f45190ff8015e69a439d95384953 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Mon, 28 Oct 2024 15:16:15 +0900 Subject: [PATCH 02/42] =?UTF-8?q?style:=20=EC=83=89=EC=83=81=20=EB=B0=8F?= =?UTF-8?q?=20Typography=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/shoppingcart/ui/theme/Color.kt | 5 +- .../nextstep/shoppingcart/ui/theme/Type.kt | 79 +++++++++++++------ 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt index 8b22251..5f7c85a 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt @@ -8,4 +8,7 @@ val Pink80 = Color(0xFFEFB8C8) val Purple40 = Color(0xFF6650a4) val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file +val Pink40 = Color(0xFF7D5260) + +val Gray20 = Color(0xFF333333) +val Gray50 = Color(0xFF1D1B20) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt index 75be968..f4ad945 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt @@ -1,34 +1,65 @@ package nextstep.shoppingcart.ui.theme import androidx.compose.material3.Typography +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp // Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp +val Typography = + Typography( + headlineMedium = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + color = Gray50, + ), + titleLarge = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + fontSize = 24.sp, + lineHeight = 28.sp, + letterSpacing = 0.5.sp, + color = Gray20, + ), + titleSmall = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + fontSize = 16.sp, + lineHeight = 14.sp, + letterSpacing = 0.5.sp, + color = Gray20, + ), + bodyMedium = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 20.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp, + color = Gray20, + ), + bodySmall = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 20.sp, + letterSpacing = 0.5.sp, + color = Gray20, + ), + labelMedium = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + fontSize = 20.sp, + lineHeight = 24.sp, + color = Color.White, + ), ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file From 1e97f779c630db33cf96bed41320226b2c01579b Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Mon, 28 Oct 2024 15:16:31 +0900 Subject: [PATCH 03/42] =?UTF-8?q?chore:=20ktlint=20=ED=98=95=EC=8B=9D=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 --- .editorConfig | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .editorConfig diff --git a/.editorConfig b/.editorConfig new file mode 100644 index 0000000..931bfa6 --- /dev/null +++ b/.editorConfig @@ -0,0 +1,2 @@ +[*.{kt,kts}] +ktlint_function_naming_ignore_when_annotated_with=Composable \ No newline at end of file From 6037c2656fb318641d4d21677e92421556bc25bc Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Mon, 28 Oct 2024 15:16:44 +0900 Subject: [PATCH 04/42] =?UTF-8?q?chore:=20ktlint=20=ED=94=8C=EB=9F=AC?= =?UTF-8?q?=EA=B7=B8=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 --- app/build.gradle.kts | 3 ++- build.gradle.kts | 1 + gradle/libs.versions.toml | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1e47716..a3782a7 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) + alias(libs.plugins.ktlint) } android { @@ -25,7 +26,7 @@ android { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } diff --git a/build.gradle.kts b/build.gradle.kts index c1e23bc..ee34f02 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,5 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false + alias(libs.plugins.ktlint) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5080245..3299cb1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,8 @@ espressoCore = "3.6.1" lifecycleRuntimeKtx = "2.8.4" activityCompose = "1.9.1" composeBom = "2024.06.00" +ktlint = "12.1.0" + [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -26,3 +28,4 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" [plugins] android-application = { id = "com.android.application", version.ref = "agp" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } From bfcb28c901fbea6a390a5cc8f64a4d103d29989b Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Mon, 28 Oct 2024 15:17:15 +0900 Subject: [PATCH 05/42] =?UTF-8?q?style:=20ktlint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/shoppingcart/MainActivity.kt | 9 ++-- .../nextstep/shoppingcart/ui/theme/Theme.kt | 51 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/MainActivity.kt b/app/src/main/java/nextstep/shoppingcart/MainActivity.kt index 5bbb116..b5ba41d 100644 --- a/app/src/main/java/nextstep/shoppingcart/MainActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/MainActivity.kt @@ -20,7 +20,7 @@ class MainActivity : ComponentActivity() { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background + color = MaterialTheme.colorScheme.background, ) { Greeting("Android") } @@ -30,10 +30,13 @@ class MainActivity : ComponentActivity() { } @Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { +fun Greeting( + name: String, + modifier: Modifier = Modifier, +) { Text( text = "Hello $name!", - modifier = modifier + modifier = modifier, ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt index 3e5e697..9142e09 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt @@ -1,6 +1,5 @@ package nextstep.shoppingcart.ui.theme -import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme @@ -9,23 +8,20 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.core.view.WindowCompat -private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 -) - -private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 +private val DarkColorScheme = + darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80, + ) +private val LightColorScheme = + lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40, /* Other default colors to override background = Color(0xFFFFFBFE), surface = Color(0xFFFFFBFE), @@ -34,29 +30,30 @@ private val LightColorScheme = lightColorScheme( onTertiary = Color.White, onBackground = Color(0xFF1C1B1F), onSurface = Color(0xFF1C1B1F), - */ -) + */ + ) @Composable fun ShoppingCartTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, - content: @Composable () -> Unit + 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) - } + 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 - } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } MaterialTheme( colorScheme = colorScheme, typography = Typography, - content = content + content = content, ) } From 939223b2dbad2af87b7a63292c9349f80a6c8e82 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 11:50:33 +0900 Subject: [PATCH 06/42] =?UTF-8?q?chore:=20coil=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 | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a3782a7..ac1268b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -67,4 +67,5 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) + implementation(libs.bundles.coil) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3299cb1..fc0e1bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ lifecycleRuntimeKtx = "2.8.4" activityCompose = "1.9.1" composeBom = "2024.06.00" ktlint = "12.1.0" +coil = "3.0.0-rc01" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -25,6 +26,12 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } +coil-network = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" } + +[bundles] +coil = ["coil-compose", "coil-network"] + [plugins] android-application = { id = "com.android.application", version.ref = "agp" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } From 0ea7b7c1b71b4c9ea8b72ec98c704c9ca02799bb Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 12:03:32 +0900 Subject: [PATCH 07/42] =?UTF-8?q?style:=20dynamicColor=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20&=20=EB=B0=B0=EA=B2=BD=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/shoppingcart/ui/theme/Theme.kt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt index 9142e09..7cfef6f 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Theme.kt @@ -1,20 +1,18 @@ package nextstep.shoppingcart.ui.theme -import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.graphics.Color private val DarkColorScheme = darkColorScheme( primary = Purple80, secondary = PurpleGrey80, tertiary = Pink80, + background = Color.Black, ) private val LightColorScheme = @@ -22,6 +20,7 @@ private val LightColorScheme = primary = Purple40, secondary = PurpleGrey40, tertiary = Pink40, + background = Color.White, /* Other default colors to override background = Color(0xFFFFFBFE), surface = Color(0xFFFFFBFE), @@ -36,17 +35,10 @@ private val LightColorScheme = @Composable fun ShoppingCartTheme( darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - 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 } From e69f7b35dfe61c86b69445b8a6bd391424d4896a Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 12:03:50 +0900 Subject: [PATCH 08/42] =?UTF-8?q?style:=20string=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=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/res/values/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cc5b76c..9f5dae9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,8 @@ Shopping Cart + 상품 목록 + + %,d원 + 장바구니 버튼 + $s 상품 이미지 \ No newline at end of file From 2748474bf16f21bf7359d13b58b464d303833849 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 12:04:26 +0900 Subject: [PATCH 09/42] =?UTF-8?q?feat:=20Product=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8D=94=EB=AF=B8=EB=8D=B0=EC=9D=B4=ED=84=B0=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 --- .../nextstep/shoppingcart/domain/Product.kt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/domain/Product.kt diff --git a/app/src/main/java/nextstep/shoppingcart/domain/Product.kt b/app/src/main/java/nextstep/shoppingcart/domain/Product.kt new file mode 100644 index 0000000..01f8c25 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/domain/Product.kt @@ -0,0 +1,74 @@ +package nextstep.shoppingcart.domain + +data class Product( + val id: Long, + val name: String, + val price: Int, + val imageUrl: String, +) { + companion object { + fun dummy() = + listOf( + Product( + 0, + "미우미우 - 오드리햅번ver", + 27000, + "https://github.com/user-attachments/assets/5470cb47-32c7-485f-9941-85eb12fc8361", + ), + Product( + 1, + "아기 복숭아 - 173cm", + 50000, + "https://github.com/user-attachments/assets/3e627034-236d-4516-9689-bd6d6f33971b", + ), + Product( + 2, + "궁극의 아이돌 - 콘서트", + 72000, + "https://github.com/user-attachments/assets/bec777ce-ac0c-48b7-8d40-997babc8a02c", + ), + Product( + 3, + "탤마뿌럼못날뤼가 - smart", + 25400, + "https://github.com/user-attachments/assets/f9576cfd-6b8a-47a3-ac3d-6ae267373bfa", + ), + Product( + 4, + "스빠이쉬쓰빠이쉬쓰빠이쉬쓰빠이쉬 - spicy", + 123456, + "https://github.com/user-attachments/assets/c9f30f84-3d73-420a-9f0f-52f4df47a36d", + ), + Product( + 5, + "이부자리 - 팬싸템ver", + 73920, + "https://github.com/user-attachments/assets/a1088a04-7e00-4c86-b5ac-bfff1a825910", + ), + Product( + 6, + "아기사막여우 - 키스오브라이프뜻이인공호흡이래요", + 353534, + "https://github.com/user-attachments/assets/358cf2a6-7b4a-40a2-b6e5-ffa6303eab27", + ), + Product( + 7, + "위플래쉬 - 개띵곡", + 5383, + "https://github.com/user-attachments/assets/bdbe4e39-3852-4fd8-8e7c-5173bfa716fa", + ), + Product( + 8, + "삐끼삐끼삐끼삐끼 - 프미나", + 23452363, + "https://github.com/user-attachments/assets/7d69b1b6-bf6c-4bc9-af82-88613ac32ac6", + ), + Product( + 9, + "천년의 아이돌 - 미스터츄", + 3582398, + "https://github.com/user-attachments/assets/50ee770a-6155-4021-a849-27a5a4078065", + ), + ) + } +} From 324850f5e74a4ee43a8889101b2322ed48b44be2 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 12:04:44 +0900 Subject: [PATCH 10/42] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20Ui=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 4 +- .../nextstep/shoppingcart/MainActivity.kt | 49 ----------------- .../ui/productlist/ProductList.kt | 26 +++++++++ .../ui/productlist/ProductListActivity.kt | 47 ++++++++++++++++ .../ui/productlist/ProductListItem.kt | 55 +++++++++++++++++++ .../ui/productlist/ProductListTopBar.kt | 51 +++++++++++++++++ 6 files changed, 182 insertions(+), 50 deletions(-) delete mode 100644 app/src/main/java/nextstep/shoppingcart/MainActivity.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 99ac770..52f65c5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + diff --git a/app/src/main/java/nextstep/shoppingcart/MainActivity.kt b/app/src/main/java/nextstep/shoppingcart/MainActivity.kt deleted file mode 100644 index b5ba41d..0000000 --- a/app/src/main/java/nextstep/shoppingcart/MainActivity.kt +++ /dev/null @@ -1,49 +0,0 @@ -package nextstep.shoppingcart - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import nextstep.shoppingcart.ui.theme.ShoppingCartTheme - -class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - ShoppingCartTheme { - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background, - ) { - Greeting("Android") - } - } - } - } -} - -@Composable -fun Greeting( - name: String, - modifier: Modifier = Modifier, -) { - Text( - text = "Hello $name!", - modifier = modifier, - ) -} - -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - ShoppingCartTheme { - Greeting("Android") - } -} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt new file mode 100644 index 0000000..f327043 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt @@ -0,0 +1,26 @@ +package nextstep.shoppingcart.ui.productlist + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import nextstep.shoppingcart.domain.Product + +@Composable +fun ProductList(modifier: Modifier = Modifier) { + LazyVerticalGrid( + columns = GridCells.Fixed(2), + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(20.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + contentPadding = PaddingValues(18.dp), + ) { + items(Product.dummy()) { item -> + ProductListItem(item) + } + } +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt new file mode 100644 index 0000000..85d0c9e --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt @@ -0,0 +1,47 @@ +package nextstep.shoppingcart.ui.productlist + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import nextstep.shoppingcart.ui.theme.ShoppingCartTheme + +class ProductListActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + ShoppingCartTheme { + ProductListScreen() + } + } + } +} + +@Composable +fun ProductListScreen() { + Scaffold( + topBar = { + ProductListTopBar({}) + }, + modifier = Modifier.fillMaxSize(), + ) { innerPadding -> + ProductList( + modifier = Modifier.padding(innerPadding), + ) + } +} + +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun ProductListScreenPreview() { + ShoppingCartTheme { + ProductListScreen() + } +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt new file mode 100644 index 0000000..06cf408 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt @@ -0,0 +1,55 @@ +package nextstep.shoppingcart.ui.productlist + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import coil3.compose.AsyncImage +import nextstep.shoppingcart.domain.Product +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@Composable +fun ProductListItem( + product: Product, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxSize(), + ) { + AsyncImage( + modifier = + Modifier + .fillMaxWidth() + .aspectRatio(1f), + model = product.imageUrl, + contentDescription = stringResource(R.string.product_list_image_desc, product.name), + contentScale = ContentScale.Crop, + ) + Text( + text = product.name, + style = Typography.titleSmall, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + Text( + text = stringResource(R.string.product_price_format, product.price), + style = Typography.bodySmall, + overflow = TextOverflow.Visible, + maxLines = 1, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun ProductListItemPreview() { + ProductListItem(Product.dummy().first()) +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt new file mode 100644 index 0000000..421e41f --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt @@ -0,0 +1,51 @@ +package nextstep.shoppingcart.ui.productlist + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ShoppingCart +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@Composable +fun ProductListTopBar( + onCartClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Box( + modifier = + modifier + .fillMaxWidth() + .padding(vertical = 18.dp, horizontal = 8.dp), + ) { + IconButton(onCartClick, modifier = Modifier.align(Alignment.CenterEnd)) { + Icon( + imageVector = Icons.Filled.ShoppingCart, + contentDescription = stringResource(R.string.product_list_cart_button_desc), + ) + } + Text( + stringResource(R.string.product_list_screen_title), + modifier = Modifier.align(Alignment.Center), + style = Typography.headlineMedium, + ) + } +} + +@Preview( + showBackground = true, +) +@Composable +private fun ProductListTopBarPreview() { + ProductListTopBar({}) +} From 9b38a6f35f1de43d3c9b08d8c14cc5b21b8c4f02 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 16:27:06 +0900 Subject: [PATCH 11/42] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=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 --- app/src/main/AndroidManifest.xml | 2 + .../ui/productdetail/ProductDetailActivity.kt | 48 +++++++++++++++++++ .../ui/productlist/ProductList.kt | 7 ++- .../ui/productlist/ProductListActivity.kt | 8 ++++ .../ui/productlist/ProductListItem.kt | 17 +++++-- 5 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 52f65c5..a977716 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,8 @@ android:supportsRtl="true" android:theme="@style/Theme.ShoppingCart" tools:targetApi="31"> + Unit, + modifier: Modifier = Modifier, +) { LazyVerticalGrid( columns = GridCells.Fixed(2), modifier = modifier, @@ -20,7 +23,7 @@ fun ProductList(modifier: Modifier = Modifier) { contentPadding = PaddingValues(18.dp), ) { items(Product.dummy()) { item -> - ProductListItem(item) + ProductListItem(item, onItemClick) } } } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt index 85d0c9e..9ffa841 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt @@ -9,7 +9,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity import nextstep.shoppingcart.ui.theme.ShoppingCartTheme class ProductListActivity : ComponentActivity() { @@ -32,7 +34,13 @@ fun ProductListScreen() { }, modifier = Modifier.fillMaxSize(), ) { innerPadding -> + + val context = LocalContext.current + ProductList( + onItemClick = { product -> + context.startActivity(ProductDetailActivity.newIntent(context, product.id)) + }, modifier = Modifier.padding(innerPadding), ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt index 06cf408..7e5b091 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt @@ -1,5 +1,6 @@ package nextstep.shoppingcart.ui.productlist +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize @@ -19,16 +20,22 @@ import nextstep.signup.R @Composable fun ProductListItem( product: Product, + onClick: (Product) -> Unit, modifier: Modifier = Modifier, ) { Column( - modifier = modifier.fillMaxSize(), + modifier = + modifier + .fillMaxSize() + .clickable { + onClick(product) + }, ) { AsyncImage( modifier = - Modifier - .fillMaxWidth() - .aspectRatio(1f), + Modifier + .fillMaxWidth() + .aspectRatio(1f), model = product.imageUrl, contentDescription = stringResource(R.string.product_list_image_desc, product.name), contentScale = ContentScale.Crop, @@ -51,5 +58,5 @@ fun ProductListItem( @Preview(showBackground = true) @Composable private fun ProductListItemPreview() { - ProductListItem(Product.dummy().first()) + ProductListItem(Product.dummy().first(), {}) } From 83c6f49251c597e67ead2cc970f0feb96f140c8b Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 16:47:19 +0900 Subject: [PATCH 12/42] =?UTF-8?q?refactor:=20Box=20->=20CenterAlignedTopAp?= =?UTF-8?q?pBar=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/productlist/ProductListTopBar.kt | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt index 421e41f..55b7c43 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListTopBar.kt @@ -1,15 +1,16 @@ package nextstep.shoppingcart.ui.productlist -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ShoppingCart +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -17,29 +18,31 @@ import androidx.compose.ui.unit.dp import nextstep.shoppingcart.ui.theme.Typography import nextstep.signup.R +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ProductListTopBar( onCartClick: () -> Unit, modifier: Modifier = Modifier, ) { - Box( - modifier = - modifier - .fillMaxWidth() - .padding(vertical = 18.dp, horizontal = 8.dp), - ) { - IconButton(onCartClick, modifier = Modifier.align(Alignment.CenterEnd)) { - Icon( - imageVector = Icons.Filled.ShoppingCart, - contentDescription = stringResource(R.string.product_list_cart_button_desc), + CenterAlignedTopAppBar( + title = { + Text( + stringResource(R.string.product_list_screen_title), + style = Typography.headlineMedium, + modifier = Modifier.wrapContentHeight().padding(vertical = 18.dp), ) - } - Text( - stringResource(R.string.product_list_screen_title), - modifier = Modifier.align(Alignment.Center), - style = Typography.headlineMedium, - ) - } + }, + actions = { + IconButton(onCartClick, modifier = Modifier.padding(end = 8.dp)) { + Icon( + imageVector = Icons.Filled.ShoppingCart, + contentDescription = stringResource(R.string.product_list_cart_button_desc), + ) + } + }, + modifier = + modifier.fillMaxWidth(), + ) } @Preview( From 6ff7cddb61fcf0c4f565938c186d37ad027a93b1 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:48:30 +0900 Subject: [PATCH 13/42] =?UTF-8?q?style:=20=EC=83=89=EC=83=81=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 --- app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt index 5f7c85a..46dc5b4 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt @@ -10,5 +10,8 @@ val Purple40 = Color(0xFF6650a4) val PurpleGrey40 = Color(0xFF625b71) val Pink40 = Color(0xFF7D5260) +val Gray10 = Color(0xFFAAAAAA) val Gray20 = Color(0xFF333333) val Gray50 = Color(0xFF1D1B20) + +val Blue50 = Color(0xFF2196F3) \ No newline at end of file From 983456dc1130ac5ff09785393554f68b14cf7398 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:49:44 +0900 Subject: [PATCH 14/42] =?UTF-8?q?feat:=20context=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=20activity=EB=A5=BC=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/shoppingcart/ui/util/FindActivity.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt diff --git a/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt new file mode 100644 index 0000000..d18c5de --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt @@ -0,0 +1,14 @@ +package nextstep.shoppingcart.ui.util + +import android.app.Activity +import android.content.Context +import android.content.ContextWrapper + +fun Context.findActivity(): Activity { + var context = this + while (context is ContextWrapper) { + if (context is Activity) return context + context = context.baseContext + } + throw IllegalStateException("Activity를 찾을 수 없습니다.") +} \ No newline at end of file From 5a02f7c2fdbe644e187b208ace50e421f6f3c377 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:50:15 +0900 Subject: [PATCH 15/42] =?UTF-8?q?feat:=20ProductRepository=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20=EC=83=81=ED=92=88=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EB=A1=9C=EC=A7=81=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 --- .../nextstep/shoppingcart/domain/Product.kt | 68 +----------------- .../shoppingcart/domain/ProductRepository.kt | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 67 deletions(-) create mode 100644 app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt diff --git a/app/src/main/java/nextstep/shoppingcart/domain/Product.kt b/app/src/main/java/nextstep/shoppingcart/domain/Product.kt index 01f8c25..0cd4036 100644 --- a/app/src/main/java/nextstep/shoppingcart/domain/Product.kt +++ b/app/src/main/java/nextstep/shoppingcart/domain/Product.kt @@ -5,70 +5,4 @@ data class Product( val name: String, val price: Int, val imageUrl: String, -) { - companion object { - fun dummy() = - listOf( - Product( - 0, - "미우미우 - 오드리햅번ver", - 27000, - "https://github.com/user-attachments/assets/5470cb47-32c7-485f-9941-85eb12fc8361", - ), - Product( - 1, - "아기 복숭아 - 173cm", - 50000, - "https://github.com/user-attachments/assets/3e627034-236d-4516-9689-bd6d6f33971b", - ), - Product( - 2, - "궁극의 아이돌 - 콘서트", - 72000, - "https://github.com/user-attachments/assets/bec777ce-ac0c-48b7-8d40-997babc8a02c", - ), - Product( - 3, - "탤마뿌럼못날뤼가 - smart", - 25400, - "https://github.com/user-attachments/assets/f9576cfd-6b8a-47a3-ac3d-6ae267373bfa", - ), - Product( - 4, - "스빠이쉬쓰빠이쉬쓰빠이쉬쓰빠이쉬 - spicy", - 123456, - "https://github.com/user-attachments/assets/c9f30f84-3d73-420a-9f0f-52f4df47a36d", - ), - Product( - 5, - "이부자리 - 팬싸템ver", - 73920, - "https://github.com/user-attachments/assets/a1088a04-7e00-4c86-b5ac-bfff1a825910", - ), - Product( - 6, - "아기사막여우 - 키스오브라이프뜻이인공호흡이래요", - 353534, - "https://github.com/user-attachments/assets/358cf2a6-7b4a-40a2-b6e5-ffa6303eab27", - ), - Product( - 7, - "위플래쉬 - 개띵곡", - 5383, - "https://github.com/user-attachments/assets/bdbe4e39-3852-4fd8-8e7c-5173bfa716fa", - ), - Product( - 8, - "삐끼삐끼삐끼삐끼 - 프미나", - 23452363, - "https://github.com/user-attachments/assets/7d69b1b6-bf6c-4bc9-af82-88613ac32ac6", - ), - Product( - 9, - "천년의 아이돌 - 미스터츄", - 3582398, - "https://github.com/user-attachments/assets/50ee770a-6155-4021-a849-27a5a4078065", - ), - ) - } -} +) diff --git a/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt b/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt new file mode 100644 index 0000000..fbab646 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt @@ -0,0 +1,70 @@ +package nextstep.shoppingcart.domain + +object ProductRepository { + + val dummy: List = listOf( + Product( + 0, + "미우미우 - 오드리햅번ver", + 27000, + "https://github.com/user-attachments/assets/5470cb47-32c7-485f-9941-85eb12fc8361", + ), + Product( + 1, + "아기 복숭아 - 173cm", + 50000, + "https://github.com/user-attachments/assets/3e627034-236d-4516-9689-bd6d6f33971b", + ), + Product( + 2, + "궁극의 아이돌 - 콘서트", + 72000, + "https://github.com/user-attachments/assets/bec777ce-ac0c-48b7-8d40-997babc8a02c", + ), + Product( + 3, + "탤마뿌럼못날뤼가 - smart", + 25400, + "https://github.com/user-attachments/assets/f9576cfd-6b8a-47a3-ac3d-6ae267373bfa", + ), + Product( + 4, + "스빠이쉬쓰빠이쉬쓰빠이쉬쓰빠이쉬 - spicy", + 123456, + "https://github.com/user-attachments/assets/c9f30f84-3d73-420a-9f0f-52f4df47a36d", + ), + Product( + 5, + "이부자리 - 팬싸템ver", + 73920, + "https://github.com/user-attachments/assets/a1088a04-7e00-4c86-b5ac-bfff1a825910", + ), + Product( + 6, + "아기사막여우 - 키스오브라이프뜻이인공호흡이래요", + 353534, + "https://github.com/user-attachments/assets/358cf2a6-7b4a-40a2-b6e5-ffa6303eab27", + ), + Product( + 7, + "위플래쉬 - 개띵곡", + 5383, + "https://github.com/user-attachments/assets/bdbe4e39-3852-4fd8-8e7c-5173bfa716fa", + ), + Product( + 8, + "삐끼삐끼삐끼삐끼 - 프미나", + 23452363, + "https://github.com/user-attachments/assets/7d69b1b6-bf6c-4bc9-af82-88613ac32ac6", + ), + Product( + 9, + "천년의 아이돌 - 미스터츄", + 3582398, + "https://github.com/user-attachments/assets/50ee770a-6155-4021-a849-27a5a4078065", + ), + ) + + fun productById(id: Long): Product = + dummy.find { it.id == id } ?: throw IllegalArgumentException("$id 에 해당하는 값이 없습니다.") +} \ No newline at end of file From 3032ca5ea2ce537b8e7b334d8420521c177baacf Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:50:48 +0900 Subject: [PATCH 16/42] =?UTF-8?q?style:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=99=94=EB=A9=B4=20strings.xml=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 --- app/src/main/res/values/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9f5dae9..87ee89f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,4 +5,10 @@ %,d원 장바구니 버튼 $s 상품 이미지 + + 상품 상세 + 상품 상세 이미지 + 뒤로가기 버튼 + 금액 + 장바구니 담기 \ No newline at end of file From c92353b9329f447c4e85a91f9b161befa2b33c0b Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:51:39 +0900 Subject: [PATCH 17/42] =?UTF-8?q?fix:=20repository=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=20=EC=83=81=ED=92=88=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=EC=9D=84=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=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 --- .../shoppingcart/ui/productlist/ProductListActivity.kt | 2 ++ .../nextstep/shoppingcart/ui/productlist/ProductListItem.kt | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt index 9ffa841..bf46077 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import nextstep.shoppingcart.domain.ProductRepository import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity import nextstep.shoppingcart.ui.theme.ShoppingCartTheme @@ -38,6 +39,7 @@ fun ProductListScreen() { val context = LocalContext.current ProductList( + products = ProductRepository.dummy, onItemClick = { product -> context.startActivity(ProductDetailActivity.newIntent(context, product.id)) }, diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt index 7e5b091..a0227a9 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListItem.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import coil3.compose.AsyncImage import nextstep.shoppingcart.domain.Product +import nextstep.shoppingcart.domain.ProductRepository import nextstep.shoppingcart.ui.theme.Typography import nextstep.signup.R @@ -58,5 +59,5 @@ fun ProductListItem( @Preview(showBackground = true) @Composable private fun ProductListItemPreview() { - ProductListItem(Product.dummy().first(), {}) + ProductListItem(ProductRepository.dummy.first(), {}) } From ce2393f2f57b4f5c9fd99d15cb66d4c73c43c944 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:52:00 +0900 Subject: [PATCH 18/42] =?UTF-8?q?feat:=20item=20=ED=82=A4=20=EA=B0=92=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 --- .../nextstep/shoppingcart/ui/productlist/ProductList.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt index 8c88f80..a1baaf3 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt @@ -12,6 +12,7 @@ import nextstep.shoppingcart.domain.Product @Composable fun ProductList( + products: List, onItemClick: (Product) -> Unit, modifier: Modifier = Modifier, ) { @@ -22,7 +23,12 @@ fun ProductList( horizontalArrangement = Arrangement.spacedBy(12.dp), contentPadding = PaddingValues(18.dp), ) { - items(Product.dummy()) { item -> + items( + items = products, + key = { product -> + product.id + } + ) { item -> ProductListItem(item, onItemClick) } } From fde0d162678f7b956e47f03293a542d0cd6916f9 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:56:42 +0900 Subject: [PATCH 19/42] =?UTF-8?q?feat:=20SubmitButton=20=EA=B3=B5=EC=9A=A9?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shoppingcart/ui/component/SubmitButton.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt new file mode 100644 index 0000000..da2f2bc --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt @@ -0,0 +1,40 @@ +package nextstep.shoppingcart.ui.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Text +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.graphics.RectangleShape +import androidx.compose.ui.res.stringResource +import nextstep.shoppingcart.ui.theme.Blue50 +import nextstep.shoppingcart.ui.theme.Gray10 +import nextstep.shoppingcart.ui.theme.Gray50 +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@Composable +fun SubmitButton( + modifier: Modifier +) { + Button( + shape = RectangleShape, + modifier = modifier + .fillMaxWidth(), + colors = ButtonColors( + containerColor = Blue50, + contentColor = Color.White, + disabledContentColor = Gray10, + disabledContainerColor = Gray50 + ), + onClick = {} + ) { + Text( + text = stringResource(R.string.product_detail_cart_button), + style = Typography.labelMedium + ) + } +} \ No newline at end of file From 065e933637a49e9ae375bd8fd255d6912c1208b7 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 18:56:50 +0900 Subject: [PATCH 20/42] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/productdetail/ProductDetailActivity.kt | 73 +++++++++++++-- .../ui/productdetail/ProductDetailContent.kt | 88 +++++++++++++++++++ .../ui/productdetail/ProductDetailTopBar.kt | 40 +++++++++ 3 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt index 431cf7f..2d2bedd 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt @@ -6,10 +6,34 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text 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.graphics.RectangleShape +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import nextstep.shoppingcart.ui.productlist.ProductListScreen +import nextstep.shoppingcart.domain.Product +import nextstep.shoppingcart.domain.ProductRepository +import nextstep.shoppingcart.ui.component.SubmitButton +import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity.Companion.EXTRA_PRODUCT_ID +import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity.Companion.INVALID_PRODUCT_ID +import nextstep.shoppingcart.ui.theme.Blue50 +import nextstep.shoppingcart.ui.theme.Gray10 +import nextstep.shoppingcart.ui.theme.Gray50 import nextstep.shoppingcart.ui.theme.ShoppingCartTheme +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.shoppingcart.ui.util.findActivity +import nextstep.signup.R class ProductDetailActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -17,13 +41,14 @@ class ProductDetailActivity : ComponentActivity() { enableEdgeToEdge() setContent { ShoppingCartTheme { - ProductListScreen() + ProductDetailScreen() } } } companion object { - private const val EXTRA_PRODUCT_ID = "productId" + const val EXTRA_PRODUCT_ID = "productId" + const val INVALID_PRODUCT_ID = -1L fun newIntent( context: Context, @@ -37,11 +62,49 @@ class ProductDetailActivity : ComponentActivity() { @Composable fun ProductDetailScreen() { + val activity = LocalContext.current.findActivity() + + val product: Product = ProductRepository.productById( + activity.intent.getLongExtra( + EXTRA_PRODUCT_ID, + INVALID_PRODUCT_ID + ) + ) + + Scaffold( + topBar = { + ProductDetailTopBar { + activity.finish() + } + }, + modifier = Modifier.fillMaxSize() + ) { innerPadding -> + + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + + ProductDetailContent( + product + ) + + SubmitButton( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter) + ) + } + } } -@Preview +@Preview( + showBackground = true, + showSystemUi = true +) @Composable -private fun ProductDetailScreenPreview() { +private fun ProductDetailScreenPreview() { ShoppingCartTheme { ProductDetailScreen() } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt new file mode 100644 index 0000000..6174d7b --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt @@ -0,0 +1,88 @@ +package nextstep.shoppingcart.ui.productdetail + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import nextstep.shoppingcart.domain.Product +import nextstep.shoppingcart.domain.ProductRepository +import nextstep.shoppingcart.ui.theme.Gray10 +import nextstep.shoppingcart.ui.theme.Gray20 +import nextstep.shoppingcart.ui.theme.ShoppingCartTheme +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@Composable +fun ProductDetailContent( + product: Product, + modifier: Modifier = Modifier +) { + + Column( + modifier = modifier + .fillMaxWidth() + ) { + AsyncImage( + model = product.imageUrl, + contentDescription = stringResource(R.string.product_detail_image_desc), + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + ) + + Text( + text = product.name, + color = Gray20, + modifier = Modifier.padding(18.dp), + style = Typography.titleLarge + ) + + HorizontalDivider( + thickness = 1.dp, + color = Gray10 + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = stringResource(R.string.product_detail_price), + color = Gray20, + modifier = Modifier.padding(18.dp), + style = Typography.bodyMedium, + ) + Text( + text = stringResource(R.string.product_price_format, product.price), + color = Gray20, + style = Typography.bodyMedium, + modifier = Modifier.padding(18.dp) + ) + } + } + +} + +@Preview( + showBackground = true +) +@Composable +private fun ProductDetailContentPreview() { + ShoppingCartTheme { + ProductDetailContent( + ProductRepository.dummy.first() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt new file mode 100644 index 0000000..d4d014d --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt @@ -0,0 +1,40 @@ +package nextstep.shoppingcart.ui.productdetail + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.ShoppingCart +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.platform.LocalContext +import androidx.compose.ui.res.stringResource +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ProductDetailTopBar( + onClickNavigationIcon: () -> Unit +) { + TopAppBar( + title = { + Text( + text = stringResource(R.string.product_detail_title), + style = Typography.headlineMedium + ) + }, + navigationIcon = { + IconButton( + onClick = onClickNavigationIcon + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.product_detail_back_button_desc), + ) + } + } + ) +} \ No newline at end of file From cf7a2074fdb58dde2af45c41b40876124a4f3c54 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 21:04:20 +0900 Subject: [PATCH 21/42] =?UTF-8?q?feat:=20CartActivity=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 8 +++- .../shoppingcart/ui/cart/CartActivity.kt | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a977716..24d383c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,8 +14,12 @@ android:supportsRtl="true" android:theme="@style/Theme.ShoppingCart" tools:targetApi="31"> - + + Date: Tue, 29 Oct 2024 21:04:30 +0900 Subject: [PATCH 22/42] =?UTF-8?q?feat:=20CartActivity=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/productlist/ProductListActivity.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt index bf46077..9777bab 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import nextstep.shoppingcart.domain.ProductRepository +import nextstep.shoppingcart.ui.cart.CartActivity import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity import nextstep.shoppingcart.ui.theme.ShoppingCartTheme @@ -29,15 +30,20 @@ class ProductListActivity : ComponentActivity() { @Composable fun ProductListScreen() { + + val context = LocalContext.current + Scaffold( topBar = { - ProductListTopBar({}) + ProductListTopBar( + onCartClick = { + context.startActivity(CartActivity.newIntent(context)) + } + ) }, modifier = Modifier.fillMaxSize(), ) { innerPadding -> - val context = LocalContext.current - ProductList( products = ProductRepository.dummy, onItemClick = { product -> From c45f4644f320861cab5bedb157e90971e60138a8 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 21:05:03 +0900 Subject: [PATCH 23/42] =?UTF-8?q?style:=20ktlint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shoppingcart/domain/ProductRepository.kt | 131 +++++++++--------- .../shoppingcart/ui/cart/CartActivity.kt | 10 +- .../shoppingcart/ui/component/SubmitButton.kt | 29 ++-- .../ui/productdetail/ProductDetailActivity.kt | 43 +++--- .../ui/productdetail/ProductDetailContent.kt | 30 ++-- .../ui/productdetail/ProductDetailTopBar.kt | 14 +- .../ui/productlist/ProductList.kt | 2 +- .../ui/productlist/ProductListActivity.kt | 3 +- .../nextstep/shoppingcart/ui/theme/Color.kt | 2 +- .../shoppingcart/ui/util/FindActivity.kt | 2 +- 10 files changed, 124 insertions(+), 142 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt b/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt index fbab646..545ea0a 100644 --- a/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt +++ b/app/src/main/java/nextstep/shoppingcart/domain/ProductRepository.kt @@ -1,70 +1,69 @@ package nextstep.shoppingcart.domain object ProductRepository { + val dummy: List = + listOf( + Product( + 0, + "미우미우 - 오드리햅번ver", + 27000, + "https://github.com/user-attachments/assets/5470cb47-32c7-485f-9941-85eb12fc8361", + ), + Product( + 1, + "아기 복숭아 - 173cm", + 50000, + "https://github.com/user-attachments/assets/3e627034-236d-4516-9689-bd6d6f33971b", + ), + Product( + 2, + "궁극의 아이돌 - 콘서트", + 72000, + "https://github.com/user-attachments/assets/bec777ce-ac0c-48b7-8d40-997babc8a02c", + ), + Product( + 3, + "탤마뿌럼못날뤼가 - smart", + 25400, + "https://github.com/user-attachments/assets/f9576cfd-6b8a-47a3-ac3d-6ae267373bfa", + ), + Product( + 4, + "스빠이쉬쓰빠이쉬쓰빠이쉬쓰빠이쉬 - spicy", + 123456, + "https://github.com/user-attachments/assets/c9f30f84-3d73-420a-9f0f-52f4df47a36d", + ), + Product( + 5, + "이부자리 - 팬싸템ver", + 73920, + "https://github.com/user-attachments/assets/a1088a04-7e00-4c86-b5ac-bfff1a825910", + ), + Product( + 6, + "아기사막여우 - 키스오브라이프뜻이인공호흡이래요", + 353534, + "https://github.com/user-attachments/assets/358cf2a6-7b4a-40a2-b6e5-ffa6303eab27", + ), + Product( + 7, + "위플래쉬 - 개띵곡", + 5383, + "https://github.com/user-attachments/assets/bdbe4e39-3852-4fd8-8e7c-5173bfa716fa", + ), + Product( + 8, + "삐끼삐끼삐끼삐끼 - 프미나", + 23452363, + "https://github.com/user-attachments/assets/7d69b1b6-bf6c-4bc9-af82-88613ac32ac6", + ), + Product( + 9, + "천년의 아이돌 - 미스터츄", + 3582398, + "https://github.com/user-attachments/assets/50ee770a-6155-4021-a849-27a5a4078065", + ), + ) - val dummy: List = listOf( - Product( - 0, - "미우미우 - 오드리햅번ver", - 27000, - "https://github.com/user-attachments/assets/5470cb47-32c7-485f-9941-85eb12fc8361", - ), - Product( - 1, - "아기 복숭아 - 173cm", - 50000, - "https://github.com/user-attachments/assets/3e627034-236d-4516-9689-bd6d6f33971b", - ), - Product( - 2, - "궁극의 아이돌 - 콘서트", - 72000, - "https://github.com/user-attachments/assets/bec777ce-ac0c-48b7-8d40-997babc8a02c", - ), - Product( - 3, - "탤마뿌럼못날뤼가 - smart", - 25400, - "https://github.com/user-attachments/assets/f9576cfd-6b8a-47a3-ac3d-6ae267373bfa", - ), - Product( - 4, - "스빠이쉬쓰빠이쉬쓰빠이쉬쓰빠이쉬 - spicy", - 123456, - "https://github.com/user-attachments/assets/c9f30f84-3d73-420a-9f0f-52f4df47a36d", - ), - Product( - 5, - "이부자리 - 팬싸템ver", - 73920, - "https://github.com/user-attachments/assets/a1088a04-7e00-4c86-b5ac-bfff1a825910", - ), - Product( - 6, - "아기사막여우 - 키스오브라이프뜻이인공호흡이래요", - 353534, - "https://github.com/user-attachments/assets/358cf2a6-7b4a-40a2-b6e5-ffa6303eab27", - ), - Product( - 7, - "위플래쉬 - 개띵곡", - 5383, - "https://github.com/user-attachments/assets/bdbe4e39-3852-4fd8-8e7c-5173bfa716fa", - ), - Product( - 8, - "삐끼삐끼삐끼삐끼 - 프미나", - 23452363, - "https://github.com/user-attachments/assets/7d69b1b6-bf6c-4bc9-af82-88613ac32ac6", - ), - Product( - 9, - "천년의 아이돌 - 미스터츄", - 3582398, - "https://github.com/user-attachments/assets/50ee770a-6155-4021-a849-27a5a4078065", - ), - ) - - fun productById(id: Long): Product = - dummy.find { it.id == id } ?: throw IllegalArgumentException("$id 에 해당하는 값이 없습니다.") -} \ No newline at end of file + fun productById(id: Long): Product = dummy.find { it.id == id } ?: throw IllegalArgumentException("$id 에 해당하는 값이 없습니다.") +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt index 44f48a2..977b1cd 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt @@ -8,10 +8,9 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import nextstep.shoppingcart.ui.productdetail.ProductDetailScreen import nextstep.shoppingcart.ui.theme.ShoppingCartTheme -class CartActivity:ComponentActivity() { +class CartActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -23,22 +22,21 @@ class CartActivity:ComponentActivity() { } companion object { - fun newIntent(context: Context):Intent = Intent(context, CartActivity::class.java) + fun newIntent(context: Context): Intent = Intent(context, CartActivity::class.java) } } @Composable fun CartScreen() { - } @Preview( showBackground = true, - showSystemUi = true + showSystemUi = true, ) @Composable private fun CartScreenPreview() { ShoppingCartTheme { CartScreen() } -} \ No newline at end of file +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt index da2f2bc..6cbddc7 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt @@ -5,7 +5,6 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors import androidx.compose.material3.Text 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.graphics.RectangleShape @@ -17,24 +16,24 @@ import nextstep.shoppingcart.ui.theme.Typography import nextstep.signup.R @Composable -fun SubmitButton( - modifier: Modifier -) { +fun SubmitButton(modifier: Modifier) { Button( shape = RectangleShape, - modifier = modifier - .fillMaxWidth(), - colors = ButtonColors( - containerColor = Blue50, - contentColor = Color.White, - disabledContentColor = Gray10, - disabledContainerColor = Gray50 - ), - onClick = {} + modifier = + modifier + .fillMaxWidth(), + colors = + ButtonColors( + containerColor = Blue50, + contentColor = Color.White, + disabledContentColor = Gray10, + disabledContainerColor = Gray50, + ), + onClick = {}, ) { Text( text = stringResource(R.string.product_detail_cart_button), - style = Typography.labelMedium + style = Typography.labelMedium, ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt index 2d2bedd..99970ba 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt @@ -10,30 +10,19 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text 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.graphics.RectangleShape import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import nextstep.shoppingcart.domain.Product import nextstep.shoppingcart.domain.ProductRepository import nextstep.shoppingcart.ui.component.SubmitButton import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity.Companion.EXTRA_PRODUCT_ID import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity.Companion.INVALID_PRODUCT_ID -import nextstep.shoppingcart.ui.theme.Blue50 -import nextstep.shoppingcart.ui.theme.Gray10 -import nextstep.shoppingcart.ui.theme.Gray50 import nextstep.shoppingcart.ui.theme.ShoppingCartTheme -import nextstep.shoppingcart.ui.theme.Typography import nextstep.shoppingcart.ui.util.findActivity -import nextstep.signup.R class ProductDetailActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -64,12 +53,13 @@ class ProductDetailActivity : ComponentActivity() { fun ProductDetailScreen() { val activity = LocalContext.current.findActivity() - val product: Product = ProductRepository.productById( - activity.intent.getLongExtra( - EXTRA_PRODUCT_ID, - INVALID_PRODUCT_ID + val product: Product = + ProductRepository.productById( + activity.intent.getLongExtra( + EXTRA_PRODUCT_ID, + INVALID_PRODUCT_ID, + ), ) - ) Scaffold( topBar = { @@ -77,23 +67,24 @@ fun ProductDetailScreen() { activity.finish() } }, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { innerPadding -> Box( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) + modifier = + Modifier + .fillMaxSize() + .padding(innerPadding), ) { - ProductDetailContent( - product + product, ) SubmitButton( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter) + modifier = + Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter), ) } } @@ -101,7 +92,7 @@ fun ProductDetailScreen() { @Preview( showBackground = true, - showSystemUi = true + showSystemUi = true, ) @Composable private fun ProductDetailScreenPreview() { diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt index 6174d7b..ee7ec29 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailContent.kt @@ -26,37 +26,38 @@ import nextstep.signup.R @Composable fun ProductDetailContent( product: Product, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { - Column( - modifier = modifier - .fillMaxWidth() + modifier = + modifier + .fillMaxWidth(), ) { AsyncImage( model = product.imageUrl, contentDescription = stringResource(R.string.product_detail_image_desc), contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f) + modifier = + Modifier + .fillMaxWidth() + .aspectRatio(1f), ) Text( text = product.name, color = Gray20, modifier = Modifier.padding(18.dp), - style = Typography.titleLarge + style = Typography.titleLarge, ) HorizontalDivider( thickness = 1.dp, - color = Gray10 + color = Gray10, ) Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = stringResource(R.string.product_detail_price), @@ -68,21 +69,20 @@ fun ProductDetailContent( text = stringResource(R.string.product_price_format, product.price), color = Gray20, style = Typography.bodyMedium, - modifier = Modifier.padding(18.dp) + modifier = Modifier.padding(18.dp), ) } } - } @Preview( - showBackground = true + showBackground = true, ) @Composable private fun ProductDetailContentPreview() { ShoppingCartTheme { ProductDetailContent( - ProductRepository.dummy.first() + ProductRepository.dummy.first(), ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt index d4d014d..f62a82e 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt @@ -2,39 +2,35 @@ package nextstep.shoppingcart.ui.productdetail import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.ShoppingCart 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.platform.LocalContext import androidx.compose.ui.res.stringResource import nextstep.shoppingcart.ui.theme.Typography import nextstep.signup.R @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ProductDetailTopBar( - onClickNavigationIcon: () -> Unit -) { +fun ProductDetailTopBar(onClickNavigationIcon: () -> Unit) { TopAppBar( title = { Text( text = stringResource(R.string.product_detail_title), - style = Typography.headlineMedium + style = Typography.headlineMedium, ) }, navigationIcon = { IconButton( - onClick = onClickNavigationIcon + onClick = onClickNavigationIcon, ) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.product_detail_back_button_desc), ) } - } + }, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt index a1baaf3..58a37ce 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductList.kt @@ -27,7 +27,7 @@ fun ProductList( items = products, key = { product -> product.id - } + }, ) { item -> ProductListItem(item, onItemClick) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt index 9777bab..d089343 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productlist/ProductListActivity.kt @@ -30,7 +30,6 @@ class ProductListActivity : ComponentActivity() { @Composable fun ProductListScreen() { - val context = LocalContext.current Scaffold( @@ -38,7 +37,7 @@ fun ProductListScreen() { ProductListTopBar( onCartClick = { context.startActivity(CartActivity.newIntent(context)) - } + }, ) }, modifier = Modifier.fillMaxSize(), diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt index 46dc5b4..e1e9e44 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Color.kt @@ -14,4 +14,4 @@ val Gray10 = Color(0xFFAAAAAA) val Gray20 = Color(0xFF333333) val Gray50 = Color(0xFF1D1B20) -val Blue50 = Color(0xFF2196F3) \ No newline at end of file +val Blue50 = Color(0xFF2196F3) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt index d18c5de..e4dc7d8 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/util/FindActivity.kt @@ -11,4 +11,4 @@ fun Context.findActivity(): Activity { context = context.baseContext } throw IllegalStateException("Activity를 찾을 수 없습니다.") -} \ No newline at end of file +} From efd1f7e580c3be875616d6f5b5360208ad4461cd Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 21:10:46 +0900 Subject: [PATCH 24/42] =?UTF-8?q?feat:=20CartProduct=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20=EB=B0=8F=20Quantity=20=EA=B0=9D=EC=B2=B4=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 --- .../shoppingcart/domain/CartProduct.kt | 34 +++++++++++++++++++ .../domain/CartQuantityUpdateResult.kt | 9 +++++ .../nextstep/shoppingcart/domain/Quantity.kt | 23 +++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/domain/CartQuantityUpdateResult.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/domain/Quantity.kt diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt new file mode 100644 index 0000000..4efb85c --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt @@ -0,0 +1,34 @@ +package nextstep.shoppingcart.domain + +data class CartProduct( + val product: Product, + val quantity: Quantity = Quantity(currentValue = INITIAL_COUNT), + val countInterval: Int = DEFAULT_COUNT_INTERVAL, +) { + fun increaseCount(): CartQuantityUpdateResult = + if (quantity.maxValue != null && quantity.currentValue < quantity.maxValue) { + CartQuantityUpdateResult.Success( + this.copy( + quantity = quantity.copy(currentValue = quantity.currentValue + countInterval), + ), + ) + } else { + CartQuantityUpdateResult.MaxFail + } + + fun decreaseCount(): CartQuantityUpdateResult = + if (quantity.maxValue != null && quantity.currentValue > quantity.minValue) { + CartQuantityUpdateResult.Success( + this.copy( + quantity = quantity.copy(currentValue = quantity.currentValue - countInterval), + ), + ) + } else { + CartQuantityUpdateResult.MinFail + } + + companion object { + private const val DEFAULT_COUNT_INTERVAL = 1 + private const val INITIAL_COUNT = 1 + } +} diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartQuantityUpdateResult.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartQuantityUpdateResult.kt new file mode 100644 index 0000000..45f930c --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartQuantityUpdateResult.kt @@ -0,0 +1,9 @@ +package nextstep.shoppingcart.domain + +sealed interface CartQuantityUpdateResult { + data class Success(val product: CartProduct) : CartQuantityUpdateResult + + data object MinFail : CartQuantityUpdateResult + + data object MaxFail : CartQuantityUpdateResult +} diff --git a/app/src/main/java/nextstep/shoppingcart/domain/Quantity.kt b/app/src/main/java/nextstep/shoppingcart/domain/Quantity.kt new file mode 100644 index 0000000..606340e --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/domain/Quantity.kt @@ -0,0 +1,23 @@ +package nextstep.shoppingcart.domain + +data class Quantity( + val currentValue: Int, + val minValue: Int = DEFAULT_MINIMUM_COUNT, + val maxValue: Int? = null, +) { + init { + require(currentValue >= minValue) { + "$currentValue : 장바구니 상품의 개수는 $minValue 이상이어야 합니다." + } + + maxValue?.let { + require(currentValue <= maxValue) { + "$currentValue : 장바구니 상품의 개수는 $maxValue 이하여야 합니다." + } + } + } + + companion object { + private const val DEFAULT_MINIMUM_COUNT = 1 + } +} From 2dd73f3cbdad67ca332a0aa118a0edb80bc52e81 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Tue, 29 Oct 2024 21:11:00 +0900 Subject: [PATCH 25/42] =?UTF-8?q?feat:=20CartProductRepository=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 --- .../domain/CartProductRepository.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt new file mode 100644 index 0000000..4e84352 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt @@ -0,0 +1,42 @@ +package nextstep.shoppingcart.domain + +object CartProductRepository { + private val cartItems: MutableList = mutableListOf() + + fun addItem(product: Product) { + if (cartItems.any { it.product.id == product.id }) { + cartItems.add( + CartProduct( + product = product, + ), + ) + return + } + plusItemCount(product) + } + + fun minusItemCount(product: Product): CartQuantityUpdateResult { + val cartProduct = findItem(product.id) + val index = cartItems.indexOf(cartProduct) + val result = cartProduct.decreaseCount() + if (result is CartQuantityUpdateResult.Success) { + cartItems[index] = result.product + } + return result + } + + fun plusItemCount(product: Product): CartQuantityUpdateResult { + val cartProduct = findItem(product.id) + val index = cartItems.indexOf(cartProduct) + val result = cartProduct.increaseCount() + if (result is CartQuantityUpdateResult.Success) { + cartItems[index] = result.product + } + return result + } + + fun findItem(productId: Long): CartProduct = + cartItems.firstOrNull { + it.product.id == productId + } ?: throw IllegalArgumentException("장바구니에 $productId 에 대한 상품이 없습니다.") +} From b25b12c15aa728123d4f3a7697a19f26a2f7bc3e Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Wed, 30 Oct 2024 17:50:09 +0900 Subject: [PATCH 26/42] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4a15df8..ad17bc4 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,22 @@ # step1 ## 기능 요구 사항 -- [ ] 상품 목록 화면을 구현한다. +- [x] 상품 목록 화면을 구현한다. ## 프로그래밍 요구 사항 -- [ ] viewModel, Hilt 등은 장바구니 미션에서 활용하지 않는다. -- [ ] 상품 목록 화면을 구현할 때 Lazy 컴포넌트를 활용한다. -- [ ] 컴포저블 함수가 너무 많은 일을 하지 않도록 분리하기 위해 노력해 본다. - - [ ] 의미있는 단위의 함수를 모아 별도의 파일로 분리해본다. +- [x] viewModel, Hilt 등은 장바구니 미션에서 활용하지 않는다. +- [x] 상품 목록 화면을 구현할 때 Lazy 컴포넌트를 활용한다. +- [x] 컴포저블 함수가 너무 많은 일을 하지 않도록 분리하기 위해 노력해 본다. + - [x] 의미있는 단위의 함수를 모아 별도의 파일로 분리해본다. + +# step2 + +## 기능 요구 사항 +- [x] 상품 상세 화면을 구현한다. + - [x] 상품 목록에서 상품을 누르면 상세 화면으로 이동한다. + - [x] 뒤로 가기 버튼이나 아이콘을 누르면 직전 화면으로 이동한다. +- [x] 장바구니 화면의 빈 껍데기를 연결한다. + - [x] 상품 목록에서 장바구니 아이콘을 누르면 장바구니 화면으로 이동한다. + - [x] 뒤로가기 버튼이나 아이콘을 누르면 직전 화면으로 돌아간다. + - [x] 장바구니에 실제로 상품이 담기는 기능은 이 단계에서 고려하지 않는다. \ No newline at end of file From 56836192573713018a38a5cb8bab9a6d2673f170 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 10:13:49 +0900 Subject: [PATCH 27/42] =?UTF-8?q?refactor:=20=EB=92=A4=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=20Topbar=20=EA=B3=B5=EC=9A=A9=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BackNavigationTopBar.kt} | 9 ++++++--- .../ui/productdetail/ProductDetailActivity.kt | 12 +++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) rename app/src/main/java/nextstep/shoppingcart/ui/{productdetail/ProductDetailTopBar.kt => component/BackNavigationTopBar.kt} (85%) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt similarity index 85% rename from app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt rename to app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt index f62a82e..61cf643 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailTopBar.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt @@ -1,4 +1,4 @@ -package nextstep.shoppingcart.ui.productdetail +package nextstep.shoppingcart.ui.component import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -14,11 +14,14 @@ import nextstep.signup.R @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ProductDetailTopBar(onClickNavigationIcon: () -> Unit) { +fun BackNavigationTopBar( + title: String, + onClickNavigationIcon: () -> Unit +) { TopAppBar( title = { Text( - text = stringResource(R.string.product_detail_title), + text = title, style = Typography.headlineMedium, ) }, diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt index 99970ba..92948d7 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt @@ -15,14 +15,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import nextstep.shoppingcart.domain.Product import nextstep.shoppingcart.domain.ProductRepository +import nextstep.shoppingcart.ui.component.BackNavigationTopBar import nextstep.shoppingcart.ui.component.SubmitButton import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity.Companion.EXTRA_PRODUCT_ID import nextstep.shoppingcart.ui.productdetail.ProductDetailActivity.Companion.INVALID_PRODUCT_ID import nextstep.shoppingcart.ui.theme.ShoppingCartTheme import nextstep.shoppingcart.ui.util.findActivity +import nextstep.signup.R class ProductDetailActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -63,9 +66,12 @@ fun ProductDetailScreen() { Scaffold( topBar = { - ProductDetailTopBar { - activity.finish() - } + BackNavigationTopBar( + title = stringResource(R.string.product_detail_title), + onClickNavigationIcon = { + activity.finish() + } + ) }, modifier = Modifier.fillMaxSize(), ) { innerPadding -> From 71801dd463e6a3e75a226d2b035ec7c0968ac995 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 10:14:25 +0900 Subject: [PATCH 28/42] =?UTF-8?q?feat:=20=EC=A0=84=EC=B2=B4=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=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 --- .../main/java/nextstep/shoppingcart/domain/CartProduct.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt index 4efb85c..ea8d872 100644 --- a/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt @@ -5,6 +5,8 @@ data class CartProduct( val quantity: Quantity = Quantity(currentValue = INITIAL_COUNT), val countInterval: Int = DEFAULT_COUNT_INTERVAL, ) { + val totalPrice: Int = product.price * quantity.currentValue + fun increaseCount(): CartQuantityUpdateResult = if (quantity.maxValue != null && quantity.currentValue < quantity.maxValue) { CartQuantityUpdateResult.Success( @@ -30,5 +32,9 @@ data class CartProduct( companion object { private const val DEFAULT_COUNT_INTERVAL = 1 private const val INITIAL_COUNT = 1 + val dummy = + CartProduct( + ProductRepository.dummy.first(), + ) } } From 185e9c0bc01bac67990e0396fc7f5f84f9d0044c Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 10:14:46 +0900 Subject: [PATCH 29/42] =?UTF-8?q?feat:=20=EC=B5=9C=EC=86=8C=EA=B0=92?= =?UTF-8?q?=EB=B3=B4=EB=8B=A4=20=EC=9E=91=EC=9D=84=20=EA=B2=BD=EC=9A=B0,?= =?UTF-8?q?=20=EC=9E=A5=EB=B0=94=EA=B5=AC=EB=8B=88=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=ED=85=9C=EC=9D=84=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/CartProductRepository.kt | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt index 4e84352..8b82a7c 100644 --- a/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt @@ -1,42 +1,50 @@ package nextstep.shoppingcart.domain object CartProductRepository { - private val cartItems: MutableList = mutableListOf() + private val _cartItems: MutableList = mutableListOf() + val cartItems: List get() = _cartItems.toList() fun addItem(product: Product) { - if (cartItems.any { it.product.id == product.id }) { - cartItems.add( + if (_cartItems.any { it.product.id == product.id }) { + _cartItems.add( CartProduct( product = product, ), ) return } - plusItemCount(product) + plusItemCount(product.id) } - fun minusItemCount(product: Product): CartQuantityUpdateResult { - val cartProduct = findItem(product.id) - val index = cartItems.indexOf(cartProduct) - val result = cartProduct.decreaseCount() - if (result is CartQuantityUpdateResult.Success) { - cartItems[index] = result.product + fun minusItemCount(productId: Long) { + val cartProduct = findItem(productId) + val index = _cartItems.indexOf(cartProduct) + when (val result = cartProduct.decreaseCount()) { + CartQuantityUpdateResult.MaxFail -> throw IllegalArgumentException("수량 감소시에는 수량 증가 실패 이벤트가 발생할 수 없습니다.") + CartQuantityUpdateResult.MinFail -> deleteCartItem(productId) + is CartQuantityUpdateResult.Success -> _cartItems[index] = result.product } - return result + } - fun plusItemCount(product: Product): CartQuantityUpdateResult { - val cartProduct = findItem(product.id) - val index = cartItems.indexOf(cartProduct) + fun plusItemCount(productId: Long): CartQuantityUpdateResult { + val cartProduct = findItem(productId) + val index = _cartItems.indexOf(cartProduct) val result = cartProduct.increaseCount() if (result is CartQuantityUpdateResult.Success) { - cartItems[index] = result.product + _cartItems[index] = result.product } return result } + fun deleteCartItem(productId: Long) { + _cartItems.removeIf { + it.product.id == productId + } + } + fun findItem(productId: Long): CartProduct = - cartItems.firstOrNull { + _cartItems.firstOrNull { it.product.id == productId } ?: throw IllegalArgumentException("장바구니에 $productId 에 대한 상품이 없습니다.") } From 365e8be5c96de028c7f912f990fc7e967257c5c8 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 10:15:00 +0900 Subject: [PATCH 30/42] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad17bc4..24d2a3b 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,12 @@ - [x] 상품 목록에서 장바구니 아이콘을 누르면 장바구니 화면으로 이동한다. - [x] 뒤로가기 버튼이나 아이콘을 누르면 직전 화면으로 돌아간다. - [x] 장바구니에 실제로 상품이 담기는 기능은 이 단계에서 고려하지 않는다. - \ No newline at end of file + +# step3 + +## 기능 요구사항 +- [ ] 상품을 장바구니 담는 기능을 구현한다. +- [ ] 장바구니 화면을 구현한다. + - [ ] 담긴 상품의 수량을 조절할 수 있어야 한다. + - [ ] 수량을 1보다 작게 하면 장바구니에서 상품이 제거된다. + - [ ] 담긴 상품 가격의 총합이 주문하기 버튼에 표시된다. \ No newline at end of file From d509536ac5171007db6fb00d1a716327ac2effd7 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 10:24:27 +0900 Subject: [PATCH 31/42] =?UTF-8?q?style:=20Typography=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/shoppingcart/ui/theme/Type.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt b/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt index f4ad945..e2dfbf7 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/theme/Type.kt @@ -10,6 +10,14 @@ import androidx.compose.ui.unit.sp // Set of Material typography styles to start with val Typography = Typography( + displayMedium = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W400, + fontSize = 22.sp, + lineHeight = 18.sp, + color = Color.Black, + ), headlineMedium = TextStyle( fontFamily = FontFamily.Default, @@ -27,6 +35,15 @@ val Typography = letterSpacing = 0.5.sp, color = Gray20, ), + titleMedium = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W700, + fontSize = 20.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp, + color = Color.Black, + ), titleSmall = TextStyle( fontFamily = FontFamily.Default, From 2124e76642e578a30dae4f395ef5d797bacd789a Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:30:23 +0900 Subject: [PATCH 32/42] =?UTF-8?q?refactor:=20submitButton=EC=9D=98=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=EB=A1=9C=20label=EA=B3=BC?= =?UTF-8?q?=20onClick=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shoppingcart/ui/component/SubmitButton.kt | 12 +++++++----- .../ui/productdetail/ProductDetailActivity.kt | 7 ++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt index 6cbddc7..cb0a5cc 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/SubmitButton.kt @@ -8,15 +8,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.res.stringResource import nextstep.shoppingcart.ui.theme.Blue50 import nextstep.shoppingcart.ui.theme.Gray10 import nextstep.shoppingcart.ui.theme.Gray50 import nextstep.shoppingcart.ui.theme.Typography -import nextstep.signup.R @Composable -fun SubmitButton(modifier: Modifier) { +fun SubmitButton( + label: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { Button( shape = RectangleShape, modifier = @@ -29,10 +31,10 @@ fun SubmitButton(modifier: Modifier) { disabledContentColor = Gray10, disabledContainerColor = Gray50, ), - onClick = {}, + onClick = onClick, ) { Text( - text = stringResource(R.string.product_detail_cart_button), + text = label, style = Typography.labelMedium, ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt index 92948d7..d03cc5a 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/productdetail/ProductDetailActivity.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import nextstep.shoppingcart.domain.CartProductRepository import nextstep.shoppingcart.domain.Product import nextstep.shoppingcart.domain.ProductRepository import nextstep.shoppingcart.ui.component.BackNavigationTopBar @@ -70,7 +71,7 @@ fun ProductDetailScreen() { title = stringResource(R.string.product_detail_title), onClickNavigationIcon = { activity.finish() - } + }, ) }, modifier = Modifier.fillMaxSize(), @@ -87,6 +88,10 @@ fun ProductDetailScreen() { ) SubmitButton( + label = stringResource(R.string.product_detail_cart_button), + onClick = { + CartProductRepository.addItem(product) + }, modifier = Modifier .fillMaxWidth() From 0607bc826808bfd1e38d70a25a970c4052d2dd26 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:30:55 +0900 Subject: [PATCH 33/42] =?UTF-8?q?style:=20string=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=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/res/values/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87ee89f..f8ad02c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -11,4 +11,10 @@ 뒤로가기 버튼 금액 장바구니 담기 + + 장바구니 + 주문하기(%,d원) + 장바구니 상품 이미지 + 수량 증가 버튼 + 수량 감소 버튼 \ No newline at end of file From d485aec788ab1c1935daab7f768bc1e5b51f9ebc Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:32:18 +0900 Subject: [PATCH 34/42] =?UTF-8?q?feat:=20=EC=88=98=EB=9F=89=20=EC=A6=9D?= =?UTF-8?q?=EA=B0=80=20=EA=B0=90=EC=86=8C=20=EA=B3=B5=EC=9A=A9=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/component/CountUpdateButton.kt | 72 +++++++++++++++++++ app/src/main/res/drawable/ic_minus.xml | 9 +++ app/src/main/res/drawable/ic_plus.xml | 9 +++ 3 files changed, 90 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt create mode 100644 app/src/main/res/drawable/ic_minus.xml create mode 100644 app/src/main/res/drawable/ic_plus.xml diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt new file mode 100644 index 0000000..943cabf --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt @@ -0,0 +1,72 @@ +package nextstep.shoppingcart.ui.component + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@Composable +fun CountUpdateButton( + count: Int, + onCountMinus: () -> Unit, + onCountPlus: () -> Unit, +) { + Row { + IconButton( + modifier = + Modifier + .width(42.dp) + .height(42.dp), + onClick = onCountMinus, + ) { + Icon( + painter = painterResource(R.drawable.ic_minus), + contentDescription = stringResource(R.string.count_minus_desc), + ) + } + + Text( + text = count.toString(), + style = Typography.displayMedium, + modifier = + Modifier + .wrapContentHeight() + .padding(horizontal = 12.dp) + .align(Alignment.CenterVertically), + ) + + IconButton( + modifier = + Modifier + .width(42.dp) + .height(42.dp), + onClick = onCountPlus, + ) { + Icon( + painter = painterResource(R.drawable.ic_plus), + contentDescription = stringResource(R.string.count_plus_desc), + ) + } + } +} + +@Preview( + showBackground = true, +) +@Composable +private fun CountUpdateButtonPreview() { + CountUpdateButton(1, {}, {}) +} diff --git a/app/src/main/res/drawable/ic_minus.xml b/app/src/main/res/drawable/ic_minus.xml new file mode 100644 index 0000000..fc61c6e --- /dev/null +++ b/app/src/main/res/drawable/ic_minus.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_plus.xml b/app/src/main/res/drawable/ic_plus.xml new file mode 100644 index 0000000..92489cf --- /dev/null +++ b/app/src/main/res/drawable/ic_plus.xml @@ -0,0 +1,9 @@ + + + From 3c03ff0663e066f931c0be3a418c67180d51eb2b Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:33:03 +0900 Subject: [PATCH 35/42] =?UTF-8?q?fix:=20cartProduct=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/shoppingcart/domain/CartProduct.kt | 4 ++-- .../shoppingcart/domain/CartProductRepository.kt | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt index ea8d872..6d2bbda 100644 --- a/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartProduct.kt @@ -8,7 +8,7 @@ data class CartProduct( val totalPrice: Int = product.price * quantity.currentValue fun increaseCount(): CartQuantityUpdateResult = - if (quantity.maxValue != null && quantity.currentValue < quantity.maxValue) { + if ((quantity.maxValue != null && quantity.currentValue < quantity.maxValue) || quantity.maxValue == null) { CartQuantityUpdateResult.Success( this.copy( quantity = quantity.copy(currentValue = quantity.currentValue + countInterval), @@ -19,7 +19,7 @@ data class CartProduct( } fun decreaseCount(): CartQuantityUpdateResult = - if (quantity.maxValue != null && quantity.currentValue > quantity.minValue) { + if (quantity.currentValue > quantity.minValue) { CartQuantityUpdateResult.Success( this.copy( quantity = quantity.copy(currentValue = quantity.currentValue - countInterval), diff --git a/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt b/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt index 8b82a7c..1c3ef61 100644 --- a/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt +++ b/app/src/main/java/nextstep/shoppingcart/domain/CartProductRepository.kt @@ -5,15 +5,15 @@ object CartProductRepository { val cartItems: List get() = _cartItems.toList() fun addItem(product: Product) { - if (_cartItems.any { it.product.id == product.id }) { + if (_cartItems.all { it.product.id != product.id }) { _cartItems.add( CartProduct( product = product, ), ) - return + } else { + plusItemCount(product.id) } - plusItemCount(product.id) } fun minusItemCount(productId: Long) { @@ -24,7 +24,6 @@ object CartProductRepository { CartQuantityUpdateResult.MinFail -> deleteCartItem(productId) is CartQuantityUpdateResult.Success -> _cartItems[index] = result.product } - } fun plusItemCount(productId: Long): CartQuantityUpdateResult { @@ -47,4 +46,6 @@ object CartProductRepository { _cartItems.firstOrNull { it.product.id == productId } ?: throw IllegalArgumentException("장바구니에 $productId 에 대한 상품이 없습니다.") + + fun totalPrice(): Int = _cartItems.sumOf { it.totalPrice } } From 7a5e0556e10f52fa3e1ada0a123354e09e71b1d2 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:33:22 +0900 Subject: [PATCH 36/42] =?UTF-8?q?feat:=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shoppingcart/ui/cart/CartActivity.kt | 69 +++++++++ .../nextstep/shoppingcart/ui/cart/CartItem.kt | 134 ++++++++++++++++++ .../nextstep/shoppingcart/ui/cart/CartList.kt | 60 ++++++++ 3 files changed, 263 insertions(+) create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt create mode 100644 app/src/main/java/nextstep/shoppingcart/ui/cart/CartList.kt diff --git a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt index 977b1cd..0a2328b 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartActivity.kt @@ -6,9 +6,27 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import nextstep.shoppingcart.domain.CartProductRepository +import nextstep.shoppingcart.ui.component.BackNavigationTopBar +import nextstep.shoppingcart.ui.component.SubmitButton import nextstep.shoppingcart.ui.theme.ShoppingCartTheme +import nextstep.shoppingcart.ui.util.findActivity +import nextstep.signup.R class CartActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -28,6 +46,57 @@ class CartActivity : ComponentActivity() { @Composable fun CartScreen() { + val context = LocalContext.current + var totalPrice by remember { mutableIntStateOf(CartProductRepository.totalPrice()) } + var cartItems by remember { mutableStateOf(CartProductRepository.cartItems) } + + fun updateInfo() { + totalPrice = CartProductRepository.totalPrice() + cartItems = CartProductRepository.cartItems + } + + Scaffold( + topBar = { + BackNavigationTopBar( + title = stringResource(R.string.cart_title), + ) { + context.findActivity().finish() + } + }, + ) { innerPadding -> + Box( + modifier = + Modifier + .padding(innerPadding) + .fillMaxSize(), + ) { + CartList( + cartItems = cartItems, + onCountMinus = { cartProduct -> + CartProductRepository.minusItemCount(cartProduct.product.id) + updateInfo() + }, + onCountPlus = { cartProduct -> + CartProductRepository.plusItemCount(cartProduct.product.id) + updateInfo() + }, + onItemDelete = { cartProduct -> + CartProductRepository.deleteCartItem(cartProduct.product.id) + updateInfo() + }, + ) + + SubmitButton( + label = + stringResource( + R.string.cart_order_button, + totalPrice, + ), + onClick = {}, + modifier = Modifier.align(Alignment.BottomCenter), + ) + } + } } @Preview( diff --git a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt new file mode 100644 index 0000000..08af9d9 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt @@ -0,0 +1,134 @@ +package nextstep.shoppingcart.ui.cart + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +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.layout.ContentScale +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import nextstep.shoppingcart.domain.CartProduct +import nextstep.shoppingcart.ui.component.CountUpdateButton +import nextstep.shoppingcart.ui.theme.Gray10 +import nextstep.shoppingcart.ui.theme.ShoppingCartTheme +import nextstep.shoppingcart.ui.theme.Typography +import nextstep.signup.R + +@Composable +fun CartItem( + cartProduct: CartProduct, + onCountMinus: () -> Unit, + onCountPlus: () -> Unit, + onItemDelete: () -> Unit, +) { + OutlinedCard( + modifier = Modifier.fillMaxWidth(), + border = BorderStroke(1.dp, Gray10), + colors = CardDefaults.cardColors().copy(containerColor = Color.White), + ) { + Column( + modifier = Modifier.fillMaxWidth(), + ) { + Row( + modifier = + Modifier + .fillMaxWidth() + .padding(start = 18.dp, end = 6.dp, top = 6.dp), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + modifier = Modifier + .align(Alignment.CenterVertically) + .weight(1f), + text = cartProduct.product.name, + style = Typography.titleMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + IconButton( + modifier = + Modifier + .height(48.dp) + .width(48.dp), + onClick = onItemDelete, + ) { + Icon( + modifier = + Modifier + .height(24.dp) + .width(24.dp), + imageVector = Icons.Filled.Close, + contentDescription = stringResource(R.string.product_list_cart_button_desc), + ) + } + } + + Row( + modifier = + Modifier + .padding(horizontal = 18.dp) + .padding(bottom = 18.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + AsyncImage( + model = cartProduct.product.imageUrl, + modifier = + Modifier + .width(136.dp) + .height(84.dp), + contentScale = ContentScale.Crop, + contentDescription = stringResource(R.string.cart_product_image_desc), + ) + + Column( + modifier = Modifier.align(Alignment.Bottom), + ) { + Text( + modifier = Modifier.align(Alignment.End), + text = + stringResource( + R.string.product_price_format, + cartProduct.totalPrice, + ), + ) + + CountUpdateButton( + count = cartProduct.quantity.currentValue, + onCountMinus = onCountMinus, + onCountPlus = onCountPlus, + ) + } + } + } + } +} + +@Preview( + showBackground = true, +) +@Composable +private fun CartItemPreview() { + ShoppingCartTheme { + CartItem(CartProduct.dummy, { }, {}, {}) + } +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartList.kt b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartList.kt new file mode 100644 index 0000000..fbe4200 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartList.kt @@ -0,0 +1,60 @@ +package nextstep.shoppingcart.ui.cart + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import nextstep.shoppingcart.domain.CartProduct + +@Composable +fun CartList( + cartItems: List, + onCountMinus: (CartProduct) -> Unit, + onCountPlus: (CartProduct) -> Unit, + onItemDelete: (CartProduct) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues(18.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + items( + items = cartItems, + ) { item -> + CartItem( + cartProduct = item, + onCountMinus = { + onCountMinus(item) + }, + onCountPlus = { + onCountPlus(item) + }, + onItemDelete = { + onItemDelete(item) + }, + ) + } + } +} + +@Preview( + showBackground = true, + showSystemUi = true, +) +@Composable +private fun CartListPreview() { + CartList( + listOf( + CartProduct.dummy, + CartProduct.dummy, + ), + {}, + {}, + {}, + ) +} From ffed077d5ce87ea17155c99e17636e7f2dfcd782 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:33:31 +0900 Subject: [PATCH 37/42] =?UTF-8?q?style:=20ktlint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt index 61cf643..5db7591 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/BackNavigationTopBar.kt @@ -16,7 +16,7 @@ import nextstep.signup.R @Composable fun BackNavigationTopBar( title: String, - onClickNavigationIcon: () -> Unit + onClickNavigationIcon: () -> Unit, ) { TopAppBar( title = { From 703309d27f860c3f64f07aa16f49c300bfa1142f Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:56:53 +0900 Subject: [PATCH 38/42] =?UTF-8?q?docs:=203=EB=8B=A8=EA=B3=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=AA=A9=EB=A1=9D=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 24d2a3b..0c93ea1 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ # step3 ## 기능 요구사항 -- [ ] 상품을 장바구니 담는 기능을 구현한다. -- [ ] 장바구니 화면을 구현한다. - - [ ] 담긴 상품의 수량을 조절할 수 있어야 한다. - - [ ] 수량을 1보다 작게 하면 장바구니에서 상품이 제거된다. - - [ ] 담긴 상품 가격의 총합이 주문하기 버튼에 표시된다. \ No newline at end of file +- [x] 상품을 장바구니 담는 기능을 구현한다. +- [x] 장바구니 화면을 구현한다. + - [x] 담긴 상품의 수량을 조절할 수 있어야 한다. + - [x] 수량을 1보다 작게 하면 장바구니에서 상품이 제거된다. + - [x] 담긴 상품 가격의 총합이 주문하기 버튼에 표시된다. From 97783387dbc7cb3f9647da51102bcc61cdcab994 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 15:57:08 +0900 Subject: [PATCH 39/42] =?UTF-8?q?docs:=203=EB=8B=A8=EA=B3=84=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EB=9E=98=EB=B0=8D=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0c93ea1..3a4685c 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,7 @@ - [x] 담긴 상품의 수량을 조절할 수 있어야 한다. - [x] 수량을 1보다 작게 하면 장바구니에서 상품이 제거된다. - [x] 담긴 상품 가격의 총합이 주문하기 버튼에 표시된다. + +## 프로그래밍 요구사항 +- [ ] 상품을 주문하는 기능에 대해서는 구현하지 않아도 된다. +- [ ] 장바구니 화면에 대한 테스트코드를 작성한다. \ No newline at end of file From 3de7fe796570de434f4cb792b7642d526514cd41 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 16:48:27 +0900 Subject: [PATCH 40/42] =?UTF-8?q?test:=20=EC=9E=A5=EB=B0=94=EA=B5=AC?= =?UTF-8?q?=EB=8B=88=20=ED=99=94=EB=A9=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/shoppingcart/CartScreenTest.kt | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 app/src/androidTest/java/nextstep/shoppingcart/CartScreenTest.kt diff --git a/app/src/androidTest/java/nextstep/shoppingcart/CartScreenTest.kt b/app/src/androidTest/java/nextstep/shoppingcart/CartScreenTest.kt new file mode 100644 index 0000000..90d7b6d --- /dev/null +++ b/app/src/androidTest/java/nextstep/shoppingcart/CartScreenTest.kt @@ -0,0 +1,87 @@ +package nextstep.shoppingcart + +import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import nextstep.shoppingcart.domain.CartProductRepository +import nextstep.shoppingcart.domain.Product +import nextstep.shoppingcart.ui.cart.CartScreen +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class CartScreenTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Before + fun setUp() { + if (CartProductRepository.cartItems.isEmpty()) { + CartProductRepository.addItem( + Product( + id = 0, + name = "레아 사랑해요", + imageUrl = "히히", + price = 1004, + ), + ) + } + composeTestRule.setContent { + CartScreen() + } + } + + @Test + fun `수량_증가_버튼을_누르면_상품의_수량이_수량_단위만큼_증가한다`() { + // given + val cartItem = CartProductRepository.cartItems.first() + // when + composeTestRule.onAllNodesWithContentDescription( + "수량 증가 버튼", + ).onFirst().performClick() + + // then + composeTestRule.onNodeWithText( + (cartItem.quantity.currentValue + cartItem.countInterval).toString(), + ).isDisplayed() + } + + @Test + fun `수량_감소_버튼을_누르면_상품의_수량이_수량_단위만큼_감소한다`() { + // given + val cartItem = CartProductRepository.cartItems.first() + + // when + composeTestRule.onAllNodesWithContentDescription( + "수량 감소 버튼", + ).onFirst().performClick() + + // then + composeTestRule.onNodeWithText( + (cartItem.quantity.currentValue - cartItem.countInterval).toString(), + ).isDisplayed() + } + + @Test + fun `상품의_수량을_증가시키면_변경된_가격이_버튼에_표시된다`() { + // given + val cartItem = CartProductRepository.cartItems.first() + val previousPrice = CartProductRepository.totalPrice() + composeTestRule.onNodeWithText( + "주문하기(${previousPrice}원)", + ).isDisplayed() + + // when + composeTestRule.onAllNodesWithContentDescription( + "수량 증가 버튼", + ).onFirst().performClick() + + // then + composeTestRule.onNodeWithText( + "주문하기(${previousPrice + cartItem.product.price}원)", + ).isDisplayed() + } +} From ee05806e95b096cc4ac36d38e37eb393fe532362 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 16:48:40 +0900 Subject: [PATCH 41/42] =?UTF-8?q?docs:=203=EB=8B=A8=EA=B3=84=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=82=AC=ED=95=AD=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3a4685c..a4d5511 100644 --- a/README.md +++ b/README.md @@ -32,5 +32,5 @@ - [x] 담긴 상품 가격의 총합이 주문하기 버튼에 표시된다. ## 프로그래밍 요구사항 -- [ ] 상품을 주문하는 기능에 대해서는 구현하지 않아도 된다. -- [ ] 장바구니 화면에 대한 테스트코드를 작성한다. \ No newline at end of file +- [x] 상품을 주문하는 기능에 대해서는 구현하지 않아도 된다. +- [x] 장바구니 화면에 대한 테스트코드를 작성한다. \ No newline at end of file From d5232cf16fec2d0d6cba77b1f229b597362fee14 Mon Sep 17 00:00:00 2001 From: gaeun5744 Date: Fri, 1 Nov 2024 16:48:51 +0900 Subject: [PATCH 42/42] =?UTF-8?q?style:=20ktlint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/shoppingcart/ui/cart/CartItem.kt | 39 ++++++++++--------- .../ui/component/CountUpdateButton.kt | 20 +++++----- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt index 08af9d9..1bc2bb3 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/cart/CartItem.kt @@ -49,15 +49,16 @@ fun CartItem( ) { Row( modifier = - Modifier - .fillMaxWidth() - .padding(start = 18.dp, end = 6.dp, top = 6.dp), + Modifier + .fillMaxWidth() + .padding(start = 18.dp, end = 6.dp, top = 6.dp), horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - modifier = Modifier - .align(Alignment.CenterVertically) - .weight(1f), + modifier = + Modifier + .align(Alignment.CenterVertically) + .weight(1f), text = cartProduct.product.name, style = Typography.titleMedium, maxLines = 1, @@ -66,16 +67,16 @@ fun CartItem( IconButton( modifier = - Modifier - .height(48.dp) - .width(48.dp), + Modifier + .height(48.dp) + .width(48.dp), onClick = onItemDelete, ) { Icon( modifier = - Modifier - .height(24.dp) - .width(24.dp), + Modifier + .height(24.dp) + .width(24.dp), imageVector = Icons.Filled.Close, contentDescription = stringResource(R.string.product_list_cart_button_desc), ) @@ -84,18 +85,18 @@ fun CartItem( Row( modifier = - Modifier - .padding(horizontal = 18.dp) - .padding(bottom = 18.dp) - .fillMaxWidth(), + Modifier + .padding(horizontal = 18.dp) + .padding(bottom = 18.dp) + .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, ) { AsyncImage( model = cartProduct.product.imageUrl, modifier = - Modifier - .width(136.dp) - .height(84.dp), + Modifier + .width(136.dp) + .height(84.dp), contentScale = ContentScale.Crop, contentDescription = stringResource(R.string.cart_product_image_desc), ) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt index 943cabf..b28b2c9 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/CountUpdateButton.kt @@ -27,9 +27,9 @@ fun CountUpdateButton( Row { IconButton( modifier = - Modifier - .width(42.dp) - .height(42.dp), + Modifier + .width(42.dp) + .height(42.dp), onClick = onCountMinus, ) { Icon( @@ -42,17 +42,17 @@ fun CountUpdateButton( text = count.toString(), style = Typography.displayMedium, modifier = - Modifier - .wrapContentHeight() - .padding(horizontal = 12.dp) - .align(Alignment.CenterVertically), + Modifier + .wrapContentHeight() + .padding(horizontal = 12.dp) + .align(Alignment.CenterVertically), ) IconButton( modifier = - Modifier - .width(42.dp) - .height(42.dp), + Modifier + .width(42.dp) + .height(42.dp), onClick = onCountPlus, ) { Icon(