-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
17 changed files
with
721 additions
and
2 deletions.
There are no files selected for viewing
195 changes: 195 additions & 0 deletions
195
app/src/main/java/org/techtown/twosomeheart/presentation/detail/DetailRoute.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package org.techtown.twosomeheart.presentation.detail | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.PaddingValues | ||
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.rememberScrollState | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material3.HorizontalDivider | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.LaunchedEffect | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.graphics.vector.ImageVector | ||
import androidx.compose.ui.layout.ContentScale | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.res.vectorResource | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||
import androidx.lifecycle.viewmodel.compose.viewModel | ||
import coil.compose.AsyncImage | ||
import kotlinx.collections.immutable.toPersistentList | ||
import org.techtown.twosomeheart.R | ||
import org.techtown.twosomeheart.core.component.BlackBottomButton | ||
import org.techtown.twosomeheart.core.component.Topbar | ||
import org.techtown.twosomeheart.core.extension.noRippleClickable | ||
import org.techtown.twosomeheart.core.util.UiState | ||
import org.techtown.twosomeheart.presentation.detail.component.MenuDetailAllergyText | ||
import org.techtown.twosomeheart.presentation.detail.component.MenuDetailContent | ||
import org.techtown.twosomeheart.presentation.detail.component.MenuNutritionColumn | ||
import org.techtown.twosomeheart.presentation.detail.model.DetailModel | ||
import org.techtown.twosomeheart.ui.theme.Gray20 | ||
import org.techtown.twosomeheart.ui.theme.TwosomeHeartTheme | ||
import org.techtown.twosomeheart.ui.theme.White | ||
|
||
@Composable | ||
fun DetailRoute( | ||
paddingValues: PaddingValues, | ||
navigateUp: () -> Unit, | ||
viewModel: DetailViewModel = viewModel() | ||
) { | ||
val state by viewModel.state.collectAsStateWithLifecycle() | ||
|
||
LaunchedEffect(Unit) { | ||
viewModel.getDetailDummy() | ||
} | ||
|
||
DetailScreen( | ||
paddingValues = paddingValues, | ||
navigateUp = navigateUp, | ||
state = state.uiState | ||
) | ||
|
||
} | ||
|
||
@Composable | ||
fun DetailScreen( | ||
paddingValues: PaddingValues, | ||
navigateUp: () -> Unit, | ||
state: UiState<DetailModel>, | ||
modifier: Modifier = Modifier | ||
) { | ||
Scaffold( | ||
modifier = modifier | ||
.background(White) | ||
.padding(paddingValues) | ||
.fillMaxSize(), | ||
topBar = { | ||
Topbar( | ||
leadingIcon = { | ||
Icon( | ||
imageVector = ImageVector.vectorResource(R.drawable.ic_back), | ||
contentDescription = stringResource(R.string.top_bar_back), | ||
modifier = Modifier.noRippleClickable(onClick = navigateUp) | ||
) | ||
}, | ||
leadingIcon2 = { | ||
Icon( | ||
imageVector = ImageVector.vectorResource(R.drawable.ic_home), | ||
contentDescription = stringResource(R.string.top_bar_home), | ||
) | ||
}, | ||
text = stringResource(R.string.menu_detail_top_bar), | ||
trailingIcon3 = { | ||
Icon( | ||
imageVector = ImageVector.vectorResource(R.drawable.ic_basket), | ||
contentDescription = stringResource(R.string.top_bar_basket), | ||
modifier = Modifier.noRippleClickable(onClick = navigateUp), | ||
tint = Color.Unspecified | ||
) | ||
} | ||
) | ||
}, | ||
bottomBar = { | ||
BlackBottomButton( | ||
text = stringResource(R.string.menu_detail_order_text), | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(16.dp) | ||
) | ||
} | ||
) { innerPadding -> | ||
Column( | ||
modifier = Modifier | ||
.padding(innerPadding) | ||
.verticalScroll(rememberScrollState()) | ||
.fillMaxWidth() | ||
) { | ||
when (state) { | ||
is UiState.Loading -> {} | ||
|
||
is UiState.Empty -> {} | ||
|
||
is UiState.Failure -> {} | ||
|
||
is UiState.Success -> { | ||
|
||
AsyncImage( | ||
model = state.data.menuImageUrl, | ||
contentDescription = stringResource(R.string.menu_detail_image), | ||
modifier = Modifier.fillMaxWidth(), | ||
contentScale = ContentScale.Crop | ||
) | ||
|
||
Spacer(modifier = Modifier.height(26.dp)) | ||
|
||
MenuDetailContent( | ||
isBestMenu = true, | ||
menuName = state.data.menuName, | ||
menuDescription = state.data.menuDescription, | ||
menuPrice = state.data.menuPrice, | ||
) | ||
|
||
HorizontalDivider( | ||
thickness = 4.dp, | ||
color = Gray20 | ||
) | ||
|
||
MenuNutritionColumn( | ||
nutritionText = state.data.menuNutrition | ||
) | ||
|
||
if (state.data.menuAllergy != null) { | ||
MenuDetailAllergyText( | ||
allergyText = state.data.menuAllergy | ||
) | ||
|
||
Spacer(modifier = Modifier.height(58.dp)) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun DetailScreenPreview() { | ||
TwosomeHeartTheme { | ||
DetailScreen( | ||
paddingValues = PaddingValues(), | ||
navigateUp = {}, | ||
state = UiState.Success( | ||
DetailModel( | ||
menuId = 1, | ||
menuName = "바나나 샷 라떼", | ||
menuStatus = "New", | ||
menuDescription = "달콤한 바나나 라떼에 에스프레소 샷 추가!", | ||
menuPrice = 5500, | ||
menuCaution = "고카페인, 우유", | ||
menuNutrition = listOf( | ||
"1회 제공량: 325ml", | ||
"총 제공량: 1잔", | ||
"열량(Kcal): 260", | ||
"당류(g/%): 30/30", | ||
"단백질(g/%): 7/13", | ||
"포화지방(g/%): 5/33", | ||
"나트륨(mg/%): 160/8", | ||
"카페인(mg/%): 92" | ||
).toPersistentList(), | ||
menuAllergy = "우유", | ||
menuImageUrl = "https://private-user-images.githubusercontent.com/69308068/389300533-8d353883-fb4b-4608-b0b4-24d2bc074133.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzI1MzI2OTksIm5iZiI6MTczMjUzMjM5OSwicGF0aCI6Ii82OTMwODA2OC8zODkzMDA1MzMtOGQzNTM4ODMtZmI0Yi00NjA4LWIwYjQtMjRkMmJjMDc0MTMzLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDExMjUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQxMTI1VDEwNTk1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTMwZTViNmNmMDUwODlmYjRiNGUzZDYzYmY5MmQxOTE0ZTkyMzI3ODJkNDk2MjljZjZjMTRkODE1MzNiM2JkMzkmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.BcvGmMQweWhlRzbTBNEGhu9K6WTQOUE6Fkntktloog0" | ||
) | ||
) | ||
) | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
app/src/main/java/org/techtown/twosomeheart/presentation/detail/DetailState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package org.techtown.twosomeheart.presentation.detail | ||
|
||
import org.techtown.twosomeheart.core.util.UiState | ||
import org.techtown.twosomeheart.presentation.detail.model.DetailModel | ||
|
||
data class DetailState( | ||
val uiState: UiState<DetailModel> = UiState.Loading | ||
) |
41 changes: 41 additions & 0 deletions
41
app/src/main/java/org/techtown/twosomeheart/presentation/detail/DetailViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package org.techtown.twosomeheart.presentation.detail | ||
|
||
import androidx.lifecycle.ViewModel | ||
import kotlinx.collections.immutable.PersistentList | ||
import kotlinx.collections.immutable.toPersistentList | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import org.techtown.twosomeheart.core.util.UiState | ||
import org.techtown.twosomeheart.presentation.detail.model.DetailModel | ||
|
||
class DetailViewModel: ViewModel() { | ||
|
||
private val _state = MutableStateFlow(DetailState()) | ||
val state: StateFlow<DetailState> | ||
get() = _state.asStateFlow() | ||
|
||
fun getDetailDummy() { | ||
_state.value = _state.value.copy( | ||
uiState = UiState.Success(detailDummy) | ||
) | ||
} | ||
|
||
private val detailDummy = DetailModel( | ||
menuId = 1, | ||
menuName = "바나나 샷 라떼", | ||
menuStatus = "New", | ||
menuDescription = "달콤한 바나나 라떼에 에스프레소 샷 추가!", | ||
menuPrice = 5500, | ||
menuCaution = "고카페인, 우유", | ||
menuNutrition = splitNutritionText( | ||
"1회 제공량: 325ml\n총 제공량: 1잔\n열량(Kcal): 260\n당류(g/%): 30/30\n단백질(g/%): 7/13\n포화지방(g/%): 5/33\n나트륨(mg/%): 160/8\n카페인(mg/%): 92" | ||
), | ||
menuAllergy = "우유", | ||
menuImageUrl = "https://private-user-images.githubusercontent.com/69308068/389300533-8d353883-fb4b-4608-b0b4-24d2bc074133.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzI1MzI2OTksIm5iZiI6MTczMjUzMjM5OSwicGF0aCI6Ii82OTMwODA2OC8zODkzMDA1MzMtOGQzNTM4ODMtZmI0Yi00NjA4LWIwYjQtMjRkMmJjMDc0MTMzLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDExMjUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQxMTI1VDEwNTk1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTMwZTViNmNmMDUwODlmYjRiNGUzZDYzYmY5MmQxOTE0ZTkyMzI3ODJkNDk2MjljZjZjMTRkODE1MzNiM2JkMzkmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.BcvGmMQweWhlRzbTBNEGhu9K6WTQOUE6Fkntktloog0" | ||
) | ||
|
||
private fun splitNutritionText(nutritionTexts: String): PersistentList<String>? { | ||
return nutritionTexts.split("\n").toPersistentList() | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...ain/java/org/techtown/twosomeheart/presentation/detail/component/MenuDetailAllergyText.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.techtown.twosomeheart.presentation.detail.component | ||
|
||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.unit.dp | ||
import org.techtown.twosomeheart.R | ||
import org.techtown.twosomeheart.ui.theme.Black | ||
import org.techtown.twosomeheart.ui.theme.Gray80 | ||
import org.techtown.twosomeheart.ui.theme.TwosomeHeartTypography | ||
|
||
@Composable | ||
fun MenuDetailAllergyText( | ||
allergyText: String, | ||
modifier: Modifier = Modifier | ||
) { | ||
Column( | ||
modifier = modifier | ||
.padding(start = 16.dp) | ||
) { | ||
Text( | ||
text = stringResource(R.string.menu_detail_allergy_title_text), | ||
style = TwosomeHeartTypography.title1B16, | ||
color = Black | ||
) | ||
|
||
Spacer(Modifier.height(13.dp)) | ||
|
||
Text( | ||
text = allergyText, | ||
style = TwosomeHeartTypography.body1R14, | ||
color = Gray80 | ||
) | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
...rc/main/java/org/techtown/twosomeheart/presentation/detail/component/MenuDetailContent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package org.techtown.twosomeheart.presentation.detail.component | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxHeight | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.unit.dp | ||
import org.techtown.twosomeheart.core.component.MenuStatusChip | ||
import org.techtown.twosomeheart.core.util.PriceFormatter.formatPrice | ||
import org.techtown.twosomeheart.ui.theme.Black | ||
import org.techtown.twosomeheart.ui.theme.TwosomeHeartTypography | ||
import org.techtown.twosomeheart.ui.theme.White | ||
|
||
@Composable | ||
fun MenuDetailContent( | ||
isBestMenu: Boolean, | ||
menuName: String, | ||
menuDescription: String, | ||
menuPrice: Int, | ||
modifier: Modifier = Modifier | ||
){ | ||
Column( | ||
modifier = modifier | ||
.padding(start = 16.dp) | ||
.background(White) | ||
) { | ||
MenuStatusChip(isBestMenu = isBestMenu) | ||
|
||
Spacer(modifier = Modifier.height(7.dp)) | ||
|
||
Text( | ||
text = menuName, | ||
style = TwosomeHeartTypography.head3B20, | ||
color = Black | ||
) | ||
|
||
Spacer(modifier = Modifier.height(5.dp)) | ||
|
||
Text( | ||
text = menuDescription, | ||
style = TwosomeHeartTypography.body1R14, | ||
color = Black, | ||
modifier = Modifier.padding(start = 6.dp) | ||
) | ||
|
||
Spacer(modifier = Modifier.height(10.dp)) | ||
|
||
MenuDetailPriceText( | ||
price = formatPrice(menuPrice) | ||
) | ||
|
||
Row( | ||
horizontalArrangement = Arrangement.spacedBy(4.5.dp) | ||
) { | ||
// TODO: menu caution 처리 어떻게 할건지 생각 | ||
MenuCautionChip(text = "고카페인") | ||
MenuCautionChip( | ||
text = "우유", | ||
isAllergen = true | ||
) | ||
} | ||
|
||
Spacer(modifier = Modifier.height(22.dp)) | ||
} | ||
} |
Oops, something went wrong.