From c10eb104bf3609a12511ba7d34f6b2f834bfb712 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Fri, 8 Aug 2025 23:44:26 +0900 Subject: [PATCH 01/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20response=20=EC=83=9D=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rooms/response/RoomsPostsResponse.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt new file mode 100644 index 00000000..e99382c5 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt @@ -0,0 +1,38 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsPostsResponse( + val postList: List, + val roomId: Int, + val isbn: String, + val nextCursor: String?, + val isLast: Boolean, +) + +@Serializable +data class PostList( + val postId: Int, + val postDate: String, + val postType: String, + val page: Int, + val userId: Int, + val nickName: String, + val profileImageUrl: String?, + val content: String, + val likeCount: Int, + val commentCount: Int, + val isLiked: Boolean, + val isWriter: Boolean, + val isLocked: Boolean, + val voteItems: List, +) + +@Serializable +data class VoteItems( + val voteItemId: Int, + val itemName: String, + val percentage: Int, + val isVoted: Boolean, +) \ No newline at end of file From 5b3b0416cf58e0ac24e3c46005f2dbb65c8f871e Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Fri, 8 Aug 2025 23:44:48 +0900 Subject: [PATCH 02/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20service=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/texthip/thip/data/service/RoomsService.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index d493c456..b236f4be 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -2,9 +2,11 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse +import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse import retrofit2.http.GET import retrofit2.http.Path +import retrofit2.http.Query interface RoomsService { @GET("rooms/{roomId}/playing") @@ -16,4 +18,16 @@ interface RoomsService { suspend fun getRoomsUsers( @Path("roomId") roomId: Int ): BaseResponse + + @GET("rooms/{roomId}/posts") + suspend fun getRoomsPosts( + @Path("roomId") roomId: Int, + @Query("type") type: String = "group", + @Query("sort") sort: String? = "latest", + @Query("pageStart") pageStart: Int? = null, + @Query("pageEnd") pageEnd: Int? = null, + @Query("isOverview") isOverview: Boolean? = false, + @Query("isPageFilter") isPageFilter: Boolean? = false, + @Query("cursor") cursor: String? = null, + ): BaseResponse } \ No newline at end of file From 895f3e6ac09e1fff6af800c9a1adb24a5638e6d5 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Fri, 8 Aug 2025 23:44:54 +0900 Subject: [PATCH 03/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20repository=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/repository/RoomsRepository.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index 141a40ed..a5d5bc16 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -24,4 +24,26 @@ class RoomsRepository @Inject constructor( roomId = roomId ).handleBaseResponse().getOrThrow() } + + suspend fun getRoomsPosts( + roomId: Int, + type: String = "group", + sort: String? = "latest", + pageStart: Int? = null, + pageEnd: Int? = null, + isOverview: Boolean? = false, + isPageFilter: Boolean? = false, + cursor: String? = null, + ) = runCatching { + roomsService.getRoomsPosts( + roomId = roomId, + type = type, + sort = sort, + pageStart = pageStart, + pageEnd = pageEnd, + isOverview = isOverview, + isPageFilter = isPageFilter, + cursor = cursor + ).handleBaseResponse().getOrThrow() + } } \ No newline at end of file From 0c0e9601de3a3703791b81c20a07b2c31d81fea3 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Fri, 8 Aug 2025 23:47:21 +0900 Subject: [PATCH 04/47] =?UTF-8?q?[feat]:=20=EC=A0=95=EB=A0=AC=20enum=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/common/buttons/GroupVoteButton.kt | 10 +++++----- .../thip/ui/feed/screen/FeedCommentScreen.kt | 17 +++++++---------- .../search/screen/SearchBookDetailScreen.kt | 4 ++-- .../com/texthip/thip/utils/type/SortType.kt | 19 +++++++++++++++++++ app/src/main/res/values/strings.xml | 5 +++-- 5 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/com/texthip/thip/utils/type/SortType.kt diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt index 4da40172..82c5901b 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt @@ -25,7 +25,7 @@ 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.group.note.mock.VoteItem +import com.texthip.thip.data.model.rooms.response.VoteItems import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography @@ -33,7 +33,7 @@ import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun GroupVoteButton( modifier: Modifier = Modifier, - voteItems: List, + voteItems: List, selectedIndex: Int?, // 선택한 인덱스 hasVoted: Boolean = false, // 투표 여부 onOptionSelected: (Int?) -> Unit @@ -123,9 +123,9 @@ private fun GroupVoteButtonPreview() { var voteItems by remember { mutableStateOf( listOf( - VoteItem(1, "밥", 25, false), - VoteItem(2, "국수", 35, false), - VoteItem(3, "고기", 40, false) + VoteItems(1, "밥", 25, false), + VoteItems(2, "국수", 35, false), + VoteItems(3, "고기", 40, false) ) ) } 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 cdf7c34d..5360e46a 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,8 +48,6 @@ 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 @@ -70,7 +68,6 @@ fun FeedCommentScreen( bookImage: Painter? = null, profileImage: String, feedType: FeedItemType, - profileImage: Painter? = null, currentUserId: Int, currentUserName: String, currentUserGenre: String, @@ -502,7 +499,7 @@ private fun FeedCommentScreenWithMockComments() { FeedCommentScreen( feedItem = mockFeedItem, feedType = FeedItemType.SAVABLE, - profileImage = painterResource(R.drawable.character_literature), + profileImage = "https://example.com/image1.jpg", currentUserId = 999, currentUserName = "나", currentUserGenre = "문학", @@ -535,11 +532,11 @@ private fun FeedCommentScreenPrev() { R.drawable.bookcover_sample, R.drawable.bookcover_sample ), - bookImage = painterResource(R.drawable.bookcover_sample), - profileImage = "https://example.com/image1.jpg", - onLikeClick = {}, - onCommentInputChange = {}, - onSendClick = {}, +// bookImage = painterResource(R.drawable.bookcover_sample), +// profileImage = "https://example.com/image1.jpg", +// onLikeClick = {}, +// onCommentInputChange = {}, +// onSendClick = {}, tags = listOf("에세이", "문학", "힐링") ) val commentList = remember { mutableStateListOf() } @@ -547,7 +544,7 @@ private fun FeedCommentScreenPrev() { FeedCommentScreen( feedItem = mockFeedItem, feedType = FeedItemType.SAVABLE, - profileImage = painterResource(R.drawable.character_literature), + profileImage = "https://example.com/image1.jpg", currentUserId = 999, currentUserName = "나", currentUserGenre = "문학", diff --git a/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt b/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt index 29bf101c..f6018495 100644 --- a/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt @@ -65,8 +65,8 @@ fun SearchBookDetailScreen( var selectedFilterOption by remember { mutableIntStateOf(0) } val filterOptions = listOf( - stringResource(R.string.search_filter_popular), - stringResource(R.string.search_filter_latest) + stringResource(R.string.sort_like), + stringResource(R.string.sort_latest) ) // 알림 5초간 노출 diff --git a/app/src/main/java/com/texthip/thip/utils/type/SortType.kt b/app/src/main/java/com/texthip/thip/utils/type/SortType.kt new file mode 100644 index 00000000..3b12aeb6 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/utils/type/SortType.kt @@ -0,0 +1,19 @@ +package com.texthip.thip.utils.type + +import androidx.annotation.StringRes +import com.texthip.thip.R + +enum class SortType ( + @StringRes val displayNameRes: Int, + val apiKey: String +) { + LATEST(R.string.sort_latest, "latest"), + LIKE(R.string.sort_like, "like"), + COMMENT(R.string.sort_comment, "comment"); + + companion object { + fun fromApiKey(key: String?): SortType { + return entries.find { it.apiKey == key } ?: LATEST + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 33dcceb3..37da0440 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -222,6 +222,7 @@ 첫번째 댓글을 남겨보세요 이 기록을 피드에 핀할까요? 핀하면 내 피드에 글을 옮길 수 있어요. + 댓글 많은 순 피드 @@ -337,8 +338,8 @@ 피드 글 둘러보기 이 책으로 작성된 피드가 없어요. 첫번째 피드를 작성해보세요! - 인기순 - 최신순 + 인기순 + 최신순 소개 %1$s 저 · %2$s 인기순 From c78770ee7eab17ce78c3ff4e0901ffc9d424ebf5 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 00:08:59 +0900 Subject: [PATCH 05/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20viewmodel=20=EC=83=9D=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rooms/request/RoomsPostsRequestParams.kt | 14 ++ .../note/viewmodel/GroupNoteViewModel.kt | 138 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsRequestParams.kt create mode 100644 app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsRequestParams.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsRequestParams.kt new file mode 100644 index 00000000..6b98cf0e --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsRequestParams.kt @@ -0,0 +1,14 @@ +package com.texthip.thip.data.model.rooms.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsPostsRequestParams( + val type: String, + val sort: String? = null, + val pageStart: Int? = null, + val pageEnd: Int? = null, + val isOverview: Boolean? = null, + val isPageFilter: Boolean? = null, + val cursor: String? = null +) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt new file mode 100644 index 00000000..39e1af07 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -0,0 +1,138 @@ +package com.texthip.thip.ui.group.note.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.texthip.thip.data.model.rooms.request.RoomsPostsRequestParams +import com.texthip.thip.data.model.rooms.response.PostList +import com.texthip.thip.data.repository.RoomsRepository +import com.texthip.thip.utils.type.SortType +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 GroupNoteUiState( + // 데이터 로딩 상태 + val isLoading: Boolean = false, + val isLoadingMore: Boolean = false, + val error: String? = null, + val isLastPage: Boolean = false, + + // 화면 데이터 + val posts: List = emptyList(), + + // 필터 및 탭 상태 + val selectedTabIndex: Int = 0, + val selectedSort: SortType = SortType.LATEST, + val pageStart: String = "", + val pageEnd: String = "", + val isOverview: Boolean = false, + val isPageFilter: Boolean = false, + val totalEnabled: Boolean = false +) + +sealed interface GroupNoteEvent { + data class OnTabSelected(val index: Int) : GroupNoteEvent + data class OnSortSelected(val sortType: SortType) : GroupNoteEvent + data class OnPageStartChanged(val page: String) : GroupNoteEvent + data class OnPageEndChanged(val page: String) : GroupNoteEvent + data class OnOverviewToggled(val isSelected: Boolean) : GroupNoteEvent + data object ApplyPageFilter : GroupNoteEvent + data object LoadMorePosts : GroupNoteEvent +} + + +@HiltViewModel +class GroupNoteViewModel @Inject constructor( + private val roomsRepository: RoomsRepository +) : ViewModel() { + + private val _uiState = MutableStateFlow(GroupNoteUiState()) + val uiState = _uiState.asStateFlow() + + private var nextCursor: String? = null + private var roomId: Int = -1 + + fun initialize(roomId: Int) { + this.roomId = roomId + loadPosts(isRefresh = true) + } + + fun onEvent(event: GroupNoteEvent) { + when (event) { + is GroupNoteEvent.OnTabSelected -> { + _uiState.update { it.copy(selectedTabIndex = event.index) } + loadPosts(isRefresh = true) + } + is GroupNoteEvent.OnSortSelected -> { + _uiState.update { it.copy(selectedSort = event.sortType) } + loadPosts(isRefresh = true) + } + is GroupNoteEvent.OnPageStartChanged -> _uiState.update { it.copy(pageStart = event.page) } + is GroupNoteEvent.OnPageEndChanged -> _uiState.update { it.copy(pageEnd = event.page) } + is GroupNoteEvent.OnOverviewToggled -> _uiState.update { it.copy(isOverview = event.isSelected) } + GroupNoteEvent.ApplyPageFilter -> loadPosts(isRefresh = true) + GroupNoteEvent.LoadMorePosts -> loadPosts(isRefresh = false) + } + } + + private fun loadPosts(isRefresh: Boolean = false) { + val currentState = _uiState.value + if (currentState.isLoading || currentState.isLoadingMore || (currentState.isLastPage && !isRefresh)) return + + viewModelScope.launch { + _uiState.update { + if (isRefresh) it.copy(isLoading = true, posts = emptyList(), error = null, isLastPage = false) + else it.copy(isLoadingMore = true, error = null) + } + + val cursor = if (isRefresh) null else nextCursor + val type = if (currentState.selectedTabIndex == 0) "group" else "mine" + + val params = if (type == "mine") { + // "mine" 탭일 경우 필수 파라미터만 채워 넣음 + RoomsPostsRequestParams(type = type, cursor = cursor) + } else { + // "group" 탭일 경우 모든 필터 파라미터 포함 + RoomsPostsRequestParams( + type = type, + sort = currentState.selectedSort.apiKey, + pageStart = currentState.pageStart.toIntOrNull(), + pageEnd = currentState.pageEnd.toIntOrNull(), + isOverview = currentState.isOverview, + isPageFilter = currentState.isPageFilter, + cursor = cursor + ) + } + + roomsRepository.getRoomsPosts( + roomId = roomId, + type = params.type, + sort = params.sort, + pageStart = params.pageStart, + pageEnd = params.pageEnd, + isOverview = params.isOverview, + isPageFilter = params.isPageFilter, + cursor = params.cursor + ).onSuccess { response -> + if (response != null) { + nextCursor = response.nextCursor + _uiState.update { + it.copy( + isLoading = false, + isLoadingMore = false, + posts = if (isRefresh) response.postList else it.posts + response.postList, + isLastPage = response.isLast + ) + } + } + }.onFailure { throwable -> + _uiState.update { + it.copy(isLoading = false, isLoadingMore = false, error = throwable.message) + } + } + } + } +} From f8c2504ec0abde76068f2e6e80672a2f0822f332 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 00:20:53 +0900 Subject: [PATCH 06/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20s?= =?UTF-8?q?creen=EC=97=90=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/note/component/TextCommentCard.kt | 10 +- .../group/note/component/VoteCommentCard.kt | 19 +- .../ui/group/note/screen/GroupNoteScreen.kt | 227 ++++++++++-------- 3 files changed, 145 insertions(+), 111 deletions(-) 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 1363f3e0..df00ff2b 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 @@ -17,16 +17,16 @@ 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.rooms.response.PostList import com.texthip.thip.ui.common.buttons.ActionBarButton import com.texthip.thip.ui.common.header.ProfileBar -import com.texthip.thip.ui.group.note.mock.GroupNoteRecord import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun TextCommentCard( modifier: Modifier = Modifier, - data: GroupNoteRecord, + data: PostList, onCommentClick: () -> Unit = {}, onLongPress: () -> Unit = {}, onPinClick: () -> Unit = {} @@ -84,7 +84,9 @@ fun TextCommentCard( @Composable fun TextCommentCardPreview() { TextCommentCard( - data = GroupNoteRecord( + data = PostList( + postId = 1, + postType = "group", page = 132, postDate = "12시간 전", userId = 1, @@ -96,7 +98,7 @@ fun TextCommentCardPreview() { isLiked = true, isWriter = false, isLocked = false, - recordId = 1 + voteItems = emptyList() ) ) } \ No newline at end of file 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 1662f38f..566e3dcb 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 @@ -17,18 +17,17 @@ 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.rooms.response.PostList import com.texthip.thip.ui.common.buttons.ActionBarButton import com.texthip.thip.ui.common.buttons.GroupVoteButton import com.texthip.thip.ui.common.header.ProfileBar -import com.texthip.thip.ui.group.note.mock.GroupNoteVote -import com.texthip.thip.ui.group.note.mock.VoteItem import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun VoteCommentCard( modifier: Modifier = Modifier, - data: GroupNoteVote, + data: PostList, onCommentClick: () -> Unit = {}, onLongPress: () -> Unit = {}, onPinClick: () -> Unit = {} @@ -110,23 +109,21 @@ fun VoteCommentCard( @Composable private fun VoteCommentCardPreview() { VoteCommentCard( - data = GroupNoteVote( + data = PostList( + postId = 1, + postType = "group", + page = 132, postDate = "12시간 전", - page = 12, userId = 1, nickName = "user.01", profileImageUrl = "https://example.com/profile.jpg", - content = "3연에 나오는 심장은 무엇을 의미하는 걸까요?", + content = "내 생각에 이 부분이 가장 어려운 것 같다. 비유도 난해하고 잘 이해가 가지 않는데 다른 메이트들은 어떻게 읽었나요?", likeCount = 123, commentCount = 123, isLiked = true, isWriter = false, isLocked = false, - voteId = 1, - voteItems = listOf( - VoteItem(1, "김땡땡", 90, false), - VoteItem(2, "김땡땡", 10, false), - ) + voteItems = emptyList() ) ) } \ No newline at end of file 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 3e24b774..2437fec7 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 @@ -14,15 +14,16 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon 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.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -32,7 +33,10 @@ 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 androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.texthip.thip.R +import com.texthip.thip.data.model.rooms.response.PostList import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet import com.texthip.thip.ui.common.buttons.ExpandableFloatingButton import com.texthip.thip.ui.common.buttons.FabMenuItem @@ -45,56 +49,78 @@ 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.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.note.viewmodel.GroupNoteEvent +import com.texthip.thip.ui.group.note.viewmodel.GroupNoteUiState +import com.texthip.thip.ui.group.note.viewmodel.GroupNoteViewModel 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 com.texthip.thip.utils.type.SortType import kotlinx.coroutines.delay @Composable -fun GroupNoteScreen() { - val tabs = listOf(stringResource(R.string.group_record), stringResource(R.string.my_record)) - var selectedTabIndex by rememberSaveable { mutableIntStateOf(0) } - - var firstPage by rememberSaveable { mutableStateOf("") } - var lastPage by rememberSaveable { mutableStateOf("") } - var isTotalSelected by rememberSaveable { mutableStateOf(false) } - var totalEnabled by rememberSaveable { mutableStateOf(false) } +fun GroupNoteScreen( + roomId: Int, + onBackClick: () -> Unit = {}, + viewModel: GroupNoteViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() - var selectedFilter by rememberSaveable { mutableStateOf("최신순") } - val filters = listOf("최신순", "인기순", "댓글 많은 순") - - val filteredItems = when (selectedTabIndex) { - 0 -> mockGroupNoteItems // 전체 기록 - 1 -> mockGroupNoteItems.filter { it.isWriter } // 내 기록만 - else -> emptyList() + LaunchedEffect(key1 = roomId) { + viewModel.initialize(roomId) } - 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) } + GroupNoteContent( + uiState = uiState, + onEvent = viewModel::onEvent, + onBackClick = onBackClick + ) +} +@Composable +fun GroupNoteContent( + uiState: GroupNoteUiState, + onEvent: (GroupNoteEvent) -> Unit, + onBackClick: () -> Unit +) { + var isCommentBottomSheetVisible by remember { mutableStateOf(false) } + var selectedPostForComment by remember { mutableStateOf(null) } + var selectedPostForMenu by remember { mutableStateOf(null) } 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초 + delay(3000) + showToast = false + } + } + + val tabs = listOf(stringResource(R.string.group_record), stringResource(R.string.my_record)) + val sortOptions = remember { SortType.entries.map { it.displayNameRes } } + val sortDisplayStrings = remember { SortType.entries.map { it.displayNameRes } } + .map { stringResource(it) } + + val listState = rememberLazyListState() + + val isScrolledToEnd by remember { + derivedStateOf { + val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull() + // 마지막 아이템이 보이고, 전체 아이템 수와 일치하며, 마지막 페이지가 아닐 때 + lastVisibleItem != null && lastVisibleItem.index == listState.layoutInfo.totalItemsCount - 1 && !uiState.isLastPage + } + } + + LaunchedEffect(isScrolledToEnd) { + if (isScrolledToEnd) { + onEvent(GroupNoteEvent.LoadMorePosts) } } Box( - if (isCommentBottomSheetVisible || isMenuBottomSheetVisible || isPinDialogVisible) { + if (isCommentBottomSheetVisible || selectedPostForMenu != null || isPinDialogVisible) { Modifier .fillMaxSize() .blur(5.dp) @@ -126,19 +152,23 @@ fun GroupNoteScreen() { Column(modifier = Modifier.fillMaxSize()) { DefaultTopAppBar( title = stringResource(R.string.record_book), - onLeftClick = {} + onLeftClick = onBackClick ) HeaderMenuBarTab( titles = tabs, - selectedTabIndex = selectedTabIndex, - onTabSelected = { selectedTabIndex = it }, + selectedTabIndex = uiState.selectedTabIndex, + onTabSelected = { onEvent(GroupNoteEvent.OnTabSelected(it)) }, modifier = Modifier .fillMaxWidth() .padding(top = 20.dp) ) - if (filteredItems.isEmpty()) { + if (uiState.isLoading) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } else if (uiState.posts.isEmpty()) { // 기록이 없을 때 중앙에 메시지 Column( modifier = Modifier @@ -156,7 +186,7 @@ fun GroupNoteScreen() { color = colors.White ) Text( - text = when (selectedTabIndex) { + text = when (uiState.selectedTabIndex) { 0 -> stringResource(R.string.no_group_record_subtext) 1 -> stringResource(R.string.no_my_record_subtext) else -> "" @@ -167,8 +197,8 @@ fun GroupNoteScreen() { } } else { // 피드 리스트 영역 - LazyColumn(modifier = Modifier.fillMaxSize()) { - if (selectedTabIndex == 0) { + LazyColumn(state = listState, modifier = Modifier.weight(1f)) { + if (uiState.selectedTabIndex == 0) { item { Row( modifier = Modifier.padding(top = 76.dp, start = 20.dp, end = 20.dp), @@ -189,54 +219,44 @@ fun GroupNoteScreen() { } } } - itemsIndexed(filteredItems) { index, item -> - val isLast = index == filteredItems.lastIndex - - val itemModifier = if (isLast) { + itemsIndexed(uiState.posts, key = { _, post -> post.postId }) { index, post -> + val itemModifier = if (index == uiState.posts.lastIndex) { Modifier.padding(bottom = 20.dp) } else { Modifier } - when (item) { - is GroupNoteRecord -> TextCommentCard( - data = item, + when (post.postType) { + "RECORD" -> TextCommentCard( + data = post, modifier = itemModifier, - onCommentClick = { - selectedNoteRecord = item - isCommentBottomSheetVisible = true - }, - onLongPress = { - selectedItemForMenu = item - isMenuBottomSheetVisible = true - }, - onPinClick = { - isPinDialogVisible = true - } + onCommentClick = { isCommentBottomSheetVisible = true }, + onLongPress = { selectedPostForMenu = post }, + onPinClick = { isPinDialogVisible = true } ) - is GroupNoteVote -> VoteCommentCard( - data = item, + "VOTE" -> VoteCommentCard( + data = post, modifier = itemModifier, - onCommentClick = { - selectedNoteVote = item - isCommentBottomSheetVisible = true - }, - onLongPress = { - selectedItemForMenu = item - isMenuBottomSheetVisible = true - }, - onPinClick = { - isPinDialogVisible = true - } + onCommentClick = { isCommentBottomSheetVisible = true }, + onLongPress = { selectedPostForMenu = post }, + onPinClick = { isPinDialogVisible = true } ) } } + + if (uiState.isLoadingMore) { + item { + Box(modifier = Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } + } } } } - if (selectedTabIndex == 0) { + if (uiState.selectedTabIndex == 0) { Box( modifier = Modifier .fillMaxWidth() @@ -248,19 +268,25 @@ fun GroupNoteScreen() { modifier = Modifier .align(Alignment.CenterEnd) .padding(end = 20.dp), - selectedOption = selectedFilter, - options = filters, - onOptionSelected = { selectedFilter = it } + selectedOption = stringResource(uiState.selectedSort.displayNameRes), + options = sortDisplayStrings, + onOptionSelected = { selectedString -> + val selectedIndex = sortDisplayStrings.indexOf(selectedString) + if (selectedIndex != -1) { + val selectedSortType = SortType.entries[selectedIndex] + onEvent(GroupNoteEvent.OnSortSelected(selectedSortType)) + } + } ) FilterHeaderSection( - firstPage = firstPage, - lastPage = lastPage, - isTotalSelected = isTotalSelected, - totalEnabled = totalEnabled, - onFirstPageChange = { firstPage = it }, - onLastPageChange = { lastPage = it }, - onTotalToggle = { isTotalSelected = !isTotalSelected }, + firstPage = uiState.pageStart, + lastPage = uiState.pageEnd, + isTotalSelected = uiState.isOverview, + totalEnabled = uiState.totalEnabled, + onFirstPageChange = { onEvent(GroupNoteEvent.OnPageStartChanged(it)) }, + onLastPageChange = { onEvent(GroupNoteEvent.OnPageEndChanged(it)) }, + onTotalToggle = { onEvent(GroupNoteEvent.OnOverviewToggled(!uiState.isOverview)) }, onDisabledClick = { showToast = true } ) } @@ -283,14 +309,15 @@ fun GroupNoteScreen() { } } - if (isCommentBottomSheetVisible && (selectedNoteRecord != null || selectedNoteVote != null)) { + if (isCommentBottomSheetVisible && selectedPostForComment != null) { CommentBottomSheet( commentResponse = listOf(mockComment, mockComment, mockComment), // commentResponse = emptyList(), onDismiss = { isCommentBottomSheetVisible = false - selectedNoteRecord = null - selectedNoteVote = null +// selectedNoteRecord = null +// selectedNoteVote = null + selectedPostForComment = null }, onSendReply = { replyText, commentId, replyTo -> // 댓글 전송 로직 구현 @@ -298,12 +325,14 @@ fun GroupNoteScreen() { ) } - if (isMenuBottomSheetVisible && selectedItemForMenu != null) { - val isWriter = when (val item = selectedItemForMenu) { - is GroupNoteRecord -> item.isWriter - is GroupNoteVote -> item.isWriter - else -> false - } +// if (isMenuBottomSheetVisible && selectedItemForMenu != null) { + if (selectedPostForMenu != null) { +// val isWriter = when (val item = selectedItemForMenu) { +// is GroupNoteRecord -> item.isWriter +// is GroupNoteVote -> item.isWriter +// else -> false +// } + val isWriter = selectedPostForMenu!!.isWriter val menuItems = if (isWriter) { listOf( @@ -312,8 +341,9 @@ fun GroupNoteScreen() { color = colors.Red, onClick = { // TODO: 삭제 처리 - isMenuBottomSheetVisible = false - selectedItemForMenu = null +// isMenuBottomSheetVisible = false +// selectedItemForMenu = null + selectedPostForMenu = null } ) ) @@ -324,8 +354,9 @@ fun GroupNoteScreen() { color = colors.Red, onClick = { // TODO: 신고 처리 - isMenuBottomSheetVisible = false - selectedItemForMenu = null +// isMenuBottomSheetVisible = false +// selectedItemForMenu = null + selectedPostForMenu = null } ) ) @@ -334,8 +365,9 @@ fun GroupNoteScreen() { MenuBottomSheet( items = menuItems, onDismiss = { - isMenuBottomSheetVisible = false - selectedItemForMenu = null +// isMenuBottomSheetVisible = false +// selectedItemForMenu = null + selectedPostForMenu = null } ) } @@ -365,6 +397,9 @@ fun GroupNoteScreen() { @Composable private fun GroupNoteScreenPreview() { ThipTheme { - GroupNoteScreen() + GroupNoteScreen( + roomId = 1, // 예시로 1번 방 ID 사용 + onBackClick = { /* 뒤로가기 동작 */ } + ) } } \ No newline at end of file From 0e48948af72d56b0811effa16bffe884d376d554 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 00:21:01 +0900 Subject: [PATCH 07/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=EC=9E=A5=20n?= =?UTF-8?q?avigation=20=EC=97=B0=EA=B2=B0=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/room/component/GroupRoomBody.kt | 7 ++++-- .../ui/group/room/screen/GroupRoomScreen.kt | 10 ++++++--- .../extensions/GroupNavigationExtensions.kt | 5 +++++ .../navigator/navigations/GroupNavigation.kt | 22 +++++++++++++++++++ .../thip/ui/navigator/routes/GroupRoutes.kt | 3 +++ 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt index ecfbf519..cc7fb42b 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt @@ -23,7 +23,8 @@ fun GroupRoomBody( authorName: String, currentPage: Int, userPercentage: Double, - currentVotes: List + currentVotes: List, + onNavigateToNote: () -> Unit = {}, ) { Column( modifier = modifier.padding(horizontal = 20.dp), @@ -37,7 +38,9 @@ fun GroupRoomBody( CardNote( currentPage = currentPage, percentage = userPercentage - ) {} + ) { + onNavigateToNote() + } CardChat( title = stringResource(R.string.group_room_chat), 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 9a40056b..ab29f59d 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 @@ -50,6 +50,7 @@ fun GroupRoomScreen( roomId: Int, onBackClick: () -> Unit = {}, onNavigateToMates: () -> Unit = {}, + onNavigateToNote: () -> Unit = {}, viewModel: GroupRoomViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -72,7 +73,8 @@ fun GroupRoomScreen( GroupRoomContent( roomDetails = state.roomsPlaying, onBackClick = onBackClick, - onNavigateToMates = onNavigateToMates + onNavigateToMates = onNavigateToMates, + onNavigateToNote = onNavigateToNote ) } @@ -89,7 +91,8 @@ fun GroupRoomScreen( fun GroupRoomContent( roomDetails: RoomsPlayingResponse, onBackClick: () -> Unit = {}, - onNavigateToMates: () -> Unit = {} + onNavigateToMates: () -> Unit = {}, + onNavigateToNote: () -> Unit = {}, ) { val scrollState = rememberScrollState() @@ -170,7 +173,8 @@ fun GroupRoomContent( authorName = roomDetails.authorName, currentPage = roomDetails.currentPage, userPercentage = roomDetails.userPercentage, - currentVotes = roomDetails.currentVotes + currentVotes = roomDetails.currentVotes, + onNavigateToNote = onNavigateToNote ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt index 2a6151ca..a6a39621 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt @@ -59,3 +59,8 @@ fun NavHostController.navigateToGroupRoom(roomId: Int) { fun NavHostController.navigateToGroupRoomMates(roomId: Int) { navigate(GroupRoutes.RoomMates(roomId)) } + +// 기록장 화면으로 이동 +fun NavHostController.navigateToGroupNote(roomId: Int) { + navigate(GroupRoutes.Note(roomId)) +} 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 e6693f61..c73fc453 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 @@ -17,6 +17,7 @@ import com.texthip.thip.ui.group.makeroom.viewmodel.GroupMakeRoomViewModel import com.texthip.thip.ui.group.myroom.mock.GroupBottomButtonType import com.texthip.thip.ui.group.myroom.mock.GroupRoomData import com.texthip.thip.ui.group.myroom.screen.GroupMyScreen +import com.texthip.thip.ui.group.note.screen.GroupNoteScreen import com.texthip.thip.ui.group.room.screen.GroupRoomMatesScreen import com.texthip.thip.ui.group.room.screen.GroupRoomRecruitScreen import com.texthip.thip.ui.group.room.screen.GroupRoomScreen @@ -28,6 +29,7 @@ import com.texthip.thip.ui.navigator.extensions.navigateToAlarm import com.texthip.thip.ui.navigator.extensions.navigateToGroupDone import com.texthip.thip.ui.navigator.extensions.navigateToGroupMakeRoom import com.texthip.thip.ui.navigator.extensions.navigateToGroupMy +import com.texthip.thip.ui.navigator.extensions.navigateToGroupNote import com.texthip.thip.ui.navigator.extensions.navigateToGroupRecruit import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoom import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoomMates @@ -251,6 +253,9 @@ fun NavGraphBuilder.groupNavigation( onNavigateToMates = { navController.navigateToGroupRoomMates(roomId) }, + onNavigateToNote = { + navController.navigateToGroupNote(roomId) + }, ) } @@ -270,4 +275,21 @@ fun NavGraphBuilder.groupNavigation( } ) } + + // Group Note 화면 + composable { backStackEntry -> + val route = backStackEntry.toRoute() + val roomId = route.roomId + + GroupNoteScreen( +// roomId = roomId, + roomId = 1, + onBackClick = { + navigateBack() + }, +// onNoteClick = { noteId -> +// // 노트 상세 화면으로 이동 +// } + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt index 533bf084..b1c48d7e 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt @@ -24,4 +24,7 @@ sealed class GroupRoutes : Routes() { @Serializable data class RoomMates(val roomId: Int) : GroupRoutes() + + @Serializable + data class Note(val roomId: Int) : GroupRoutes() } \ No newline at end of file From bd1975dfeb94dd2d2eab5bb0708d292486dda9c2 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 00:37:34 +0900 Subject: [PATCH 08/47] =?UTF-8?q?[ui]:=20=ED=95=84=ED=84=B0=EC=AA=BD=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/component/FilterHeaderSection.kt | 3 +- .../ui/group/note/screen/GroupNoteScreen.kt | 63 ++++++++++++++++--- 2 files changed, 55 insertions(+), 11 deletions(-) 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 33e37221..0d4b398b 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 @@ -24,6 +24,7 @@ import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun FilterHeaderSection( + modifier: Modifier = Modifier, firstPage: String, lastPage: String, isTotalSelected: Boolean, @@ -37,7 +38,7 @@ fun FilterHeaderSection( val isPageFiltered = firstPage.isNotBlank() || lastPage.isNotBlank() Box( - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(horizontal = 20.dp), contentAlignment = Alignment.Center 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 2437fec7..69d27ec6 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 @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.itemsIndexed @@ -201,7 +202,11 @@ fun GroupNoteContent( if (uiState.selectedTabIndex == 0) { item { Row( - modifier = Modifier.padding(top = 76.dp, start = 20.dp, end = 20.dp), + modifier = Modifier.padding( + top = 76.dp, + start = 20.dp, + end = 20.dp + ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), ) { @@ -219,7 +224,9 @@ fun GroupNoteContent( } } } - itemsIndexed(uiState.posts, key = { _, post -> post.postId }) { index, post -> + itemsIndexed( + uiState.posts, + key = { _, post -> post.postId }) { index, post -> val itemModifier = if (index == uiState.posts.lastIndex) { Modifier.padding(bottom = 20.dp) } else { @@ -247,7 +254,12 @@ fun GroupNoteContent( if (uiState.isLoadingMore) { item { - Box(modifier = Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { CircularProgressIndicator() } } @@ -260,14 +272,16 @@ fun GroupNoteContent( Box( modifier = Modifier .fillMaxWidth() - .padding(top = 119.dp) - .background(color = colors.Black) - .padding(top = 20.dp) + .padding(top = 118.dp) ) { + Box( + modifier = Modifier.fillMaxWidth().height(56.dp).background(color=colors.Black) + ) + FilterButton( modifier = Modifier .align(Alignment.CenterEnd) - .padding(end = 20.dp), + .padding(top = 20.dp, end = 20.dp), selectedOption = stringResource(uiState.selectedSort.displayNameRes), options = sortDisplayStrings, onOptionSelected = { selectedString -> @@ -280,6 +294,7 @@ fun GroupNoteContent( ) FilterHeaderSection( + modifier = Modifier.padding(top = 20.dp), firstPage = uiState.pageStart, lastPage = uiState.pageEnd, isTotalSelected = uiState.isOverview, @@ -397,9 +412,37 @@ fun GroupNoteContent( @Composable private fun GroupNoteScreenPreview() { ThipTheme { - GroupNoteScreen( - roomId = 1, // 예시로 1번 방 ID 사용 - onBackClick = { /* 뒤로가기 동작 */ } + GroupNoteContent( + uiState = GroupNoteUiState( + posts = listOf( + PostList( + userId = 1, + profileImageUrl = "https://example.com/profile.jpg", + voteItems = emptyList(), + postId = 1, + postType = "RECORD", + page = 1, + postDate = "12시간 전", + nickName = "사용자1", + content = "첫 번째 기록입니다.", + isLiked = false, + likeCount = 10, + commentCount = 2, + isLocked = false, + isWriter = true + ) + ), + selectedTabIndex = 0, + selectedSort = SortType.LATEST, + isLoading = false, + isLoadingMore = false, + pageStart = "1", + pageEnd = "10", + isOverview = false, + totalEnabled = true + ), + onEvent = {}, + onBackClick = {} ) } } \ No newline at end of file From be8b564e57020047e947d150caa1548e074e183f Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 00:47:33 +0900 Subject: [PATCH 09/47] =?UTF-8?q?[refactor]:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=20=ED=95=84=ED=84=B0=EB=A7=81=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/group/note/component/FilterHeaderSection.kt | 7 +++++-- .../texthip/thip/ui/group/note/screen/GroupNoteScreen.kt | 3 ++- .../thip/ui/group/note/viewmodel/GroupNoteViewModel.kt | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) 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 0d4b398b..aff12d5a 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 @@ -32,7 +32,8 @@ fun FilterHeaderSection( onFirstPageChange: (String) -> Unit, onLastPageChange: (String) -> Unit, onTotalToggle: () -> Unit, - onDisabledClick: () -> Unit = { } + onDisabledClick: () -> Unit = { }, + onApplyPageFilter: () -> Unit ) { var isPageInputVisible by rememberSaveable { mutableStateOf(false) } val isPageFiltered = firstPage.isNotBlank() || lastPage.isNotBlank() @@ -77,6 +78,7 @@ fun FilterHeaderSection( lastPage = lastPage, onLastPageChange = onLastPageChange, onFinishClick = { + onApplyPageFilter() isPageInputVisible = false } ) @@ -98,6 +100,7 @@ private fun FilterHeaderSectionPreview() { onFirstPageChange = { firstPage = it }, onLastPageChange = { lastPage = it }, onTotalToggle = { isTotalSelected = !isTotalSelected }, - totalEnabled = true + totalEnabled = true, + onApplyPageFilter = {}, ) } \ No newline at end of file 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 69d27ec6..8d7389db 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 @@ -302,7 +302,8 @@ fun GroupNoteContent( onFirstPageChange = { onEvent(GroupNoteEvent.OnPageStartChanged(it)) }, onLastPageChange = { onEvent(GroupNoteEvent.OnPageEndChanged(it)) }, onTotalToggle = { onEvent(GroupNoteEvent.OnOverviewToggled(!uiState.isOverview)) }, - onDisabledClick = { showToast = true } + onDisabledClick = { showToast = true }, + onApplyPageFilter = { onEvent(GroupNoteEvent.ApplyPageFilter) } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index 39e1af07..b91af084 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -73,7 +73,13 @@ class GroupNoteViewModel @Inject constructor( is GroupNoteEvent.OnPageStartChanged -> _uiState.update { it.copy(pageStart = event.page) } is GroupNoteEvent.OnPageEndChanged -> _uiState.update { it.copy(pageEnd = event.page) } is GroupNoteEvent.OnOverviewToggled -> _uiState.update { it.copy(isOverview = event.isSelected) } - GroupNoteEvent.ApplyPageFilter -> loadPosts(isRefresh = true) + GroupNoteEvent.ApplyPageFilter -> { + val currentState = _uiState.value + val isFilterActive = currentState.pageStart.isNotBlank() || currentState.pageEnd.isNotBlank() + + _uiState.update { it.copy(isPageFilter = isFilterActive) } + loadPosts(isRefresh = true) + } GroupNoteEvent.LoadMorePosts -> loadPosts(isRefresh = false) } } From 7bdd7dac3ce35a29778580f1840485c65303fb49 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:01:32 +0900 Subject: [PATCH 10/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20request,=20response=20=EC=83=9D=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/model/rooms/request/RoomsRecordRequest.kt | 10 ++++++++++ .../data/model/rooms/response/RoomsRecordResponse.kt | 9 +++++++++ 2 files changed, 19 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsRecordRequest.kt create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsRecordResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsRecordRequest.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsRecordRequest.kt new file mode 100644 index 00000000..649fcf96 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsRecordRequest.kt @@ -0,0 +1,10 @@ +package com.texthip.thip.data.model.rooms.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsRecordRequest( + val page: Int, + val isOverview: Boolean = false, + val content: String, +) diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsRecordResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsRecordResponse.kt new file mode 100644 index 00000000..003c1e71 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsRecordResponse.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsRecordResponse( + val recordId: Int, + val roomId: Int, +) From 7bfed3bf7cf3395b241badff77dae4eee73f9d26 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:01:49 +0900 Subject: [PATCH 11/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20request,=20service=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/data/service/RoomsService.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index b236f4be..ab86cbcc 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -1,10 +1,14 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse +import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse +import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse +import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -30,4 +34,10 @@ interface RoomsService { @Query("isPageFilter") isPageFilter: Boolean? = false, @Query("cursor") cursor: String? = null, ): BaseResponse + + @POST("rooms/{roomId}/record") + suspend fun postRoomsRecord( + @Path("roomId") roomId: Int, + @Body request: RoomsRecordRequest + ): BaseResponse } \ No newline at end of file From 2a72200ab6d94f39728334dbcb39fdf0aafe7849 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:01:55 +0900 Subject: [PATCH 12/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20request,=20repository=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/repository/RoomsRepository.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index a5d5bc16..7dbbba32 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -1,6 +1,7 @@ package com.texthip.thip.data.repository import com.texthip.thip.data.model.base.handleBaseResponse +import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest import com.texthip.thip.data.service.RoomsService import javax.inject.Inject import javax.inject.Singleton @@ -46,4 +47,20 @@ class RoomsRepository @Inject constructor( cursor = cursor ).handleBaseResponse().getOrThrow() } + + suspend fun postRoomsRecord( + roomId: Int, + content: String, + isOverview: Boolean = false, + page: Int = 0 + ) = runCatching { + roomsService.postRoomsRecord( + roomId = roomId, + request = RoomsRecordRequest( + page = page, + isOverview = isOverview, + content = content + ) + ).handleBaseResponse().getOrThrow() + } } \ No newline at end of file From 5ac5643a30b822eafdcd55e5d5ed59103ba862cc Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:19:52 +0900 Subject: [PATCH 13/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20viewmodel=20=EC=83=9D=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../viewmodel/GroupNoteCreateViewModel.kt | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt new file mode 100644 index 00000000..36c78b25 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt @@ -0,0 +1,87 @@ +package com.texthip.thip.ui.group.note.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.texthip.thip.data.repository.RoomsRepository +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 GroupNoteCreateUiState( + val pageText: String = "", + val opinionText: String = "", + val isGeneralReview: Boolean = false, + val isLoading: Boolean = false, + val isSuccess: Boolean = false, + val error: String? = null +) { + // 입력 폼이 모두 채워졌는지 확인 + val isFormFilled: Boolean get() = pageText.isNotBlank() && opinionText.isNotBlank() +} + +sealed interface GroupNoteCreateEvent { + data class PageChanged(val text: String) : GroupNoteCreateEvent + data class OpinionChanged(val text: String) : GroupNoteCreateEvent + data class GeneralReviewToggled(val isChecked: Boolean) : GroupNoteCreateEvent + data object CreateRecordClicked : GroupNoteCreateEvent +} + +@HiltViewModel +class GroupNoteCreateViewModel @Inject constructor( + private val roomsRepository: RoomsRepository +) : ViewModel() { + private val _uiState = MutableStateFlow(GroupNoteCreateUiState()) + val uiState = _uiState.asStateFlow() + + private var roomId: Int = -1 + + fun initialize(id: Int) { + roomId = id + } + + fun onEvent(event: GroupNoteCreateEvent) { + when (event) { + is GroupNoteCreateEvent.PageChanged -> { + _uiState.update { it.copy(pageText = event.text) } + } + is GroupNoteCreateEvent.OpinionChanged -> { + _uiState.update { it.copy(opinionText = event.text) } + } + is GroupNoteCreateEvent.GeneralReviewToggled -> { + _uiState.update { it.copy(isGeneralReview = event.isChecked) } + } + GroupNoteCreateEvent.CreateRecordClicked -> { + createRecord() + } + } + } + + private fun createRecord() { + val currentState = _uiState.value + val pageNumber = currentState.pageText.toIntOrNull() + if (pageNumber == null || !currentState.isFormFilled) { + _uiState.update { it.copy(error = "페이지 번호를 정확히 입력해주세요.") } + return + } + + viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + + roomsRepository.postRoomsRecord( + roomId = roomId, + content = currentState.opinionText, + isOverview = currentState.isGeneralReview, + page = pageNumber + ).onSuccess { + _uiState.update { it.copy(isLoading = false, isSuccess = true) } + }.onFailure { throwable -> + _uiState.update { + it.copy(isLoading = false, error = throwable.message) + } + } + } + } +} \ No newline at end of file From e2c03e6731ca6e103f362b567cc931bc20b10c6e Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:20:32 +0900 Subject: [PATCH 14/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=A1=9C=EC=A7=81=20screen=EC=97=90=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/screen/GroupNoteCreateScreen.kt | 75 +++++++++++++++---- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt index 083c01c5..f108a7af 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt @@ -6,12 +6,15 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.absoluteOffset import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.positionInRoot @@ -20,22 +23,50 @@ 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 androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.texthip.thip.R import com.texthip.thip.ui.common.modal.ArrowPosition import com.texthip.thip.ui.common.modal.PopupModal import com.texthip.thip.ui.common.topappbar.InputTopAppBar import com.texthip.thip.ui.group.note.component.OpinionInputSection import com.texthip.thip.ui.group.note.component.PageInputSection +import com.texthip.thip.ui.group.note.viewmodel.GroupNoteCreateEvent +import com.texthip.thip.ui.group.note.viewmodel.GroupNoteCreateUiState +import com.texthip.thip.ui.group.note.viewmodel.GroupNoteCreateViewModel import com.texthip.thip.ui.theme.ThipTheme @Composable -fun GroupNoteCreateScreen() { - var pageText by rememberSaveable { mutableStateOf("") } - var isGeneralReview by rememberSaveable { mutableStateOf(false) } - var opinionText by rememberSaveable { mutableStateOf("") } +fun GroupNoteCreateScreen( + roomId: Int, + onBackClick: () -> Unit, + viewModel: GroupNoteCreateViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val isFormFilled = pageText.isNotBlank() && opinionText.isNotBlank() + LaunchedEffect(key1 = roomId) { + viewModel.initialize(roomId) + } + + LaunchedEffect(key1 = uiState.isSuccess) { + if (uiState.isSuccess) { + onBackClick() + } + } + GroupNoteCreateContent( + uiState = uiState, + onEvent = viewModel::onEvent, + onBackClick = onBackClick + ) +} + +@Composable +fun GroupNoteCreateContent( + uiState: GroupNoteCreateUiState, + onEvent: (GroupNoteCreateEvent) -> Unit, + onBackClick: () -> Unit +) { val density = LocalDensity.current var showTooltip by rememberSaveable { mutableStateOf(false) } @@ -50,9 +81,9 @@ fun GroupNoteCreateScreen() { Column { InputTopAppBar( title = stringResource(R.string.write_record), - isRightButtonEnabled = isFormFilled, - onLeftClick = { /* 뒤로가기 동작 */ }, - onRightClick = { /* 완료 동작 */ } + isRightButtonEnabled = uiState.isFormFilled, + onLeftClick = onBackClick, + onRightClick = { onEvent(GroupNoteCreateEvent.CreateRecordClicked) } ) Column( @@ -61,10 +92,10 @@ fun GroupNoteCreateScreen() { verticalArrangement = Arrangement.spacedBy(32.dp), ) { PageInputSection( - pageText = pageText, - onPageTextChange = { pageText = it }, - isGeneralReview = isGeneralReview, - onGeneralReviewToggle = { isGeneralReview = it }, + pageText = uiState.pageText, + onPageTextChange = { onEvent(GroupNoteCreateEvent.PageChanged(it)) }, + isGeneralReview = uiState.isGeneralReview, + onGeneralReviewToggle = { onEvent(GroupNoteCreateEvent.GeneralReviewToggled(it)) }, isEligible = isEligible, bookTotalPage = 600, onInfoClick = { showTooltip = true }, @@ -72,8 +103,8 @@ fun GroupNoteCreateScreen() { ) OpinionInputSection( - text = opinionText, - onTextChange = { opinionText = it } + text = uiState.opinionText, + onTextChange = { onEvent(GroupNoteCreateEvent.OpinionChanged(it)) } ) } @@ -97,14 +128,26 @@ fun GroupNoteCreateScreen() { ) } } - } + if (uiState.isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + } } @Preview @Composable private fun GroupNoteCreateScreenPreview() { ThipTheme { - GroupNoteCreateScreen() + GroupNoteCreateContent( + uiState = GroupNoteCreateUiState(pageText = "123", opinionText = "재미있었다."), + onEvent = {}, + onBackClick = {} + ) } } \ No newline at end of file From 8f21cfd51589fd9d5f8f127628dc38223a7473d3 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:21:02 +0900 Subject: [PATCH 15/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20navigation=20=EC=97=B0=EA=B2=B0=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/note/screen/GroupNoteScreen.kt | 12 +++++++---- .../extensions/GroupNavigationExtensions.kt | 5 +++++ .../navigator/navigations/GroupNavigation.kt | 21 ++++++++++++++++--- .../thip/ui/navigator/routes/GroupRoutes.kt | 3 +++ 4 files changed, 34 insertions(+), 7 deletions(-) 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 8d7389db..90510c4a 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 @@ -65,6 +65,7 @@ import kotlinx.coroutines.delay fun GroupNoteScreen( roomId: Int, onBackClick: () -> Unit = {}, + onCreateNoteClick: () -> Unit = {}, viewModel: GroupNoteViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -76,7 +77,8 @@ fun GroupNoteScreen( GroupNoteContent( uiState = uiState, onEvent = viewModel::onEvent, - onBackClick = onBackClick + onBackClick = onBackClick, + onCreateNoteClick = onCreateNoteClick ) } @@ -84,7 +86,8 @@ fun GroupNoteScreen( fun GroupNoteContent( uiState: GroupNoteUiState, onEvent: (GroupNoteEvent) -> Unit, - onBackClick: () -> Unit + onBackClick: () -> Unit, + onCreateNoteClick: () -> Unit ) { var isCommentBottomSheetVisible by remember { mutableStateOf(false) } var selectedPostForComment by remember { mutableStateOf(null) } @@ -313,7 +316,7 @@ fun GroupNoteContent( FabMenuItem( icon = painterResource(R.drawable.ic_write), text = stringResource(R.string.write_record), - onClick = { } + onClick = onCreateNoteClick ), FabMenuItem( icon = painterResource(R.drawable.ic_vote), @@ -443,7 +446,8 @@ private fun GroupNoteScreenPreview() { totalEnabled = true ), onEvent = {}, - onBackClick = {} + onBackClick = {}, + onCreateNoteClick = {} ) } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt index a6a39621..649d0249 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt @@ -64,3 +64,8 @@ fun NavHostController.navigateToGroupRoomMates(roomId: Int) { fun NavHostController.navigateToGroupNote(roomId: Int) { navigate(GroupRoutes.Note(roomId)) } + +// 기록 생성 화면으로 이동 +fun NavHostController.navigateToGroupNoteCreate(roomId: Int) { + navigate(GroupRoutes.NoteCreate(roomId)) +} \ No newline at end of file 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 c73fc453..1c986432 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 @@ -17,6 +17,7 @@ import com.texthip.thip.ui.group.makeroom.viewmodel.GroupMakeRoomViewModel import com.texthip.thip.ui.group.myroom.mock.GroupBottomButtonType import com.texthip.thip.ui.group.myroom.mock.GroupRoomData import com.texthip.thip.ui.group.myroom.screen.GroupMyScreen +import com.texthip.thip.ui.group.note.screen.GroupNoteCreateScreen import com.texthip.thip.ui.group.note.screen.GroupNoteScreen import com.texthip.thip.ui.group.room.screen.GroupRoomMatesScreen import com.texthip.thip.ui.group.room.screen.GroupRoomRecruitScreen @@ -30,6 +31,7 @@ import com.texthip.thip.ui.navigator.extensions.navigateToGroupDone import com.texthip.thip.ui.navigator.extensions.navigateToGroupMakeRoom import com.texthip.thip.ui.navigator.extensions.navigateToGroupMy import com.texthip.thip.ui.navigator.extensions.navigateToGroupNote +import com.texthip.thip.ui.navigator.extensions.navigateToGroupNoteCreate import com.texthip.thip.ui.navigator.extensions.navigateToGroupRecruit import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoom import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoomMates @@ -287,9 +289,22 @@ fun NavGraphBuilder.groupNavigation( onBackClick = { navigateBack() }, -// onNoteClick = { noteId -> -// // 노트 상세 화면으로 이동 -// } + onCreateNoteClick = { + navController.navigateToGroupNoteCreate(1) + }, + ) + } + + // Group Note Create 화면 + composable { backStackEntry -> + val route = backStackEntry.toRoute() + val roomId = route.roomId + + GroupNoteCreateScreen( + roomId = roomId, + onBackClick = { + navigateBack() + } ) } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt index b1c48d7e..88f914b2 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt @@ -27,4 +27,7 @@ sealed class GroupRoutes : Routes() { @Serializable data class Note(val roomId: Int) : GroupRoutes() + + @Serializable + data class NoteCreate(val roomId: Int) : GroupRoutes() } \ No newline at end of file From 9882cbdc8a0adf30ab71d978d589948709887e6d Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:28:40 +0900 Subject: [PATCH 16/47] =?UTF-8?q?[refactor]:=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EC=99=84=EB=A3=8C=EC=8B=9C=20=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=ED=83=AD=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#7?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/note/screen/GroupNoteCreateScreen.kt | 3 ++- .../thip/ui/group/note/screen/GroupNoteScreen.kt | 13 ++++++++++++- .../ui/navigator/navigations/GroupNavigation.kt | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt index f108a7af..38e03064 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt @@ -40,6 +40,7 @@ import com.texthip.thip.ui.theme.ThipTheme fun GroupNoteCreateScreen( roomId: Int, onBackClick: () -> Unit, + onNavigateBackWithResult: () -> Unit, viewModel: GroupNoteCreateViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -50,7 +51,7 @@ fun GroupNoteCreateScreen( LaunchedEffect(key1 = uiState.isSuccess) { if (uiState.isSuccess) { - onBackClick() + onNavigateBackWithResult() } } 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 90510c4a..babdb913 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 @@ -66,12 +66,23 @@ fun GroupNoteScreen( roomId: Int, onBackClick: () -> Unit = {}, onCreateNoteClick: () -> Unit = {}, + resultTabIndex: Int? = null, + onResultConsumed: () -> Unit = {}, viewModel: GroupNoteViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + LaunchedEffect(resultTabIndex) { + if (resultTabIndex != null) { + viewModel.onEvent(GroupNoteEvent.OnTabSelected(resultTabIndex)) + onResultConsumed() + } + } + LaunchedEffect(key1 = roomId) { - viewModel.initialize(roomId) + if (resultTabIndex == null) { + viewModel.initialize(roomId) + } } GroupNoteContent( 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 1c986432..a0289c69 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 @@ -283,14 +283,20 @@ fun NavGraphBuilder.groupNavigation( val route = backStackEntry.toRoute() val roomId = route.roomId + val result = backStackEntry.savedStateHandle.get("selected_tab_index") + GroupNoteScreen( // roomId = roomId, roomId = 1, + resultTabIndex = result, + onResultConsumed = { + backStackEntry.savedStateHandle.remove("selected_tab_index") + }, onBackClick = { navigateBack() }, onCreateNoteClick = { - navController.navigateToGroupNoteCreate(1) + navController.navigateToGroupNoteCreate(roomId) }, ) } @@ -301,9 +307,15 @@ fun NavGraphBuilder.groupNavigation( val roomId = route.roomId GroupNoteCreateScreen( - roomId = roomId, + roomId = 1, onBackClick = { navigateBack() + }, + onNavigateBackWithResult = { + navController.previousBackStackEntry + ?.savedStateHandle + ?.set("selected_tab_index", 1) + navigateBack() } ) } From 7e20b538367a4af78808f72bf00ed6cea67a1948 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sat, 9 Aug 2025 02:04:09 +0900 Subject: [PATCH 17/47] =?UTF-8?q?[refactor]:=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EC=99=84=EB=A3=8C=20=EC=8B=9C=20progress?= =?UTF-8?q?=20bar=20=EB=82=98=ED=83=80=EB=82=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/note/screen/GroupNoteScreen.kt | 91 ++++++++++++++++++- app/src/main/res/values/strings.xml | 2 + 2 files changed, 89 insertions(+), 4 deletions(-) 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 babdb913..a330700c 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,6 +1,8 @@ package com.texthip.thip.ui.group.note.screen import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically @@ -9,6 +11,7 @@ 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.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -16,6 +19,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn 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.Icon import androidx.compose.material3.Text @@ -25,10 +29,12 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope 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.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -59,7 +65,9 @@ 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.type.SortType +import kotlinx.coroutines.Job import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable fun GroupNoteScreen( @@ -71,11 +79,39 @@ fun GroupNoteScreen( viewModel: GroupNoteViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + + var showProgressBar by remember { mutableStateOf(false) } + val progress = remember { Animatable(0f) } + var progressJob by remember { mutableStateOf(null) } LaunchedEffect(resultTabIndex) { if (resultTabIndex != null) { viewModel.onEvent(GroupNoteEvent.OnTabSelected(resultTabIndex)) onResultConsumed() + + showProgressBar = true + progress.snapTo(0f) + progressJob = scope.launch { + progress.animateTo( + targetValue = 1f, + animationSpec = tween(durationMillis = 3000, easing = LinearEasing) + ) + delay(500) + if (showProgressBar) { + showProgressBar = false + } + } + } + } + + LaunchedEffect(uiState.isLoading) { + // 로딩이 끝났고, 프로그레스 바가 보이는 중이라면 + if (!uiState.isLoading && showProgressBar) { + progressJob?.cancel() // 진행 중인 3초 애니메이션 취소 + progress.snapTo(1f) // 즉시 100%로 변경 + delay(500) // 100% 상태를 잠시 보여줌 + showProgressBar = false // 프로그레스 바 숨기기 } } @@ -89,7 +125,9 @@ fun GroupNoteScreen( uiState = uiState, onEvent = viewModel::onEvent, onBackClick = onBackClick, - onCreateNoteClick = onCreateNoteClick + onCreateNoteClick = onCreateNoteClick, + showProgressBar = showProgressBar, + progress = progress.value ) } @@ -98,7 +136,9 @@ fun GroupNoteContent( uiState: GroupNoteUiState, onEvent: (GroupNoteEvent) -> Unit, onBackClick: () -> Unit, - onCreateNoteClick: () -> Unit + onCreateNoteClick: () -> Unit, + showProgressBar: Boolean, // [추가] + progress: Float // [추가] ) { var isCommentBottomSheetVisible by remember { mutableStateOf(false) } var selectedPostForComment by remember { mutableStateOf(null) } @@ -238,6 +278,44 @@ fun GroupNoteContent( } } } + item { + AnimatedVisibility(visible = showProgressBar) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(start = 20.dp, end = 20.dp, top = 32.dp), + ) { + Text( + modifier = Modifier.padding(bottom = 12.dp), + text = if (progress < 1.0f) { + stringResource(R.string.posting_in_progress) + } else { + stringResource(R.string.posting_complete) + }, + style = typography.view_m500_s14, + color = colors.NeonGreen + ) + + Box( + modifier = Modifier + .fillMaxWidth() + .height(8.dp) + .clip(RoundedCornerShape(12.dp)) + .background(color = colors.Grey02) // 트랙(배경) 색상 + ) { + Box( + modifier = Modifier + .fillMaxWidth(fraction = progress) + .fillMaxHeight() + .background( + color = colors.NeonGreen, + shape = RoundedCornerShape(12.dp) + ) + ) + } + } + } + } itemsIndexed( uiState.posts, key = { _, post -> post.postId }) { index, post -> @@ -289,7 +367,10 @@ fun GroupNoteContent( .padding(top = 118.dp) ) { Box( - modifier = Modifier.fillMaxWidth().height(56.dp).background(color=colors.Black) + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .background(color = colors.Black) ) FilterButton( @@ -458,7 +539,9 @@ private fun GroupNoteScreenPreview() { ), onEvent = {}, onBackClick = {}, - onCreateNoteClick = {} + onCreateNoteClick = {}, + showProgressBar = true, + progress = 0.5f ) } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 37da0440..a95adcb1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -223,6 +223,8 @@ 이 기록을 피드에 핀할까요? 핀하면 내 피드에 글을 옮길 수 있어요. 댓글 많은 순 + 기록을 게시 중입니다... + 기록이 게시되었습니다! 피드 From 52255039a2041c9015bb569b5dd3660efeac0dc8 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Sun, 10 Aug 2025 23:16:12 +0900 Subject: [PATCH 18/47] =?UTF-8?q?[refactor]:=20=EB=AA=A8=EC=9E=84=EB=B0=A9?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20=ED=88=AC=ED=91=9C=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=ED=95=98=EB=A9=B4=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=95=84=ED=84=B0=20=EA=B1=B8=EC=96=B4=EC=84=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/note/screen/GroupNoteScreen.kt | 5 ++++- .../group/note/viewmodel/GroupNoteViewModel.kt | 17 ++++++++++++++++- .../ui/group/room/component/GroupRoomBody.kt | 6 ++++-- .../ui/group/room/screen/GroupRoomScreen.kt | 18 ++++++++++++++---- .../extensions/GroupNavigationExtensions.kt | 8 ++++++-- .../navigator/navigations/GroupNavigation.kt | 12 +++++++----- .../thip/ui/navigator/routes/GroupRoutes.kt | 13 +++++++------ 7 files changed, 58 insertions(+), 21 deletions(-) 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 a330700c..7384c975 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 @@ -76,6 +76,8 @@ fun GroupNoteScreen( onCreateNoteClick: () -> Unit = {}, resultTabIndex: Int? = null, onResultConsumed: () -> Unit = {}, + initialPage: Int? = null, + initialIsOverview: Boolean? = null, viewModel: GroupNoteViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -116,8 +118,9 @@ fun GroupNoteScreen( } LaunchedEffect(key1 = roomId) { + // 기록 생성 후 돌아온 경우가 아닐 때 (처음 진입 시) 초기화 if (resultTabIndex == null) { - viewModel.initialize(roomId) + viewModel.initialize(roomId, initialPage, initialIsOverview) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index b91af084..250cebf5 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -55,8 +55,23 @@ class GroupNoteViewModel @Inject constructor( private var nextCursor: String? = null private var roomId: Int = -1 - fun initialize(roomId: Int) { + fun initialize( + roomId: Int, + initialPage: Int? = null, + initialIsOverview: Boolean? = null + ) { this.roomId = roomId + + if (initialPage != null || initialIsOverview != null) { + _uiState.update { + it.copy( + pageStart = initialPage?.toString() ?: "", + pageEnd = initialPage?.toString() ?: "", + isOverview = initialIsOverview ?: false, + isPageFilter = initialPage != null + ) + } + } loadPosts(isRefresh = true) } diff --git a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt index cc7fb42b..1e25e71d 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/room/component/GroupRoomBody.kt @@ -25,6 +25,7 @@ fun GroupRoomBody( userPercentage: Double, currentVotes: List, onNavigateToNote: () -> Unit = {}, + onVoteClick: (CurrentVote) -> Unit = {} ) { Column( modifier = modifier.padding(horizontal = 20.dp), @@ -48,7 +49,8 @@ fun GroupRoomBody( ) {} CardVote( - voteData = currentVotes + voteData = currentVotes, + onVoteClick = onVoteClick ) } } @@ -61,7 +63,7 @@ private fun GroupRoomBodyPreview() { authorName = "저자 이름", currentPage = 100, userPercentage = 50.0, - currentVotes = listOf( + currentVotes = listOf( CurrentVote( content = "3연에 나오는 심장은 무엇을 의미하는 걸까요?", page = 12, 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 ab29f59d..9ac9b3b8 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 @@ -1,6 +1,5 @@ package com.texthip.thip.ui.group.room.screen -import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -33,6 +32,8 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage import com.texthip.thip.R +import com.texthip.thip.data.model.rooms.response.CurrentVote +import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet import com.texthip.thip.ui.common.modal.DialogPopup import com.texthip.thip.ui.common.topappbar.GradationTopAppBar @@ -50,7 +51,7 @@ fun GroupRoomScreen( roomId: Int, onBackClick: () -> Unit = {}, onNavigateToMates: () -> Unit = {}, - onNavigateToNote: () -> Unit = {}, + onNavigateToNote: (page: Int?, isOverview: Boolean?) -> Unit = { _, _ -> }, viewModel: GroupRoomViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -92,7 +93,7 @@ fun GroupRoomContent( roomDetails: RoomsPlayingResponse, onBackClick: () -> Unit = {}, onNavigateToMates: () -> Unit = {}, - onNavigateToNote: () -> Unit = {}, + onNavigateToNote: (page: Int?, isOverview: Boolean?) -> Unit = { _, _ -> }, ) { val scrollState = rememberScrollState() @@ -174,7 +175,16 @@ fun GroupRoomContent( currentPage = roomDetails.currentPage, userPercentage = roomDetails.userPercentage, currentVotes = roomDetails.currentVotes, - onNavigateToNote = onNavigateToNote + // 일반 노트 카드 클릭 시 필터 없이 이동 + onNavigateToNote = { onNavigateToNote(null, null) }, + // 투표 카드 클릭 시 필터 값과 함께 이동 + onVoteClick = { vote: CurrentVote -> + if (vote.isOverview) { + onNavigateToNote(null, true) + } else { + onNavigateToNote(vote.page, false) + } + } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt index 649d0249..1bff06f3 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt @@ -61,8 +61,12 @@ fun NavHostController.navigateToGroupRoomMates(roomId: Int) { } // 기록장 화면으로 이동 -fun NavHostController.navigateToGroupNote(roomId: Int) { - navigate(GroupRoutes.Note(roomId)) +fun NavHostController.navigateToGroupNote( + roomId: Int, + page: Int? = null, + isOverview: Boolean? = null +) { + navigate(GroupRoutes.Note(roomId = roomId, page = page, isOverview = isOverview)) } // 기록 생성 화면으로 이동 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 a0289c69..021af6b4 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 @@ -255,8 +255,8 @@ fun NavGraphBuilder.groupNavigation( onNavigateToMates = { navController.navigateToGroupRoomMates(roomId) }, - onNavigateToNote = { - navController.navigateToGroupNote(roomId) + onNavigateToNote = { page, isOverview -> + navController.navigateToGroupNote(roomId, page, isOverview) }, ) } @@ -282,6 +282,8 @@ fun NavGraphBuilder.groupNavigation( composable { backStackEntry -> val route = backStackEntry.toRoute() val roomId = route.roomId + val page = route.page + val isOverview = route.isOverview val result = backStackEntry.savedStateHandle.get("selected_tab_index") @@ -289,12 +291,12 @@ fun NavGraphBuilder.groupNavigation( // roomId = roomId, roomId = 1, resultTabIndex = result, + initialPage = page, + initialIsOverview = isOverview, onResultConsumed = { backStackEntry.savedStateHandle.remove("selected_tab_index") }, - onBackClick = { - navigateBack() - }, + onBackClick = { navigateBack() }, onCreateNoteClick = { navController.navigateToGroupNoteCreate(roomId) }, diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt index 88f914b2..f62c9729 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt @@ -6,19 +6,19 @@ import kotlinx.serialization.Serializable sealed class GroupRoutes : Routes() { @Serializable data object MakeRoom : GroupRoutes() - + @Serializable data object Done : GroupRoutes() - + @Serializable data object Search : GroupRoutes() - + @Serializable data object My : GroupRoutes() - + @Serializable data class Recruit(val roomId: Int) : GroupRoutes() - + @Serializable data class Room(val roomId: Int) : GroupRoutes() @@ -26,7 +26,8 @@ sealed class GroupRoutes : Routes() { data class RoomMates(val roomId: Int) : GroupRoutes() @Serializable - data class Note(val roomId: Int) : GroupRoutes() + data class Note(val roomId: Int, val page: Int? = null, val isOverview: Boolean? = null) : + GroupRoutes() @Serializable data class NoteCreate(val roomId: Int) : GroupRoutes() From 5f4d446e31465553032a0bf499507dcd25b86abb Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:44:27 +0900 Subject: [PATCH 19/47] =?UTF-8?q?[fix]:=20group=20navigation=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EB=88=84=EB=9D=BD=20=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/ui/navigator/navigations/GroupNavigation.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 8e9f25bc..8b8a1d6d 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 @@ -27,6 +27,7 @@ import com.texthip.thip.ui.navigator.extensions.navigateToAlarm import com.texthip.thip.ui.navigator.extensions.navigateToGroupDone import com.texthip.thip.ui.navigator.extensions.navigateToGroupMakeRoom import com.texthip.thip.ui.navigator.extensions.navigateToGroupMy +import com.texthip.thip.ui.navigator.extensions.navigateToGroupNote import com.texthip.thip.ui.navigator.extensions.navigateToGroupNoteCreate import com.texthip.thip.ui.navigator.extensions.navigateToGroupRecruit import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoom @@ -183,6 +184,9 @@ fun NavGraphBuilder.groupNavigation( onNavigateToMates = { navController.navigateToGroupRoomMates(roomId) }, + onNavigateToNote = { page, isOverview -> + navController.navigateToGroupNote(roomId, page, isOverview) + }, ) } From 464bfed38395de870fadb61b6e153b707eeb503c Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:10:00 +0900 Subject: [PATCH 20/47] =?UTF-8?q?[feat]:=20=EC=B1=85=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98,=20=EC=B4=9D?= =?UTF-8?q?=ED=8F=89=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20response=20=EC=83=9D=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/rooms/response/RoomsBookPageResponse.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsBookPageResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsBookPageResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsBookPageResponse.kt new file mode 100644 index 00000000..2ed41c16 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsBookPageResponse.kt @@ -0,0 +1,11 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsBookPageResponse( + val totalBookPage: Int, + val recentBookPage: Int, + val isOverviewPossible: Boolean, + val roomId: Int, +) From 80832dc9834ce4a4ed0fa28904207337ebed5403 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:10:09 +0900 Subject: [PATCH 21/47] =?UTF-8?q?[feat]:=20=EC=B1=85=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98,=20=EC=B4=9D?= =?UTF-8?q?=ED=8F=89=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20service=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/texthip/thip/data/service/RoomsService.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index ab86cbcc..ce4a189e 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -2,6 +2,7 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest +import com.texthip.thip.data.model.rooms.response.RoomsBookPageResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse @@ -40,4 +41,9 @@ interface RoomsService { @Path("roomId") roomId: Int, @Body request: RoomsRecordRequest ): BaseResponse + + @GET("rooms/{roomId}/book-page") + suspend fun getRoomsBookPage( + @Path("roomId") roomId: Int, + ): BaseResponse } \ No newline at end of file From de6f58832d6688ac3e5919275c2e3d8e17688fc0 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:10:16 +0900 Subject: [PATCH 22/47] =?UTF-8?q?[feat]:=20=EC=B1=85=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98,=20=EC=B4=9D?= =?UTF-8?q?=ED=8F=89=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20repository=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/texthip/thip/data/repository/RoomsRepository.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index 7dbbba32..b4316915 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -63,4 +63,12 @@ class RoomsRepository @Inject constructor( ) ).handleBaseResponse().getOrThrow() } + + suspend fun getRoomsBookPage( + roomId: Int, + ) = runCatching { + roomsService.getRoomsBookPage( + roomId = roomId + ).handleBaseResponse().getOrThrow() + } } \ No newline at end of file From 868c89d9080a9a0d60c4ab15d01fc13c899a50c1 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:22:29 +0900 Subject: [PATCH 23/47] =?UTF-8?q?[feat]:=20=EC=B1=85=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98,=20=EC=B4=9D?= =?UTF-8?q?=ED=8F=89=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20viewmodel,=20navigation=20=EC=9E=91=EC=84=B1=20(#71?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/screen/GroupNoteCreateScreen.kt | 15 +++++---- .../ui/group/note/screen/GroupNoteScreen.kt | 4 +-- .../viewmodel/GroupNoteCreateViewModel.kt | 31 ++++++++++++++---- .../note/viewmodel/GroupNoteViewModel.kt | 32 ++++++++++++++++++- .../extensions/GroupNavigationExtensions.kt | 16 ++++++++-- .../navigator/navigations/GroupNavigation.kt | 16 +++++++++- .../thip/ui/navigator/routes/GroupRoutes.kt | 7 +++- 7 files changed, 101 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt index 38e03064..bf69ed75 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteCreateScreen.kt @@ -39,14 +39,17 @@ import com.texthip.thip.ui.theme.ThipTheme @Composable fun GroupNoteCreateScreen( roomId: Int, + recentPage: Int, + totalPage: Int, + isOverviewPossible: Boolean, onBackClick: () -> Unit, onNavigateBackWithResult: () -> Unit, viewModel: GroupNoteCreateViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - LaunchedEffect(key1 = roomId) { - viewModel.initialize(roomId) + LaunchedEffect(key1 = Unit) { + viewModel.initialize(roomId, recentPage, totalPage, isOverviewPossible) } LaunchedEffect(key1 = uiState.isSuccess) { @@ -74,8 +77,6 @@ fun GroupNoteCreateContent( // Tooltip 위치 측정용 state val iconCoordinates = remember { mutableStateOf(null) } - var isEligible by rememberSaveable { mutableStateOf(true) } // TODO: 서버 데이터? - Box( modifier = Modifier.fillMaxSize() ) { @@ -97,8 +98,8 @@ fun GroupNoteCreateContent( onPageTextChange = { onEvent(GroupNoteCreateEvent.PageChanged(it)) }, isGeneralReview = uiState.isGeneralReview, onGeneralReviewToggle = { onEvent(GroupNoteCreateEvent.GeneralReviewToggled(it)) }, - isEligible = isEligible, - bookTotalPage = 600, + isEligible = uiState.isOverviewPossible, + bookTotalPage = uiState.totalPage, onInfoClick = { showTooltip = true }, onInfoPositionCaptured = { iconCoordinates.value = it } ) @@ -124,7 +125,7 @@ fun GroupNoteCreateContent( PopupModal( text = stringResource(R.string.condition_of_general_review), arrowPosition = ArrowPosition.RIGHT, - isEligible = isEligible, + isEligible = uiState.isOverviewPossible, onClose = { showTooltip = false } ) } 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 7384c975..ba0587ca 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 @@ -140,8 +140,8 @@ fun GroupNoteContent( onEvent: (GroupNoteEvent) -> Unit, onBackClick: () -> Unit, onCreateNoteClick: () -> Unit, - showProgressBar: Boolean, // [추가] - progress: Float // [추가] + showProgressBar: Boolean, + progress: Float ) { var isCommentBottomSheetVisible by remember { mutableStateOf(false) } var selectedPostForComment by remember { mutableStateOf(null) } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt index 36c78b25..3b55081a 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt @@ -16,7 +16,9 @@ data class GroupNoteCreateUiState( val isGeneralReview: Boolean = false, val isLoading: Boolean = false, val isSuccess: Boolean = false, - val error: String? = null + val error: String? = null, + val totalPage: Int = 0, + val isOverviewPossible: Boolean = false ) { // 입력 폼이 모두 채워졌는지 확인 val isFormFilled: Boolean get() = pageText.isNotBlank() && opinionText.isNotBlank() @@ -38,20 +40,37 @@ class GroupNoteCreateViewModel @Inject constructor( private var roomId: Int = -1 - fun initialize(id: Int) { - roomId = id + fun initialize( + roomId: Int, + recentPage: Int, + totalPage: Int, + isOverviewPossible: Boolean + ) { + this.roomId = roomId + _uiState.update { + it.copy( + pageText = recentPage.toString(), + totalPage = totalPage, + isOverviewPossible = isOverviewPossible + ) + } } fun onEvent(event: GroupNoteCreateEvent) { when (event) { is GroupNoteCreateEvent.PageChanged -> { - _uiState.update { it.copy(pageText = event.text) } + if (!_uiState.value.isGeneralReview) { + _uiState.update { it.copy(pageText = event.text) } + } } is GroupNoteCreateEvent.OpinionChanged -> { _uiState.update { it.copy(opinionText = event.text) } } is GroupNoteCreateEvent.GeneralReviewToggled -> { - _uiState.update { it.copy(isGeneralReview = event.isChecked) } + _uiState.update { + val newPageText = if (event.isChecked) "" else it.pageText + it.copy(isGeneralReview = event.isChecked, pageText = newPageText) + } } GroupNoteCreateEvent.CreateRecordClicked -> { createRecord() @@ -61,7 +80,7 @@ class GroupNoteCreateViewModel @Inject constructor( private fun createRecord() { val currentState = _uiState.value - val pageNumber = currentState.pageText.toIntOrNull() + val pageNumber = if (currentState.isGeneralReview) 0 else currentState.pageText.toIntOrNull() if (pageNumber == null || !currentState.isFormFilled) { _uiState.update { it.copy(error = "페이지 번호를 정확히 입력해주세요.") } return diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index 250cebf5..e7735df2 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -7,6 +7,8 @@ import com.texthip.thip.data.model.rooms.response.PostList import com.texthip.thip.data.repository.RoomsRepository import com.texthip.thip.utils.type.SortType import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update @@ -22,6 +24,9 @@ data class GroupNoteUiState( // 화면 데이터 val posts: List = emptyList(), + val recentBookPage: Int = 0, + val totalBookPage: Int = 0, + val isOverviewPossible: Boolean = false, // 필터 및 탭 상태 val selectedTabIndex: Int = 0, @@ -72,7 +77,32 @@ class GroupNoteViewModel @Inject constructor( ) } } - loadPosts(isRefresh = true) + + viewModelScope.launch { + val postsJob = async { loadPosts(isRefresh = true) } + val bookPageJob = async { loadBookPageInfo() } + awaitAll(postsJob, bookPageJob) + } + } + + private fun loadBookPageInfo() { + viewModelScope.launch { + roomsRepository.getRoomsBookPage(roomId) + .onSuccess { response -> + if (response != null) { + _uiState.update { + it.copy( + recentBookPage = response.recentBookPage, + totalBookPage = response.totalBookPage, + isOverviewPossible = response.isOverviewPossible + ) + } + } + } + .onFailure { throwable -> + _uiState.update { it.copy(error = throwable.message) } + } + } } fun onEvent(event: GroupNoteEvent) { diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt index 1bff06f3..aa0d346d 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt @@ -70,6 +70,18 @@ fun NavHostController.navigateToGroupNote( } // 기록 생성 화면으로 이동 -fun NavHostController.navigateToGroupNoteCreate(roomId: Int) { - navigate(GroupRoutes.NoteCreate(roomId)) +fun NavHostController.navigateToGroupNoteCreate( + roomId: Int, + recentBookPage: Int, + totalBookPage: Int, + isOverviewPossible: Boolean +) { + navigate( + GroupRoutes.NoteCreate( + roomId = roomId, + recentBookPage = recentBookPage, + totalBookPage = totalBookPage, + isOverviewPossible = isOverviewPossible + ) + ) } \ No newline at end of file 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 8b8a1d6d..d9d001d3 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 @@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -17,6 +18,7 @@ import com.texthip.thip.ui.group.myroom.screen.GroupMyScreen import com.texthip.thip.ui.group.myroom.viewmodel.GroupMyViewModel import com.texthip.thip.ui.group.note.screen.GroupNoteCreateScreen import com.texthip.thip.ui.group.note.screen.GroupNoteScreen +import com.texthip.thip.ui.group.note.viewmodel.GroupNoteViewModel import com.texthip.thip.ui.group.room.screen.GroupRoomMatesScreen import com.texthip.thip.ui.group.room.screen.GroupRoomRecruitScreen import com.texthip.thip.ui.group.room.screen.GroupRoomScreen @@ -216,6 +218,9 @@ fun NavGraphBuilder.groupNavigation( val result = backStackEntry.savedStateHandle.get("selected_tab_index") + val viewModel: GroupNoteViewModel = hiltViewModel(backStackEntry) + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + GroupNoteScreen( // roomId = roomId, roomId = 1, @@ -227,8 +232,14 @@ fun NavGraphBuilder.groupNavigation( }, onBackClick = { navigateBack() }, onCreateNoteClick = { - navController.navigateToGroupNoteCreate(roomId) + navController.navigateToGroupNoteCreate( + roomId = roomId, + recentBookPage = uiState.recentBookPage, + totalBookPage = uiState.totalBookPage, + isOverviewPossible = uiState.isOverviewPossible + ) }, + viewModel = viewModel ) } @@ -239,6 +250,9 @@ fun NavGraphBuilder.groupNavigation( GroupNoteCreateScreen( roomId = 1, + recentPage = route.recentBookPage, + totalPage = route.totalBookPage, + isOverviewPossible = route.isOverviewPossible, onBackClick = { navigateBack() }, diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt index f62c9729..d2e749e5 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt @@ -30,5 +30,10 @@ sealed class GroupRoutes : Routes() { GroupRoutes() @Serializable - data class NoteCreate(val roomId: Int) : GroupRoutes() + data class NoteCreate( + val roomId: Int, + val recentBookPage: Int, + val totalBookPage: Int, + val isOverviewPossible: Boolean + ) } \ No newline at end of file From 71fe1bbdc4645c20ddf60c57660251a479a63a42 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:01:56 +0900 Subject: [PATCH 24/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20request,=20response=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/model/rooms/request/RoomsVoteRequest.kt | 16 ++++++++++++++++ .../model/rooms/response/RoomsVoteResponse.kt | 9 +++++++++ 2 files changed, 25 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt new file mode 100644 index 00000000..6b1cbce6 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt @@ -0,0 +1,16 @@ +package com.texthip.thip.data.model.rooms.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsVoteRequest( + val page: Int, + val isOverview: Boolean, + val content: String, + val voteItemList: List +) + +@Serializable +data class VoteItem( + val itemName: String +) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt new file mode 100644 index 00000000..5de4396d --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsVoteResponse( + val voteId: Int, + val roomId: Int +) From e78faaf48e81c47717c5de88f922a390a612c458 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:02:02 +0900 Subject: [PATCH 25/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20service=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/data/service/RoomsService.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index ce4a189e..36da811c 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -2,11 +2,13 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest +import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.response.RoomsBookPageResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse +import com.texthip.thip.data.model.rooms.response.RoomsVoteResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST @@ -42,6 +44,12 @@ interface RoomsService { @Body request: RoomsRecordRequest ): BaseResponse + @POST("rooms/{roomId}/vote") + suspend fun postRoomsVote( + @Path("roomId") roomId: Int, + @Body request: RoomsVoteRequest + ): BaseResponse + @GET("rooms/{roomId}/book-page") suspend fun getRoomsBookPage( @Path("roomId") roomId: Int, From 108839623e62d5afa85be4f2d3d699071d0ed905 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:02:08 +0900 Subject: [PATCH 26/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20repository=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/repository/RoomsRepository.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index b4316915..50164bee 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -2,6 +2,8 @@ package com.texthip.thip.data.repository import com.texthip.thip.data.model.base.handleBaseResponse import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest +import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest +import com.texthip.thip.data.model.rooms.request.VoteItem import com.texthip.thip.data.service.RoomsService import javax.inject.Inject import javax.inject.Singleton @@ -64,6 +66,24 @@ class RoomsRepository @Inject constructor( ).handleBaseResponse().getOrThrow() } + suspend fun postRoomsVote( + roomId: Int, + page: Int, + isOverview: Boolean, + content: String, + voteItemList: List + ) = runCatching { + roomsService.postRoomsVote( + roomId = roomId, + request = RoomsVoteRequest( + page = page, + isOverview = isOverview, + content = content, + voteItemList = voteItemList + ) + ).handleBaseResponse().getOrThrow() + } + suspend fun getRoomsBookPage( roomId: Int, ) = runCatching { From 72a299f7ccb346774b0a19db423b38ab23086c0c Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Tue, 12 Aug 2025 23:49:41 +0900 Subject: [PATCH 27/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20viewmodel,=20naviagation=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=ED=95=98=EA=B3=A0=20screen=EC=97=90=20=EC=97=B0=EA=B2=B0=20(#7?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/note/screen/GroupNoteScreen.kt | 22 ++- .../note/screen/GroupVoteCreateScreen.kt | 113 +++++++++----- .../viewmodel/GroupVoteCreateViewModel.kt | 141 ++++++++++++++++++ .../extensions/GroupNavigationExtensions.kt | 17 +++ .../navigator/navigations/GroupNavigation.kt | 40 ++++- .../thip/ui/navigator/routes/GroupRoutes.kt | 8 + 6 files changed, 296 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt 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 ba0587ca..1adb8305 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 @@ -73,7 +73,8 @@ import kotlinx.coroutines.launch fun GroupNoteScreen( roomId: Int, onBackClick: () -> Unit = {}, - onCreateNoteClick: () -> Unit = {}, + onCreateNoteClick: (recentPage: Int, totalPage: Int, isOverviewPossible: Boolean) -> Unit, + onCreateVoteClick: (recentPage: Int, totalPage: Int, isOverviewPossible: Boolean) -> Unit, resultTabIndex: Int? = null, onResultConsumed: () -> Unit = {}, initialPage: Int? = null, @@ -128,7 +129,16 @@ fun GroupNoteScreen( uiState = uiState, onEvent = viewModel::onEvent, onBackClick = onBackClick, - onCreateNoteClick = onCreateNoteClick, + onCreateNoteClick = { + uiState.let { s -> + onCreateNoteClick(s.recentBookPage, s.totalBookPage, s.isOverviewPossible) + } + }, + onCreateVoteClick = { + uiState.let { s -> + onCreateVoteClick(s.recentBookPage, s.totalBookPage, s.isOverviewPossible) + } + }, showProgressBar = showProgressBar, progress = progress.value ) @@ -140,6 +150,7 @@ fun GroupNoteContent( onEvent: (GroupNoteEvent) -> Unit, onBackClick: () -> Unit, onCreateNoteClick: () -> Unit, + onCreateVoteClick: () -> Unit, showProgressBar: Boolean, progress: Float ) { @@ -163,6 +174,10 @@ fun GroupNoteContent( val listState = rememberLazyListState() + LaunchedEffect(uiState.selectedTabIndex) { + listState.scrollToItem(0) + } + val isScrolledToEnd by remember { derivedStateOf { val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull() @@ -416,7 +431,7 @@ fun GroupNoteContent( FabMenuItem( icon = painterResource(R.drawable.ic_vote), text = stringResource(R.string.create_vote), - onClick = { } + onClick = onCreateVoteClick ) ) ) @@ -543,6 +558,7 @@ private fun GroupNoteScreenPreview() { onEvent = {}, onBackClick = {}, onCreateNoteClick = {}, + onCreateVoteClick = {}, showProgressBar = true, progress = 0.5f ) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt index f3b3953d..832bda37 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupVoteCreateScreen.kt @@ -6,13 +6,15 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.absoluteOffset import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.positionInRoot @@ -21,44 +23,65 @@ 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 androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.texthip.thip.R import com.texthip.thip.ui.common.modal.ArrowPosition import com.texthip.thip.ui.common.modal.PopupModal import com.texthip.thip.ui.common.topappbar.InputTopAppBar import com.texthip.thip.ui.group.note.component.PageInputSection import com.texthip.thip.ui.group.note.component.VoteInputSection +import com.texthip.thip.ui.group.note.viewmodel.GroupVoteCreateEvent +import com.texthip.thip.ui.group.note.viewmodel.GroupVoteCreateUiState +import com.texthip.thip.ui.group.note.viewmodel.GroupVoteCreateViewModel import com.texthip.thip.ui.theme.ThipTheme @Composable -fun GroupVoteCreateScreen() { - var pageText by rememberSaveable { mutableStateOf("") } - var isGeneralReview by rememberSaveable { mutableStateOf(false) } +fun GroupVoteCreateScreen( + roomId: Int, + recentPage: Int, + totalPage: Int, + isOverviewPossible: Boolean, + onBackClick: () -> Unit, + onNavigateBackWithResult: () -> Unit, + viewModel: GroupVoteCreateViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() - var title by rememberSaveable { mutableStateOf("") } - val options = remember { mutableStateListOf("", "") } + LaunchedEffect(Unit) { + viewModel.initialize(roomId, recentPage, totalPage, isOverviewPossible) + } + + LaunchedEffect(uiState.isSuccess) { + if (uiState.isSuccess) { + onNavigateBackWithResult() + } + } + GroupVoteCreateContent( + uiState = uiState, + onEvent = viewModel::onEvent, + onBackClick = onBackClick + ) +} + +@Composable +fun GroupVoteCreateContent( + uiState: GroupVoteCreateUiState, + onEvent: (GroupVoteCreateEvent) -> Unit, + onBackClick: () -> Unit, +) { val density = LocalDensity.current var showTooltip by rememberSaveable { mutableStateOf(false) } - - // Tooltip 위치 측정용 state val iconCoordinates = remember { mutableStateOf(null) } - var isEligible by rememberSaveable { mutableStateOf(false) } // TODO: 서버 데이터? - - // 완료 버튼 활성화 조건 - val filledOptionsCount = options.count { it.isNotBlank() } - val isRightButtonEnabled = - (isGeneralReview || pageText.isNotBlank()) && - title.isNotBlank() && - filledOptionsCount >= 2 - Box(modifier = Modifier.fillMaxSize()) { Column { InputTopAppBar( title = stringResource(R.string.create_vote), - isRightButtonEnabled = isRightButtonEnabled, - onLeftClick = { /* 뒤로가기 동작 */ }, - onRightClick = { /* 완료 동작 */ } + isRightButtonEnabled = uiState.isFormFilled, + onLeftClick = onBackClick, + onRightClick = { onEvent(GroupVoteCreateEvent.CreateVoteClicked) } ) Column( @@ -67,35 +90,39 @@ fun GroupVoteCreateScreen() { verticalArrangement = Arrangement.spacedBy(32.dp), ) { PageInputSection( - pageText = pageText, - onPageTextChange = { pageText = it }, - isGeneralReview = isGeneralReview, - onGeneralReviewToggle = { isGeneralReview = it }, - isEligible = isEligible, - bookTotalPage = 600, + pageText = uiState.pageText, + onPageTextChange = { onEvent(GroupVoteCreateEvent.PageChanged(it)) }, + isGeneralReview = uiState.isGeneralReview, + onGeneralReviewToggle = { onEvent(GroupVoteCreateEvent.GeneralReviewToggled(it)) }, + isEligible = uiState.isGeneralReviewEnabled, + bookTotalPage = uiState.bookTotalPage, onInfoClick = { showTooltip = true }, onInfoPositionCaptured = { iconCoordinates.value = it } ) VoteInputSection( - title = title, - onTitleChange = { title = it }, - options = options, + title = uiState.title, + onTitleChange = { onEvent(GroupVoteCreateEvent.TitleChanged(it)) }, + options = uiState.options, onOptionChange = { index, newText -> - options[index] = newText - }, - onAddOption = { - if (options.size < 5) { - options.add("") - } + onEvent(GroupVoteCreateEvent.OptionChanged(index, newText)) }, + onAddOption = { onEvent(GroupVoteCreateEvent.AddOptionClicked) }, onRemoveOption = { index -> - options.removeAt(index) + onEvent(GroupVoteCreateEvent.RemoveOptionClicked(index)) } ) } } + if (uiState.isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } if (showTooltip && iconCoordinates.value != null) { val yOffsetDp = with(density) { @@ -111,7 +138,7 @@ fun GroupVoteCreateScreen() { PopupModal( text = stringResource(R.string.condition_of_general_review), arrowPosition = ArrowPosition.RIGHT, - isEligible = isEligible, + isEligible = uiState.isGeneralReviewEnabled, onClose = { showTooltip = false } ) } @@ -123,6 +150,16 @@ fun GroupVoteCreateScreen() { @Composable private fun GroupVoteCreateScreenPreview() { ThipTheme { - GroupVoteCreateScreen() + GroupVoteCreateContent( + uiState = GroupVoteCreateUiState( + pageText = "123", + title = "가장 인상깊은 구절은?", + options = listOf("1연 1행", "2연 3행", ""), + bookTotalPage = 600, + isGeneralReviewEnabled = true + ), + onEvent = {}, + onBackClick = {} + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt new file mode 100644 index 00000000..15a35773 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt @@ -0,0 +1,141 @@ +package com.texthip.thip.ui.group.note.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.texthip.thip.data.model.rooms.request.VoteItem +import com.texthip.thip.data.repository.RoomsRepository +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 GroupVoteCreateUiState( + // 입력 값 + val pageText: String = "", + val title: String = "", + val options: List = listOf("", ""), // 옵션은 최소 2개로 시작 + val isGeneralReview: Boolean = false, + + // 상태 값 + val isLoading: Boolean = false, + val isSuccess: Boolean = false, + val error: String? = null, + + // 초기 설정 값 + val bookTotalPage: Int = 0, + val isGeneralReviewEnabled: Boolean = false, + + // 내부 관리용 + internal val savedPageText: String = "" +) { + // 완료 버튼 활성화 조건 + val isFormFilled: Boolean + get() { + val filledOptionsCount = options.count { it.isNotBlank() } + return (isGeneralReview || pageText.isNotBlank()) && + title.isNotBlank() && + filledOptionsCount >= 2 + } +} + +sealed interface GroupVoteCreateEvent { + data class PageChanged(val text: String) : GroupVoteCreateEvent + data class TitleChanged(val text: String) : GroupVoteCreateEvent + data class OptionChanged(val index: Int, val text: String) : GroupVoteCreateEvent + data class GeneralReviewToggled(val isChecked: Boolean) : GroupVoteCreateEvent + data object AddOptionClicked : GroupVoteCreateEvent + data class RemoveOptionClicked(val index: Int) : GroupVoteCreateEvent + data object CreateVoteClicked : GroupVoteCreateEvent +} + +@HiltViewModel +class GroupVoteCreateViewModel @Inject constructor( + private val roomsRepository: RoomsRepository +) : ViewModel() { + + private val _uiState = MutableStateFlow(GroupVoteCreateUiState()) + val uiState = _uiState.asStateFlow() + + private var roomId: Int = -1 + + fun initialize( + roomId: Int, + recentPage: Int, + totalPage: Int, + isOverviewPossible: Boolean + ) { + this.roomId = roomId + _uiState.update { + it.copy( + pageText = recentPage.toString(), + bookTotalPage = totalPage, + isGeneralReviewEnabled = isOverviewPossible + ) + } + } + + fun onEvent(event: GroupVoteCreateEvent) { + when (event) { + is GroupVoteCreateEvent.PageChanged -> if (!_uiState.value.isGeneralReview) { + _uiState.update { it.copy(pageText = event.text) } + } + is GroupVoteCreateEvent.TitleChanged -> _uiState.update { it.copy(title = event.text) } + is GroupVoteCreateEvent.OptionChanged -> _uiState.update { + val newOptions = it.options.toMutableList() + newOptions[event.index] = event.text + it.copy(options = newOptions) + } + is GroupVoteCreateEvent.GeneralReviewToggled -> _uiState.update { + if (event.isChecked) { + it.copy(isGeneralReview = true, savedPageText = it.pageText, pageText = "") + } else { + it.copy(isGeneralReview = false, pageText = it.savedPageText) + } + } + GroupVoteCreateEvent.AddOptionClicked -> if (_uiState.value.options.size < 5) { + _uiState.update { it.copy(options = it.options + "") } + } + is GroupVoteCreateEvent.RemoveOptionClicked -> if (_uiState.value.options.size > 2) { + _uiState.update { + val newOptions = it.options.toMutableList() + newOptions.removeAt(event.index) + it.copy(options = newOptions) + } + } + GroupVoteCreateEvent.CreateVoteClicked -> createVote() + } + } + + private fun createVote() { + val currentState = _uiState.value + if (!currentState.isFormFilled) return + + val pageNumber = if (currentState.isGeneralReview) 0 else currentState.pageText.toIntOrNull() + if (pageNumber == null) { + _uiState.update { it.copy(error = "페이지 번호를 정확히 입력해주세요.") } + return + } + + viewModelScope.launch { + _uiState.update { it.copy(isLoading = true) } + + val voteItems = currentState.options + .filter { it.isNotBlank() } + .map { VoteItem(itemName = it) } + + roomsRepository.postRoomsVote( + roomId = roomId, + page = pageNumber, + isOverview = currentState.isGeneralReview, + content = currentState.title, + voteItemList = voteItems + ).onSuccess { + _uiState.update { it.copy(isLoading = false, isSuccess = true) } + }.onFailure { throwable -> + _uiState.update { it.copy(isLoading = false, error = throwable.message) } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt index aa0d346d..481601e1 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt @@ -84,4 +84,21 @@ fun NavHostController.navigateToGroupNoteCreate( isOverviewPossible = isOverviewPossible ) ) +} + +// 투표 생성 화면으로 이동 +fun NavHostController.navigateToGroupVoteCreate( + roomId: Int, + recentPage: Int, + totalPage: Int, + isOverviewPossible: Boolean +) { + navigate( + GroupRoutes.VoteCreate( + roomId = roomId, + recentPage = recentPage, + totalPage = totalPage, + isOverviewPossible = isOverviewPossible + ) + ) } \ No newline at end of file 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 d9d001d3..e8facc43 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 @@ -18,6 +18,7 @@ import com.texthip.thip.ui.group.myroom.screen.GroupMyScreen import com.texthip.thip.ui.group.myroom.viewmodel.GroupMyViewModel import com.texthip.thip.ui.group.note.screen.GroupNoteCreateScreen import com.texthip.thip.ui.group.note.screen.GroupNoteScreen +import com.texthip.thip.ui.group.note.screen.GroupVoteCreateScreen import com.texthip.thip.ui.group.note.viewmodel.GroupNoteViewModel import com.texthip.thip.ui.group.room.screen.GroupRoomMatesScreen import com.texthip.thip.ui.group.room.screen.GroupRoomRecruitScreen @@ -35,6 +36,7 @@ import com.texthip.thip.ui.navigator.extensions.navigateToGroupRecruit import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoom import com.texthip.thip.ui.navigator.extensions.navigateToGroupRoomMates import com.texthip.thip.ui.navigator.extensions.navigateToGroupSearch +import com.texthip.thip.ui.navigator.extensions.navigateToGroupVoteCreate import com.texthip.thip.ui.navigator.extensions.navigateToRecommendedGroupRecruit import com.texthip.thip.ui.navigator.routes.GroupRoutes import com.texthip.thip.ui.navigator.routes.MainTabRoutes @@ -231,12 +233,21 @@ fun NavGraphBuilder.groupNavigation( backStackEntry.savedStateHandle.remove("selected_tab_index") }, onBackClick = { navigateBack() }, - onCreateNoteClick = { + onCreateNoteClick = { recentPage, totalPage, isOverviewPossible -> navController.navigateToGroupNoteCreate( roomId = roomId, - recentBookPage = uiState.recentBookPage, - totalBookPage = uiState.totalBookPage, - isOverviewPossible = uiState.isOverviewPossible + recentBookPage = recentPage, + totalBookPage = totalPage, + isOverviewPossible = isOverviewPossible + ) + }, + // [수정] '투표 생성' 클릭 시 페이지 정보와 함께 내비게이션 + onCreateVoteClick = { recentPage, totalPage, isOverviewPossible -> + navController.navigateToGroupVoteCreate( + roomId = roomId, + recentPage = recentPage, + totalPage = totalPage, + isOverviewPossible = isOverviewPossible ) }, viewModel = viewModel @@ -264,4 +275,25 @@ fun NavGraphBuilder.groupNavigation( } ) } + + composable { backStackEntry -> + val route = backStackEntry.toRoute() + val roomId = route.roomId + + GroupVoteCreateScreen( +// roomId = roomId, + roomId = 1, + recentPage = route.recentPage, + totalPage = route.totalPage, + isOverviewPossible = route.isOverviewPossible, + onBackClick = { navigateBack() }, + onNavigateBackWithResult = { + // 투표 생성 후 '내 기록' 탭으로 이동 + navController.previousBackStackEntry + ?.savedStateHandle + ?.set("selected_tab_index", 1) + navigateBack() + } + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt index d2e749e5..ce599479 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt @@ -36,4 +36,12 @@ sealed class GroupRoutes : Routes() { val totalBookPage: Int, val isOverviewPossible: Boolean ) + + @Serializable + data class VoteCreate( + val roomId: Int, + val recentPage: Int, + val totalPage: Int, + val isOverviewPossible: Boolean + ) } \ No newline at end of file From a3587dd5ea715eec2de36aef833961ada00a159e Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:08:44 +0900 Subject: [PATCH 28/47] =?UTF-8?q?[refactor]:=20=EA=B8=B0=EB=A1=9D=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=B4=9D=ED=8F=89=EB=B3=B4=EA=B8=B0=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94=20=EC=A0=81=EC=9A=A9=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rooms/response/RoomsPostsResponse.kt | 1 + .../note/viewmodel/GroupNoteViewModel.kt | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt index e99382c5..7a77e1f8 100644 --- a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsResponse.kt @@ -7,6 +7,7 @@ data class RoomsPostsResponse( val postList: List, val roomId: Int, val isbn: String, + val isOverviewEnabled: Boolean, val nextCursor: String?, val isLast: Boolean, ) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index e7735df2..5ae39009 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -111,20 +111,28 @@ class GroupNoteViewModel @Inject constructor( _uiState.update { it.copy(selectedTabIndex = event.index) } loadPosts(isRefresh = true) } + is GroupNoteEvent.OnSortSelected -> { _uiState.update { it.copy(selectedSort = event.sortType) } loadPosts(isRefresh = true) } + is GroupNoteEvent.OnPageStartChanged -> _uiState.update { it.copy(pageStart = event.page) } is GroupNoteEvent.OnPageEndChanged -> _uiState.update { it.copy(pageEnd = event.page) } - is GroupNoteEvent.OnOverviewToggled -> _uiState.update { it.copy(isOverview = event.isSelected) } + is GroupNoteEvent.OnOverviewToggled -> { + _uiState.update { it.copy(isOverview = event.isSelected) } + loadPosts(isRefresh = true) + } + GroupNoteEvent.ApplyPageFilter -> { val currentState = _uiState.value - val isFilterActive = currentState.pageStart.isNotBlank() || currentState.pageEnd.isNotBlank() + val isFilterActive = + currentState.pageStart.isNotBlank() || currentState.pageEnd.isNotBlank() _uiState.update { it.copy(isPageFilter = isFilterActive) } loadPosts(isRefresh = true) } + GroupNoteEvent.LoadMorePosts -> loadPosts(isRefresh = false) } } @@ -135,7 +143,12 @@ class GroupNoteViewModel @Inject constructor( viewModelScope.launch { _uiState.update { - if (isRefresh) it.copy(isLoading = true, posts = emptyList(), error = null, isLastPage = false) + if (isRefresh) it.copy( + isLoading = true, + posts = emptyList(), + error = null, + isLastPage = false + ) else it.copy(isLoadingMore = true, error = null) } @@ -175,7 +188,8 @@ class GroupNoteViewModel @Inject constructor( isLoading = false, isLoadingMore = false, posts = if (isRefresh) response.postList else it.posts + response.postList, - isLastPage = response.isLast + isLastPage = response.isLast, + totalEnabled = response.isOverviewEnabled ) } } From b18b536fd06aadb097e489bc530d1f13c423fc65 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:14:56 +0900 Subject: [PATCH 29/47] =?UTF-8?q?[chore]:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=98=EA=B8=B0=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{RoomsVoteRequest.kt => RoomsCreateVoteRequest.kt} | 2 +- ...RoomsVoteResponse.kt => RoomsCreateVoteResponse.kt} | 2 +- .../texthip/thip/data/repository/RoomsRepository.kt | 8 ++++---- .../java/com/texthip/thip/data/service/RoomsService.kt | 10 +++++----- .../group/note/viewmodel/GroupVoteCreateViewModel.kt | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) rename app/src/main/java/com/texthip/thip/data/model/rooms/request/{RoomsVoteRequest.kt => RoomsCreateVoteRequest.kt} (88%) rename app/src/main/java/com/texthip/thip/data/model/rooms/response/{RoomsVoteResponse.kt => RoomsCreateVoteResponse.kt} (80%) diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsCreateVoteRequest.kt similarity index 88% rename from app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt rename to app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsCreateVoteRequest.kt index 6b1cbce6..043e09aa 100644 --- a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsCreateVoteRequest.kt @@ -3,7 +3,7 @@ package com.texthip.thip.data.model.rooms.request import kotlinx.serialization.Serializable @Serializable -data class RoomsVoteRequest( +data class RoomsCreateVoteRequest( val page: Int, val isOverview: Boolean, val content: String, diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsCreateVoteResponse.kt similarity index 80% rename from app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt rename to app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsCreateVoteResponse.kt index 5de4396d..c68c122b 100644 --- a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsCreateVoteResponse.kt @@ -3,7 +3,7 @@ package com.texthip.thip.data.model.rooms.response import kotlinx.serialization.Serializable @Serializable -data class RoomsVoteResponse( +data class RoomsCreateVoteResponse( val voteId: Int, val roomId: Int ) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index 50164bee..120b4d24 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -1,8 +1,8 @@ package com.texthip.thip.data.repository import com.texthip.thip.data.model.base.handleBaseResponse +import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest -import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.request.VoteItem import com.texthip.thip.data.service.RoomsService import javax.inject.Inject @@ -66,16 +66,16 @@ class RoomsRepository @Inject constructor( ).handleBaseResponse().getOrThrow() } - suspend fun postRoomsVote( + suspend fun postRoomsCreateVote( roomId: Int, page: Int, isOverview: Boolean, content: String, voteItemList: List ) = runCatching { - roomsService.postRoomsVote( + roomsService.postRoomsCreateVote( roomId = roomId, - request = RoomsVoteRequest( + request = RoomsCreateVoteRequest( page = page, isOverview = isOverview, content = content, diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index 36da811c..2474e320 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -2,13 +2,13 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest -import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest +import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest import com.texthip.thip.data.model.rooms.response.RoomsBookPageResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse -import com.texthip.thip.data.model.rooms.response.RoomsVoteResponse +import com.texthip.thip.data.model.rooms.response.RoomsCreateVoteResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST @@ -45,10 +45,10 @@ interface RoomsService { ): BaseResponse @POST("rooms/{roomId}/vote") - suspend fun postRoomsVote( + suspend fun postRoomsCreateVote( @Path("roomId") roomId: Int, - @Body request: RoomsVoteRequest - ): BaseResponse + @Body request: RoomsCreateVoteRequest + ): BaseResponse @GET("rooms/{roomId}/book-page") suspend fun getRoomsBookPage( diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt index 15a35773..3d0906ef 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt @@ -125,7 +125,7 @@ class GroupVoteCreateViewModel @Inject constructor( .filter { it.isNotBlank() } .map { VoteItem(itemName = it) } - roomsRepository.postRoomsVote( + roomsRepository.postRoomsCreateVote( roomId = roomId, page = pageNumber, isOverview = currentState.isGeneralReview, From 762dd72761517a235f701b826ba0701326257b61 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:23:01 +0900 Subject: [PATCH 30/47] =?UTF-8?q?[refactor]:=20=EC=B4=9D=ED=8F=89=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EC=97=86=EC=9D=84=20=EB=95=8C=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/note/screen/GroupNoteScreen.kt | 22 ++++++++++++++----- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) 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 1adb8305..e47c474b 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 @@ -242,6 +242,20 @@ fun GroupNoteContent( CircularProgressIndicator() } } else if (uiState.posts.isEmpty()) { + val noRecordTextTitle = if (uiState.isOverview) { + stringResource(R.string.no_overviews_yet) + } else { + stringResource(R.string.no_records_yet) + } + val noRecordTextContent = when(uiState.selectedTabIndex) { + 0 -> if( uiState.isOverview) { + stringResource(R.string.no_overview_subtext) + } else { + stringResource(R.string.no_group_record_subtext) + } + 1 -> stringResource(R.string.no_my_record_subtext) + else -> "" + } // 기록이 없을 때 중앙에 메시지 Column( modifier = Modifier @@ -254,16 +268,12 @@ fun GroupNoteContent( ) ) { Text( - text = stringResource(R.string.no_records_yet), + text = noRecordTextTitle, style = typography.smalltitle_sb600_s18_h24, color = colors.White ) Text( - text = when (uiState.selectedTabIndex) { - 0 -> stringResource(R.string.no_group_record_subtext) - 1 -> stringResource(R.string.no_my_record_subtext) - else -> "" - }, + text = noRecordTextContent, style = typography.copy_r400_s14, color = colors.Grey ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ef24f22d..2307feb6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -216,8 +216,10 @@ 항목 추가 독서 진행도 80% 이상부터 총평을 볼 수 있어요. 아직 기록이 없어요. + 아직 총평이 없어요. 우리 모임의 첫번째 기록을 남겨보세요 나의 첫번째 기록을 남겨보세요 + 책을 끝까지 읽고 든 생각을 남겨보세요 아직 댓글이 없어요. 첫번째 댓글을 남겨보세요 이 기록을 피드에 핀할까요? From 32ffc3228824dc21eb425c3d8cf04cf244f50cdb Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:51:14 +0900 Subject: [PATCH 31/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20request,=20response=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/model/rooms/request/RoomsVoteRequest.kt | 9 +++++++++ .../data/model/rooms/response/RoomsVoteResponse.kt | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt new file mode 100644 index 00000000..fa8c3823 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsVoteRequest.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.rooms.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsVoteRequest( + val voteItemId: Int, + val type: Boolean +) diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt new file mode 100644 index 00000000..1fd74ff9 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsVoteResponse.kt @@ -0,0 +1,10 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsVoteResponse( + val voteItemId: Int, + val roomId: Int, + val type: Boolean, +) From 8e9002eb5823f67619d93b30432f6dc8d5ebb79f Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:51:23 +0900 Subject: [PATCH 32/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20service=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/texthip/thip/data/service/RoomsService.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index 2474e320..a30ef77a 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -1,14 +1,16 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse -import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest +import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest +import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.response.RoomsBookPageResponse +import com.texthip.thip.data.model.rooms.response.RoomsCreateVoteResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse -import com.texthip.thip.data.model.rooms.response.RoomsCreateVoteResponse +import com.texthip.thip.data.model.rooms.response.RoomsVoteResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST @@ -54,4 +56,11 @@ interface RoomsService { suspend fun getRoomsBookPage( @Path("roomId") roomId: Int, ): BaseResponse + + @POST("rooms/{roomId}/vote/{voteId}/") + suspend fun postRoomsVote( + @Path("roomId") roomId: Int, + @Path("voteId") voteId: Int, + @Body request: RoomsVoteRequest + ): BaseResponse } \ No newline at end of file From 1d648550943f881cb1124d2fd10703b0c4dfe7d7 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:51:33 +0900 Subject: [PATCH 33/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20repository=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/repository/RoomsRepository.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index 120b4d24..83abeab5 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -3,6 +3,7 @@ package com.texthip.thip.data.repository import com.texthip.thip.data.model.base.handleBaseResponse import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest +import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.request.VoteItem import com.texthip.thip.data.service.RoomsService import javax.inject.Inject @@ -91,4 +92,20 @@ class RoomsRepository @Inject constructor( roomId = roomId ).handleBaseResponse().getOrThrow() } + + suspend fun postRoomsVote( + roomId: Int, + voteId: Int, + voteItemId: Int, + type: Boolean + ) = runCatching { + roomsService.postRoomsVote( + roomId = roomId, + voteId = voteId, + request = RoomsVoteRequest( + voteItemId = voteItemId, + type = type + ) + ).handleBaseResponse().getOrThrow() + } } \ No newline at end of file From d8df01e1891277ec62907922198d734f10881045 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:26:20 +0900 Subject: [PATCH 34/47] =?UTF-8?q?[fix]:=20api=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=EA=B1=B0=20=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/texthip/thip/data/service/RoomsService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index a30ef77a..81bf84c0 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -57,7 +57,7 @@ interface RoomsService { @Path("roomId") roomId: Int, ): BaseResponse - @POST("rooms/{roomId}/vote/{voteId}/") + @POST("rooms/{roomId}/vote/{voteId}") suspend fun postRoomsVote( @Path("roomId") roomId: Int, @Path("voteId") voteId: Int, From 4380431d7afaebe5d89b5ed5b523f191798cb9b2 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:28:40 +0900 Subject: [PATCH 35/47] =?UTF-8?q?[ui]:=20=ED=88=AC=ED=91=9C=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=98=A4=EB=A5=B8=EC=AA=BD=20=EC=BD=94=EB=84=88=20?= =?UTF-8?q?=EA=B0=80=EB=A0=A4=EC=A7=80=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/ui/common/buttons/GroupVoteButton.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt index 82c5901b..bc6b9e2d 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/GroupVoteButton.kt @@ -23,6 +23,7 @@ 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.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.texthip.thip.data.model.rooms.response.VoteItems @@ -69,8 +70,9 @@ fun GroupVoteButton( Box( modifier = Modifier .fillMaxWidth() - .background(color = backgroundColor, shape = RoundedCornerShape(12.dp)) .height(44.dp) + .clip(RoundedCornerShape(12.dp)) + .background(color = backgroundColor) .clickable { if (isSelected) { onOptionSelected(null) @@ -84,10 +86,7 @@ fun GroupVoteButton( modifier = Modifier .fillMaxHeight() .fillMaxWidth(animatedPercent) - .background( - color = percentBarColor, - shape = RoundedCornerShape(topStart = 12.dp, bottomStart = 12.dp) - ) + .background(color = percentBarColor) ) } From 9fad48323d08842ca74c97e7ca3bd84bf6c378df Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:32:24 +0900 Subject: [PATCH 36/47] =?UTF-8?q?[feat]:=20=ED=88=AC=ED=91=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20viewmodel=20=EC=9E=91=EC=84=B1=20=EB=B0=8F=20screen?= =?UTF-8?q?=EC=97=90=20=EC=97=B0=EA=B2=B0=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/note/component/VoteCommentCard.kt | 26 +++++++++---------- .../ui/group/note/screen/GroupNoteScreen.kt | 10 ++++--- .../note/viewmodel/GroupNoteViewModel.kt | 22 ++++++++++++++++ 3 files changed, 42 insertions(+), 16 deletions(-) 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 566e3dcb..8381dae8 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 @@ -28,14 +28,14 @@ import com.texthip.thip.ui.theme.ThipTheme.typography fun VoteCommentCard( modifier: Modifier = Modifier, data: PostList, + onVote: (postId: Int, voteItemId: Int, type: Boolean) -> 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 selectedIndex = data.voteItems.indexOfFirst { it.isVoted }.takeIf { it != -1 } + val hasVoted = selectedIndex != null val isLocked = data.isLocked val isWriter = data.isWriter @@ -68,19 +68,19 @@ fun VoteCommentCard( ) GroupVoteButton( - voteItems = voteItems, - selectedIndex = selected, + voteItems = data.voteItems, + selectedIndex = selectedIndex, hasVoted = hasVoted, - onOptionSelected = { + onOptionSelected = { index -> 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) + if (index == null) { + selectedIndex?.let { + val votedItemId = data.voteItems[it].voteItemId + onVote(data.postId, votedItemId, false) // type: false (취소) } + } else { + val votedItemId = data.voteItems[index].voteItemId + onVote(data.postId, votedItemId, true) // type: true (투표) } } } 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 e47c474b..a61dd13b 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 @@ -247,12 +247,13 @@ fun GroupNoteContent( } else { stringResource(R.string.no_records_yet) } - val noRecordTextContent = when(uiState.selectedTabIndex) { - 0 -> if( uiState.isOverview) { + val noRecordTextContent = when (uiState.selectedTabIndex) { + 0 -> if (uiState.isOverview) { stringResource(R.string.no_overview_subtext) } else { stringResource(R.string.no_group_record_subtext) } + 1 -> stringResource(R.string.no_my_record_subtext) else -> "" } @@ -367,7 +368,10 @@ fun GroupNoteContent( modifier = itemModifier, onCommentClick = { isCommentBottomSheetVisible = true }, onLongPress = { selectedPostForMenu = post }, - onPinClick = { isPinDialogVisible = true } + onPinClick = { isPinDialogVisible = true }, + onVote = { postId, voteItemId, type -> + onEvent(GroupNoteEvent.OnVote(postId, voteItemId, type)) + } ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index 5ae39009..9c9f849b 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -46,6 +46,7 @@ sealed interface GroupNoteEvent { data class OnOverviewToggled(val isSelected: Boolean) : GroupNoteEvent data object ApplyPageFilter : GroupNoteEvent data object LoadMorePosts : GroupNoteEvent + data class OnVote(val postId: Int, val voteItemId: Int, val type: Boolean) : GroupNoteEvent } @@ -134,6 +135,27 @@ class GroupNoteViewModel @Inject constructor( } GroupNoteEvent.LoadMorePosts -> loadPosts(isRefresh = false) + + is GroupNoteEvent.OnVote -> vote( + postId = event.postId, + voteItemId = event.voteItemId, + type = event.type + ) + } + } + + private fun vote(postId: Int, voteItemId: Int, type: Boolean) { + viewModelScope.launch { + roomsRepository.postRoomsVote( + roomId = roomId, + voteId = postId, + voteItemId = voteItemId, + type = type + ).onSuccess { + loadPosts(isRefresh = true) + }.onFailure { throwable -> + _uiState.update { it.copy(error = throwable.message) } + } } } From 9fad3276f8c70039a1d67a99bbf33bc4c56194c4 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:47:15 +0900 Subject: [PATCH 37/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EA=B8=B0=20response=20=EC=83=9D=EC=84=B1=20(?= =?UTF-8?q?#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/rooms/response/RoomsDeleteRecordResponse.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDeleteRecordResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDeleteRecordResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDeleteRecordResponse.kt new file mode 100644 index 00000000..66b46cd0 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsDeleteRecordResponse.kt @@ -0,0 +1,8 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsDeleteRecordResponse( + val roomId: Int, +) From 593845cdd51bf89da9ad9eab399e81d834e6abae Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:47:23 +0900 Subject: [PATCH 38/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EA=B8=B0=20service=20=EC=9E=91=EC=84=B1=20(#?= =?UTF-8?q?71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/data/service/RoomsService.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index 81bf84c0..bfb94c1e 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -6,12 +6,14 @@ import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.response.RoomsBookPageResponse import com.texthip.thip.data.model.rooms.response.RoomsCreateVoteResponse +import com.texthip.thip.data.model.rooms.response.RoomsDeleteRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse import com.texthip.thip.data.model.rooms.response.RoomsVoteResponse import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path @@ -63,4 +65,10 @@ interface RoomsService { @Path("voteId") voteId: Int, @Body request: RoomsVoteRequest ): BaseResponse + + @DELETE("rooms/{roomId}/record/{recordId}") + suspend fun deleteRoomsRecord( + @Path("roomId") roomId: Int, + @Path("recordId") recordId: Int + ): BaseResponse } \ No newline at end of file From 524708ad7c708cc4c7394cfc0e1966c875636eec Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:47:29 +0900 Subject: [PATCH 39/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EA=B8=B0=20repository=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/data/repository/RoomsRepository.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index 83abeab5..012424a7 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -108,4 +108,14 @@ class RoomsRepository @Inject constructor( ) ).handleBaseResponse().getOrThrow() } + + suspend fun deleteRoomsRecord( + roomId: Int, + recordId: Int + ) = runCatching { + roomsService.deleteRoomsRecord( + roomId = roomId, + recordId = recordId + ).handleBaseResponse().getOrThrow() + } } \ No newline at end of file From 6b56cb9c318b0517c8e59072562a80f86e98d5a3 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 01:47:58 +0900 Subject: [PATCH 40/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EA=B8=B0=20viewmodel=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20screen=EC=97=90=20=EC=97=B0=EA=B2=B0=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/group/note/screen/GroupNoteScreen.kt | 16 +++------------- .../group/note/viewmodel/GroupNoteViewModel.kt | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) 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 a61dd13b..1b948395 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 @@ -157,6 +157,7 @@ fun GroupNoteContent( var isCommentBottomSheetVisible by remember { mutableStateOf(false) } var selectedPostForComment by remember { mutableStateOf(null) } var selectedPostForMenu by remember { mutableStateOf(null) } + var showDeleteDialog by remember { mutableStateOf(false) } var isPinDialogVisible by remember { mutableStateOf(false) } var showToast by remember { mutableStateOf(false) } @@ -468,13 +469,7 @@ fun GroupNoteContent( ) } -// if (isMenuBottomSheetVisible && selectedItemForMenu != null) { if (selectedPostForMenu != null) { -// val isWriter = when (val item = selectedItemForMenu) { -// is GroupNoteRecord -> item.isWriter -// is GroupNoteVote -> item.isWriter -// else -> false -// } val isWriter = selectedPostForMenu!!.isWriter val menuItems = if (isWriter) { @@ -483,9 +478,8 @@ fun GroupNoteContent( text = stringResource(R.string.delete), color = colors.Red, onClick = { - // TODO: 삭제 처리 -// isMenuBottomSheetVisible = false -// selectedItemForMenu = null + onEvent(GroupNoteEvent.OnDeleteRecord(selectedPostForMenu!!.postId)) + showDeleteDialog = false selectedPostForMenu = null } ) @@ -497,8 +491,6 @@ fun GroupNoteContent( color = colors.Red, onClick = { // TODO: 신고 처리 -// isMenuBottomSheetVisible = false -// selectedItemForMenu = null selectedPostForMenu = null } ) @@ -508,8 +500,6 @@ fun GroupNoteContent( MenuBottomSheet( items = menuItems, onDismiss = { -// isMenuBottomSheetVisible = false -// selectedItemForMenu = null selectedPostForMenu = null } ) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index 9c9f849b..6d37271e 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -47,6 +47,7 @@ sealed interface GroupNoteEvent { data object ApplyPageFilter : GroupNoteEvent data object LoadMorePosts : GroupNoteEvent data class OnVote(val postId: Int, val voteItemId: Int, val type: Boolean) : GroupNoteEvent + data class OnDeleteRecord(val postId: Int) : GroupNoteEvent } @@ -141,6 +142,21 @@ class GroupNoteViewModel @Inject constructor( voteItemId = event.voteItemId, type = event.type ) + + is GroupNoteEvent.OnDeleteRecord -> deleteRecord(event.postId) + } + } + + private fun deleteRecord(postId: Int) { + viewModelScope.launch { + roomsRepository.deleteRoomsRecord(roomId = roomId, recordId = postId) + .onSuccess { + val updatedPosts = _uiState.value.posts.filter { it.postId != postId } + _uiState.update { it.copy(posts = updatedPosts) } + } + .onFailure { throwable -> + _uiState.update { it.copy(error = throwable.message) } + } } } From c192fefeec67f17b0192918e78fa85b1e3ca9194 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:11:30 +0900 Subject: [PATCH 41/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20response,=20request=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/model/rooms/request/RoomsPostsLikesRequest.kt | 9 +++++++++ .../data/model/rooms/response/RoomsPostsLikesResponse.kt | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsLikesRequest.kt create mode 100644 app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsLikesResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsLikesRequest.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsLikesRequest.kt new file mode 100644 index 00000000..6c3240c2 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/request/RoomsPostsLikesRequest.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.rooms.request + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsPostsLikesRequest( + val type: Boolean, + val roomPostType: String, +) diff --git a/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsLikesResponse.kt b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsLikesResponse.kt new file mode 100644 index 00000000..be694d64 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomsPostsLikesResponse.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.rooms.response + +import kotlinx.serialization.Serializable + +@Serializable +data class RoomsPostsLikesResponse( + val postId: Int, + val isLiked: Boolean, +) From 26dc7e565459968a4c682738e810ad405392b021 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:11:50 +0900 Subject: [PATCH 42/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20service=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/data/service/RoomsService.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt index bfb94c1e..3e8e05e0 100644 --- a/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/RoomsService.kt @@ -2,12 +2,14 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest +import com.texthip.thip.data.model.rooms.request.RoomsPostsLikesRequest import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.response.RoomsBookPageResponse import com.texthip.thip.data.model.rooms.response.RoomsCreateVoteResponse import com.texthip.thip.data.model.rooms.response.RoomsDeleteRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsPlayingResponse +import com.texthip.thip.data.model.rooms.response.RoomsPostsLikesResponse import com.texthip.thip.data.model.rooms.response.RoomsPostsResponse import com.texthip.thip.data.model.rooms.response.RoomsRecordResponse import com.texthip.thip.data.model.rooms.response.RoomsUsersResponse @@ -71,4 +73,10 @@ interface RoomsService { @Path("roomId") roomId: Int, @Path("recordId") recordId: Int ): BaseResponse + + @POST("room-posts/{postId}/likes") + suspend fun postRoomsPostsLikes( + @Path("postId") postId: Int, + @Body request: RoomsPostsLikesRequest + ): BaseResponse } \ No newline at end of file From fc710d10fdb810b5943c56f1042dd2bdaef8692f Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:11:57 +0900 Subject: [PATCH 43/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20repository=20=EC=9E=91=EC=84=B1=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/repository/RoomsRepository.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt index 012424a7..8b16c7a2 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt @@ -2,6 +2,7 @@ package com.texthip.thip.data.repository import com.texthip.thip.data.model.base.handleBaseResponse import com.texthip.thip.data.model.rooms.request.RoomsCreateVoteRequest +import com.texthip.thip.data.model.rooms.request.RoomsPostsLikesRequest import com.texthip.thip.data.model.rooms.request.RoomsRecordRequest import com.texthip.thip.data.model.rooms.request.RoomsVoteRequest import com.texthip.thip.data.model.rooms.request.VoteItem @@ -118,4 +119,18 @@ class RoomsRepository @Inject constructor( recordId = recordId ).handleBaseResponse().getOrThrow() } + + suspend fun postRoomsPostsLikes( + postId: Int, + type: Boolean, + roomPostType: String + ) = runCatching { + roomsService.postRoomsPostsLikes( + postId = postId, + request = RoomsPostsLikesRequest( + type = type, + roomPostType = roomPostType + ) + ).handleBaseResponse().getOrThrow() + } } \ No newline at end of file From b28de01d82f7b2be639b289b6175ab7511d44be5 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:12:14 +0900 Subject: [PATCH 44/47] =?UTF-8?q?[feat]:=20=EA=B8=B0=EB=A1=9D=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20viewmodel=20=EC=9E=91=EC=84=B1=20=EB=B0=8F?= =?UTF-8?q?=20screen=20=EC=97=B0=EA=B2=B0=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/note/component/TextCommentCard.kt | 10 ++---- .../group/note/component/VoteCommentCard.kt | 10 ++---- .../ui/group/note/screen/GroupNoteScreen.kt | 8 ++++- .../note/viewmodel/GroupNoteViewModel.kt | 33 +++++++++++++++++++ 4 files changed, 46 insertions(+), 15 deletions(-) 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 df00ff2b..aa55b37c 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 @@ -6,10 +6,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding 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.Modifier import androidx.compose.ui.draw.blur import androidx.compose.ui.input.pointer.pointerInput @@ -27,11 +23,11 @@ import com.texthip.thip.ui.theme.ThipTheme.typography fun TextCommentCard( modifier: Modifier = Modifier, data: PostList, + onLikeClick: (postId: Int, postType: String) -> Unit = { _, _ -> }, onCommentClick: () -> Unit = {}, onLongPress: () -> Unit = {}, onPinClick: () -> Unit = {} ) { - var isLiked by remember { mutableStateOf(data.isLiked) } val isLocked = data.isLocked val isWriter = data.isWriter @@ -63,12 +59,12 @@ fun TextCommentCard( ) ActionBarButton( - isLiked = isLiked, + isLiked = data.isLiked, likeCount = data.likeCount, commentCount = data.commentCount, isPinVisible = isWriter, onLikeClick = { - if (!isLocked) isLiked = !isLiked + if (!isLocked) onLikeClick(data.postId, data.postType) }, onCommentClick = { if (!isLocked) onCommentClick() 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 8381dae8..9705ef67 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 @@ -6,10 +6,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding 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.Modifier import androidx.compose.ui.draw.blur import androidx.compose.ui.input.pointer.pointerInput @@ -28,12 +24,12 @@ import com.texthip.thip.ui.theme.ThipTheme.typography fun VoteCommentCard( modifier: Modifier = Modifier, data: PostList, + onLikeClick: (postId: Int, postType: String) -> Unit = { _, _ -> }, onVote: (postId: Int, voteItemId: Int, type: Boolean) -> Unit = { _, _, _ -> }, onCommentClick: () -> Unit = {}, onLongPress: () -> Unit = {}, onPinClick: () -> Unit = {} ) { - var isLiked by remember { mutableStateOf(data.isLiked) } val selectedIndex = data.voteItems.indexOfFirst { it.isVoted }.takeIf { it != -1 } val hasVoted = selectedIndex != null @@ -88,12 +84,12 @@ fun VoteCommentCard( } ActionBarButton( - isLiked = isLiked, + isLiked = data.isLiked, likeCount = data.likeCount, commentCount = data.commentCount, isPinVisible = isWriter, onLikeClick = { - if (!isLocked) isLiked = !isLiked + if (!isLocked) onLikeClick(data.postId, data.postType) }, onCommentClick = { if (!isLocked) onCommentClick() 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 1b948395..709e058d 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 @@ -361,7 +361,10 @@ fun GroupNoteContent( modifier = itemModifier, onCommentClick = { isCommentBottomSheetVisible = true }, onLongPress = { selectedPostForMenu = post }, - onPinClick = { isPinDialogVisible = true } + onPinClick = { isPinDialogVisible = true }, + onLikeClick = { postId, postType -> + onEvent(GroupNoteEvent.OnLikeRecord(postId, postType)) + } ) "VOTE" -> VoteCommentCard( @@ -372,6 +375,9 @@ fun GroupNoteContent( onPinClick = { isPinDialogVisible = true }, onVote = { postId, voteItemId, type -> onEvent(GroupNoteEvent.OnVote(postId, voteItemId, type)) + }, + onLikeClick = { postId, postType -> + onEvent(GroupNoteEvent.OnLikeRecord(postId, postType)) } ) } diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt index 6d37271e..e79db56a 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt @@ -48,6 +48,7 @@ sealed interface GroupNoteEvent { data object LoadMorePosts : GroupNoteEvent data class OnVote(val postId: Int, val voteItemId: Int, val type: Boolean) : GroupNoteEvent data class OnDeleteRecord(val postId: Int) : GroupNoteEvent + data class OnLikeRecord(val postId: Int, val postType: String) : GroupNoteEvent } @@ -144,6 +145,38 @@ class GroupNoteViewModel @Inject constructor( ) is GroupNoteEvent.OnDeleteRecord -> deleteRecord(event.postId) + is GroupNoteEvent.OnLikeRecord -> likeRecord(event.postId, event.postType) + } + } + + private fun likeRecord(postId: Int, postType: String) { + val currentPosts = _uiState.value.posts + val postIndex = currentPosts.indexOfFirst { it.postId == postId } + if (postIndex == -1) return + + val oldPost = currentPosts[postIndex] + + val newIsLiked = !oldPost.isLiked + val newLikeCount = if (newIsLiked) oldPost.likeCount + 1 else oldPost.likeCount - 1 + + val optimisticPost = oldPost.copy( + isLiked = newIsLiked, + likeCount = newLikeCount.coerceAtLeast(0) + ) + + val newPosts = currentPosts.toMutableList().apply { this[postIndex] = optimisticPost } + _uiState.update { it.copy(posts = newPosts) } + + viewModelScope.launch { + roomsRepository.postRoomsPostsLikes( + postId = postId, + type = newIsLiked, + roomPostType = postType + ) + .onFailure { + val rollbackPosts = currentPosts.toMutableList().apply { this[postIndex] = oldPost } + _uiState.update { it.copy(posts = rollbackPosts) } + } } } From 1866fcc6dbd81c36c0f407dac3be0833d8003d63 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:32:28 +0900 Subject: [PATCH 45/47] =?UTF-8?q?[refactor]:=20=EB=8F=85=EC=84=9C=EB=A9=94?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20aliasName=20=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/model/rooms/response/RoomsUsersResponse.kt | 2 +- .../thip/ui/group/room/component/GroupRoomMatesList.kt | 6 +++--- .../thip/ui/group/room/screen/GroupRoomMatesScreen.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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 604d8062..b1c3e442 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,6 @@ data class UserList( val userId: Int, val nickname: String, val imageUrl: String, - val alias: String, + val aliasName: String, val followerCount: Int, ) \ No newline at end of file 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 0188acd7..af83361a 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 @@ -30,7 +30,7 @@ fun GroupRoomMatesList( ProfileBar( profileImage = member.imageUrl, topText = member.nickname, - bottomText = member.alias, + bottomText = member.aliasName, // bottomTextColor = member.aliasColor, bottomTextColor = colors.ScienceIt, // TODO: 서버에서 보내주는 색상으로 수정 showSubscriberInfo = true, @@ -57,14 +57,14 @@ private fun GroupRoomMatesListPreview() { UserList( userId = 1, nickname = "김희용", - alias = "문학가", + aliasName = "문학가", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), UserList( userId = 2, nickname = "노성준", - alias = "문학가", + aliasName = "문학가", 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 a511d0a7..ce2dfa52 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 @@ -103,14 +103,14 @@ private fun GroupRoomMatesScreenPreview() { UserList( userId = 1, nickname = "김희용", - alias = "문학가", + aliasName = "문학가", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), UserList( userId = 2, nickname = "노성준", - alias = "문학가", + aliasName = "문학가", imageUrl = "https://example.com/image1.jpg", followerCount = 100 ), From 612da00104cf1a8493f995b12c1ec9fb81e5bc76 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 02:46:15 +0900 Subject: [PATCH 46/47] =?UTF-8?q?[refactor]:=20=EA=B8=B0=EB=A1=9D,=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=EC=B4=9D=ED=8F=89=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=95=A0=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../group/note/viewmodel/GroupNoteCreateViewModel.kt | 12 ++++++++++-- .../group/note/viewmodel/GroupVoteCreateViewModel.kt | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt index 3b55081a..1788a565 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteCreateViewModel.kt @@ -21,7 +21,8 @@ data class GroupNoteCreateUiState( val isOverviewPossible: Boolean = false ) { // 입력 폼이 모두 채워졌는지 확인 - val isFormFilled: Boolean get() = pageText.isNotBlank() && opinionText.isNotBlank() + val isFormFilled: Boolean + get() = (pageText.isNotBlank() || isGeneralReview) && opinionText.isNotBlank() } sealed interface GroupNoteCreateEvent { @@ -63,15 +64,18 @@ class GroupNoteCreateViewModel @Inject constructor( _uiState.update { it.copy(pageText = event.text) } } } + is GroupNoteCreateEvent.OpinionChanged -> { _uiState.update { it.copy(opinionText = event.text) } } + is GroupNoteCreateEvent.GeneralReviewToggled -> { _uiState.update { val newPageText = if (event.isChecked) "" else it.pageText it.copy(isGeneralReview = event.isChecked, pageText = newPageText) } } + GroupNoteCreateEvent.CreateRecordClicked -> { createRecord() } @@ -80,7 +84,11 @@ class GroupNoteCreateViewModel @Inject constructor( private fun createRecord() { val currentState = _uiState.value - val pageNumber = if (currentState.isGeneralReview) 0 else currentState.pageText.toIntOrNull() + val pageNumber = if (currentState.isGeneralReview) { + currentState.totalPage + } else { + currentState.pageText.toIntOrNull() + } if (pageNumber == null || !currentState.isFormFilled) { _uiState.update { it.copy(error = "페이지 번호를 정확히 입력해주세요.") } return diff --git a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt index 3d0906ef..cf758a4f 100644 --- a/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupVoteCreateViewModel.kt @@ -81,12 +81,14 @@ class GroupVoteCreateViewModel @Inject constructor( is GroupVoteCreateEvent.PageChanged -> if (!_uiState.value.isGeneralReview) { _uiState.update { it.copy(pageText = event.text) } } + is GroupVoteCreateEvent.TitleChanged -> _uiState.update { it.copy(title = event.text) } is GroupVoteCreateEvent.OptionChanged -> _uiState.update { val newOptions = it.options.toMutableList() newOptions[event.index] = event.text it.copy(options = newOptions) } + is GroupVoteCreateEvent.GeneralReviewToggled -> _uiState.update { if (event.isChecked) { it.copy(isGeneralReview = true, savedPageText = it.pageText, pageText = "") @@ -94,9 +96,11 @@ class GroupVoteCreateViewModel @Inject constructor( it.copy(isGeneralReview = false, pageText = it.savedPageText) } } + GroupVoteCreateEvent.AddOptionClicked -> if (_uiState.value.options.size < 5) { _uiState.update { it.copy(options = it.options + "") } } + is GroupVoteCreateEvent.RemoveOptionClicked -> if (_uiState.value.options.size > 2) { _uiState.update { val newOptions = it.options.toMutableList() @@ -104,6 +108,7 @@ class GroupVoteCreateViewModel @Inject constructor( it.copy(options = newOptions) } } + GroupVoteCreateEvent.CreateVoteClicked -> createVote() } } @@ -111,8 +116,11 @@ class GroupVoteCreateViewModel @Inject constructor( private fun createVote() { val currentState = _uiState.value if (!currentState.isFormFilled) return - - val pageNumber = if (currentState.isGeneralReview) 0 else currentState.pageText.toIntOrNull() + val pageNumber = if (currentState.isGeneralReview) { + currentState.bookTotalPage + } else { + currentState.pageText.toIntOrNull() + } if (pageNumber == null) { _uiState.update { it.copy(error = "페이지 번호를 정확히 입력해주세요.") } return From a34a03c4ff8eb6f6110dd637a4e1e1316fc746e0 Mon Sep 17 00:00:00 2001 From: Naeun Kim <102296721+Nico1eKim@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:42:32 +0900 Subject: [PATCH 47/47] =?UTF-8?q?[refactor]:=20bottom=20bar=EC=97=90=20pad?= =?UTF-8?q?ding=20=EC=B6=94=EA=B0=80=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/ui/navigator/BottomNavigationBar.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/BottomNavigationBar.kt b/app/src/main/java/com/texthip/thip/ui/navigator/BottomNavigationBar.kt index 3e25db24..d2fe0c45 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/BottomNavigationBar.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/BottomNavigationBar.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon @@ -27,8 +28,8 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import com.texthip.thip.ui.navigator.data.NavBarItems -import com.texthip.thip.ui.navigator.extensions.navigateToTab import com.texthip.thip.ui.navigator.extensions.isRoute +import com.texthip.thip.ui.navigator.extensions.navigateToTab import com.texthip.thip.ui.theme.ThipTheme.colors import com.texthip.thip.ui.theme.ThipTheme.typography @@ -40,6 +41,7 @@ fun BottomNavigationBar(navController: NavHostController) { Box( modifier = Modifier .fillMaxWidth() + .navigationBarsPadding() .height(73.dp) .clip( RoundedCornerShape(