Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c8df7f0
말풍선과 좋아요 버튼
wjshim2003 Aug 14, 2025
d04b41a
MenuReviewItem UI 만듦
wjshim2003 Aug 14, 2025
e1adaa3
MenuReviewImage(보여주기만 하는 이미지) 추가
wjshim2003 Aug 16, 2025
86d0d74
리뷰 작성 시 이미지 UI 추가
wjshim2003 Aug 16, 2025
666c41a
FlowRow 적용
wjshim2003 Aug 27, 2025
1160b6e
메뉴 통계 컴포넌트
wjshim2003 Aug 27, 2025
d6b0e2d
리뷰 시 키워드 선택 칩 컴포넌트
wjshim2003 Aug 27, 2025
e9030b2
키워드 관련 API 추가
wjshim2003 Sep 20, 2025
c7d41b6
리포지토리에 추가
wjshim2003 Sep 20, 2025
aa88e3d
임시커밋
wjshim2003 Sep 21, 2025
3d1769d
MenuReviewScreen, MenuDetailScreen 추가
wjshim2003 Sep 29, 2025
6bc0494
일단 레이아웃 완성
wjshim2003 Sep 29, 2025
256e95c
임시커밋
wjshim2003 Oct 4, 2025
1054abc
URL과 AUTH 수정
wjshim2003 Oct 4, 2025
913bbe0
대소문자 수정
wjshim2003 Oct 4, 2025
cb0dbda
Merge branch 'wjshim2003/api-spring' into wjshim2003/review-keyword
wjshim2003 Oct 5, 2025
04e6aba
MenuDetailFragment 화면 대체
wjshim2003 Oct 5, 2025
47d6f5a
기본값 추가
wjshim2003 Oct 5, 2025
09742b2
MenuRatingsInfo, MenuKeywordStats 수정
wjshim2003 Oct 5, 2025
8356d34
일단 돌아감
wjshim2003 Nov 8, 2025
c9e355b
날짜 표기 수정
wjshim2003 Nov 8, 2025
d142363
LeaveReviewScreen 디자인 수정
wjshim2003 Nov 8, 2025
f87dbc4
키워드 선택되도록 수정
wjshim2003 Nov 8, 2025
2daafa6
1차 QA 반영
wjshim2003 Jan 2, 2026
b0b68fa
와 리뷰가 보인다
wjshim2003 Jan 2, 2026
1705d6b
MenuDetailScreen UI 수정
wjshim2003 Jan 2, 2026
faebd7e
리뷰 전송 가능
wjshim2003 Jan 2, 2026
49e4559
ReviewPhotoFragment 리팩토링
wjshim2003 Jan 2, 2026
7ce9209
이미지리뷰 필터링 수정
wjshim2003 Jan 2, 2026
6f163ee
Merge branch 'develop' into wjshim2003/review-keyword
kevin990222 Jan 11, 2026
9d5cb6e
lint
kevin990222 Jan 11, 2026
987f26b
api 수정
kevin990222 Jan 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ fun PostImages(
)
}
AddPostImageIcon(
modifier = Modifier.clickable { onAddImage() }
modifier = Modifier.size(106.dp)
.clickable { onAddImage() }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.wafflestudio.siksha2.compose.ui.menudetail

import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.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.unit.sp
import androidx.compose.ui.zIndex
import coil.compose.rememberAsyncImagePainter
import com.wafflestudio.siksha2.ui.SikshaTheme

@Composable
fun MenuDetailImagesShowMore(
imageUri: Uri,
modifier: Modifier = Modifier,
onShowMore: () -> Unit = {},
showMoreCount: Int = 0
) {
Box(
modifier = modifier
) {
Image(
modifier = Modifier
.fillMaxSize()
.align(Alignment.Center),
painter = rememberAsyncImagePainter(imageUri),
contentDescription = null,
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.fillMaxSize()
.background(color = Color(0x40000000))
.zIndex(1f)
.align(Alignment.Center)
.clickable { onShowMore() }
) {
Column(
modifier = Modifier.align(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "+",
fontSize = 12.sp,
color = SikshaTheme.colors.TextDim
)
Text(
text = showMoreCount.toString() + "건 더 보기",
fontSize = 12.sp,
color = SikshaTheme.colors.TextDim
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
package com.wafflestudio.siksha2.compose.ui.menudetail

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.wafflestudio.siksha2.R
import com.wafflestudio.siksha2.compose.ui.reviews.MenuReviewImage
import com.wafflestudio.siksha2.compose.ui.reviews.MenuReviewItem
import com.wafflestudio.siksha2.models.KeywordDist
import com.wafflestudio.siksha2.models.Menu
import com.wafflestudio.siksha2.models.Review
import com.wafflestudio.siksha2.ui.SikshaTheme
import com.wafflestudio.siksha2.ui.menuDetail.MenuDetailViewModel
import kotlin.math.min
import com.wafflestudio.siksha2.ui.KeywordFoodComposition
import com.wafflestudio.siksha2.ui.KeywordPrice
import com.wafflestudio.siksha2.ui.KeywordTaste
import timber.log.Timber

@Composable
fun MenuDetailRoute(
menuId: Long,
vm: MenuDetailViewModel,
onToggleLikeMenu: () -> Unit,
onClickLeaveReview: () -> Unit,
onNavigateToReviewPhoto: (Long) -> Unit,
modifier: Modifier = Modifier
) {
LaunchedEffect(menuId) {
vm.setMenuId(menuId)
}

val menu by vm.menu.observeAsState()
val reviews = vm.reviewPagingData.collectAsLazyPagingItems()
val imageReviews = vm.getReviewsWithImages(menuId).collectAsLazyPagingItems()
val keywordDist by vm.keywordDistribution.observeAsState()

MenuDetailScreen(
menu = menu,
reviews = reviews,
imageReviews = imageReviews,
keywordDist = keywordDist ?: KeywordDist.Empty,
onToggleLikeMenu = onToggleLikeMenu,
onClickLeaveReview = onClickLeaveReview,
onNavigateToReviewPhoto = { menu?.let { onNavigateToReviewPhoto(it.id) } },
modifier = modifier
)
}

@Composable
fun MenuDetailScreen(
menu: Menu?,
reviews: LazyPagingItems<Review>,
imageReviews: LazyPagingItems<Review>,
keywordDist: KeywordDist,
onToggleLikeMenu: () -> Unit,
onClickLeaveReview: () -> Unit,
onNavigateToReviewPhoto: () -> Unit,
modifier: Modifier = Modifier
) {
LaunchedEffect(reviews) {
snapshotFlow { reviews.itemCount }
.collect { count ->
Timber.d("Item count changed: $count")
}
}

LazyColumn(
modifier = modifier
) {
item {
Column(
modifier = Modifier.fillMaxWidth()
.background(SikshaTheme.colors.BackgroundPrimary)
.padding(top = 20.dp, bottom = 18.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(if (menu?.isLiked ?: false) R.drawable.ic_heart_filled else R.drawable.ic_heart_outline),
contentDescription = null,
modifier = Modifier.size(30.dp)
.clickable { onToggleLikeMenu() }
)
Text(
text = "찜 ${(menu?.likeCount ?: 0)}개",
fontSize = 13.sp,
fontWeight = FontWeight.Bold,
color = SikshaTheme.colors.Black
)
}
}
item {
Spacer(Modifier.height(10.dp).fillMaxWidth().background(SikshaTheme.colors.Gray100))
}
item {
Column(
modifier = Modifier.fillMaxWidth()
.background(SikshaTheme.colors.BackgroundPrimary)
.padding(vertical = 32.dp, horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally

) {
Row(
modifier = Modifier.fillMaxWidth()
.height(IntrinsicSize.Min),
verticalAlignment = Alignment.CenterVertically
) {
MenuRatingsInfo(
rating = menu?.score?.toFloat() ?: 0f,
reviewCount = menu?.reviewCount?.toInt() ?: 0
)
Spacer(Modifier.width(12.dp))
MenuKeywordStats(
keywordDist.keywords,
keywordDist.keywordCounts,
keywordIcons = listOf(
{ KeywordTaste() },
{ KeywordPrice() },
{ KeywordFoodComposition() }
),
modifier = Modifier.weight(1f)
.fillMaxHeight()
)
}
Spacer(Modifier.height(18.dp))
Text(
text = stringResource(R.string.leave_review_title),
fontSize = 14.sp,
fontWeight = FontWeight.ExtraBold,
color = SikshaTheme.colors.TextButton,
modifier = Modifier.background(
color = SikshaTheme.colors.Orange500,
shape = RoundedCornerShape(50.dp)
)
.clickable { onClickLeaveReview() }
.padding(vertical = 10.dp, horizontal = 20.dp)
)
}
}
item {
Spacer(Modifier.height(10.dp).fillMaxWidth().background(SikshaTheme.colors.Gray100))
}
item {
BriefImageReviews(
imageReviews = imageReviews,
onNavigateToReviewPhoto = onNavigateToReviewPhoto
)
}
item {
Box(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 15.5.dp, vertical = 14.dp)
) {
Text(
text = stringResource(R.string.menu_detail_review_gather),
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.align(Alignment.CenterStart),
color = SikshaTheme.colors.Black
)
}
}
items(
reviews.itemCount,
key = reviews.itemKey { it.id }
) { idx ->
val review = reviews[idx]
if (review != null) {
MenuReviewItem(
userName = review.userId.toString(),
menuRating = review.score.toFloat(),
timeText = review.createdAt,
reviewText = review.comment,
isLiked = review.isLiked,
likeCount = review.likeCount,
keywords = review.keywordReviews.filter { it != "" },
imageUris = review.etc.images?.map { it.toUri() } ?: listOf(),
modifier = Modifier.padding(horizontal = 14.dp)
)
}
}
}
}

@Composable
fun BriefImageReviews(
imageReviews: LazyPagingItems<Review>,
onNavigateToReviewPhoto: () -> Unit,
modifier: Modifier = Modifier
) {
val imagePreviewScrollState = rememberScrollState()
Box(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 15.5.dp, vertical = 14.dp)
) {
Text(
text = stringResource(R.string.menu_detail_photo_review_gather),
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.align(Alignment.CenterStart),
color = SikshaTheme.colors.Black
)
Image(
painter = painterResource(R.drawable.ic_back_arrow),
contentDescription = stringResource(R.string.menu_detail_photo_review_gather),
colorFilter = ColorFilter.tint(SikshaTheme.colors.Gray600),
modifier = Modifier
.align(Alignment.CenterEnd)
.rotate(180f)
.clickable {
onNavigateToReviewPhoto()
}
)
}
Row(
modifier = Modifier
.padding(horizontal = 16.dp)
.horizontalScroll(imagePreviewScrollState),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
for (i: Int in 1..min(imageReviews.itemCount, 3)) {
if (imageReviews.itemSnapshotList.items[i - 1].etc.images?.isNotEmpty() == true) {
val it = imageReviews.itemSnapshotList.items[i - 1].etc.images?.get(0)
if (i == 3) {
MenuDetailImagesShowMore(
imageUri = it!!.toUri(),
modifier = Modifier
.size(120.dp)
.clip(RoundedCornerShape(10.dp)),
showMoreCount = imageReviews.itemCount - 2,
onShowMore = {
onNavigateToReviewPhoto()
}
)
} else {
MenuReviewImage(
imageUri = it!!.toUri(),
modifier = Modifier
.size(120.dp)
.clip(RoundedCornerShape(10.dp))
)
}
} else {
Box(
modifier = Modifier
.size(120.dp)
.clip(RoundedCornerShape(10.dp))
.background(SikshaTheme.colors.Gray100)
)
}
}
}
}
Loading
Loading