diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 79bce4b1..82f9c596 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ diff --git a/app/src/main/java/com/texthip/thip/data/di/ServiceModule.kt b/app/src/main/java/com/texthip/thip/data/di/ServiceModule.kt index eb8dd0fd..84a7a240 100644 --- a/app/src/main/java/com/texthip/thip/data/di/ServiceModule.kt +++ b/app/src/main/java/com/texthip/thip/data/di/ServiceModule.kt @@ -1,6 +1,7 @@ package com.texthip.thip.data.di import com.texthip.thip.data.service.BookService +import com.texthip.thip.data.service.CommentsService import com.texthip.thip.data.service.GroupService import com.texthip.thip.data.service.RoomsService import com.texthip.thip.data.service.UserService @@ -36,4 +37,9 @@ object ServiceModule { fun provideUserService(retrofit: Retrofit): UserService { return retrofit.create(UserService::class.java) } + + @Provides + @Singleton + fun providesCommentsService(retrofit: Retrofit): CommentsService = + retrofit.create(CommentsService::class.java) } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/comments/request/CommentsCreateRequest.kt b/app/src/main/java/com/texthip/thip/data/model/comments/request/CommentsCreateRequest.kt new file mode 100644 index 00000000..da8d8438 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/comments/request/CommentsCreateRequest.kt @@ -0,0 +1,11 @@ +package com.texthip.thip.data.model.comments.request + +import kotlinx.serialization.Serializable + +@Serializable +data class CommentsCreateRequest( + val content: String, + val isReplyRequest: Boolean, + val parentId: Int? = null, + val postType: String, +) diff --git a/app/src/main/java/com/texthip/thip/data/model/comments/request/CommentsLikesRequest.kt b/app/src/main/java/com/texthip/thip/data/model/comments/request/CommentsLikesRequest.kt new file mode 100644 index 00000000..ec0867b6 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/comments/request/CommentsLikesRequest.kt @@ -0,0 +1,8 @@ +package com.texthip.thip.data.model.comments.request + +import kotlinx.serialization.Serializable + +@Serializable +data class CommentsLikesRequest( + val type: Boolean +) diff --git a/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsCreateResponse.kt b/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsCreateResponse.kt new file mode 100644 index 00000000..362a6df6 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsCreateResponse.kt @@ -0,0 +1,8 @@ +package com.texthip.thip.data.model.comments.response + +import kotlinx.serialization.Serializable + +@Serializable +data class CommentsCreateResponse( + val commentId: Int, +) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsLikesResponse.kt b/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsLikesResponse.kt new file mode 100644 index 00000000..4f420f90 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsLikesResponse.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.comments.response + +import kotlinx.serialization.Serializable + +@Serializable +data class CommentsLikesResponse( + val commentId: Int, + val isLiked: Boolean, +) diff --git a/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsResponse.kt b/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsResponse.kt new file mode 100644 index 00000000..0db6c4d7 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/comments/response/CommentsResponse.kt @@ -0,0 +1,41 @@ +package com.texthip.thip.data.model.comments.response + +import kotlinx.serialization.Serializable + +@Serializable +data class CommentsResponse( + val commentList: List, + val nextCursor: String?, + val isLast: Boolean, +) + +@Serializable +data class CommentList( + val commentId: Int, + val creatorId: Int, + val creatorProfileImageUrl: String, + val creatorNickname: String, + val aliasName: String, + val aliasColor: String, + val postDate: String, + val content: String, + val likeCount: Int, + val isDeleted: Boolean, + val isLike: Boolean, + val replyList: List, +) + +@Serializable +data class ReplyList( + val commentId: Int, + val parentCommentCreatorNickname: String, + val creatorId: Int, + val creatorProfileImageUrl: String, + val creatorNickname: String, + val aliasName: String, + val aliasColor: String, + val postDate: String, + val content: String, + val likeCount: Int, + val isLike: Boolean, +) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPlayingResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPlayingResponse.kt index 978c97fc..f05496c6 100644 --- a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPlayingResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPlayingResponse.kt @@ -12,6 +12,7 @@ data class RoomsPlayingResponse( val progressStartDate: String, val progressEndDate: String, val category: String, + val categoryColor: String, val roomDescription: String, val memberCount: Int, val recruitCount: Int, diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsUsersResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsUsersResponse.kt index b1c3e442..92d6a0d1 100644 --- a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsUsersResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsUsersResponse.kt @@ -12,6 +12,7 @@ data class UserList( val userId: Int, val nickname: String, val imageUrl: String, + val aliasColor: String, val aliasName: String, val followerCount: Int, ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/repository/CommentsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/CommentsRepository.kt new file mode 100644 index 00000000..90e618ee --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/repository/CommentsRepository.kt @@ -0,0 +1,53 @@ +package com.texthip.thip.data.repository + +import com.texthip.thip.data.model.base.handleBaseResponse +import com.texthip.thip.data.model.comments.request.CommentsCreateRequest +import com.texthip.thip.data.model.comments.request.CommentsLikesRequest +import com.texthip.thip.data.service.CommentsService +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CommentsRepository @Inject constructor( + private val commentsService: CommentsService, +) { + suspend fun getComments( + postId: Long, + postType: String = "RECORD", + cursor: String? = null, + ) = runCatching { + commentsService.getComments( + postId = postId, + postType = postType, + cursor = cursor + ).handleBaseResponse().getOrThrow() + } + + suspend fun likeComment( + commentId: Long, + type: Boolean + ) = runCatching { + commentsService.likeComment( + commentId = commentId, + response = CommentsLikesRequest(type) + ).handleBaseResponse().getOrThrow() + } + + suspend fun createComment( + postId: Long, + content: String, + isReplyRequest: Boolean, + parentId: Int? = null, + postType: String = "RECORD", + ) = runCatching { + commentsService.createComment( + postId = postId, + request = CommentsCreateRequest( + content = content, + isReplyRequest = isReplyRequest, + parentId = parentId, + postType = postType + ) + ).handleBaseResponse().getOrThrow() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/service/CommentsService.kt b/app/src/main/java/com/texthip/thip/data/service/CommentsService.kt new file mode 100644 index 00000000..4d50efdc --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/service/CommentsService.kt @@ -0,0 +1,34 @@ +package com.texthip.thip.data.service + +import com.texthip.thip.data.model.base.BaseResponse +import com.texthip.thip.data.model.comments.request.CommentsCreateRequest +import com.texthip.thip.data.model.comments.request.CommentsLikesRequest +import com.texthip.thip.data.model.comments.response.CommentsCreateResponse +import com.texthip.thip.data.model.comments.response.CommentsLikesResponse +import com.texthip.thip.data.model.comments.response.CommentsResponse +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.Query + +interface CommentsService { + @GET("comments/{postId}") + suspend fun getComments( + @Path("postId") postId: Long, + @Query("postType") postType: String = "RECORD", + @Query("cursor") cursor: String? = null, + ): BaseResponse + + @POST("comments/{commentId}/likes") + suspend fun likeComment( + @Path("commentId") commentId: Long, + @Body response: CommentsLikesRequest + ): BaseResponse + + @POST("comments/{postId}") + suspend fun createComment( + @Path("postId") postId: Long, + @Body request: CommentsCreateRequest + ): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBar.kt b/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBar.kt index e0d8efd6..ee49681d 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBar.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBar.kt @@ -32,7 +32,7 @@ fun ProfileBar( profileImage: String, topText: String, bottomText: String, - bottomTextColor: Color = colors.NeonGreen, // todo: 서버에서 색 보내주는걸로 받기? + bottomTextColor: Color = colors.NeonGreen, showSubscriberInfo: Boolean, subscriberCount: Int = 0, hoursAgo: String = "", diff --git a/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBarFeed.kt b/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBarFeed.kt index 03690bd2..bc5f842f 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBarFeed.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/header/ProfileBarFeed.kt @@ -1,6 +1,5 @@ package com.texthip.thip.ui.common.header -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -18,16 +17,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage 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 ProfileBarFeed( - profileImage: Painter?, + profileImage: String?, nickname: String, genreName: String, genreColor: Color = colors.NeonGreen, @@ -41,8 +40,8 @@ fun ProfileBarFeed( ) { Row { if (profileImage != null) { - Image( - painter = profileImage, + AsyncImage( + model = profileImage, contentDescription = "프로필 이미지", modifier = Modifier .size(24.dp) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt index 77b256c8..4df9988c 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt @@ -48,12 +48,8 @@ import com.texthip.thip.ui.common.forms.CommentTextField import com.texthip.thip.ui.common.header.ProfileBar import com.texthip.thip.ui.common.modal.DialogPopup import com.texthip.thip.ui.common.topappbar.DefaultTopAppBar -import com.texthip.thip.ui.group.note.component.CommentItem -import com.texthip.thip.ui.group.note.component.ReplyItem import com.texthip.thip.ui.feed.component.ImageViewerModal import com.texthip.thip.ui.feed.mock.FeedItemType -import com.texthip.thip.ui.group.note.component.CommentItem -import com.texthip.thip.ui.group.note.component.ReplyItem import com.texthip.thip.ui.group.note.mock.mockCommentList import com.texthip.thip.ui.group.room.mock.MenuBottomSheetItem import com.texthip.thip.ui.mypage.mock.FeedItem @@ -241,10 +237,10 @@ fun FeedCommentScreen( ) } ) { - CommentItem( - data = commentItem, - onReplyClick = { replyTo.value = it } - ) +// CommentItem( +// data = commentItem, +// onReplyClick = { replyTo.value = it } +// ) } if (selectedComment == commentItem) { Row( @@ -304,7 +300,7 @@ fun FeedCommentScreen( ) } ) { - ReplyItem(data = reply, onReplyClick = { replyTo.value = it }) +// ReplyItem(data = reply, onReplyClick = { replyTo.value = it }) } if (selectedReply == reply) { 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 4bd21837..84038c4c 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,13 +1,21 @@ package com.texthip.thip.ui.group.note.component import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight +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.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -18,29 +26,33 @@ 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.data.model.comments.response.CommentList import com.texthip.thip.ui.common.bottomsheet.CustomBottomSheet import com.texthip.thip.ui.common.forms.CommentTextField -import com.texthip.thip.ui.group.note.mock.CommentItem -import com.texthip.thip.ui.group.note.mock.ReplyItem -import com.texthip.thip.ui.group.note.mock.mockComment +import com.texthip.thip.ui.group.note.viewmodel.CommentsEvent +import com.texthip.thip.ui.group.note.viewmodel.CommentsUiState import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography +import com.texthip.thip.utils.rooms.advancedImePadding @Composable fun CommentBottomSheet( - commentResponse: List, + uiState: CommentsUiState, + onEvent: (CommentsEvent) -> Unit, onDismiss: () -> Unit, - onSendReply: (String, Int?, String?) -> Unit + onSendReply: (text: String, parentCommentId: Int?, replyToNickname: String?) -> Unit ) { var inputText by remember { mutableStateOf("") } - var replyingTo by remember { mutableStateOf(null) } + var replyingToCommentId by remember { mutableStateOf(null) } + var replyingToNickname by remember { mutableStateOf(null) } CustomBottomSheet(onDismiss = onDismiss) { Column( modifier = Modifier .fillMaxWidth() .height(600.dp) + .advancedImePadding() ) { Column( modifier = Modifier @@ -54,37 +66,30 @@ fun CommentBottomSheet( modifier = Modifier.padding(start = 20.dp, top = 20.dp, end = 20.dp) ) - 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) + Box(modifier = Modifier.weight(1f)) { + if (uiState.isLoading) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator() + } + } else if (uiState.comments.isEmpty()) { + EmptyCommentView() + } else { + CommentLazyList( + commentList = uiState.comments, + isLoadingMore = uiState.isLoadingMore, + isLastPage = uiState.isLast, + onLoadMore = { onEvent(CommentsEvent.LoadMoreComments) }, + onReplyClick = { commentId, nickname -> + replyingToCommentId = commentId + replyingToNickname = nickname + }, + onEvent = onEvent ) } - } else { - CommentList( - commentList = commentResponse, - onSendReply = { replyText, commentId, replyTo -> - onSendReply(replyText, commentId, replyTo) - inputText = "" - }, - onReplyClick = { replyItem -> - replyingTo = replyItem - } - ) } } @@ -96,19 +101,100 @@ fun CommentBottomSheet( onSendClick = { onSendReply( inputText, - replyingTo?.replyId, - replyingTo?.nickName + replyingToCommentId, + replyingToNickname ) inputText = "" - replyingTo = null + replyingToCommentId = null + replyingToNickname = null }, - replyTo = replyingTo?.nickName, - onCancelReply = { replyingTo = null } + replyTo = replyingToNickname, + onCancelReply = { + replyingToCommentId = null + replyingToNickname = null + } ) } } } +@Composable +private fun CommentLazyList( + commentList: List, + isLoadingMore: Boolean, + isLastPage: Boolean, + onLoadMore: () -> Unit, + onReplyClick: (commentId: Int, nickname: String) -> Unit, + onEvent: (CommentsEvent) -> Unit +) { + val lazyListState = rememberLazyListState() + + val isScrolledToEnd by remember { + derivedStateOf { + val layoutInfo = lazyListState.layoutInfo + if (layoutInfo.totalItemsCount == 0) return@derivedStateOf false + val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 + lastVisibleItemIndex >= layoutInfo.totalItemsCount - 1 + } + } + + LaunchedEffect(isScrolledToEnd) { + if (isScrolledToEnd && !isLoadingMore && !isLastPage) { + onLoadMore() + } + } + + LazyColumn(state = lazyListState) { + items( + items = commentList, + key = { it.commentId } + ) { comment -> + CommentSection( + commentItem = comment, + onReplyClick = onReplyClick, + onEvent = onEvent + ) + } + + if (isLoadingMore) { + item { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator() + } + } + } + } +} + + +@Composable +private fun EmptyCommentView() { + Column( + modifier = Modifier + .fillMaxSize() + .padding(bottom = 60.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + 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) + ) + } +} + @Preview @Composable private fun CommentBottomSheetPreview() { @@ -116,7 +202,28 @@ private fun CommentBottomSheetPreview() { var showSheet by remember { mutableStateOf(true) } if (showSheet) { CommentBottomSheet( - commentResponse = listOf(mockComment, mockComment, mockComment), + uiState = CommentsUiState( + comments = listOf( + CommentList( + commentId = 1, + creatorId = 1, + creatorNickname = "User1", + content = "This is a comment.", + postDate = "2023-10-01", + likeCount = 5, + creatorProfileImageUrl = "https://example.com/image1.jpg", + aliasName = "칭호칭호", + aliasColor = "#A0F8E8", + isDeleted = false, + isLike = false, + replyList = emptyList() + ) + ), + isLoading = false, + isLoadingMore = false, + isLast = false + ), + onEvent = {}, onDismiss = { showSheet = false }, onSendReply = { _, _, _ -> } ) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentItem.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentItem.kt index 8a5de977..af7a5ad5 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentItem.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/CommentItem.kt @@ -8,10 +8,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -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.graphics.Color @@ -20,30 +16,29 @@ 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.data.model.comments.response.CommentList import com.texthip.thip.ui.common.header.ProfileBarFeed -import com.texthip.thip.ui.group.note.mock.CommentItem import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography +import com.texthip.thip.utils.color.hexToColor @Composable fun CommentItem( modifier: Modifier = Modifier, - data: CommentItem, - onReplyClick: (String) -> Unit = { } + data: CommentList, + onReplyClick: (String) -> Unit = { }, + onLikeClick: () -> Unit = {} ) { - var isLiked by remember { mutableStateOf(data.isLiked) } - Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(12.dp) ) { ProfileBarFeed( -// profileImage = data.profileImageUrl, - profileImage = painterResource(R.drawable.character_literature), - nickname = data.nickName, - genreName = data.genreName, - genreColor = colors.SocialScience, + profileImage = data.creatorProfileImageUrl, + nickname = data.creatorNickname, + genreName = data.aliasName, + genreColor = hexToColor(data.aliasColor), date = data.postDate ) @@ -61,7 +56,7 @@ fun CommentItem( style = typography.feedcopy_r400_s14_h20, ) Text( - modifier = Modifier.clickable(onClick = { onReplyClick(data.nickName) }), + modifier = Modifier.clickable(onClick = { onReplyClick(data.creatorNickname) }), text = stringResource(R.string.write_reply), style = typography.menu_sb600_s12, color = colors.Grey02, @@ -69,12 +64,12 @@ fun CommentItem( } Column( - modifier = Modifier.clickable(onClick = { isLiked = !isLiked }), + modifier = Modifier.clickable(onClick = onLikeClick), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(2.dp), ) { Icon( - painter = painterResource(if (isLiked) R.drawable.ic_heart_center_filled else R.drawable.ic_heart_center), + painter = painterResource(if (data.isLike) R.drawable.ic_heart_center_filled else R.drawable.ic_heart_center), contentDescription = null, tint = Color.Unspecified ) @@ -100,47 +95,53 @@ private fun CommentItemPreview() { verticalArrangement = Arrangement.spacedBy(12.dp) ) { CommentItem( - data = CommentItem( + data = CommentList( commentId = 1, - userId = 1, - nickName = "user.01", - genreName = "칭호칭호", - profileImageUrl = "https://example.com/profile.jpg", - content = "입력하세요. 댓글 내용을 입력하세요오. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. ", - postDate = "2025.01.12", - isWriter = false, - isLiked = true, + creatorId = 1, + creatorNickname = "User1", + creatorProfileImageUrl = "https://example.com/image1.jpg", + aliasName= "칭호칭호", + aliasColor = "#FF5733", + content = "This is a comment.", + postDate = "2023-10-01T12:00:00Z", + isLike = false, likeCount = 10, + isDeleted = false, + replyList = emptyList() ) ) CommentItem( - data = CommentItem( + data = CommentList( commentId = 1, - userId = 1, - nickName = "user.01", - genreName = "칭호칭호", - profileImageUrl = "https://example.com/profile.jpg", - content = "입력하세요. 댓글 내용을 입력하세요오. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요.", - postDate = "12시간 전", - isWriter = false, - isLiked = true, - likeCount = 10 + creatorId = 1, + creatorNickname = "User1", + creatorProfileImageUrl = "https://example.com/image1.jpg", + aliasName= "칭호칭호", + aliasColor = "#FF5733", + content = "This is a comment.", + postDate = "2023-10-01T12:00:00Z", + isLike = false, + likeCount = 10, + isDeleted = false, + replyList = emptyList() ) ) CommentItem( - data = CommentItem( + data = CommentList( commentId = 1, - userId = 1, - nickName = "user.01", - genreName = "칭호칭호", - profileImageUrl = "https://example.com/profile.jpg", - content = "입력하세요.", - postDate = "12시간 전", - isWriter = false, - isLiked = true, - likeCount = 10 + creatorId = 1, + creatorNickname = "User1", + creatorProfileImageUrl = "https://example.com/image1.jpg", + aliasName= "칭호칭호", + aliasColor = "#FF5733", + content = "This is a comment.", + postDate = "2023-10-01T12:00:00Z", + isLike = false, + likeCount = 10, + isDeleted = false, + replyList = emptyList() ) ) } 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 273b8530..fcc2ea4d 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 @@ -4,58 +4,22 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.texthip.thip.ui.common.modal.drawVerticalScrollbar -import com.texthip.thip.ui.group.note.mock.CommentItem -import com.texthip.thip.ui.group.note.mock.ReplyItem -import com.texthip.thip.ui.group.note.mock.mockComment +import com.texthip.thip.data.model.comments.response.CommentList +import com.texthip.thip.ui.group.note.viewmodel.CommentsEvent import com.texthip.thip.ui.theme.ThipTheme -@Composable -fun CommentList( - modifier: Modifier = Modifier, - commentList: List, - onSendReply: (String, Int?, String?) -> Unit, - onReplyClick: (ReplyItem) -> Unit -) { - val scrollState = rememberScrollState() - - Box( - modifier = modifier - .fillMaxWidth() - .height(482.dp) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .verticalScroll(scrollState) - .drawVerticalScrollbar(scrollState), - ) { - commentList.forEach { commentItem -> - CommentSection( - commentItem = commentItem, - onSendReply = onSendReply, - onReplyClick = onReplyClick - ) - } - } - } -} - @Composable fun CommentSection( - commentItem: CommentItem, - onSendReply: (String, Int?, String?) -> Unit, - onReplyClick: (ReplyItem) -> Unit + commentItem: CommentList, + onSendReply: (String, Int?, String?) -> Unit = { _, _, _ -> }, + onReplyClick: (commentId: Int, nickname: String) -> Unit, + onEvent: (CommentsEvent) -> Unit = { _ -> } ) { Box { Column( @@ -67,31 +31,23 @@ fun CommentSection( ) { CommentItem( data = commentItem, - onReplyClick = { - onReplyClick( - ReplyItem( - replyId = commentItem.commentId, - userId = commentItem.userId, - nickName = commentItem.nickName, - parentNickname = "", // 댓글에는 parentNickname이 필요 없음 - genreName = commentItem.genreName, - profileImageUrl = commentItem.profileImageUrl, - content = commentItem.content, - postDate = commentItem.postDate, - isWriter = commentItem.isWriter, - isLiked = commentItem.isLiked, - likeCount = commentItem.likeCount - ) - ) - } + onReplyClick = { onReplyClick(commentItem.commentId, commentItem.creatorNickname) }, + onLikeClick = { onEvent(CommentsEvent.LikeComment(commentItem.commentId)) } ) commentItem.replyList.forEach { reply -> ReplyItem( data = reply, - onReplyClick = { - onReplyClick(reply) + onReplyClick = { onReplyClick(commentItem.commentId, reply.creatorNickname) }, + onLikeClick = { + onEvent( + CommentsEvent.LikeReply( + commentItem.commentId, + reply.commentId + ) + ) } + ) } } @@ -103,12 +59,25 @@ fun CommentSection( fun CommentSectionPreview() { ThipTheme { Column { - CommentList( - commentList = listOf( - mockComment, mockComment, mockComment - ), + CommentSection( + commentItem = + CommentList( + commentId = 1, + creatorId = 1, + creatorNickname = "User1", + creatorProfileImageUrl = "https://example.com/image1.jpg", + aliasName = "칭호칭호", + aliasColor = "#A0F8E8", + content = "This is a comment.", + postDate = "2023-10-01", + isLike = false, + likeCount = 10, + isDeleted = false, + replyList = emptyList() + + ), onSendReply = { _, _, _ -> }, - onReplyClick = { replyItem -> + onReplyClick = { commentId, nickname -> // Handle reply click } ) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/component/ReplyItem.kt b/app/src/main/java/com/texthip/thip/ui/group/note/component/ReplyItem.kt index 29b5ec69..d0729746 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/component/ReplyItem.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/component/ReplyItem.kt @@ -8,10 +8,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -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.graphics.Color @@ -22,20 +18,20 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.texthip.thip.R +import com.texthip.thip.data.model.comments.response.ReplyList import com.texthip.thip.ui.common.header.ProfileBarFeed -import com.texthip.thip.ui.group.note.mock.ReplyItem import com.texthip.thip.ui.theme.ThipTheme import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography +import com.texthip.thip.utils.color.hexToColor @Composable fun ReplyItem( modifier: Modifier = Modifier, - data: ReplyItem, - onReplyClick: (String) -> Unit = { } + data: ReplyList, + onReplyClick: () -> Unit = { }, + onLikeClick: () -> Unit = {} ) { - var isLiked by remember { mutableStateOf(data.isLiked) } - Row( horizontalArrangement = Arrangement.spacedBy(8.dp) ) { @@ -50,11 +46,10 @@ fun ReplyItem( verticalArrangement = Arrangement.spacedBy(12.dp) ) { ProfileBarFeed( -// profileImage = data.profileImageUrl, - profileImage = painterResource(R.drawable.character_literature), - nickname = data.nickName, - genreName = data.genreName, - genreColor = colors.SocialScience, + profileImage = data.creatorProfileImageUrl, + nickname = data.creatorNickname, + genreName = data.aliasName, + genreColor = hexToColor(data.aliasColor), date = data.postDate ) @@ -72,7 +67,11 @@ fun ReplyItem( style = typography.copy_m500_s14_h20.copy(color = colors.White) .toSpanStyle() ) { - append(stringResource(R.string.annotation) + data.parentNickname + stringResource(R.string.space_bar)) + append( + stringResource(R.string.annotation) + data.parentCommentCreatorNickname + stringResource( + R.string.space_bar + ) + ) } append(data.content) }, @@ -80,7 +79,7 @@ fun ReplyItem( style = typography.feedcopy_r400_s14_h20, ) Text( - modifier = Modifier.clickable(onClick = { onReplyClick(data.nickName) }), + modifier = Modifier.clickable(onClick = onReplyClick), text = stringResource(R.string.write_reply), style = typography.menu_sb600_s12, color = colors.Grey02, @@ -88,12 +87,12 @@ fun ReplyItem( } Column( - modifier = Modifier.clickable(onClick = { isLiked = !isLiked }), + modifier = Modifier.clickable(onClick = onLikeClick), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(2.dp), ) { Icon( - painter = painterResource(if (isLiked) R.drawable.ic_heart_center_filled else R.drawable.ic_heart_center), + painter = painterResource(if (data.isLike) R.drawable.ic_heart_center_filled else R.drawable.ic_heart_center), contentDescription = null, tint = Color.Unspecified ) @@ -120,18 +119,18 @@ private fun ReplyItemPreview() { verticalArrangement = Arrangement.spacedBy(12.dp) ) { ReplyItem( - data = ReplyItem( - replyId = 1, - userId = 1, - nickName = "user.01", - parentNickname = "사용자태그", - genreName = "칭호칭호", - profileImageUrl = "https://example.com/profile.jpg", - content = "입력하세요. 댓글 내용을 입력하세요오. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. 댓글 내용을 입력하세요. ", - postDate = "12시간 전", - isWriter = false, - isLiked = true, - likeCount = 10, + data = ReplyList( + commentId = 1, + parentCommentCreatorNickname = "User1", + creatorId = 2, + creatorNickname = "User1", + aliasName = "칭호칭호", + aliasColor = "#FF5733", + creatorProfileImageUrl = "https://example.com/image2.jpg", + content = "This is a reply.", + postDate = "2023-10-01T12:05:00Z", + isLike = false, + likeCount = 5 ) ) } 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 709e058d..2c2c8944 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 @@ -56,7 +56,8 @@ import com.texthip.thip.ui.group.note.component.CommentBottomSheet import com.texthip.thip.ui.group.note.component.FilterHeaderSection import com.texthip.thip.ui.group.note.component.TextCommentCard import com.texthip.thip.ui.group.note.component.VoteCommentCard -import com.texthip.thip.ui.group.note.mock.mockComment +import com.texthip.thip.ui.group.note.viewmodel.CommentsEvent +import com.texthip.thip.ui.group.note.viewmodel.CommentsViewModel import com.texthip.thip.ui.group.note.viewmodel.GroupNoteEvent import com.texthip.thip.ui.group.note.viewmodel.GroupNoteUiState import com.texthip.thip.ui.group.note.viewmodel.GroupNoteViewModel @@ -161,6 +162,9 @@ fun GroupNoteContent( var isPinDialogVisible by remember { mutableStateOf(false) } var showToast by remember { mutableStateOf(false) } + val commentsViewModel: CommentsViewModel = hiltViewModel() + val commentsUiState by commentsViewModel.uiState.collectAsStateWithLifecycle() + LaunchedEffect(showToast) { if (showToast) { delay(3000) @@ -359,7 +363,10 @@ fun GroupNoteContent( "RECORD" -> TextCommentCard( data = post, modifier = itemModifier, - onCommentClick = { isCommentBottomSheetVisible = true }, + onCommentClick = { + selectedPostForComment = post + isCommentBottomSheetVisible = true + }, onLongPress = { selectedPostForMenu = post }, onPinClick = { isPinDialogVisible = true }, onLikeClick = { postId, postType -> @@ -370,7 +377,10 @@ fun GroupNoteContent( "VOTE" -> VoteCommentCard( data = post, modifier = itemModifier, - onCommentClick = { isCommentBottomSheetVisible = true }, + onCommentClick = { + selectedPostForComment = post + isCommentBottomSheetVisible = true + }, onLongPress = { selectedPostForMenu = post }, onPinClick = { isPinDialogVisible = true }, onVote = { postId, voteItemId, type -> @@ -460,17 +470,29 @@ fun GroupNoteContent( } if (isCommentBottomSheetVisible && selectedPostForComment != null) { + LaunchedEffect(selectedPostForComment) { + commentsViewModel.initialize( + postId = selectedPostForComment!!.postId.toLong(), + postType = selectedPostForComment!!.postType + ) + } + CommentBottomSheet( - commentResponse = listOf(mockComment, mockComment, mockComment), -// commentResponse = emptyList(), + uiState = commentsUiState, + onEvent = commentsViewModel::onEvent, onDismiss = { isCommentBottomSheetVisible = false -// selectedNoteRecord = null -// selectedNoteVote = null selectedPostForComment = null }, - onSendReply = { replyText, commentId, replyTo -> - // 댓글 전송 로직 구현 + onSendReply = { text, parentId, _ -> + if (text.isNotBlank()) { + commentsViewModel.onEvent( + CommentsEvent.CreateComment( + content = text, + parentId = parentId + ) + ) + } } ) } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt new file mode 100644 index 00000000..4247ab32 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt @@ -0,0 +1,178 @@ +package com.texthip.thip.ui.group.note.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.texthip.thip.data.model.comments.response.CommentList +import com.texthip.thip.data.repository.CommentsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject + +data class CommentsUiState( + val isLoading: Boolean = false, + val isLoadingMore: Boolean = false, + val error: String? = null, + val isLast: Boolean = false, + val comments: List = emptyList() +) + +sealed interface CommentsEvent { + data object LoadMoreComments : CommentsEvent + data class LikeComment(val commentId: Int) : CommentsEvent // 댓글 좋아요 이벤트 + data class LikeReply(val parentCommentId: Int, val replyId: Int) : CommentsEvent // 대댓글 좋아요 이벤트 + data class CreateComment(val content: String, val parentId: Int?) : CommentsEvent +} + +@HiltViewModel +class CommentsViewModel @Inject constructor( + private val commentsRepository: CommentsRepository +) : ViewModel() { + + private val _uiState = MutableStateFlow(CommentsUiState()) + val uiState = _uiState.asStateFlow() + + private var nextCursor: String? = null + private var currentPostId: Long = -1L + private var currentPostType: String = "RECORD" + + fun initialize(postId: Long, postType: String) { + if (currentPostId == postId) return + this.currentPostId = postId + this.currentPostType = postType + fetchComments(isRefresh = true) + } + + fun onEvent(event: CommentsEvent) { + when (event) { + is CommentsEvent.LoadMoreComments -> fetchComments(isRefresh = false) + is CommentsEvent.LikeComment -> toggleCommentLike(event.commentId) + is CommentsEvent.LikeReply -> toggleReplyLike(event.parentCommentId, event.replyId) + is CommentsEvent.CreateComment -> createComment( + content = event.content, + parentId = event.parentId + ) + } + } + + private fun createComment(content: String, parentId: Int?) { + if (content.isBlank()) return + + viewModelScope.launch { + val isReply = parentId != null + + commentsRepository.createComment( + postId = currentPostId, + content = content, + isReplyRequest = isReply, + parentId = parentId, + postType = currentPostType + ).onSuccess { + fetchComments(isRefresh = true) + }.onFailure { throwable -> + _uiState.update { it.copy(error = "댓글 작성 실패: ${throwable.message}") } + } + } + } + + private fun toggleCommentLike(commentId: Int) { + // 클릭한 댓글 찾기 + val comments = _uiState.value.comments + val commentIndex = comments.indexOfFirst { it.commentId == commentId } + if (commentIndex == -1) return + + val comment = comments[commentIndex] + val currentIsLiked = comment.isLike + val newLikeCount = if (currentIsLiked) comment.likeCount - 1 else comment.likeCount + 1 + + // 즉시 UI 업데이트 + val updatedComment = comment.copy(isLike = !currentIsLiked, likeCount = newLikeCount) + val newComments = comments.toMutableList().apply { set(commentIndex, updatedComment) } + _uiState.update { it.copy(comments = newComments) } + + viewModelScope.launch { + commentsRepository.likeComment(commentId.toLong(), !currentIsLiked) + .onFailure { + _uiState.update { + val originalComments = it.comments.toMutableList() + originalComments[commentIndex] = comment + it.copy(comments = originalComments) + } + } + } + } + + private fun toggleReplyLike(parentCommentId: Int, replyId: Int) { + // 부모 댓글 및 대댓글 찾기 + val comments = _uiState.value.comments + val parentCommentIndex = comments.indexOfFirst { it.commentId == parentCommentId } + if (parentCommentIndex == -1) return + + val parentComment = comments[parentCommentIndex] + val replyIndex = parentComment.replyList.indexOfFirst { it.commentId == replyId } + if (replyIndex == -1) return + + val reply = parentComment.replyList[replyIndex] + val currentIsLiked = reply.isLike + val newLikeCount = if (currentIsLiked) reply.likeCount - 1 else reply.likeCount + 1 + + // 즉시 UI 업데이트 + val updatedReply = reply.copy(isLike = !currentIsLiked, likeCount = newLikeCount) + val newReplyList = + parentComment.replyList.toMutableList().apply { set(replyIndex, updatedReply) } + val updatedParentComment = parentComment.copy(replyList = newReplyList) + val newComments = + comments.toMutableList().apply { set(parentCommentIndex, updatedParentComment) } + _uiState.update { it.copy(comments = newComments) } + + viewModelScope.launch { + commentsRepository.likeComment(replyId.toLong(), !currentIsLiked) + .onFailure { + _uiState.update { + val originalComments = it.comments.toMutableList() + originalComments[parentCommentIndex] = parentComment + it.copy(comments = originalComments) + } + } + } + } + + private fun fetchComments(isRefresh: Boolean) { + val currentState = _uiState.value + if (currentState.isLoading || currentState.isLoadingMore || (currentState.isLast && !isRefresh)) { + return + } + + viewModelScope.launch { + _uiState.update { + if (isRefresh) it.copy(isLoading = true, comments = emptyList(), isLast = false) + else it.copy(isLoadingMore = true) + } + + val cursorToFetch = if (isRefresh) null else nextCursor + + commentsRepository.getComments(postId = currentPostId, cursor = cursorToFetch) + .onSuccess { response -> + if (response != null) { + nextCursor = response.nextCursor + _uiState.update { + it.copy( + isLoading = false, + isLoadingMore = false, + // isRefresh일 경우 새 목록으로, 아닐 경우 기존 목록에 추가 + comments = if (isRefresh) response.commentList else it.comments + response.commentList, + isLast = response.isLast + ) + } + } + } + .onFailure { throwable -> + _uiState.update { + it.copy(isLoading = false, isLoadingMore = false, error = throwable.message) + } + } + } + } +} diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomHeader.kt b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomHeader.kt index faed3109..195ff46a 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomHeader.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomHeader.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp import com.texthip.thip.R import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography -import com.texthip.thip.utils.type.GenreColor +import com.texthip.thip.utils.color.hexToColor @Composable fun GroupRoomHeader( @@ -37,12 +37,9 @@ fun GroupRoomHeader( progressEndDate: String, memberCount: Int, category: String, - color: String = "RED", // TODO: 서버에서 색상 추가해주면 수정, + categoryColor: String = "#A0F8E8", onNavigateToMates: () -> Unit = { } ) { - val categoryColorEnum = GenreColor.fromString(color) - val categoryColor = categoryColorEnum.colorProvider() - Column( modifier = Modifier.padding(horizontal = 20.dp) ) { @@ -176,7 +173,7 @@ fun GroupRoomHeader( Text( text = category, style = typography.info_m500_s12, - color = categoryColor + color = hexToColor(categoryColor) ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomMatesList.kt b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomMatesList.kt index af83361a..158b31b6 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomMatesList.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomMatesList.kt @@ -13,6 +13,7 @@ import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse import com.texthip.thip.data.model.rooms.response.UserList import com.texthip.thip.ui.common.header.ProfileBar import com.texthip.thip.ui.theme.ThipTheme.colors +import com.texthip.thip.utils.color.hexToColor @Composable fun GroupRoomMatesList( @@ -31,8 +32,7 @@ fun GroupRoomMatesList( profileImage = member.imageUrl, topText = member.nickname, bottomText = member.aliasName, -// bottomTextColor = member.aliasColor, - bottomTextColor = colors.ScienceIt, // TODO: 서버에서 보내주는 색상으로 수정 + bottomTextColor = hexToColor(member.aliasColor), showSubscriberInfo = true, subscriberCount = member.followerCount ) { onUserClick(member.userId) } @@ -58,6 +58,7 @@ private fun GroupRoomMatesListPreview() { userId = 1, nickname = "김희용", aliasName = "문학가", + aliasColor= "#A0F8E8", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), @@ -65,6 +66,7 @@ private fun GroupRoomMatesListPreview() { userId = 2, nickname = "노성준", aliasName = "문학가", + aliasColor= "#A0F8E8", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomMatesScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomMatesScreen.kt index ce2dfa52..3748aff7 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomMatesScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomMatesScreen.kt @@ -104,6 +104,7 @@ private fun GroupRoomMatesScreenPreview() { userId = 1, nickname = "김희용", aliasName = "문학가", + aliasColor = "#A0F8E8", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), @@ -111,6 +112,7 @@ private fun GroupRoomMatesScreenPreview() { userId = 2, nickname = "노성준", aliasName = "문학가", + aliasColor = "#A0F8E8", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), 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 9ac9b3b8..19ccfba6 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 @@ -162,7 +162,8 @@ fun GroupRoomContent( progressStartDate = roomDetails.progressStartDate, progressEndDate = roomDetails.progressEndDate, memberCount = roomDetails.memberCount, - category = roomDetails.category + category = roomDetails.category, + categoryColor = roomDetails.categoryColor, ) { onNavigateToMates() } @@ -282,6 +283,7 @@ private fun GroupRoomScreenPreview() { progressStartDate = "2023.10.01", progressEndDate = "2023.10.31", category = "문학", + categoryColor = "#A0F8E8", roomDescription = "‘시집만 읽는 사람들’ 3월 모임입니다.", memberCount = 22, recruitCount = 30, diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt index 8470680c..8c913a27 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt @@ -206,8 +206,7 @@ fun NavGraphBuilder.groupNavigation( val roomId = route.roomId GroupRoomScreen( -// roomId = roomId, - roomId = 1, + roomId = roomId, onBackClick = { navigateBack() }, @@ -226,8 +225,7 @@ fun NavGraphBuilder.groupNavigation( val roomId = route.roomId GroupRoomMatesScreen( -// roomId = roomId, - roomId = 1, + roomId = roomId, onBackClick = { navigateBack() }, @@ -250,8 +248,7 @@ fun NavGraphBuilder.groupNavigation( val uiState by viewModel.uiState.collectAsStateWithLifecycle() GroupNoteScreen( -// roomId = roomId, - roomId = 1, + roomId = roomId, resultTabIndex = result, initialPage = page, initialIsOverview = isOverview, @@ -286,7 +283,7 @@ fun NavGraphBuilder.groupNavigation( val roomId = route.roomId GroupNoteCreateScreen( - roomId = 1, + roomId = roomId, recentPage = route.recentBookPage, totalPage = route.totalBookPage, isOverviewPossible = route.isOverviewPossible, @@ -307,8 +304,7 @@ fun NavGraphBuilder.groupNavigation( val roomId = route.roomId GroupVoteCreateScreen( -// roomId = roomId, - roomId = 1, + roomId = roomId, recentPage = route.recentPage, totalPage = route.totalPage, isOverviewPossible = route.isOverviewPossible, diff --git a/app/src/main/java/com/texthip/thip/utils/color/HexToColor.kt b/app/src/main/java/com/texthip/thip/utils/color/HexToColor.kt index 381ef36f..aa005a48 100644 --- a/app/src/main/java/com/texthip/thip/utils/color/HexToColor.kt +++ b/app/src/main/java/com/texthip/thip/utils/color/HexToColor.kt @@ -1,10 +1,11 @@ package com.texthip.thip.utils.color import androidx.compose.ui.graphics.Color +import androidx.core.graphics.toColorInt fun hexToColor(hex: String): Color { return try { - Color(android.graphics.Color.parseColor(hex)) + Color(hex.toColorInt()) } catch (e: IllegalArgumentException) { //잘못된 형식이면 기본 색 Color.White diff --git a/app/src/main/java/com/texthip/thip/utils/rooms/AdvancedImePadding.kt b/app/src/main/java/com/texthip/thip/utils/rooms/AdvancedImePadding.kt new file mode 100644 index 00000000..9e963723 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/utils/rooms/AdvancedImePadding.kt @@ -0,0 +1,28 @@ +package com.texthip.thip.utils.rooms + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.imePadding +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.layout.findRootCoordinates +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.positionInWindow +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp + +fun Modifier.advancedImePadding() = composed { + var consumePadding by remember { mutableStateOf(0) } + onGloballyPositioned { coordinates -> + val rootCoordinate = coordinates.findRootCoordinates() + val bottom = coordinates.positionInWindow().y + coordinates.size.height + + consumePadding = (rootCoordinate.size.height - bottom).toInt().coerceAtLeast(0) + } + .consumeWindowInsets(PaddingValues(bottom = (consumePadding / LocalDensity.current.density).dp)) + .imePadding() +} \ No newline at end of file