Skip to content

Commit

Permalink
[Android] feat: 레시피 상세 페이지 구현(#21) (#22)
Browse files Browse the repository at this point in the history
* feat(reciperecommend): 레시피 추천 화면 하단 로딩바

* refactor(data): RecipeRepository 코드 정리

* feat: 화면에 따라 상태 바를 다른 색으로 변경할 수 있다.

* refactor: 사용하지 않는 코드 제거

* feat: 레시피 상세 화면 뼈대 작성

* feat(Stars): 별 개수에 따라 별이 그려지는 Composable 구현

* feat(RecipeDifficult): 레시피는 요리 난이도를 가진다

* feat(RecipeDifficult): 레시피 추천 난이도 화면을 그린다

* refactor: Recipe 상세 이동 버튼 분리

* feat: 레시피 상세 TopBar 생성

* feat: 좋아요 표시할 수 있는 레시피 객체 정의

* feat(RecipeRepository): RecipeRepository 에 좋아요를 요청할 수 있다

* feat(RecipeDetailCard): 레시피 상세 정보를 레시피 상세 전체 화면에서에서 분리한다

* feat(statusBarColor): systemBar 변경에서 statusBar 변경으로 바꾼다

* feat(RecipeDetailScreen): LikableRecipe 로 화면을 보여준다.

* feat(RecipeDetailScreen): 상단까지 스크롤 하면 색깔이 변경되는 TopBar 를 구현한다

* feat(RecipeDetailScreen): Recipe 이미지에 그림자 그라데이션을 넣는다.

* feat(RecipeDetailScreen): 보여주는 화면 크기를 네비게이션 바 전까지로 한다

* chore: gitIgnore 추가
  • Loading branch information
SeongHoonC authored Jul 28, 2024
1 parent 235e5c6 commit 3b6c362
Show file tree
Hide file tree
Showing 30 changed files with 992 additions and 178 deletions.
1 change: 1 addition & 0 deletions Android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
/.idea/deploymentTargetSelector.xml
.DS_Store
/build
/captures
Expand Down
11 changes: 8 additions & 3 deletions Android/.idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.sundaegukbap.banchango.core.data.repository.api

import com.sundaegukbap.banchango.LikableRecipe
import com.sundaegukbap.banchango.Recipe

interface RecipeRepository {
suspend fun getRecipeRecommendation(): Result<List<Recipe>>
suspend fun getRecipeDetail(id: Long): Result<Recipe>

suspend fun getRecipeDetail(id: Long): Result<LikableRecipe>

suspend fun likeRecipe(
id: Long,
isLiked: Boolean,
): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ data class RecipeRecommendResponse(
val need: List<String>,
val servings: Int,
val cookingTime: Int,
val isBookmarked: Boolean,
val difficulty: String
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sundaegukbap.banchango.core.data.di

import com.sundaegukbap.banchango.core.data.repository.DefaultRecipeRepository
import com.sundaegukbap.banchango.core.data.repository.FakeRecipeRepository
import com.sundaegukbap.banchango.core.data.repository.api.RecipeRepository
import dagger.Binds
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package com.sundaegukbap.banchango.core.data.mapper

import com.sundaegukbap.banchango.Recipe
import com.sundaegukbap.banchango.RecipeDifficulty
import com.sundaegukbap.banchango.core.data.api.model.RecipeRecommendResponse

internal fun List<RecipeRecommendResponse>.toData(): List<Recipe> = map { it.toData() }

internal fun RecipeRecommendResponse.toData(): Recipe {
val difficulty = when (difficulty) {
"아무나" -> RecipeDifficulty.ANYONE
"초보" -> RecipeDifficulty.BEGINNER
"중급" -> RecipeDifficulty.INTERMEDIATE
"고급" -> RecipeDifficulty.ADVANCED
"신의경지" -> RecipeDifficulty.GODLIKE
else -> RecipeDifficulty.ANYONE
}
return Recipe(
id = id,
name = name,
Expand All @@ -16,7 +25,6 @@ internal fun RecipeRecommendResponse.toData(): Recipe {
need = need,
servings = servings,
cookingTime = cookingTime,
isBookmarked = isBookmarked,
difficulty = difficulty,
)
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,47 @@
package com.sundaegukbap.banchango.core.data.repository

import com.sundaegukbap.banchango.LikableRecipe
import com.sundaegukbap.banchango.Recipe
import com.sundaegukbap.banchango.core.data.api.RecipeApi
import com.sundaegukbap.banchango.core.data.mapper.toData
import com.sundaegukbap.banchango.core.data.repository.api.RecipeRepository
import javax.inject.Inject

internal class DefaultRecipeRepository @Inject constructor(
private val recipeApi: RecipeApi,
) : RecipeRepository {
override suspend fun getRecipeRecommendation(): Result<List<Recipe>> {
return runCatching {
val response = recipeApi.getRecipeRecommendation(1)
if (response.isSuccessful) {
if (response.body() == null) {
throw IllegalStateException("Response body is null")
internal class DefaultRecipeRepository
@Inject
constructor(
private val recipeApi: RecipeApi,
) : RecipeRepository {
override suspend fun getRecipeRecommendation(): Result<List<Recipe>> =
runCatching {
val response = recipeApi.getRecipeRecommendation(1)
if (response.isSuccessful) {
if (response.body() == null) {
throw IllegalStateException("Response body is null")
}
response.body()!!.toData()
} else {
throw IllegalStateException("Response is not successful")
}
response.body()!!.toData()
} else {
throw IllegalStateException("Response is not successful")
}
}
}

override suspend fun getRecipeDetail(id: Long): Result<Recipe> {
return runCatching {
val response = recipeApi.getRecipeDetail(1, id.toLong())
if (response.isSuccessful) {
if (response.body() == null) {
throw IllegalStateException("Response body is null")
override suspend fun getRecipeDetail(id: Long): Result<LikableRecipe> =
runCatching {
val response = recipeApi.getRecipeDetail(1, id)
if (response.isSuccessful) {
if (response.body() == null) {
throw IllegalStateException("Response body is null")
}
LikableRecipe(response.body()!!.toData(), false)
} else {
throw IllegalStateException("Response is not successful")
}
response.body()!!.toData()
} else {
throw IllegalStateException("Response is not successful")
}

override suspend fun likeRecipe(
id: Long,
isLiked: Boolean,
): Result<Unit> {
TODO("호감 기능 추가시 구현")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,72 +1,64 @@
package com.sundaegukbap.banchango.core.data.repository

import com.sundaegukbap.banchango.LikableRecipe
import com.sundaegukbap.banchango.Recipe
import com.sundaegukbap.banchango.RecipeDifficulty
import com.sundaegukbap.banchango.core.data.repository.api.RecipeRepository
import javax.inject.Inject

class FakeRecipeRepository @Inject constructor() : RecipeRepository {
override suspend fun getRecipeRecommendation(): Result<List<Recipe>> {
return Result.success(
listOf(
Recipe(
id = 1,
name = "간장계란볶음밥",
introduction = "아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. 아이들이 더 좋아할거예요.",
image = "https://recipe1.ezmember.co.kr/cache/recipe/2018/05/26/d0c6701bc673ac5c18183b47212a58571.jpg",
link = "https://www.10000recipe.com/recipe/6889616",
cookingTime = 10,
servings = 2,
difficulty = "Easy",
have = listOf(""),
need = listOf(""),
isBookmarked = false,
),
Recipe(
id = 2,
name = "간장계란볶음밥",
introduction = "아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. 아이들이 더 좋아할거예요.",
image = "https://recipe1.ezmember.co.kr/cache/recipe/2018/05/26/d0c6701bc673ac5c18183b47212a58571.jpg",
link = "https://www.10000recipe.com/recipe/6889616",
cookingTime = 10,
servings = 2,
difficulty = "Easy",
have = listOf(""),
need = listOf(""),
isBookmarked = false,
),
class FakeRecipeRepository
@Inject
constructor() : RecipeRepository {
override suspend fun getRecipeRecommendation(): Result<List<Recipe>> =
Result.success(
List(3) {
Recipe(
id = (it + 1).toLong(),
name = "간장계란볶음밥",
introduction = "아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. 아이들이 더 좋아할거예요.",
image = "https://recipe1.ezmember.co.kr/cache/recipe/2018/05/26/d0c6701bc673ac5c18183b47212a58571.jpg",
link = "https://www.10000recipe.com/recipe/6889616",
cookingTime = 10,
servings = 2,
difficulty = RecipeDifficulty.BEGINNER,
have = listOf("계란", "간장"),
need = listOf("식초", "당근", "감자"),
)
},
)

Recipe(
id = 3,
name = "간장계란볶음밥",
introduction = "아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. 아이들이 더 좋아할거예요.",
image = "https://recipe1.ezmember.co.kr/cache/recipe/2018/05/26/d0c6701bc673ac5c18183b47212a58571.jpg",
link = "https://www.10000recipe.com/recipe/6889616",
cookingTime = 10,
servings = 2,
difficulty = "Easy",
have = listOf(""),
need = listOf(""),
isBookmarked = false,
override suspend fun getRecipeDetail(id: Long): Result<LikableRecipe> =
Result.success(
LikableRecipe(
recipe =
Recipe(
id = id,
name = "간장계란볶음밥",
introduction =
"아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. 아이들이 더 좋아할거예요." +
"아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. \n " +
"아이들이 더 좋아할거예요. \n" +
"아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요.\n" +
" 아이들이 더 좋아할거예요.\n" +
" 아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요.\n 아이들이 더 좋아할거예요.\n " +
"아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요.\n 아이들이 더 좋아할거예요.\n " +
"아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요.\n 아이들이 더 좋아할거예요.\n" +
" 아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요.\n 아이들이 더 좋아할거예요.\n " +
"아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요.\n 아이들이 더 좋아할거예요.\n",
image = "https://recipe1.ezmember.co.kr/cache/recipe/2018/05/26/d0c6701bc673ac5c18183b47212a58571.jpg",
link = "https://www.10000recipe.com/recipe/6889616",
cookingTime = 10,
servings = 2,
difficulty = RecipeDifficulty.BEGINNER,
have = listOf("계란", "간장"),
need = listOf("식초", "당근", "감자"),
),
isLiked = false,
),
)
)
}

override suspend fun getRecipeDetail(id: Long): Result<Recipe> {
return Result.success(
Recipe(
id = id,
name = "간장계란볶음밥",
introduction = "아주 간단하면서 맛있는 계란간장볶음밥으로 한끼식사 만들어보세요. 아이들이 더 좋아할거예요.",
image = "https://recipe1.ezmember.co.kr/cache/recipe/2018/05/26/d0c6701bc673ac5c18183b47212a58571.jpg",
link = "https://www.10000recipe.com/recipe/6889616",
cookingTime = 10,
servings = 2,
difficulty = "Easy",
have = listOf(""),
need = listOf(""),
isBookmarked = false,
)
)
override suspend fun likeRecipe(
id: Long,
isLiked: Boolean,
): Result<Unit> = Result.success(Unit)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

val Orange = Color(0xFFFF7A00)
val LightBeige = Color(0xFFF5EB)
val LightOrange = Color(0xFFFFC085)
val White = Color(0xFFFFFFFF)
val Black = Color(0xFF000000)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sundaegukbap.banchango

data class LikableRecipe(
val recipe: Recipe,
val isLiked: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ data class Recipe(
val link: String,
val cookingTime: Int,
val servings: Int,
val difficulty: String,
val isBookmarked: Boolean,
val difficulty: RecipeDifficulty,
val have: List<String>,
val need: List<String>
val need: List<String>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sundaegukbap.banchango

enum class RecipeDifficulty(val level: Int, val explain: String) {
ANYONE(1, "아무나"),
BEGINNER(2, "초보"),
INTERMEDIATE(3, "중급"),
ADVANCED(4, "고급"),
GODLIKE(5, "신의 경지"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package com.sundaegukbap.banchango.feature.home

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color

@Composable
fun HomeScreen(padding: PaddingValues) {
fun HomeScreen(
padding: PaddingValues,
onChangeStatusBarColor: (color: Color, darkIcons: Boolean) -> Unit,
) {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sundaegukbap.banchango.feature.home.navigation

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.graphics.Color
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
Expand All @@ -12,8 +13,11 @@ fun NavController.navigateHome(navOptions: NavOptions) {
navigate(MainTabRoute.Home, navOptions)
}

fun NavGraphBuilder.homeNavGraph(padding: PaddingValues) {
fun NavGraphBuilder.homeNavGraph(
padding: PaddingValues,
onChangeStatusBarColor: (color: Color, darkIcons: Boolean) -> Unit,
) {
composable<MainTabRoute.Home> {
HomeScreen(padding)
HomeScreen(padding, onChangeStatusBarColor)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.ui.graphics.Color
import com.google.accompanist.systemuicontroller.SystemUiController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.sundaegukbap.banchango.core.designsystem.theme.BanchangoTheme
import dagger.hilt.android.AndroidEntryPoint


@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private lateinit var systemUiController: SystemUiController

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
BanchangoTheme {
val navController = rememberMainNavigator()

val systemUiController = rememberSystemUiController()
systemUiController.setSystemBarsColor(color = Color(0xBFFFFFFF), darkIcons = true)
systemUiController.setNavigationBarColor(
color = Color(0xFFFFFFFF)
)
systemUiController = rememberSystemUiController()
systemUiController.setNavigationBarColor(color = Color(0xFFFFFFFF))

MainScreen(
navigator = navController,
onChangeDarkTheme = {}
onChangeDarkTheme = {},
onChangeStatusBarColor = { color, darkIcons ->
systemUiController.setStatusBarColor(color = color, darkIcons = darkIcons,)
},
)
}
}
Expand Down
Loading

0 comments on commit 3b6c362

Please sign in to comment.