diff --git a/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt index 1a32c7bd..ebda7048 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/bottomsheet/MenuBottomSheet.kt @@ -27,30 +27,32 @@ fun MenuBottomSheet( CustomBottomSheet( onDismiss = onDismiss, ) { - items.forEachIndexed { index, item -> - if (index > 0) { - Spacer(modifier = Modifier.height(8.dp)) - HorizontalDivider(modifier = Modifier.height(1.dp), color = colors.Grey03) - Spacer(modifier = Modifier.height(8.dp)) - } + Column(modifier = Modifier.padding(20.dp)) { + items.forEachIndexed { index, item -> + if (index > 0) { + Spacer(modifier = Modifier.height(8.dp)) + HorizontalDivider(modifier = Modifier.height(1.dp), color = colors.Grey03) + Spacer(modifier = Modifier.height(8.dp)) + } - Column( - modifier = Modifier - .height(50.dp) - .padding(horizontal = 12.dp, vertical = 8.dp), - verticalArrangement = Arrangement.Center - ) { - Text( - text = item.text, - style = typography.menu_m500_s16_h24, - color = item.color, + Column( modifier = Modifier - .fillMaxWidth() - .clickable { - item.onClick() - onDismiss() - } - ) + .height(50.dp) + .padding(horizontal = 12.dp, vertical = 8.dp), + verticalArrangement = Arrangement.Center + ) { + Text( + text = item.text, + style = typography.menu_m500_s16_h24, + color = item.color, + modifier = Modifier + .fillMaxWidth() + .clickable { + item.onClick() + onDismiss() + } + ) + } } } } diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt index a631aebf..21f0ba2a 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/OptionChipButton.kt @@ -32,10 +32,11 @@ fun OptionChipButton( modifier: Modifier = Modifier, text: String, isFilled: Boolean = false, - isSelected: Boolean? = null, // 추가 + isSelected: Boolean? = null, enabled: Boolean = true, textStyle: TextStyle = typography.info_r400_s12, onClick: () -> Unit, + onDisabledClick: () -> Unit = { } ) { var isClicked by remember { mutableStateOf(false) } val checked = isSelected ?: isClicked @@ -69,9 +70,13 @@ fun OptionChipButton( color = borderColor, shape = RoundedCornerShape(20.dp) ) - .clickable(enabled = enabled) { - if (isSelected == null) isClicked = !isClicked - onClick() + .clickable { + if (enabled) { + if (isSelected == null) isClicked = !isClicked + onClick() + } else { + onDisabledClick.invoke() + } } .height(30.dp) .padding(horizontal = 12.dp), diff --git a/app/src/main/java/com/texthip/thip/ui/common/cards/CardCommentGroup.kt b/app/src/main/java/com/texthip/thip/ui/common/cards/CardCommentGroup.kt index caf7a8e1..73036875 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/cards/CardCommentGroup.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/cards/CardCommentGroup.kt @@ -17,7 +17,8 @@ import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun CardCommentGroup( - data: GroupRoomChatData + data: GroupRoomChatData, + onMenuClick: () -> Unit ) { Column( modifier = Modifier @@ -27,7 +28,8 @@ fun CardCommentGroup( ProfileBarWithDate( profileImage = data.profileImage, nickname = data.nickname, - dateText = data.date + dateText = data.date, + onMenuClick = onMenuClick ) Spacer(Modifier.height(8.dp)) Text( @@ -47,7 +49,9 @@ private fun CardCommentGroupPreview() { profileImage = null, nickname = "user.01", date = "11시간 전", - content = "이것은 그룹 채팅의 댓글입니다. 이곳에 댓글 내용을 작성할 수 있습니다. 여러 줄로 작성해도 됩니다." - ) + content = "이것은 그룹 채팅의 댓글입니다. 이곳에 댓글 내용을 작성할 수 있습니다. 여러 줄로 작성해도 됩니다.", + isMine = false + ), + onMenuClick = {} ) } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/common/cards/CardVote.kt b/app/src/main/java/com/texthip/thip/ui/common/cards/CardVote.kt index 12df707f..7795853e 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/cards/CardVote.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/cards/CardVote.kt @@ -1,11 +1,13 @@ package com.texthip.thip.ui.common.cards import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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.size import androidx.compose.foundation.pager.HorizontalPager @@ -14,9 +16,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateMapOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.toMutableStateList +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -30,17 +30,12 @@ import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun CardVote( - voteData: List + voteData: List, + onVoteClick: (VoteData) -> Unit = {} ) { val pageCount = voteData.size val pagerState = rememberPagerState(pageCount = { pageCount }) - // 각 페이지별 상태 기억: 선택 인덱스, 선택 여부 포함한 voteItems - val selectedIndexes = remember { mutableStateMapOf() } - val voteItemStates = remember { - voteData.map { it.voteItems.toMutableStateList() }.toMutableStateList() - } - Column( modifier = Modifier .fillMaxWidth() @@ -55,52 +50,65 @@ fun CardVote( modifier = Modifier.padding(horizontal = 12.dp) ) - HorizontalPager( - state = pagerState, - modifier = Modifier.fillMaxWidth() - ) { page -> - val voteItems = voteItemStates[page] - val selectedIndex = selectedIndexes[page] - val hasVoted = voteItems.any { it.isVoted } - - Column( - modifier = Modifier.padding(horizontal = 12.dp), - verticalArrangement = Arrangement.spacedBy(10.dp) + if (voteData.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + .padding(vertical = 60.dp), + contentAlignment = Alignment.Center ) { Text( - text = voteData[page].description, + text = stringResource(R.string.no_vote), style = typography.info_m500_s12, - color = colors.White, - ) - - GroupVoteButton( - voteItems = voteItems, - selectedIndex = selectedIndex, - hasVoted = hasVoted, - onOptionSelected = { index -> - selectedIndexes[page] = if (selectedIndex == index) null else index - - voteItemStates[page] = voteItems.mapIndexed { i, item -> - item.copy(isVoted = i == index && selectedIndex != index) - }.toMutableStateList() - } + color = colors.Grey ) } - } + } else { + HorizontalPager( + state = pagerState, + modifier = Modifier.fillMaxWidth() + ) { page -> + val vote = voteData[page] + val voteItems = vote.voteItems.take(3) // 최대 3개만 - Row( - Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.Center - ) { - repeat(pageCount) { iteration -> - val color = if (pagerState.currentPage == iteration) colors.White else colors.Grey02 - Box( + Column( modifier = Modifier + .height(176.dp) .padding(horizontal = 12.dp) - .background(color, CircleShape) - .size(4.dp) - ) + .clickable { onVoteClick(vote) }, // 전체 클릭 시 이동 + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Text( + text = vote.description, + style = typography.info_m500_s12, + color = colors.White, + ) + + GroupVoteButton( + voteItems = voteItems, + selectedIndex = null, // 표시용이므로 선택 없음 + hasVoted = false, // 투표 상태 없음 + onOptionSelected = { onVoteClick(vote) } // 어떤 항목을 눌러도 이동 + ) + } + } + + Row( + Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + repeat(pageCount) { iteration -> + val color = + if (pagerState.currentPage == iteration) colors.White else colors.Grey02 + Box( + modifier = Modifier + .padding(horizontal = 12.dp) + .background(color, CircleShape) + .size(4.dp) + ) + } } } } @@ -109,5 +117,13 @@ fun CardVote( @Preview @Composable private fun CardVotePreview() { - CardVote(voteData = mockVoteData) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + CardVote(voteData = mockVoteData) + CardVote(voteData = emptyList()) + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/common/forms/CommentTextField.kt b/app/src/main/java/com/texthip/thip/ui/common/forms/CommentTextField.kt index 851afb5a..220dc637 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/forms/CommentTextField.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/forms/CommentTextField.kt @@ -6,11 +6,13 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize 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.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.Icon @@ -93,28 +95,31 @@ fun CommentTextField( modifier = Modifier .fillMaxWidth() .padding(horizontal = 20.dp, vertical = 10.dp) - .height(36.dp) .background(colors.DarkGrey, shape = RoundedCornerShape(20.dp)), verticalAlignment = Alignment.CenterVertically ) { BasicTextField( value = input, onValueChange = onInputChange, - singleLine = true, + maxLines = 5, textStyle = typography.menu_r400_s14_h24.copy(color = colors.White), modifier = Modifier .weight(1f) - .padding(start = 12.dp), + .padding(start = 12.dp, top = 4.dp, bottom = 4.dp, end = 12.dp) + .defaultMinSize(minHeight = 36.dp) + .wrapContentHeight(), // 높이 자동 확장 cursorBrush = SolidColor(colors.NeonGreen), decorationBox = { innerTextField -> - if (input.isEmpty()) { - Text( - text = hint, - color = colors.Grey02, - style = typography.menu_r400_s14_h24 - ) + Box { + if (input.isEmpty()) { + Text( + text = hint, + color = colors.Grey02, + style = typography.menu_r400_s14_h24 + ) + } + innerTextField() } - innerTextField() } ) @@ -122,8 +127,9 @@ fun CommentTextField( val isEnabled = input.isNotBlank() Box( modifier = Modifier - .padding(end = 4.dp) + .padding(end = 8.dp, bottom = 8.dp) .size(width = 42.dp, height = 28.dp) + .align(Alignment.Bottom) .background( color = if (isEnabled) colors.Purple else colors.Grey02, shape = RoundedCornerShape(20.dp) diff --git a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt index 857948fa..37229e1d 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/modal/DialogPopup.kt @@ -2,15 +2,11 @@ package com.texthip.thip.ui.common.modal import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column 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.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -31,54 +27,45 @@ fun DialogPopup( onConfirm: () -> Unit, onCancel: () -> Unit ) { - Box( + Column( modifier = modifier - .width(320.dp) - .height(182.dp) + .size(width = 320.dp, height = 182.dp) .background( color = colors.DarkGrey, shape = RoundedCornerShape(12.dp) ) - .padding(20.dp) + .padding(20.dp), + verticalArrangement = Arrangement.SpaceBetween, ) { - Column( - verticalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxSize() - ) { - Column (){ - Text( - text = title, - color = colors.White, - style = typography.smalltitle_m500_s18_h24, - ) - Spacer(modifier = Modifier.height(12.dp)) - Text( - text = description, - color = colors.White, - style = typography.copy_r400_s14, - ) - } - - Row( - horizontalArrangement = Arrangement.spacedBy(20.dp), - modifier = Modifier.fillMaxWidth() - ) { - ActionMediumButton( - text = stringResource(R.string.no), - contentColor = colors.White, - backgroundColor = colors.Grey02, - modifier = Modifier.weight(1f), - onClick = onCancel, - ) - ActionMediumButton( - text = stringResource(R.string.yes), - contentColor = colors.White, - backgroundColor = colors.Purple, - modifier = Modifier.weight(1f), - onClick = onConfirm, - ) - } + Text( + text = title, + color = colors.White, + style = typography.smalltitle_m500_s18_h24, + ) + Text( + text = description, + color = colors.White, + style = typography.copy_r400_s14, + ) + Row( + horizontalArrangement = Arrangement.spacedBy(20.dp), + modifier = Modifier.fillMaxWidth() + ) { + ActionMediumButton( + text = stringResource(R.string.no), + contentColor = colors.White, + backgroundColor = colors.Grey02, + modifier = Modifier.weight(1f), + onClick = onCancel, + ) + ActionMediumButton( + text = stringResource(R.string.yes), + contentColor = colors.White, + backgroundColor = colors.Purple, + modifier = Modifier.weight(1f), + onClick = onConfirm, + ) } } } diff --git a/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt b/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt index 9c762d0e..f5c60751 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/topappbar/DefaultTopAppBar.kt @@ -1,6 +1,5 @@ package com.texthip.thip.ui.common.topappbar -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -22,6 +21,7 @@ import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun DefaultTopAppBar( + modifier: Modifier = Modifier, title: String = stringResource(R.string.page_name), isTitleVisible: Boolean = true, isRightIconVisible: Boolean = false, @@ -29,7 +29,7 @@ fun DefaultTopAppBar( onRightClick: () -> Unit = {}, ) { Box( - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(horizontal = 20.dp, vertical = 16.dp) ) { diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt index b67589c5..4bd21837 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt @@ -1,5 +1,10 @@ package com.texthip.thip.ui.group.note.component +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +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.material3.Text import androidx.compose.runtime.Composable @@ -7,6 +12,7 @@ import androidx.compose.runtime.getValue 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.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -31,41 +37,75 @@ fun CommentBottomSheet( var replyingTo by remember { mutableStateOf(null) } CustomBottomSheet(onDismiss = onDismiss) { - Text( - text = stringResource(R.string.comments), - style = typography.title_b700_s20_h24, - color = colors.White, - modifier = Modifier.padding(start = 20.dp, top = 20.dp, end = 20.dp) - ) + Column( + modifier = Modifier + .fillMaxWidth() + .height(600.dp) + ) { + Column( + modifier = Modifier + .weight(1f) + .fillMaxHeight(0.8f) + ) { + Text( + text = stringResource(R.string.comments), + style = typography.title_b700_s20_h24, + color = colors.White, + modifier = Modifier.padding(start = 20.dp, top = 20.dp, end = 20.dp) + ) - CommentList( - commentList = commentResponse, - onSendReply = { replyText, commentId, replyTo -> - onSendReply(replyText, commentId, replyTo) - inputText = "" - }, - onReplyClick = { replyItem -> - replyingTo = replyItem + if (commentResponse.isEmpty()) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 210.dp), // TODO: 유동적으로 수정 가능할수도 + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically) + ) { + Text( + text = stringResource(R.string.no_comments_yet), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White + ) + Text( + text = stringResource(R.string.no_comment_subtext), + style = typography.copy_r400_s14, + color = colors.Grey, + modifier = Modifier.padding(top = 4.dp) + ) + } + } else { + CommentList( + commentList = commentResponse, + onSendReply = { replyText, commentId, replyTo -> + onSendReply(replyText, commentId, replyTo) + inputText = "" + }, + onReplyClick = { replyItem -> + replyingTo = replyItem + } + ) + } } - ) - // 입력창 - CommentTextField( - hint = stringResource(R.string.reply_to), - input = inputText, - onInputChange = { inputText = it }, - onSendClick = { - onSendReply( - inputText, - replyingTo?.replyId, - replyingTo?.nickName - ) - inputText = "" - replyingTo = null - }, - replyTo = replyingTo?.nickName, - onCancelReply = { replyingTo = null } - ) + CommentTextField( + modifier = Modifier.fillMaxWidth(), + hint = stringResource(R.string.reply_to), + input = inputText, + onInputChange = { inputText = it }, + onSendClick = { + onSendReply( + inputText, + replyingTo?.replyId, + replyingTo?.nickName + ) + inputText = "" + replyingTo = null + }, + replyTo = replyingTo?.nickName, + onCancelReply = { replyingTo = null } + ) + } } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt index f7af2ee9..273b8530 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt @@ -22,6 +22,7 @@ import com.texthip.thip.ui.theme.ThipTheme @Composable fun CommentList( + modifier: Modifier = Modifier, commentList: List, onSendReply: (String, Int?, String?) -> Unit, onReplyClick: (ReplyItem) -> Unit @@ -29,7 +30,7 @@ fun CommentList( val scrollState = rememberScrollState() Box( - modifier = Modifier + modifier = modifier .fillMaxWidth() .height(482.dp) ) { diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt index b1452684..33e37221 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/FilterHeaderSection.kt @@ -31,6 +31,7 @@ fun FilterHeaderSection( onFirstPageChange: (String) -> Unit, onLastPageChange: (String) -> Unit, onTotalToggle: () -> Unit, + onDisabledClick: () -> Unit = { } ) { var isPageInputVisible by rememberSaveable { mutableStateOf(false) } val isPageFiltered = firstPage.isNotBlank() || lastPage.isNotBlank() @@ -63,7 +64,8 @@ fun FilterHeaderSection( isSelected = isTotalSelected, enabled = totalEnabled, textStyle = typography.menu_r400_s14_h24, - onClick = onTotalToggle + onClick = onTotalToggle, + onDisabledClick = onDisabledClick ) } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt index a93dccfe..85e2b952 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/TextCommentCard.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.group.note.component +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -11,6 +12,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.blur +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -26,19 +28,27 @@ import com.texthip.thip.ui.theme.ThipTheme.typography fun TextCommentCard( modifier: Modifier = Modifier, data: GroupNoteRecord, - onCommentClick: () -> Unit = {} + onCommentClick: () -> Unit = {}, + onLongPress: () -> Unit = {}, + onPinClick: () -> Unit = {} ) { var isLiked by remember { mutableStateOf(data.isLiked) } + val isLocked = data.isLocked + val isWriter = data.isWriter Column( modifier = modifier - .blur(if (data.isLocked) 5.dp else 0.dp) - .padding(vertical = 16.dp, horizontal = 20.dp), + .blur(if (isLocked) 5.dp else 0.dp) + .pointerInput(Unit) { + if (!isLocked) { + detectTapGestures(onLongPress = { onLongPress() }) + } + } + .padding(start = 20.dp, end = 20.dp, top = 32.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { ProfileBar( modifier = Modifier.padding(0.dp), -// profileImage = data.profileImageUrl, profileImage = painterResource(R.drawable.character_literature), topText = data.nickName, bottomText = data.page.toString() + stringResource(R.string.page), @@ -57,10 +67,16 @@ fun TextCommentCard( isLiked = isLiked, likeCount = data.likeCount, commentCount = data.commentCount, + isPinVisible = isWriter, onLikeClick = { - isLiked = !isLiked + if (!isLocked) isLiked = !isLiked }, - onCommentClick = { onCommentClick() }, + onCommentClick = { + if (!isLocked) onCommentClick() + }, + onPinClick = { + if (!isLocked) onPinClick() + } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/VoteCommentCard.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/VoteCommentCard.kt index b227b296..60def258 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/VoteCommentCard.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/VoteCommentCard.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.group.note.component +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -11,6 +12,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.blur +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -28,21 +30,30 @@ import com.texthip.thip.ui.theme.ThipTheme.typography fun VoteCommentCard( modifier: Modifier = Modifier, data: GroupNoteVote, - onCommentClick: () -> Unit = {} + onCommentClick: () -> Unit = {}, + onLongPress: () -> Unit = {}, + onPinClick: () -> Unit = {} ) { var isLiked by remember { mutableStateOf(data.isLiked) } var selected by remember { mutableStateOf(null) } var voteItems by remember { mutableStateOf(data.voteItems) } val hasVoted = voteItems.any { it.isVoted } + val isLocked = data.isLocked + val isWriter = data.isWriter + Column( modifier = modifier - .blur(if (data.isLocked) 5.dp else 0.dp) - .padding(vertical = 16.dp, horizontal = 20.dp), + .blur(if (isLocked) 5.dp else 0.dp) + .pointerInput(Unit) { + if (!isLocked) { + detectTapGestures(onLongPress = { onLongPress() }) + } + } + .padding(start = 20.dp, end = 20.dp, top = 32.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { ProfileBar( -// profileImage = data.profileImageUrl, profileImage = painterResource(R.drawable.character_literature), topText = data.nickName, bottomText = data.page.toString() + stringResource(R.string.page), @@ -51,9 +62,7 @@ fun VoteCommentCard( hoursAgo = data.postDate ) - Column( - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text( text = data.content, style = typography.feedcopy_r400_s14_h20, @@ -65,13 +74,15 @@ fun VoteCommentCard( selectedIndex = selected, hasVoted = hasVoted, onOptionSelected = { - if (selected == it) { - selected = null - voteItems = voteItems.map { it.copy(isVoted = false) } - } else { - selected = it - voteItems = voteItems.mapIndexed { index, item -> - item.copy(isVoted = index == it) + if (!isLocked) { + if (selected == it) { + selected = null + voteItems = voteItems.map { it.copy(isVoted = false) } + } else { + selected = it + voteItems = voteItems.mapIndexed { index, item -> + item.copy(isVoted = index == it) + } } } } @@ -82,10 +93,16 @@ fun VoteCommentCard( isLiked = isLiked, likeCount = data.likeCount, commentCount = data.commentCount, + isPinVisible = isWriter, onLikeClick = { - isLiked = !isLiked + if (!isLocked) isLiked = !isLiked + }, + onCommentClick = { + if (!isLocked) onCommentClick() }, - onCommentClick = { onCommentClick() }, + onPinClick = { + if (!isLocked) onPinClick() + } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt index 8a5cfbb1..3e24b774 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt @@ -1,5 +1,10 @@ package com.texthip.thip.ui.group.note.screen +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -8,10 +13,11 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -25,11 +31,15 @@ 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 androidx.compose.ui.zIndex import com.texthip.thip.R +import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet import com.texthip.thip.ui.common.buttons.ExpandableFloatingButton import com.texthip.thip.ui.common.buttons.FabMenuItem import com.texthip.thip.ui.common.buttons.FilterButton import com.texthip.thip.ui.common.header.HeaderMenuBarTab +import com.texthip.thip.ui.common.modal.DialogPopup +import com.texthip.thip.ui.common.modal.ToastWithDate import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar import com.texthip.thip.ui.group.note.component.CommentBottomSheet import com.texthip.thip.ui.group.note.component.FilterHeaderSection @@ -39,9 +49,11 @@ import com.texthip.thip.ui.group.note.mock.GroupNoteRecord import com.texthip.thip.ui.group.note.mock.GroupNoteVote import com.texthip.thip.ui.group.note.mock.mockComment import com.texthip.thip.ui.group.note.mock.mockGroupNoteItems +import com.texthip.thip.ui.group.room.mock.MenuBottomSheetItem import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography +import kotlinx.coroutines.delay @Composable fun GroupNoteScreen() { @@ -57,17 +69,32 @@ fun GroupNoteScreen() { val filters = listOf("최신순", "인기순", "댓글 많은 순") val filteredItems = when (selectedTabIndex) { - 0 -> mockGroupNoteItems.filter { !it.isWriter } // 다른 사람 기록 - 1 -> mockGroupNoteItems.filter { it.isWriter } // 내 기록 + 0 -> mockGroupNoteItems // 전체 기록 + 1 -> mockGroupNoteItems.filter { it.isWriter } // 내 기록만 else -> emptyList() } - var isBottomSheetVisible by rememberSaveable { mutableStateOf(false) } + var isCommentBottomSheetVisible by rememberSaveable { mutableStateOf(false) } var selectedNoteRecord by remember { mutableStateOf(null) } var selectedNoteVote by remember { mutableStateOf(null) } + var selectedItemForMenu by remember { mutableStateOf(null) } + + var isMenuBottomSheetVisible by rememberSaveable { mutableStateOf(false) } + + var isPinDialogVisible by remember { mutableStateOf(false) } + + var showToast by remember { mutableStateOf(false) } + + // 토스트 3초 + LaunchedEffect(showToast) { + if (showToast) { + delay(6000) // 2초 등장, 4초 노출 + showToast = false // exit 에니메이션 2초 + } + } Box( - if (isBottomSheetVisible) { + if (isCommentBottomSheetVisible || isMenuBottomSheetVisible || isPinDialogVisible) { Modifier .fillMaxSize() .blur(5.dp) @@ -75,119 +102,193 @@ fun GroupNoteScreen() { Modifier.fillMaxSize() } ) { - Column(modifier = Modifier.fillMaxSize()) { - DefaultTopAppBar( - title = stringResource(R.string.record_book), - onLeftClick = {} - ) - - HeaderMenuBarTab( - titles = tabs, - selectedTabIndex = selectedTabIndex, - onTabSelected = { selectedTabIndex = it }, - modifier = Modifier - .fillMaxWidth() - .padding(top = 20.dp, bottom = 56.dp) - ) - - // 피드 리스트 영역 - LazyColumn( + Box { + AnimatedVisibility( + visible = showToast, + enter = slideInVertically( + initialOffsetY = { -it }, // 위에서 아래로 + animationSpec = tween(durationMillis = 2000) + ), + exit = slideOutVertically( + targetOffsetY = { -it }, // 위로 사라짐 + animationSpec = tween(durationMillis = 2000) + ), modifier = Modifier - .fillMaxSize() - .padding(bottom = 82.dp), + .align(Alignment.TopCenter) + .padding(horizontal = 20.dp, vertical = 16.dp) + .zIndex(3f) ) { - item { - Row( - modifier = Modifier.padding( - start = 20.dp, - end = 20.dp, - bottom = 16.dp, - top = 20.dp - ), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp), + ToastWithDate( + message = stringResource(R.string.condition_of_view_general_review), + ) + } + + Column(modifier = Modifier.fillMaxSize()) { + DefaultTopAppBar( + title = stringResource(R.string.record_book), + onLeftClick = {} + ) + + HeaderMenuBarTab( + titles = tabs, + selectedTabIndex = selectedTabIndex, + onTabSelected = { selectedTabIndex = it }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 20.dp) + ) + + if (filteredItems.isEmpty()) { + // 기록이 없을 때 중앙에 메시지 + Column( + modifier = Modifier + .fillMaxSize() + .padding(bottom = 102.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy( + 8.dp, + alignment = Alignment.CenterVertically + ) ) { - Icon( - painterResource(R.drawable.ic_information), - contentDescription = null, - tint = colors.White, + Text( + text = stringResource(R.string.no_records_yet), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White ) Text( - text = stringResource(R.string.group_note_info), - modifier = Modifier.padding(start = 8.dp), - color = colors.Grey01, - style = typography.info_r400_s12 + text = when (selectedTabIndex) { + 0 -> stringResource(R.string.no_group_record_subtext) + 1 -> stringResource(R.string.no_my_record_subtext) + else -> "" + }, + style = typography.copy_r400_s14, + color = colors.Grey ) } - } - items(filteredItems) { item -> - when (item) { - is GroupNoteRecord -> TextCommentCard( - data = item, - onCommentClick = { - selectedNoteRecord = item - isBottomSheetVisible = true + } else { + // 피드 리스트 영역 + LazyColumn(modifier = Modifier.fillMaxSize()) { + if (selectedTabIndex == 0) { + item { + Row( + modifier = Modifier.padding(top = 76.dp, start = 20.dp, end = 20.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Icon( + painterResource(R.drawable.ic_information), + contentDescription = null, + tint = colors.White, + ) + Text( + text = stringResource(R.string.group_note_info), + modifier = Modifier.padding(start = 8.dp), + color = colors.Grey01, + style = typography.info_r400_s12 + ) + } } - ) + } + itemsIndexed(filteredItems) { index, item -> + val isLast = index == filteredItems.lastIndex - is GroupNoteVote -> VoteCommentCard( - data = item, - onCommentClick = { - selectedNoteVote = item - isBottomSheetVisible = true + val itemModifier = if (isLast) { + Modifier.padding(bottom = 20.dp) + } else { + Modifier } - ) + + when (item) { + is GroupNoteRecord -> TextCommentCard( + data = item, + modifier = itemModifier, + onCommentClick = { + selectedNoteRecord = item + isCommentBottomSheetVisible = true + }, + onLongPress = { + selectedItemForMenu = item + isMenuBottomSheetVisible = true + }, + onPinClick = { + isPinDialogVisible = true + } + ) + + is GroupNoteVote -> VoteCommentCard( + data = item, + modifier = itemModifier, + onCommentClick = { + selectedNoteVote = item + isCommentBottomSheetVisible = true + }, + onLongPress = { + selectedItemForMenu = item + isMenuBottomSheetVisible = true + }, + onPinClick = { + isPinDialogVisible = true + } + ) + } + } } } } - } - Box( - modifier = Modifier - .fillMaxWidth() - .padding(top = 136.dp), - ) { - FilterButton( - modifier = Modifier - .align(Alignment.CenterEnd) - .padding(end = 20.dp), - selectedOption = selectedFilter, - options = filters, - onOptionSelected = { selectedFilter = it } - ) + if (selectedTabIndex == 0) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 119.dp) + .background(color = colors.Black) + .padding(top = 20.dp) + ) { + FilterButton( + modifier = Modifier + .align(Alignment.CenterEnd) + .padding(end = 20.dp), + selectedOption = selectedFilter, + options = filters, + onOptionSelected = { selectedFilter = it } + ) - FilterHeaderSection( - firstPage = firstPage, - lastPage = lastPage, - isTotalSelected = isTotalSelected, - totalEnabled = totalEnabled, - onFirstPageChange = { firstPage = it }, - onLastPageChange = { lastPage = it }, - onTotalToggle = { isTotalSelected = !isTotalSelected }, - ) - } + FilterHeaderSection( + firstPage = firstPage, + lastPage = lastPage, + isTotalSelected = isTotalSelected, + totalEnabled = totalEnabled, + onFirstPageChange = { firstPage = it }, + onLastPageChange = { lastPage = it }, + onTotalToggle = { isTotalSelected = !isTotalSelected }, + onDisabledClick = { showToast = true } + ) + } + } - ExpandableFloatingButton( - menuItems = listOf( - FabMenuItem( - icon = painterResource(R.drawable.ic_write), - text = stringResource(R.string.write_record), - onClick = { } - ), - FabMenuItem( - icon = painterResource(R.drawable.ic_vote), - text = stringResource(R.string.create_vote), - onClick = { } + ExpandableFloatingButton( + menuItems = listOf( + FabMenuItem( + icon = painterResource(R.drawable.ic_write), + text = stringResource(R.string.write_record), + onClick = { } + ), + FabMenuItem( + icon = painterResource(R.drawable.ic_vote), + text = stringResource(R.string.create_vote), + onClick = { } + ) ) ) - ) + } } - if (isBottomSheetVisible && (selectedNoteRecord != null || selectedNoteVote != null)) { + if (isCommentBottomSheetVisible && (selectedNoteRecord != null || selectedNoteVote != null)) { CommentBottomSheet( commentResponse = listOf(mockComment, mockComment, mockComment), +// commentResponse = emptyList(), onDismiss = { - isBottomSheetVisible = false + isCommentBottomSheetVisible = false selectedNoteRecord = null selectedNoteVote = null }, @@ -196,6 +297,68 @@ fun GroupNoteScreen() { } ) } + + if (isMenuBottomSheetVisible && selectedItemForMenu != null) { + val isWriter = when (val item = selectedItemForMenu) { + is GroupNoteRecord -> item.isWriter + is GroupNoteVote -> item.isWriter + else -> false + } + + val menuItems = if (isWriter) { + listOf( + MenuBottomSheetItem( + text = stringResource(R.string.delete), + color = colors.Red, + onClick = { + // TODO: 삭제 처리 + isMenuBottomSheetVisible = false + selectedItemForMenu = null + } + ) + ) + } else { + listOf( + MenuBottomSheetItem( + text = stringResource(R.string.report), + color = colors.Red, + onClick = { + // TODO: 신고 처리 + isMenuBottomSheetVisible = false + selectedItemForMenu = null + } + ) + ) + } + + MenuBottomSheet( + items = menuItems, + onDismiss = { + isMenuBottomSheetVisible = false + selectedItemForMenu = null + } + ) + } + + if (isPinDialogVisible) { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + DialogPopup( + title = stringResource(R.string.pin_modal_title), + description = stringResource(R.string.pin_modal_content), + onConfirm = { + // 핀하기 로직 + isPinDialogVisible = false + }, + onCancel = { + isPinDialogVisible = false + } + ) + } + } } @Preview diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/mock/GroupRoomChatData.kt b/app/src/main/java/com/texthip/thip/ui/group/room/mock/GroupRoomChatData.kt index 039859bb..84b22e23 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/mock/GroupRoomChatData.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/mock/GroupRoomChatData.kt @@ -6,5 +6,52 @@ data class GroupRoomChatData( val profileImage: Painter?, val nickname: String, val date: String, - val content: String + val content: String, + val isMine: Boolean ) + +val mockMessages = listOf( + GroupRoomChatData( + null, + "user.01", + "2024.04.29", + "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다.", + isMine = true + ), + GroupRoomChatData( + null, + "user.01", + "2024.04.28", + "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다.", + isMine = true + ), + GroupRoomChatData(null, "user.01", "2024.04.30", "공백 포함 글자 입력입니다.", isMine = false), + GroupRoomChatData( + null, + "user.01", + "2024.04.30", + "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다.", + isMine = true + ), + GroupRoomChatData( + null, + "user.01", + "2024.04.30", + "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다.", + isMine = false + ), + GroupRoomChatData( + null, + "user.01", + "2024.04.27", + "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다.", + isMine = true + ), + GroupRoomChatData( + null, + "user.01", + "2024.04.27", + "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다.", + isMine = true + ), +).sortedByDescending { it.date } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/mock/VoteData.kt b/app/src/main/java/com/texthip/thip/ui/group/room/mock/VoteData.kt index 638cd8f7..acff9b1a 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/mock/VoteData.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/mock/VoteData.kt @@ -13,9 +13,9 @@ val mockVoteData = listOf( voteItems = listOf( VoteItem(1, "김땡땡", 50, false), VoteItem(2, "이땡땡", 10, false), - VoteItem(3, "박땡땡", 20, false), - VoteItem(4, "최땡땡", 15, false), - VoteItem(5, "정땡땡", 5, false) +// VoteItem(3, "박땡땡", 20, false), +// VoteItem(4, "최땡땡", 15, false), +// VoteItem(5, "정땡땡", 5, false) ) ), VoteData( diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt index 1b86cb09..693aa802 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt @@ -1,12 +1,14 @@ package com.texthip.thip.ui.group.room.screen import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -14,97 +16,168 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.texthip.thip.R +import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet import com.texthip.thip.ui.common.cards.CardCommentGroup import com.texthip.thip.ui.common.forms.CommentTextField import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar import com.texthip.thip.ui.common.view.CountingBar import com.texthip.thip.ui.group.room.mock.GroupRoomChatData +import com.texthip.thip.ui.group.room.mock.MenuBottomSheetItem +import com.texthip.thip.ui.group.room.mock.mockMessages import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors +import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun GroupRoomChatScreen() { - val messages = listOf( - GroupRoomChatData( - null, - "user.01", - "2024.04.29", - "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다." - ), - GroupRoomChatData( - null, - "user.01", - "2024.04.28", - "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다." - ), - GroupRoomChatData(null, "user.01", "2024.04.30", "공백 포함 글자 입력입니다."), - GroupRoomChatData(null, "user.01", "2024.04.30", "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다."), - GroupRoomChatData( - null, - "user.01", - "2024.04.30", - "공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다. 공백 포함 글자 입력입니다." - ), - ).sortedByDescending { it.date } +// val mockMessages = emptyList() var input by remember { mutableStateOf("") } var replyTo by remember { mutableStateOf(null) } - Column( - modifier = Modifier - .fillMaxSize() - ) { - DefaultTopAppBar( - title = stringResource(R.string.group_room_chat), - onLeftClick = {}, - ) + var isBottomSheetVisible by remember { mutableStateOf(false) } + var selectedMessage by remember { mutableStateOf(null) } - LazyColumn( - reverseLayout = true, - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(20.dp) + Box( + if (isBottomSheetVisible) { + Modifier + .fillMaxSize() + .blur(5.dp) + } else { + Modifier.fillMaxSize() + } + ) { + Column( + modifier = Modifier + .fillMaxSize() ) { - itemsIndexed(messages) { index, message -> - val isNewDate = when { - index == 0 -> true - messages[index - 1].date != message.date -> true - else -> false - } + DefaultTopAppBar( + title = stringResource(R.string.group_room_chat), + onLeftClick = {}, + ) - val isBottomItem = index == 0 - - Column( - verticalArrangement = Arrangement.spacedBy(20.dp), - modifier = if (isBottomItem) Modifier.padding(bottom = 20.dp) else Modifier + if (mockMessages.isEmpty()) { + Box( + modifier = Modifier + .weight(1f) + .fillMaxSize(), + contentAlignment = Alignment.Center ) { - if (isNewDate) { - HorizontalDivider( - color = colors.DarkGrey02, - thickness = 10.dp + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = stringResource(R.string.group_room_no_chat_title), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White ) - CountingBar( - modifier = Modifier.align(Alignment.CenterHorizontally), - content = message.date + Text( + text = stringResource(R.string.group_room_no_chat_content), + style = typography.copy_r400_s14, + color = colors.Grey ) } - CardCommentGroup(data = message) + } + } else { + LazyColumn( + reverseLayout = true, + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.Bottom) + ) { + itemsIndexed(mockMessages) { index, message -> + val isNewDate = when { + index == mockMessages.lastIndex -> true + mockMessages[index + 1].date != message.date -> true + else -> false + } + val isBottomItem = index == 0 + + Column( + verticalArrangement = Arrangement.spacedBy(20.dp), + modifier = if (isBottomItem) Modifier.padding(bottom = 20.dp) else Modifier + ) { + if (isNewDate) { + HorizontalDivider( + color = colors.DarkGrey02, + thickness = 10.dp + ) + CountingBar( + modifier = Modifier.align(Alignment.CenterHorizontally), + content = message.date + ) + } + + CardCommentGroup( + data = message, + onMenuClick = { + selectedMessage = message + isBottomSheetVisible = true + } + ) + } + } } } + + CommentTextField( + input = input, + hint = stringResource(R.string.group_room_chat_hint), + onInputChange = { input = it }, + onSendClick = { + input = "" + replyTo = null + }, + replyTo = replyTo, + onCancelReply = { replyTo = null } + ) + } + } + + if (isBottomSheetVisible && selectedMessage != null) { + val menuItems = if (selectedMessage!!.isMine) { + listOf( + MenuBottomSheetItem( + text = stringResource(R.string.modify), + color = colors.White, + onClick = { + // TODO: 수정 처리 + isBottomSheetVisible = false + } + ), + MenuBottomSheetItem( + text = stringResource(R.string.delete), + color = colors.Red, + onClick = { + // TODO: 삭제 처리 + isBottomSheetVisible = false + } + ) + ) + } else { + listOf( + MenuBottomSheetItem( + text = stringResource(R.string.report), + color = colors.Red, + onClick = { + // TODO: 신고 처리 + isBottomSheetVisible = false + } + ) + ) } - CommentTextField( - input = input, - hint = stringResource(R.string.group_room_chat_hint), - onInputChange = { input = it }, - onSendClick = { - input = "" - replyTo = null - }, - replyTo = replyTo, - onCancelReply = { replyTo = null } + + MenuBottomSheet( + items = menuItems, + onDismiss = { + isBottomSheetVisible = false + selectedMessage = null + } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt similarity index 98% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt rename to app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt index 426b1f96..a0cf9125 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt @@ -1,4 +1,4 @@ -package com.texthip.thip.ui.group.myroom.screen +package com.texthip.thip.ui.group.room.screen import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -51,7 +51,7 @@ import com.texthip.thip.ui.theme.ThipTheme.typography import kotlinx.coroutines.delay @Composable -fun GroupRoomScreen( +fun GroupRoomRecruitScreen( detail: GroupRoomData, buttonType: GroupBottomButtonType, onRecommendationClick: (GroupCardItemRoomData) -> Unit = {}, @@ -432,7 +432,7 @@ fun GroupRoomScreen( @Preview(name = "참여 버튼 상태") @Composable -fun GroupRoomScreenPreview_Join() { +fun GroupRoomRecruitScreenPreviewJoin() { ThipTheme { val recommendations = listOf( GroupCardItemRoomData( @@ -499,7 +499,7 @@ fun GroupRoomScreenPreview_Join() { recommendations = recommendations ) - GroupRoomScreen( + GroupRoomRecruitScreen( detail = detailJoin, buttonType = GroupBottomButtonType.JOIN, onRecommendationClick = {}, @@ -513,7 +513,7 @@ fun GroupRoomScreenPreview_Join() { @Preview(name = "참여 취소 버튼 상태") @Composable -fun GroupRoomScreenPreview_Cancel() { +fun GroupRoomRecruitScreenPreviewCancel() { ThipTheme { val recommendations = listOf( GroupCardItemRoomData( @@ -564,7 +564,7 @@ fun GroupRoomScreenPreview_Cancel() { recommendations = recommendations ) - GroupRoomScreen( + GroupRoomRecruitScreen( detail = detailCancel, buttonType = GroupBottomButtonType.CANCEL, onRecommendationClick = {}, @@ -578,7 +578,7 @@ fun GroupRoomScreenPreview_Cancel() { @Preview(name = "모집 마감 버튼 상태") @Composable -fun GroupRoomScreenPreview_Close() { +fun GroupRoomRecruitScreenClose() { ThipTheme { val recommendations = listOf( GroupCardItemRoomData( @@ -629,7 +629,7 @@ fun GroupRoomScreenPreview_Close() { recommendations = recommendations ) - GroupRoomScreen( + GroupRoomRecruitScreen( detail = detailClose, buttonType = GroupBottomButtonType.CLOSE, onRecommendationClick = {}, diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt index 92521fdd..9b42ef22 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.getValue 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.draw.blur import androidx.compose.ui.layout.ContentScale @@ -25,6 +26,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.texthip.thip.R import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet +import com.texthip.thip.ui.common.modal.DialogPopup import com.texthip.thip.ui.common.topappbar.GradationTopAppBar import com.texthip.thip.ui.group.room.component.GroupRoomBody import com.texthip.thip.ui.group.room.component.GroupRoomHeader @@ -41,9 +43,13 @@ fun GroupRoomScreen() { val scrollState = rememberScrollState() var isBottomSheetVisible by remember { mutableStateOf(false) } + var isLeaveDialogVisible by remember { mutableStateOf(false) } + var isDeleteDialogVisible by remember { mutableStateOf(false) } + + val isOwner = false // 서버에서 받아오기 Box( - if (isBottomSheetVisible) { + if (isBottomSheetVisible || isLeaveDialogVisible || isDeleteDialogVisible) { Modifier .fillMaxSize() .blur(5.dp) @@ -98,29 +104,76 @@ fun GroupRoomScreen() { } if (isBottomSheetVisible) { - MenuBottomSheet( - items = listOf( + val menuItems = if (isOwner) { + // 방 주인일 때 + listOf( + MenuBottomSheetItem( + text = stringResource(R.string.delete_room), + color = colors.Red, + onClick = { isDeleteDialogVisible = true } + ) + ) + } else { + // 방 참여자일 때 + listOf( MenuBottomSheetItem( text = stringResource(R.string.leave_room), color = colors.White, - onClick = { } + onClick = { isLeaveDialogVisible = true } ), MenuBottomSheetItem( text = stringResource(R.string.report_room), color = colors.Red, - onClick = { } - ), - MenuBottomSheetItem( - text = stringResource(R.string.delete_room), - color = colors.Red, - onClick = { } + onClick = { /* 신고 로직 */ } ) - ), - onDismiss = { - isBottomSheetVisible = false - } + ) + } + + MenuBottomSheet( + items = menuItems, + onDismiss = { isBottomSheetVisible = false } ) } + + if (isLeaveDialogVisible) { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + DialogPopup( + title = stringResource(R.string.leave_room_modal_title), + description = stringResource(R.string.leave_room_modal_content), + onConfirm = { + // 방 나가기 로직 + isLeaveDialogVisible = false + }, + onCancel = { + isLeaveDialogVisible = false + } + ) + } + } + + if (isDeleteDialogVisible) { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + DialogPopup( + title = stringResource(R.string.delete_room_modal_title), + description = stringResource(R.string.delete_room_modal_content), + onConfirm = { + // 방 삭제하기 로직 + isDeleteDialogVisible = false + }, + onCancel = { + isDeleteDialogVisible = false + } + ) + } + } } @Preview diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomSecretScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomUnlockScreen.kt similarity index 96% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomSecretScreen.kt rename to app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomUnlockScreen.kt index 6f8c8d81..f6cf68d8 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupRoomSecretScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomUnlockScreen.kt @@ -1,4 +1,4 @@ -package com.texthip.thip.ui.group.myroom.screen +package com.texthip.thip.ui.group.room.screen import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -32,9 +32,10 @@ import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography +import kotlinx.coroutines.delay @Composable -fun GroupRoomSecretScreen( +fun GroupRoomUnlockScreen( onBackClick: () -> Unit = {}, onPasswordComplete: (String) -> Unit = {}, correctPassword: String = "1234" // 실제로는 외부에서 받아올 값 @@ -52,7 +53,7 @@ fun GroupRoomSecretScreen( onPasswordComplete(fullPassword) } else { showError = true - kotlinx.coroutines.delay(1000) + delay(1000) password = arrayOf("", "", "", "") showError = false focusRequesters[0].requestFocus() @@ -153,9 +154,9 @@ fun GroupRoomSecretScreen( @Preview(showBackground = true) @Composable -fun GroupRoomSecretScreenPreview() { +fun GroupRoomUnlockScreenPreview() { ThipTheme { - GroupRoomSecretScreen( + GroupRoomUnlockScreen( onBackClick = {}, onPasswordComplete = { password -> }, diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupEmptyResult.kt b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupEmptyResult.kt similarity index 94% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupEmptyResult.kt rename to app/src/main/java/com/texthip/thip/ui/group/search/component/GroupEmptyResult.kt index 2b45da10..09ab53ba 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupEmptyResult.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupEmptyResult.kt @@ -1,6 +1,5 @@ -package com.texthip.thip.ui.group.myroom.component +package com.texthip.thip.ui.group.search.component -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupFilteredSearchResult.kt b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt similarity index 98% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupFilteredSearchResult.kt rename to app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt index 9f9c5226..554e9482 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupFilteredSearchResult.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt @@ -1,7 +1,6 @@ -package com.texthip.thip.ui.group.myroom.component +package com.texthip.thip.ui.group.search.component import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupLiveSearchResult.kt b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupLiveSearchResult.kt similarity index 98% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupLiveSearchResult.kt rename to app/src/main/java/com/texthip/thip/ui/group/search/component/GroupLiveSearchResult.kt index 53a682f1..817626d9 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupLiveSearchResult.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupLiveSearchResult.kt @@ -1,4 +1,4 @@ -package com.texthip.thip.ui.group.myroom.component +package com.texthip.thip.ui.group.search.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupRecentSearch.kt b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt similarity index 93% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupRecentSearch.kt rename to app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt index 158fe4bb..11d44197 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupRecentSearch.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt @@ -2,6 +2,9 @@ package com.texthip.thip.ui.group.myroom.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.FlowRow @@ -62,20 +65,20 @@ fun GroupRecentSearch( } @Preview(showBackground = true) -@Composable +@Composable fun GroupRecentSearchPreview() { ThipTheme { Box( modifier = Modifier .padding(16.dp) ) { - var searches by remember { + var searches by remember { mutableStateOf(listOf("해리포터", "소설", "추리소설", "로맨스", "SF", "판타지")) } - + GroupRecentSearch( recentSearches = searches, - onSearchClick = { keyword -> + onSearchClick = { keyword -> // 검색 동작 시뮬레이션 }, onRemove = { keyword -> diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupSearchScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt similarity index 97% rename from app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupSearchScreen.kt rename to app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt index 7e7d88dd..00691975 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupSearchScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt @@ -1,4 +1,4 @@ -package com.texthip.thip.ui.group.myroom.screen +package com.texthip.thip.ui.group.search.screen import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -28,11 +28,11 @@ import com.texthip.thip.R import com.texthip.thip.ui.common.buttons.FilterButton import com.texthip.thip.ui.common.forms.SearchBookTextField import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar -import com.texthip.thip.ui.group.myroom.component.GroupEmptyResult -import com.texthip.thip.ui.group.myroom.component.GroupFilteredSearchResult -import com.texthip.thip.ui.group.myroom.component.GroupLiveSearchResult import com.texthip.thip.ui.group.myroom.component.GroupRecentSearch import com.texthip.thip.ui.group.myroom.mock.GroupCardItemRoomData +import com.texthip.thip.ui.group.search.component.GroupEmptyResult +import com.texthip.thip.ui.group.search.component.GroupLiveSearchResult +import com.texthip.thip.ui.group.search.component.GroupFilteredSearchResult import com.texthip.thip.ui.theme.ThipTheme @Composable diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e2099e1..fb8797e9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,7 +50,8 @@ 도서 소개 %1$s 저 기록장 - 🔥모임방의 뜨거운 감자 + 모임방의 뜨거운 감자 + 모임방에 생성된 투표가 없어요 님의 구독자 @@ -148,8 +149,17 @@ 님에게 답글 작성 메이트들과 간단한 인사를 나눠보세요! 방 나가기 + 모임방을 나가시겠어요? + 방을 나가시게 되면\n독서메이트들과의 추억이 사라집니다. 방 신고하기 방 삭제하기 + 모임방을 삭제하시겠어요? + 방을 삭제하게 되면\n독서메이트들과의 추억이 사라집니다. + 삭제하기 + 수정하기 + 신고하기 + 아직 대화가 없어요 + 첫번째 한마디를 남겨보세요! ~ @@ -187,6 +197,14 @@ 투표 내용을 20자 이내로 입력하세요. 항목을 20자 이내로 입력 항목 추가 + 독서 진행도 80% 이상부터 총평을 볼 수 있어요. + 아직 기록이 없어요. + 우리 모임의 첫번째 기록을 남겨보세요 + 나의 첫번째 기록을 남겨보세요 + 아직 댓글이 없어요. + 첫번째 댓글을 남겨보세요 + 이 기록을 피드에 핀할까요? + 핀하면 내 피드에 글을 옮길 수 있어요. 피드