diff --git a/app/src/main/java/com/texthip/thip/data/model/book/response/RecruitingRoomsResponse.kt b/app/src/main/java/com/texthip/thip/data/model/book/response/RecruitingRoomsResponse.kt index 8dae69fe..02480466 100644 --- a/app/src/main/java/com/texthip/thip/data/model/book/response/RecruitingRoomsResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/book/response/RecruitingRoomsResponse.kt @@ -18,5 +18,6 @@ data class RecruitingRoomItem( @SerialName("roomName") val roomName: String = "", @SerialName("memberCount") val memberCount: Int = 0, @SerialName("recruitCount") val recruitCount: Int = 0, - @SerialName("deadlineEndDate") val deadlineEndDate: String = "" + @SerialName("deadlineEndDate") val deadlineEndDate: String = "", + @SerialName("isPublic") val isPublic: Boolean = true ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/feed/response/MyFeedResponse.kt b/app/src/main/java/com/texthip/thip/data/model/feed/response/MyFeedResponse.kt index d3315033..03af4f33 100644 --- a/app/src/main/java/com/texthip/thip/data/model/feed/response/MyFeedResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/feed/response/MyFeedResponse.kt @@ -23,5 +23,7 @@ data class MyFeedItem( @SerialName("likeCount") val likeCount: Int, @SerialName("commentCount") val commentCount: Int, @SerialName("isPublic") val isPublic: Boolean, + @SerialName("isSaved") val isSaved: Boolean, + @SerialName("isLiked") val isLiked: Boolean, @SerialName("isWriter") val isWriter: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/MyRoomListResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/MyRoomListResponse.kt index a4283217..7ca80e32 100644 --- a/app/src/main/java/com/texthip/thip/data/model/rooms/response/MyRoomListResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/MyRoomListResponse.kt @@ -18,5 +18,6 @@ data class MyRoomResponse( @SerialName("recruitCount") val recruitCount: Int, @SerialName("memberCount") val memberCount: Int, @SerialName("endDate") val endDate: String, - @SerialName("type") val type: String + @SerialName("type") val type: String, + @SerialName("isPublic") val isPublic: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt index cfae3e90..cf7d7463 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt @@ -263,7 +263,8 @@ class FeedRepository @Inject constructor( feedId = feedId, isLiked = it.isLiked, likeCount = newLikeCount, - isSaved = currentIsSaved // isSaved 상태는 그대로 유지 + isSaved = currentIsSaved, // isSaved 상태는 그대로 유지 + commentCount = 0 // 좋아요 함수에서는 댓글 수 정보 없음 ) _feedStateUpdateResult.emit(update) } @@ -287,7 +288,8 @@ class FeedRepository @Inject constructor( feedId = feedId, isLiked = currentIsLiked, // isLiked 상태는 그대로 유지 likeCount = currentLikeCount, - isSaved = it.isSaved + isSaved = it.isSaved, + commentCount = 0 // 저장 함수에서는 댓글 수 정보 없음 ) _feedStateUpdateResult.emit(update) } diff --git a/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt b/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt index 32b35d76..081eeba3 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoom.kt @@ -1,11 +1,14 @@ package com.texthip.thip.ui.common.cards +import androidx.compose.foundation.Image import androidx.compose.foundation.border 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.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 @@ -41,6 +44,7 @@ fun CardItemRoom( endDate: String? = null, imageUrl: String? = null, hasBorder: Boolean = false, + isSecret: Boolean? = null, onClick: () -> Unit = {} ) { Card( @@ -71,12 +75,23 @@ fun CardItemRoom( modifier = Modifier.fillMaxWidth() ) { // 이미지 - AsyncImage( - model = imageUrl ?: R.drawable.img_book_cover_sample, - contentDescription = "책 이미지", - modifier = Modifier.size(width = 80.dp, height = 107.dp), - contentScale = ContentScale.Crop - ) + Box( + modifier = Modifier.size(width = 80.dp, height = 107.dp) + ) { + AsyncImage( + model = imageUrl ?: R.drawable.img_book_cover_sample, + contentDescription = "책 이미지", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop + ) + if (isSecret == true) { + Image( + painter = painterResource(id = R.drawable.ic_secret_cover), + contentDescription = "비밀방", + modifier = Modifier.fillMaxSize() + ) + } + } Spacer(modifier = Modifier.width(12.dp)) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/mock/FeedStateUpdateResult.kt b/app/src/main/java/com/texthip/thip/ui/feed/mock/FeedStateUpdateResult.kt index db2d095e..d8c9e1cb 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/mock/FeedStateUpdateResult.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/mock/FeedStateUpdateResult.kt @@ -7,5 +7,6 @@ data class FeedStateUpdateResult( val feedId: Long, val isLiked: Boolean, val likeCount: Int, - val isSaved: Boolean + val isSaved: Boolean, + val commentCount: Int ) 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 d32c21a1..98a6aff8 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 @@ -96,6 +96,7 @@ fun FeedCommentScreen( handle.set("updated_feed_isLiked", detail.isLiked) handle.set("updated_feed_likeCount", detail.likeCount) handle.set("updated_feed_isSaved", detail.isSaved) + handle.set("updated_feed_commentCount", detail.commentCount) } } } @@ -114,6 +115,14 @@ fun FeedCommentScreen( commentsViewModel.initialize(postId = feedId.toLong(), postType = "FEED") } + // 댓글이 생성되면 피드 상세 정보를 다시 로드 + LaunchedEffect(commentsUiState.isCommentCreated) { + if (commentsUiState.isCommentCreated) { + feedDetailViewModel.loadFeedDetail(feedId) + commentsViewModel.resetCommentCreatedState() + } + } + // 로딩 상태 처리 if (feedDetailUiState.isLoading) { Box( diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedMyScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedMyScreen.kt index 8b5248e7..eda45f2f 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedMyScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedMyScreen.kt @@ -83,9 +83,9 @@ fun FeedMyContent( likeCount = this.likeCount, commentCount = this.commentCount, isPublic = this.isPublic, - isSaved = false, - isLiked = false, - isWriter = true + isSaved = this.isSaved, + isLiked = this.isLiked, + isWriter = this.isWriter ) } @@ -203,8 +203,8 @@ private fun FeedMyScreenPreview() { bookTitle = "나의 책 제목 ${it + 1}", bookAuthor = "나", contentBody = "내가 작성한 피드 내용입니다. 내용은 여기에 표시됩니다.", contentUrls = emptyList(), likeCount = 15, commentCount = 3, - isPublic = true, - isWriter = false + isPublic = true, isSaved = false, isLiked = it % 2 == 0, + isWriter = true ) } diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt index 2c8f5f49..044f27a1 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt @@ -16,8 +16,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api @@ -32,6 +32,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -85,12 +86,16 @@ fun FeedScreen( val scope = rememberCoroutineScope() var showProgressBar by remember { mutableStateOf(false) } val progress = remember { Animatable(0f) } - + val feedTabTitles = listOf(stringResource(R.string.feed), stringResource(R.string.my_feed)) // 탭별로 별도의 스크롤 상태 관리 - val allFeedListState = rememberLazyListState() - val myFeedListState = rememberLazyListState() + val allFeedListState = rememberSaveable(saver = LazyListState.Saver) { + LazyListState() + } + val myFeedListState = rememberSaveable(saver = LazyListState.Saver) { + LazyListState() + } val currentListState = when (feedUiState.selectedTabIndex) { 0 -> allFeedListState 1 -> myFeedListState @@ -98,7 +103,11 @@ fun FeedScreen( } // 무한 스크롤 로직 - val shouldLoadMore by remember(feedUiState.canLoadMoreCurrentTab, feedUiState.isLoadingMore, feedUiState.selectedTabIndex) { + val shouldLoadMore by remember( + feedUiState.canLoadMoreCurrentTab, + feedUiState.isLoadingMore, + feedUiState.selectedTabIndex + ) { derivedStateOf { val layoutInfo = currentListState.layoutInfo val totalItems = layoutInfo.totalItemsCount @@ -127,14 +136,27 @@ fun FeedScreen( feedViewModel.loadMoreFeeds() } } - + + var isUserTabChange by remember { mutableStateOf(false) } + LaunchedEffect(Unit) { - feedViewModel.resetToInitialState() + // 최초 진입시에만 데이터 로딩 + if (feedUiState.allFeeds.isEmpty() && feedUiState.myFeeds.isEmpty()) { + feedViewModel.refreshData() + } + val hasUpdatedFeedData = + navController.currentBackStackEntry?.savedStateHandle?.get("updated_feed_id") != null + + if (!hasUpdatedFeedData) { + allFeedListState.scrollToItem(0) + } } - // 탭 변경 시 해당 탭의 스크롤을 최상단으로 부드럽게 이동 LaunchedEffect(feedUiState.selectedTabIndex) { - currentListState.scrollToItem(0) + if (isUserTabChange) { + currentListState.scrollToItem(0) + isUserTabChange = false + } } LaunchedEffect(resultFeedId) { @@ -156,12 +178,13 @@ fun FeedScreen( } } } - + LaunchedEffect(refreshFeed) { if (refreshFeed == true) { onRefreshConsumed() if (resultFeedId == null) { feedViewModel.refreshData() + currentListState.scrollToItem(0) } } } @@ -172,12 +195,14 @@ fun FeedScreen( val isLiked = handle.get("updated_feed_isLiked") ?: false val likeCount = handle.get("updated_feed_likeCount") ?: 0 val isSaved = handle.get("updated_feed_isSaved") ?: false + val commentCount = handle.get("updated_feed_commentCount") ?: 0 val result = FeedStateUpdateResult( feedId = feedId, isLiked = isLiked, likeCount = likeCount, - isSaved = isSaved + isSaved = isSaved, + commentCount = commentCount ) feedViewModel.updateFeedStateFromResult(result) @@ -186,6 +211,7 @@ fun FeedScreen( handle.remove("updated_feed_isLiked") handle.remove("updated_feed_likeCount") handle.remove("updated_feed_isSaved") + handle.remove("updated_feed_commentCount") } } } @@ -222,7 +248,10 @@ fun FeedScreen( HeaderMenuBarTab( titles = feedTabTitles, selectedTabIndex = feedUiState.selectedTabIndex, - onTabSelected = feedViewModel::onTabSelected + onTabSelected = { index -> + isUserTabChange = true + feedViewModel.onTabSelected(index) + } ) // 스크롤 영역 전체 @@ -269,7 +298,7 @@ fun FeedScreen( } } } - + if (feedUiState.selectedTabIndex == 1) { // 내 피드 item { @@ -280,7 +309,8 @@ fun FeedScreen( profileImage = myFeedInfo?.profileImageUrl, nickname = myFeedInfo?.nickname ?: "", badgeText = myFeedInfo?.aliasName ?: "", - badgeTextColor = myFeedInfo?.aliasColor?.let { hexToColor(it) } ?: colors.NeonGreen, + badgeTextColor = myFeedInfo?.aliasColor?.let { hexToColor(it) } + ?: colors.NeonGreen, buttonText = "", buttonWidth = 60.dp, showButton = false @@ -288,7 +318,8 @@ fun FeedScreen( Spacer(modifier = Modifier.height(16.dp)) FeedSubscribeBarlist( modifier = Modifier.padding(horizontal = 20.dp), - followerProfileImageUrls = myFeedInfo?.latestFollowerProfileImageUrls ?: emptyList(), + followerProfileImageUrls = myFeedInfo?.latestFollowerProfileImageUrls + ?: emptyList(), onClick = { myFeedInfo?.creatorId?.let { creatorId -> onNavigateToOthersSubscription(creatorId) @@ -297,7 +328,10 @@ fun FeedScreen( ) Spacer(modifier = Modifier.height(40.dp)) Text( - text = stringResource(R.string.whole_num, myFeedInfo?.totalFeedCount ?: 0), + text = stringResource( + R.string.whole_num, + myFeedInfo?.totalFeedCount ?: 0 + ), style = typography.menu_m500_s14_h24, color = colors.Grey, modifier = Modifier @@ -327,7 +361,9 @@ fun FeedScreen( } } } else { - itemsIndexed(feedUiState.myFeeds, key = { _, item -> item.feedId }) { index, myFeed -> + itemsIndexed( + feedUiState.myFeeds, + key = { _, item -> item.feedId }) { index, myFeed -> Spacer(modifier = Modifier.height(if (index == 0) 20.dp else 40.dp)) // MyFeedItem을 FeedItem으로 변환 @@ -342,8 +378,8 @@ fun FeedScreen( content = myFeed.contentBody, likeCount = myFeed.likeCount, commentCount = myFeed.commentCount, - isLiked = false, // 내 피드는 좋아요 개념 없음 - isSaved = false, // 내 피드는 저장 개념 없음 + isLiked = myFeed.isLiked, + isSaved = myFeed.isSaved, isLocked = !myFeed.isPublic, // isPublic의 반대값 tags = emptyList(), imageUrls = myFeed.contentUrls @@ -351,7 +387,7 @@ fun FeedScreen( MyFeedCard( feedItem = feedItem, - onLikeClick = {}, + onLikeClick = { feedViewModel.changeFeedLike(feedItem.id) }, onContentClick = { onNavigateToFeedComment(feedItem.id) }, @@ -378,7 +414,9 @@ fun FeedScreen( onClick = onNavigateToMySubscription ) } - itemsIndexed(feedUiState.allFeeds, key = { _, item -> item.feedId }) { index, allFeed -> + itemsIndexed( + feedUiState.allFeeds, + key = { _, item -> item.feedId }) { index, allFeed -> // AllFeedItem을 FeedItem으로 변환 val feedItem = FeedItem( id = allFeed.feedId.toLong(), @@ -454,7 +492,7 @@ fun FeedScreen( icon = painterResource(id = R.drawable.ic_write), onClick = onNavigateToFeedWrite ) - + // 탭 전환 시 화면 가운데 로딩 인디케이터 if (feedUiState.isRefreshing) { Box( diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt index a89bbbda..11b0353f 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt @@ -280,25 +280,11 @@ class FeedViewModel @Inject constructor( } fun refreshData() { - loadAllFeeds() - fetchRecentWriters() - } - - fun resetToInitialState() { - // 탭과 데이터를 모두 초기 상태로 리셋 - updateState { - it.copy( - selectedTabIndex = 0, - allFeeds = emptyList(), - myFeeds = emptyList(), - isLastPageAllFeeds = false, - isLastPageMyFeeds = false - ) + viewModelScope.launch { + refreshAllFeeds() + refreshMyFeeds() + fetchRecentWriters() } - allFeedsNextCursor = null - myFeedsNextCursor = null - loadAllFeeds(isInitial = true) - fetchRecentWriters() } private fun fetchRecentWriters() { @@ -343,11 +329,27 @@ class FeedViewModel @Inject constructor( fun changeFeedLike(feedId: Long) { viewModelScope.launch { - val currentFeeds = _uiState.value.allFeeds - val feedToUpdate = currentFeeds.find { it.feedId.toLong() == feedId } ?: return@launch + val currentAllFeeds = _uiState.value.allFeeds + val currentMyFeeds = _uiState.value.myFeeds + + val allFeedToUpdate = currentAllFeeds.find { it.feedId.toLong() == feedId } + val myFeedToUpdate = currentMyFeeds.find { it.feedId.toLong() == feedId } + + if (allFeedToUpdate == null && myFeedToUpdate == null) return@launch //ui 먼저 변경 ( 낙관적 업데이트 ) - val newFeeds = currentFeeds.map { + val newAllFeeds = currentAllFeeds.map { + if (it.feedId.toLong() == feedId) { + it.copy( + isLiked = !it.isLiked, + likeCount = if (it.isLiked) it.likeCount - 1 else it.likeCount + 1 + ) + } else { + it + } + } + + val newMyFeeds = currentMyFeeds.map { if (it.feedId.toLong() == feedId) { it.copy( isLiked = !it.isLiked, @@ -357,61 +359,106 @@ class FeedViewModel @Inject constructor( it } } - _uiState.update { it.copy(allFeeds = newFeeds) } + + _uiState.update { it.copy(allFeeds = newAllFeeds, myFeeds = newMyFeeds) } //api 호출 - val newLikeStatus = !feedToUpdate.isLiked + val newLikeStatus = if (allFeedToUpdate != null) { + !allFeedToUpdate.isLiked + } else { + !myFeedToUpdate!!.isLiked + } + + val currentLikeCount = allFeedToUpdate?.likeCount ?: myFeedToUpdate!!.likeCount + val currentIsSaved = allFeedToUpdate?.isSaved ?: myFeedToUpdate!!.isSaved + changeFeedLikeUseCase( - feedId, newLikeStatus, feedToUpdate.likeCount, - feedToUpdate.isSaved + feedId, newLikeStatus, currentLikeCount, currentIsSaved ) .onFailure { - _uiState.update { it.copy(allFeeds = currentFeeds) } + _uiState.update { it.copy(allFeeds = currentAllFeeds, myFeeds = currentMyFeeds) } } } } fun changeFeedSave(feedId: Long) { viewModelScope.launch { - val currentFeeds = _uiState.value.allFeeds - val feedToUpdate = currentFeeds.find { it.feedId.toLong() == feedId } ?: return@launch + val currentAllFeeds = _uiState.value.allFeeds + val currentMyFeeds = _uiState.value.myFeeds + + val allFeedToUpdate = currentAllFeeds.find { it.feedId.toLong() == feedId } + val myFeedToUpdate = currentMyFeeds.find { it.feedId.toLong() == feedId } + + if (allFeedToUpdate == null && myFeedToUpdate == null) return@launch // (낙관적 업데이트) UI 즉시 변경 - val newFeeds = currentFeeds.map { + val newAllFeeds = currentAllFeeds.map { if (it.feedId.toLong() == feedId) { - it.copy(isSaved = !it.isSaved) // isSaved 상태 반전 + it.copy(isSaved = !it.isSaved) } else { it } } - updateState { it.copy(allFeeds = newFeeds) } + + val newMyFeeds = currentMyFeeds.map { + if (it.feedId.toLong() == feedId) { + it.copy(isSaved = !it.isSaved) + } else { + it + } + } + + updateState { it.copy(allFeeds = newAllFeeds, myFeeds = newMyFeeds) } // API 호출 - val newSaveStatus = !feedToUpdate.isSaved + val newSaveStatus = if (allFeedToUpdate != null) { + !allFeedToUpdate.isSaved + } else { + !myFeedToUpdate!!.isSaved + } + + val currentIsLiked = allFeedToUpdate?.isLiked ?: myFeedToUpdate!!.isLiked + val currentLikeCount = allFeedToUpdate?.likeCount ?: myFeedToUpdate!!.likeCount + changeFeedSaveUseCase( feedId = feedId, newSaveStatus = newSaveStatus, - currentIsLiked = feedToUpdate.isLiked, - currentLikeCount = feedToUpdate.likeCount + currentIsLiked = currentIsLiked, + currentLikeCount = currentLikeCount ).onFailure { - _uiState.update { it.copy(allFeeds = currentFeeds) } + _uiState.update { it.copy(allFeeds = currentAllFeeds, myFeeds = currentMyFeeds) } } } } fun updateFeedStateFromResult(result: FeedStateUpdateResult) { - val updatedFeeds = _uiState.value.allFeeds.map { feed -> + val updatedAllFeeds = _uiState.value.allFeeds.map { feed -> + if (feed.feedId.toLong() == result.feedId) { + feed.copy( + isLiked = result.isLiked, + likeCount = result.likeCount, + isSaved = result.isSaved, + commentCount = result.commentCount + ) + } else { + feed + } + } + + val updatedMyFeeds = _uiState.value.myFeeds.map { feed -> if (feed.feedId.toLong() == result.feedId) { feed.copy( isLiked = result.isLiked, likeCount = result.likeCount, - isSaved = result.isSaved + isSaved = result.isSaved, + commentCount = result.commentCount ) } else { feed } } - _uiState.update { it.copy(allFeeds = updatedFeeds) } + + _uiState.update { it.copy(allFeeds = updatedAllFeeds, myFeeds = updatedMyFeeds) } } fun removeDeletedFeed(feedId: Long) { diff --git a/app/src/main/java/com/texthip/thip/ui/group/done/screen/GroupDoneScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/done/screen/GroupDoneScreen.kt index 63c26da1..25ff4f69 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/done/screen/GroupDoneScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/done/screen/GroupDoneScreen.kt @@ -117,6 +117,7 @@ fun GroupDoneContent( participants = room.memberCount, maxParticipants = room.recruitCount, // 모집 인원 수 사용 isRecruiting = RoomUtils.isRecruitingByType(room.type), + isSecret = !room.isPublic, onClick = { /* 완료된 모임방은 클릭 불가 */ } ) } @@ -142,7 +143,8 @@ fun GroupDoneScreenPreview() { memberCount = 18, recruitCount = 20, endDate = "2025-01-31", - type = "EXPIRED" + type = "EXPIRED", + isPublic = true ), MyRoomResponse( roomId = 2, @@ -151,7 +153,8 @@ fun GroupDoneScreenPreview() { memberCount = 12, recruitCount = 15, endDate = "2024-12-28", - type = "EXPIRED" + type = "EXPIRED", + isPublic = false ), MyRoomResponse( roomId = 3, @@ -160,7 +163,8 @@ fun GroupDoneScreenPreview() { memberCount = 25, recruitCount = 30, endDate = "2024-12-15", - type = "EXPIRED" + type = "EXPIRED", + isPublic = true ), MyRoomResponse( roomId = 4, @@ -169,7 +173,8 @@ fun GroupDoneScreenPreview() { memberCount = 10, recruitCount = 12, endDate = "2024-11-20", - type = "EXPIRED" + type = "EXPIRED", + isPublic = true ), MyRoomResponse( roomId = 5, @@ -178,7 +183,8 @@ fun GroupDoneScreenPreview() { memberCount = 16, recruitCount = 20, endDate = "2024-10-31", - type = "EXPIRED" + type = "EXPIRED", + isPublic = false ) ), isLoading = false, diff --git a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt index 86f4f002..617328d0 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/myroom/screen/GroupMyScreen.kt @@ -162,6 +162,7 @@ fun GroupMyContent( isRecruiting = RoomUtils.isRecruitingByType(room.type), endDate = room.endDate, imageUrl = room.bookImageUrl, + isSecret = !room.isPublic, onClick = { onCardClick(room) } ) } @@ -206,7 +207,8 @@ fun GroupMyScreenPreview() { memberCount = 18, recruitCount = 20, type = "RECRUITING", - endDate = "2025-02-15" + endDate = "2025-02-15", + isPublic = true ), MyRoomResponse( roomId = 2, @@ -215,7 +217,8 @@ fun GroupMyScreenPreview() { memberCount = 12, recruitCount = 15, type = "PLAYING", - endDate = "2025-01-28" + endDate = "2025-01-28", + isPublic = false ), MyRoomResponse( roomId = 3, @@ -224,7 +227,8 @@ fun GroupMyScreenPreview() { memberCount = 25, recruitCount = 30, type = "RECRUITING", - endDate = "2025-03-01" + endDate = "2025-03-01", + isPublic = true ), MyRoomResponse( roomId = 4, @@ -233,7 +237,8 @@ fun GroupMyScreenPreview() { memberCount = 8, recruitCount = 12, type = "PLAYING", - endDate = "2025-02-10" + endDate = "2025-02-10", + isPublic = false ), MyRoomResponse( roomId = 5, @@ -242,7 +247,8 @@ fun GroupMyScreenPreview() { memberCount = 6, recruitCount = 10, type = "RECRUITING", - endDate = "2025-02-20" + endDate = "2025-02-20", + isPublic = true ), MyRoomResponse( roomId = 6, @@ -251,7 +257,8 @@ fun GroupMyScreenPreview() { memberCount = 14, recruitCount = 18, type = "PLAYING", - endDate = "2025-01-30" + endDate = "2025-01-30", + isPublic = false ) ), currentRoomType = RoomType.PLAYING_AND_RECRUITING, 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 index 4d254c96..3aa1aac3 100644 --- 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 @@ -19,7 +19,8 @@ data class CommentsUiState( val isLoadingMore: Boolean = false, val error: String? = null, val isLast: Boolean = false, - val comments: List = emptyList() + val comments: List = emptyList(), + val isCommentCreated: Boolean = false ) sealed interface CommentSideEffect { @@ -140,7 +141,10 @@ class CommentsViewModel @Inject constructor( isLike = res.isLike, replyList = res.replyList ) - currentState.copy(comments = listOf(newComment) + currentState.comments) + currentState.copy( + comments = listOf(newComment) + currentState.comments, + isCommentCreated = true + ) } else { val parentCommentIndex = currentState.comments.indexOfFirst { it.commentId == parentId } @@ -156,7 +160,10 @@ class CommentsViewModel @Inject constructor( val newCommentsList = currentState.comments.toMutableList().apply { this[parentCommentIndex] = updatedParentComment } - currentState.copy(comments = newCommentsList) + currentState.copy( + comments = newCommentsList, + isCommentCreated = true + ) } else { currentState } @@ -285,4 +292,8 @@ class CommentsViewModel @Inject constructor( } } } + + fun resetCommentCreatedState() { + _uiState.update { it.copy(isCommentCreated = false) } + } } diff --git a/app/src/main/java/com/texthip/thip/ui/search/component/SearchActiveField.kt b/app/src/main/java/com/texthip/thip/ui/search/component/SearchActiveField.kt index f8fad714..d0af5f89 100644 --- a/app/src/main/java/com/texthip/thip/ui/search/component/SearchActiveField.kt +++ b/app/src/main/java/com/texthip/thip/ui/search/component/SearchActiveField.kt @@ -60,11 +60,11 @@ fun SearchActiveField( itemsIndexed(bookList) { index, book -> Column { CardBookList( - modifier = Modifier.clickable { onBookClick(book) }, title = book.title, author = book.author, publisher = book.publisher, - imageUrl = book.imageUrl + imageUrl = book.imageUrl, + onClick = { onBookClick(book) } ) if (index < bookList.size - 1) { Spacer( diff --git a/app/src/main/java/com/texthip/thip/ui/search/component/SearchBookFilteredResult.kt b/app/src/main/java/com/texthip/thip/ui/search/component/SearchBookFilteredResult.kt index 9f0b3b9d..c8a99cfa 100644 --- a/app/src/main/java/com/texthip/thip/ui/search/component/SearchBookFilteredResult.kt +++ b/app/src/main/java/com/texthip/thip/ui/search/component/SearchBookFilteredResult.kt @@ -87,11 +87,11 @@ fun SearchBookFilteredResult( itemsIndexed(bookList) { index, book -> Column { CardBookList( - modifier = Modifier.clickable { onBookClick(book) }, title = book.title, author = book.author, publisher = book.publisher, - imageUrl = book.imageUrl + imageUrl = book.imageUrl, + onClick = { onBookClick(book) } ) if (index < bookList.size - 1) { Spacer( diff --git a/app/src/main/java/com/texthip/thip/ui/search/component/SearchRecentBook.kt b/app/src/main/java/com/texthip/thip/ui/search/component/SearchRecentBook.kt index c35cddc9..a65133dd 100644 --- a/app/src/main/java/com/texthip/thip/ui/search/component/SearchRecentBook.kt +++ b/app/src/main/java/com/texthip/thip/ui/search/component/SearchRecentBook.kt @@ -86,9 +86,10 @@ fun SearchRecentBook( } Spacer(modifier = Modifier.height(16.dp)) - Box(modifier = Modifier - .fillMaxWidth() - .weight(1f) + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f) ) { if (popularBooks.isEmpty()) { Column( @@ -129,6 +130,11 @@ fun SearchRecentBook( .height(1.dp) .background(colors.DarkGrey02) ) + } else { + Spacer( + modifier = Modifier + .padding(bottom = 20.dp) + ) } } } diff --git a/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookGroupScreen.kt b/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookGroupScreen.kt index 0f574785..7dc6a485 100644 --- a/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookGroupScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookGroupScreen.kt @@ -213,6 +213,7 @@ private fun SearchBookGroupScreenContent( isRecruiting = true, endDate = item.deadlineEndDate, imageUrl = item.bookImageUrl, + isSecret = !item.isPublic, onClick = { onCardClick(item.roomId) } ) } @@ -269,7 +270,8 @@ private val mockRecruitingList = listOf( memberCount = 8, recruitCount = 12, deadlineEndDate = "3일 뒤", - bookImageUrl = "https://example.com/demian.jpg" + bookImageUrl = "https://example.com/demian.jpg", + isPublic = true ), RecruitingRoomItem( roomId = 2, @@ -277,7 +279,8 @@ private val mockRecruitingList = listOf( memberCount = 15, recruitCount = 20, deadlineEndDate = "7일 뒤", - bookImageUrl = "https://example.com/demian.jpg" + bookImageUrl = "https://example.com/demian.jpg", + isPublic = false ), RecruitingRoomItem( roomId = 3, @@ -285,7 +288,8 @@ private val mockRecruitingList = listOf( memberCount = 5, recruitCount = 10, deadlineEndDate = "1일 뒤", - bookImageUrl = "https://example.com/demian.jpg" + bookImageUrl = "https://example.com/demian.jpg", + isPublic = true ) ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2f6c4532..56657f1e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -316,7 +316,7 @@ 설정 2/2 닉네임 (필수) 이미 사용중인 닉네임입니다. - 한글/영어/숫자로 구성 + 한글/영문소문자/숫자로 구성 안녕하세요, %1$s님 이제 Thip에서 활동할 준비를 모두 마쳤어요! 지금 바로 Thip 시작하기