From f2de6ba2099dcb61e64e2390660c84237ab81d55 Mon Sep 17 00:00:00 2001 From: Choi SeongHoon <108349655+SeongHoonC@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:50:59 +0900 Subject: [PATCH] =?UTF-8?q?[Android]=20feat:=20=EC=9E=AC=EB=A3=8C=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=B3=B4=EA=B8=B0=20=EA=B0=9C=EB=B0=9C(#4?= =?UTF-8?q?1)=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Data Response model 정의 * feat: 재료 Domain Model 정의 * feat: 재료 요청 Retrofit API 정의 * feat: 재료 DTO 맵퍼 정의 * feat: 재료 저장소 및 냉장고 식자재 가져오기 정의 * feat: Default 재료 저장소 의존성 주입 * feat: 냉장고 재료 화면 보기 개발 * feat: 냉장고 재료 상자 이름 색깔 추가 --- Android/.idea/deploymentTargetSelector.xml | 10 +- Android/app/build.gradle.kts | 1 + .../repository/api/IngredientRepository.kt | 7 + .../banchango/core/data/api/IngredientApi.kt | 13 + .../core/data/api/model/ContainerDto.kt | 9 + .../data/api/model/ContainerIngredientDto.kt | 12 + .../data/api/model/ContainerIngredientDtos.kt | 8 + .../core/data/api/model/IngredientDto.kt | 11 + .../banchango/core/data/di/ApiModule.kt | 7 + .../core/data/di/RepositoryModule.kt | 14 +- .../core/data/mapper/IngredientMapper.kt | 37 ++ .../repository/DefaultIngredientRepository.kt | 31 ++ .../repository/FakeIngredientRepository.kt | 54 +++ .../core/designsystem/theme/Color.kt | 4 +- .../com/sundaegukbap/banchango/Container.kt | 6 + .../banchango/ContainerIngredient.kt | 12 + .../banchango/IngredientContainer.kt | 7 + .../banchango/IngredientContainers.kt | 28 ++ .../sundaegukbap/banchango/IngredientKind.kt | 14 +- .../banchango/KindIngredientContainer.kt | 6 + Android/feature/home/build.gradle.kts | 1 + .../banchango/feature/home/HomeScreen.kt | 320 +++++++++++++++++- .../banchango/feature/home/HomeViewModel.kt | 45 +++ .../feature/home/navigation/HomeNavigator.kt | 5 +- .../home/src/main/res/drawable/ic_add.xml | 5 + Android/feature/recipe/build.gradle.kts | 1 - 26 files changed, 646 insertions(+), 22 deletions(-) create mode 100644 Android/core/data-api/src/main/java/com/sundaegukbap/banchango/core/data/repository/api/IngredientRepository.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/IngredientApi.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerDto.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDto.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDtos.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/IngredientDto.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/mapper/IngredientMapper.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/DefaultIngredientRepository.kt create mode 100644 Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/FakeIngredientRepository.kt create mode 100644 Android/core/model/src/main/java/com/sundaegukbap/banchango/Container.kt create mode 100644 Android/core/model/src/main/java/com/sundaegukbap/banchango/ContainerIngredient.kt create mode 100644 Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainer.kt create mode 100644 Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainers.kt create mode 100644 Android/core/model/src/main/java/com/sundaegukbap/banchango/KindIngredientContainer.kt create mode 100644 Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeViewModel.kt create mode 100644 Android/feature/home/src/main/res/drawable/ic_add.xml diff --git a/Android/.idea/deploymentTargetSelector.xml b/Android/.idea/deploymentTargetSelector.xml index da5007a..edb63e4 100644 --- a/Android/.idea/deploymentTargetSelector.xml +++ b/Android/.idea/deploymentTargetSelector.xml @@ -4,21 +4,15 @@ - - - - \ No newline at end of file diff --git a/Android/app/build.gradle.kts b/Android/app/build.gradle.kts index 0de2262..3c9df5d 100644 --- a/Android/app/build.gradle.kts +++ b/Android/app/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { // modules implementation(project(":feature:main")) + implementation(project(":core:data")) implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) diff --git a/Android/core/data-api/src/main/java/com/sundaegukbap/banchango/core/data/repository/api/IngredientRepository.kt b/Android/core/data-api/src/main/java/com/sundaegukbap/banchango/core/data/repository/api/IngredientRepository.kt new file mode 100644 index 0000000..dfc0ae7 --- /dev/null +++ b/Android/core/data-api/src/main/java/com/sundaegukbap/banchango/core/data/repository/api/IngredientRepository.kt @@ -0,0 +1,7 @@ +package com.sundaegukbap.banchango.core.data.repository.api + +import com.sundaegukbap.banchango.ContainerIngredient + +interface IngredientRepository { + suspend fun getIngredientContainers(): Result> +} \ No newline at end of file diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/IngredientApi.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/IngredientApi.kt new file mode 100644 index 0000000..ac63973 --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/IngredientApi.kt @@ -0,0 +1,13 @@ +package com.sundaegukbap.banchango.core.data.api + +import com.sundaegukbap.banchango.core.data.api.model.ContainerIngredientDtos +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Path + +internal interface IngredientApi { + @GET("api/ingredients/main/list/{userid}") + suspend fun getIngredients( + @Path("userid") userId: Long, + ): Response +} \ No newline at end of file diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerDto.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerDto.kt new file mode 100644 index 0000000..c6f2375 --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerDto.kt @@ -0,0 +1,9 @@ +package com.sundaegukbap.banchango.core.data.api.model + +import kotlinx.serialization.Serializable + +@Serializable +data class ContainerDto( + val containerId: Long, + val containerName: String +) \ No newline at end of file diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDto.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDto.kt new file mode 100644 index 0000000..97b5d19 --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDto.kt @@ -0,0 +1,12 @@ +package com.sundaegukbap.banchango.core.data.api.model + +import kotlinx.serialization.Serializable + +@Serializable +data class ContainerIngredientDto( + val containerIngredientId: Long, + val containerDto: ContainerDto, + val ingredientDto: IngredientDto, + val createdAt: String, + val expirationDate: String +) diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDtos.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDtos.kt new file mode 100644 index 0000000..f2dbaa3 --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/ContainerIngredientDtos.kt @@ -0,0 +1,8 @@ +package com.sundaegukbap.banchango.core.data.api.model + +import kotlinx.serialization.Serializable + +@Serializable +data class ContainerIngredientDtos( + val containerIngredientDtos: List +) \ No newline at end of file diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/IngredientDto.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/IngredientDto.kt new file mode 100644 index 0000000..c581203 --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/api/model/IngredientDto.kt @@ -0,0 +1,11 @@ +package com.sundaegukbap.banchango.core.data.api.model + +import kotlinx.serialization.Serializable + +@Serializable +data class IngredientDto( + val id: Long, + val name: String, + val kind: String, + val image: String?, +) \ No newline at end of file diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/ApiModule.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/ApiModule.kt index 9c31690..ca0ed81 100644 --- a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/ApiModule.kt +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/ApiModule.kt @@ -2,6 +2,7 @@ package com.sundaegukbap.banchango.core.data.di import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.sundaegukbap.banchango.core.data.BuildConfig +import com.sundaegukbap.banchango.core.data.api.IngredientApi import com.sundaegukbap.banchango.core.data.api.RecipeApi import dagger.Module import dagger.Provides @@ -55,4 +56,10 @@ internal object ApiModule { fun providesRecipeRecommendApi( @DefaultRetrofitQualifier retrofit: Retrofit ): RecipeApi = retrofit.create(RecipeApi::class.java) + + @Provides + @Singleton + fun providesIngredientApi( + @DefaultRetrofitQualifier retrofit: Retrofit + ): IngredientApi = retrofit.create(IngredientApi::class.java) } diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/RepositoryModule.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/RepositoryModule.kt index aa00fe5..fdeb08e 100644 --- a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/RepositoryModule.kt +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/di/RepositoryModule.kt @@ -1,15 +1,25 @@ package com.sundaegukbap.banchango.core.data.di +import com.sundaegukbap.banchango.core.data.repository.DefaultIngredientRepository import com.sundaegukbap.banchango.core.data.repository.DefaultRecipeRepository +import com.sundaegukbap.banchango.core.data.repository.FakeIngredientRepository +import com.sundaegukbap.banchango.core.data.repository.FakeRecipeRepository +import com.sundaegukbap.banchango.core.data.repository.api.IngredientRepository import com.sundaegukbap.banchango.core.data.repository.api.RecipeRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @InstallIn(SingletonComponent::class) @Module -internal abstract class RepositoryModule { +internal interface RepositoryModule { + @Singleton @Binds - abstract fun bindsRecipeRepository(recipeRepository: DefaultRecipeRepository): RecipeRepository + fun bindsRecipeRepository(recipeRepository: FakeRecipeRepository): RecipeRepository + + @Singleton + @Binds + fun bindsIngredientRepository(ingredientRepository: FakeIngredientRepository): IngredientRepository } diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/mapper/IngredientMapper.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/mapper/IngredientMapper.kt new file mode 100644 index 0000000..55209a4 --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/mapper/IngredientMapper.kt @@ -0,0 +1,37 @@ +package com.sundaegukbap.banchango.core.data.mapper + +import com.sundaegukbap.banchango.Container +import com.sundaegukbap.banchango.Ingredient +import com.sundaegukbap.banchango.ContainerIngredient +import com.sundaegukbap.banchango.IngredientKind +import com.sundaegukbap.banchango.core.data.api.model.ContainerDto +import com.sundaegukbap.banchango.core.data.api.model.ContainerIngredientDto +import com.sundaegukbap.banchango.core.data.api.model.IngredientDto +import java.time.LocalDateTime + +internal fun ContainerIngredientDto.toData() = ContainerIngredient( + id = containerIngredientId, + container = containerDto.toData(), + ingredient = ingredientDto.toData(), + createdAt = LocalDateTime.parse(createdAt), + expirationDate = LocalDateTime.parse(expirationDate) +) + +internal fun ContainerDto.toData() = Container( + id = containerId, + name = containerName +) + +internal fun IngredientDto.toData() = Ingredient( + id = id, + name = name, + kind = when (kind) { + "육류" -> IngredientKind.MEAT + "해산물" -> IngredientKind.SEAFOOD + "채소" -> IngredientKind.VEGETABLE + "과일" -> IngredientKind.FRUIT + "기타" -> IngredientKind.ETC + else -> IngredientKind.ETC + }, + image = image ?: "" +) \ No newline at end of file diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/DefaultIngredientRepository.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/DefaultIngredientRepository.kt new file mode 100644 index 0000000..21dc54e --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/DefaultIngredientRepository.kt @@ -0,0 +1,31 @@ +package com.sundaegukbap.banchango.core.data.repository + +import android.util.Log +import com.sundaegukbap.banchango.Container +import com.sundaegukbap.banchango.ContainerIngredient +import com.sundaegukbap.banchango.Ingredient +import com.sundaegukbap.banchango.IngredientKind +import com.sundaegukbap.banchango.core.data.api.IngredientApi +import com.sundaegukbap.banchango.core.data.mapper.toData +import com.sundaegukbap.banchango.core.data.repository.api.IngredientRepository +import java.time.LocalDateTime +import javax.inject.Inject + +internal class DefaultIngredientRepository @Inject constructor( + private val ingredientApi: IngredientApi, +) : IngredientRepository { + override suspend fun getIngredientContainers(): Result> { + return runCatching { + val response = ingredientApi.getIngredients(1) + Log.d("asdf", "response: $response") + if (response.isSuccessful) { + if (response.body() == null) { + throw IllegalStateException("Response body is null") + } + response.body()!!.containerIngredientDtos.map { it.toData() } + } else { + throw IllegalStateException("Response is not successful") + } + } + } +} diff --git a/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/FakeIngredientRepository.kt b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/FakeIngredientRepository.kt new file mode 100644 index 0000000..4f0200f --- /dev/null +++ b/Android/core/data/src/main/java/com/sundaegukbap/banchango/core/data/repository/FakeIngredientRepository.kt @@ -0,0 +1,54 @@ +package com.sundaegukbap.banchango.core.data.repository + +import com.sundaegukbap.banchango.Container +import com.sundaegukbap.banchango.ContainerIngredient +import com.sundaegukbap.banchango.Ingredient +import com.sundaegukbap.banchango.IngredientKind +import com.sundaegukbap.banchango.core.data.repository.api.IngredientRepository +import java.time.LocalDateTime +import javax.inject.Inject + +internal class FakeIngredientRepository @Inject constructor() : IngredientRepository { + override suspend fun getIngredientContainers(): Result> { + return Result.success( + listOf( + ContainerIngredient( + 1, + Container(1, "Container 1"), + Ingredient(1, "Ingredient 1", IngredientKind.SAUCE, "Ingredient 1 Image"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(1) + ), + ContainerIngredient( + 2, + Container(2, "Container 2"), + Ingredient(2, "Ingredient 2", IngredientKind.VEGETABLE, "Ingredient 2 Image"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(2) + ), + ContainerIngredient( + 3, + Container(2, "Container 2"), + Ingredient(3, "Ingredient 3", IngredientKind.MEAT, "Ingredient 3 Image"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(3) + ), + ContainerIngredient( + 4, + Container(2, "Container 2"), + Ingredient(4, "Ingredient 4", IngredientKind.VEGETABLE, "Ingredient 4 Image"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(4) + ), + ContainerIngredient( + 5, + Container(1, "Container 1"), + Ingredient(5, "Ingredient 5", IngredientKind.SAUCE, "Ingredient 5 Image"), + LocalDateTime.now(), + LocalDateTime.now().plusDays(5) + ), + ) + ) + } + +} \ No newline at end of file diff --git a/Android/core/designsystem/src/main/java/com/sundaegukbap/banchango/core/designsystem/theme/Color.kt b/Android/core/designsystem/src/main/java/com/sundaegukbap/banchango/core/designsystem/theme/Color.kt index e051596..39fbb96 100644 --- a/Android/core/designsystem/src/main/java/com/sundaegukbap/banchango/core/designsystem/theme/Color.kt +++ b/Android/core/designsystem/src/main/java/com/sundaegukbap/banchango/core/designsystem/theme/Color.kt @@ -11,10 +11,12 @@ val PurpleGrey40 = Color(0xFF625b71) val Pink40 = Color(0xFF7D5260) val Orange = Color(0xFFFF7A00) -val LightOrange = Color(0xFFFFC085) +val LightOrange = Color(0xFFFFA857) val White = Color(0xFFFFFFFF) val Black = Color(0xFF000000) val lightGray = Color(0xFFF5F1EE) +val Gray = Color(0xFFF5F1EE) + // 투명도 50% val SemiTransparentOrange = Color(0x80FF7A00) diff --git a/Android/core/model/src/main/java/com/sundaegukbap/banchango/Container.kt b/Android/core/model/src/main/java/com/sundaegukbap/banchango/Container.kt new file mode 100644 index 0000000..6674df4 --- /dev/null +++ b/Android/core/model/src/main/java/com/sundaegukbap/banchango/Container.kt @@ -0,0 +1,6 @@ +package com.sundaegukbap.banchango + +data class Container( + val id: Long, + val name: String +) diff --git a/Android/core/model/src/main/java/com/sundaegukbap/banchango/ContainerIngredient.kt b/Android/core/model/src/main/java/com/sundaegukbap/banchango/ContainerIngredient.kt new file mode 100644 index 0000000..93f88a5 --- /dev/null +++ b/Android/core/model/src/main/java/com/sundaegukbap/banchango/ContainerIngredient.kt @@ -0,0 +1,12 @@ +package com.sundaegukbap.banchango + +import java.time.LocalDateTime + +data class ContainerIngredient( + val id: Long, + val container: Container, + val ingredient: Ingredient, + val createdAt: LocalDateTime, + val expirationDate: LocalDateTime +) + diff --git a/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainer.kt b/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainer.kt new file mode 100644 index 0000000..3ac9e5d --- /dev/null +++ b/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainer.kt @@ -0,0 +1,7 @@ +package com.sundaegukbap.banchango + +data class IngredientContainer( + val container: Container, + val kindIngredientContainers: List +) + diff --git a/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainers.kt b/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainers.kt new file mode 100644 index 0000000..69627c5 --- /dev/null +++ b/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientContainers.kt @@ -0,0 +1,28 @@ +package com.sundaegukbap.banchango + +class ContainerIngredients( + containerIngredients: List +) { + private val _value: MutableList = containerIngredients.toMutableList() + val value: List get() = _value.toList() + + fun getIngredientContainers(): List { + return _value.groupBy { it.container } + .map { (container, containerIngredients) -> + IngredientContainer( + container = container, + kindIngredientContainers = containerIngredients.toKindIngredientContainers() + ) + } + } + + private fun List.toKindIngredientContainers(): List { + return groupBy { it.ingredient.kind } + .map { (kind, ingredients) -> + KindIngredientContainer( + kind = kind, + ingredients = ingredients + ) + } + } +} diff --git a/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientKind.kt b/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientKind.kt index e9907e6..bda73bb 100644 --- a/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientKind.kt +++ b/Android/core/model/src/main/java/com/sundaegukbap/banchango/IngredientKind.kt @@ -1,10 +1,10 @@ package com.sundaegukbap.banchango -enum class IngredientKind { - MEAT, - VEGETABLE, - FRUIT, - SEAFOOD, - SAUCE, - ETC, +enum class IngredientKind(val label: String) { + MEAT("육류"), + VEGETABLE("채소"), + FRUIT("과일"), + SEAFOOD("해산물"), + SAUCE("소스"), + ETC("기타"), } diff --git a/Android/core/model/src/main/java/com/sundaegukbap/banchango/KindIngredientContainer.kt b/Android/core/model/src/main/java/com/sundaegukbap/banchango/KindIngredientContainer.kt new file mode 100644 index 0000000..b5007f2 --- /dev/null +++ b/Android/core/model/src/main/java/com/sundaegukbap/banchango/KindIngredientContainer.kt @@ -0,0 +1,6 @@ +package com.sundaegukbap.banchango + +data class KindIngredientContainer( + val kind: IngredientKind, + val ingredients: List +) \ No newline at end of file diff --git a/Android/feature/home/build.gradle.kts b/Android/feature/home/build.gradle.kts index 44bbac8..5316d0c 100644 --- a/Android/feature/home/build.gradle.kts +++ b/Android/feature/home/build.gradle.kts @@ -42,6 +42,7 @@ android { dependencies { implementation(project(":core:model")) + implementation(project(":core:data-api")) implementation(project(":core:designsystem")) implementation(project(":core:navigation")) implementation(libs.androidx.core.ktx) diff --git a/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeScreen.kt b/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeScreen.kt index 0e4b374..40fed40 100644 --- a/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeScreen.kt +++ b/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeScreen.kt @@ -1,12 +1,330 @@ package com.sundaegukbap.banchango.feature.home +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Label +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Paint +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.sundaegukbap.banchango.Container +import com.sundaegukbap.banchango.ContainerIngredient +import com.sundaegukbap.banchango.Ingredient +import com.sundaegukbap.banchango.IngredientContainer +import com.sundaegukbap.banchango.IngredientKind +import com.sundaegukbap.banchango.KindIngredientContainer +import com.sundaegukbap.banchango.core.designsystem.theme.BanchangoTheme +import com.sundaegukbap.banchango.core.designsystem.theme.Gray +import com.sundaegukbap.banchango.core.designsystem.theme.LightOrange +import com.sundaegukbap.banchango.core.designsystem.theme.Orange +import com.sundaegukbap.banchango.core.designsystem.theme.White +import com.sundaegukbap.banchango.core.designsystem.theme.lightGray +import java.time.LocalDateTime +import java.time.temporal.ChronoUnit @Composable -fun HomeScreen( +fun HomeRoute( padding: PaddingValues, onChangeStatusBarColor: (color: Color, darkIcons: Boolean) -> Unit, + viewModel: HomeViewModel = hiltViewModel() ) { + val ingredientContainers by viewModel.ingredientContainers.collectAsStateWithLifecycle() + + HomeScreen( + padding = padding, + ingredientContainers = ingredientContainers, + onChangeStatusBarColor = onChangeStatusBarColor + ) +} + +@Composable +private fun HomeScreen( + padding: PaddingValues, + ingredientContainers: List, + onChangeStatusBarColor: (color: Color, darkIcons: Boolean) -> Unit +) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .padding(16.dp) + ) { + items(ingredientContainers) { ingredientContainer -> + val isEven = ingredientContainers.indexOf(ingredientContainer) % 2 == 0 + val containerColor = if (isEven) LightOrange else Gray + val itemColor = if (isEven) Gray else White + val buttonColor = if (isEven) White else Gray + val ingredientContainerNameColor = if (isEven) White else LightOrange + + ElevatedCard( + colors = CardDefaults.elevatedCardColors().copy(containerColor = containerColor), + elevation = CardDefaults.elevatedCardElevation(2.dp), + modifier = Modifier + .fillMaxSize() + ) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 8.dp, horizontal = 8.dp) + ) { + Text( + modifier = Modifier.weight(0.4f), + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.titleMedium, + color = ingredientContainerNameColor, + text = ingredientContainer.container.name + ) + Spacer(modifier = Modifier.weight(0.5f)) + Icon( + modifier = Modifier.weight(0.1f), + tint = ingredientContainerNameColor, + painter = painterResource(id = R.drawable.ic_add), + contentDescription = null + ) + } + val kindIngredients = ingredientContainer.kindIngredientContainers + val totalIngredients = kindIngredients.size + + + // Creating rows of two IngredientItems + for (index in 0 until totalIngredients step 2) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween // Distributes items evenly + ) { + // First IngredientItem + IngredientItem( + containerColor = itemColor, + kindIngredientContainer = kindIngredients[index], + buttonColor = buttonColor, + modifier = Modifier.weight(0.4f) // Fixed width for consistent size + ) + Spacer(modifier = Modifier.width(20.dp)) + // Check for the second item + if (index + 1 < totalIngredients) { + IngredientItem( + kindIngredientContainer = kindIngredients[index + 1], + modifier = Modifier.weight(0.4f), // Fixed width for consistent size + containerColor = itemColor, + buttonColor = buttonColor, + ) + } else { + // If the second item does not exist, add a spacer for alignment + Spacer(modifier = Modifier.weight(0.4f)) // Takes remaining space + } + } + } + } + Spacer(modifier = Modifier.height(20.dp)) + } + item { AddContainerButton(if (ingredientContainers.size % 2 == 0) LightOrange else Gray) } + } +} + +@Composable +private fun AddContainerButton( + containerColor: Color, +) { + ElevatedCard( + colors = CardDefaults.elevatedCardColors(containerColor = containerColor), + modifier = Modifier + ) { + Column( + modifier = Modifier + .padding(8.dp) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon( + painter = painterResource(id = R.drawable.ic_add), + contentDescription = null + ) + } + } } + +@Composable +private fun IngredientItem( + containerColor: Color, + buttonColor: Color, + kindIngredientContainer: KindIngredientContainer, + modifier: Modifier = Modifier +) { + Card( + colors = CardDefaults.cardColors(containerColor = containerColor), + modifier = modifier + .height(106.dp) + ) { + Column(Modifier.padding(8.dp)) { + val ingredients = kindIngredientContainer.ingredients + Row { + Text( + text = kindIngredientContainer.kind.label, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Bold, + ) + Spacer(modifier = Modifier.weight(0.3f)) + Text( + modifier = Modifier + .background( + color = buttonColor, + shape = RoundedCornerShape(20.dp) + ) + .padding(horizontal = 16.dp, vertical = 4.dp), + text = ingredients.size.toString() + "개", + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Bold, + color = Orange + ) + } + + // 2개까지만 표시하고, 2개 이상일 경우 ... 표시 + ingredients.subList(0, minOf(ingredients.size, 2)).forEach { ingredient -> + val dDay = ChronoUnit.DAYS.between( + LocalDateTime.now(), + ingredient.expirationDate, + ) + Row { + Text( + text = ingredient.ingredient.name, + style = MaterialTheme.typography.bodySmall, + fontSize = 12.sp + ) + Spacer(modifier = Modifier.width(10.dp)) + Text(text = "D - $dDay", style = MaterialTheme.typography.bodySmall) + } + } + if (ingredients.size > 2) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "...", + style = MaterialTheme.typography.bodySmall, + fontSize = 12.sp + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun PreviewIngredientItem() { + BanchangoTheme { + IngredientItem( + containerColor = White, + buttonColor = Gray, + kindIngredientContainer = KindIngredientContainer( + IngredientKind.VEGETABLE, + listOf( + ContainerIngredient( + 1, + Container(1, "냉장 1"), + Ingredient(1, "상추", IngredientKind.VEGETABLE, ""), + LocalDateTime.now(), + LocalDateTime.now() + ), + ContainerIngredient( + 1, + Container(1, "냉장 1"), + Ingredient(1, "상추", IngredientKind.VEGETABLE, ""), + LocalDateTime.now(), + LocalDateTime.now() + ) + ) + ) + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun PreviewHomeScreen() { + BanchangoTheme { + HomeScreen( + padding = PaddingValues(0.dp), + onChangeStatusBarColor = { _, _ -> }, + ingredientContainers = listOf( + IngredientContainer( + Container(1, "냉장 1"), + listOf( + KindIngredientContainer( + IngredientKind.VEGETABLE, listOf( + ContainerIngredient( + 1, + Container(1, "냉장 1"), + Ingredient(1, "상추", IngredientKind.VEGETABLE, ""), + LocalDateTime.now(), + LocalDateTime.now() + ), + ContainerIngredient( + 1, + Container(1, "냉장 1"), + Ingredient(1, "상추", IngredientKind.VEGETABLE, ""), + LocalDateTime.now(), + LocalDateTime.now() + ) + ) + ) + ) + ), + IngredientContainer( + Container(1, "냉장 1"), + listOf( + KindIngredientContainer( + IngredientKind.VEGETABLE, listOf( + ContainerIngredient( + 1, + Container(1, "냉장 1"), + Ingredient(1, "상추", IngredientKind.VEGETABLE, ""), + LocalDateTime.now(), + LocalDateTime.now() + ) + ) + ) + ) + ) + ) + ) + } +} + diff --git a/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeViewModel.kt b/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeViewModel.kt new file mode 100644 index 0000000..823696f --- /dev/null +++ b/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/HomeViewModel.kt @@ -0,0 +1,45 @@ +package com.sundaegukbap.banchango.feature.home + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.sundaegukbap.banchango.ContainerIngredients +import com.sundaegukbap.banchango.IngredientContainer +import com.sundaegukbap.banchango.core.data.repository.api.IngredientRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( + private val ingredientRepository: IngredientRepository +) : ViewModel() { + + private var containerIngredients: ContainerIngredients = ContainerIngredients(emptyList()) + + private val _ingredientContainers: MutableStateFlow> = + MutableStateFlow(emptyList()) + val ingredientContainers: StateFlow> = + _ingredientContainers.asStateFlow() + + init { + viewModelScope.launch { + ingredientRepository.getIngredientContainers() + .onSuccess { + containerIngredients = ContainerIngredients(it) + _ingredientContainers.value = containerIngredients.getIngredientContainers() + Log.d( + "asdf", + "containerIngredients: ${containerIngredients.getIngredientContainers()}" + ) + }.onFailure { + Log.e("asdf", "Failed to get ingredient containers", it) + } + } + } +} + diff --git a/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/navigation/HomeNavigator.kt b/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/navigation/HomeNavigator.kt index 9b3f838..5d3f52b 100644 --- a/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/navigation/HomeNavigator.kt +++ b/Android/feature/home/src/main/java/com/sundaegukbap/banchango/feature/home/navigation/HomeNavigator.kt @@ -6,8 +6,9 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import com.sundaegukbap.banchango.feature.home.HomeScreen +import com.sundaegukbap.banchango.feature.home.HomeRoute import com.sundaegukbap.banchango.navigation.MainTabRoute +import com.sundaegukbap.banchango.navigation.Route fun NavController.navigateHome(navOptions: NavOptions) { navigate(MainTabRoute.Home, navOptions) @@ -18,6 +19,6 @@ fun NavGraphBuilder.homeNavGraph( onChangeStatusBarColor: (color: Color, darkIcons: Boolean) -> Unit, ) { composable { - HomeScreen(padding, onChangeStatusBarColor) + HomeRoute(padding, onChangeStatusBarColor) } } diff --git a/Android/feature/home/src/main/res/drawable/ic_add.xml b/Android/feature/home/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..9f83b8f --- /dev/null +++ b/Android/feature/home/src/main/res/drawable/ic_add.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Android/feature/recipe/build.gradle.kts b/Android/feature/recipe/build.gradle.kts index e90a9eb..5ea806a 100644 --- a/Android/feature/recipe/build.gradle.kts +++ b/Android/feature/recipe/build.gradle.kts @@ -45,7 +45,6 @@ dependencies { implementation(project(":core:designsystem")) implementation(project(":core:navigation")) implementation(project(":core:data-api")) - implementation(project(":core:data")) implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)