From 0d88da575387cb002cbbff8541dbab6e0220f059 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 20:47:56 +0900 Subject: [PATCH 01/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/texthip/thip/data/repository/FeedRepository.kt | 7 +++++++ .../main/java/com/texthip/thip/data/service/FeedService.kt | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt index e1ee396c..a747e8c4 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt @@ -178,4 +178,11 @@ class FeedRepository @Inject constructor( .handleBaseResponse() .getOrThrow() } + + /** 피드 삭제 */ + suspend fun deleteFeed(feedId: Long): Result = runCatching { + feedService.deleteFeed(feedId) + .handleBaseResponse() + .getOrThrow() + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt index bed7573b..9febdc0c 100644 --- a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt @@ -10,6 +10,7 @@ import com.texthip.thip.data.model.feed.response.AllFeedResponse import com.texthip.thip.data.model.feed.response.MyFeedResponse import okhttp3.MultipartBody import okhttp3.RequestBody +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Multipart import retrofit2.http.POST @@ -59,4 +60,10 @@ interface FeedService { suspend fun getFeedUsers( @Path("userId") userId: Long ): BaseResponse + + /** 피드 삭제 */ + @DELETE("feeds/{feedId}") + suspend fun deleteFeed( + @Path("feedId") feedId: Long + ): BaseResponse } \ No newline at end of file From 620ffae47d2336b8a204b53a349de6c84deada6e Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 20:48:18 +0900 Subject: [PATCH 02/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EB=8B=B4=EB=8B=B9=20usecase?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/usecase/DeleteFeedUseCase.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt diff --git a/app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt b/app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt new file mode 100644 index 00000000..d8f30be6 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/usecase/DeleteFeedUseCase.kt @@ -0,0 +1,13 @@ +package com.texthip.thip.ui.feed.usecase + +import com.texthip.thip.data.repository.FeedRepository +import javax.inject.Inject + +//피드 삭제 기능만 담당하는 usecase +class DeleteFeedUseCase @Inject constructor( + private val feedRepository: FeedRepository +) { + suspend operator fun invoke(feedId: Long): Result { + return feedRepository.deleteFeed(feedId) + } +} \ No newline at end of file From 1b2bd68e80aa1b489a3769f2c79547bda50f3b1e Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 20:49:20 +0900 Subject: [PATCH 03/25] =?UTF-8?q?[feat]:=20FeedCommentScreen=EC=97=90=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EC=A0=81=EC=9A=A9(#9?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt | 1 + 1 file changed, 1 insertion(+) 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 7fef8619..023620a9 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 @@ -490,6 +490,7 @@ fun FeedCommentScreen( onConfirm = { showDialog = false isBottomSheetVisible = false + viewModel.deleteFeed(feedId.toLong()) }, onCancel = { showDialog = false From 678d7efc5db16f52c2406816a3dfd1b93d5c7e49 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 20:49:35 +0900 Subject: [PATCH 04/25] =?UTF-8?q?[feat]:=20FeedDetailViewModel=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/feed/viewmodel/FeedDetailViewModel.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt index 204d3604..0e96cd9c 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.repository.FeedRepository +import com.texthip.thip.ui.feed.usecase.DeleteFeedUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -14,12 +15,15 @@ import javax.inject.Inject data class FeedDetailUiState( val isLoading: Boolean = false, val feedDetail: FeedDetailResponse? = null, - val error: String? = null + val error: String? = null, + val isDeleting: Boolean = false, + val deleteSuccess: Boolean = false ) @HiltViewModel class FeedDetailViewModel @Inject constructor( - private val feedRepository: FeedRepository + private val feedRepository: FeedRepository, + private val deleteFeedUseCase: DeleteFeedUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(FeedDetailUiState()) @@ -54,6 +58,24 @@ class FeedDetailViewModel @Inject constructor( } } } + fun deleteFeed(feedId: Long) { + viewModelScope.launch { + updateState { it.copy(isDeleting = true, error = null) } + + deleteFeedUseCase(feedId) // UseCase 호출 + .onSuccess { + updateState { it.copy(isDeleting = false, deleteSuccess = true) } + } + .onFailure { exception -> + updateState { + it.copy( + isDeleting = false, + error = exception.message ?: "피드 삭제 중 오류가 발생했습니다." + ) + } + } + } + } fun clearError() { updateState { it.copy(error = null) } From af34da18067ec8e1102eb4c9e843c779477db29d Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:12:53 +0900 Subject: [PATCH 05/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?usecase=20=EC=83=9D=EC=84=B1(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt diff --git a/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt b/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt new file mode 100644 index 00000000..4782c16a --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedLikeUseCase.kt @@ -0,0 +1,11 @@ +package com.texthip.thip.ui.feed.usecase + +import com.texthip.thip.data.repository.FeedRepository +import javax.inject.Inject + +class ChangeFeedLikeUseCase @Inject constructor( + private val feedRepository: FeedRepository +) { + suspend operator fun invoke(feedId: Long, newLikeStatus: Boolean) = + feedRepository.changeFeedLike(feedId, newLikeStatus) +} \ No newline at end of file From 14ec98c5133f6ea7b644ec7c06b7c1090ce53cce Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:13:12 +0900 Subject: [PATCH 06/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD,=20=EC=9D=91=EB=8B=B5=20dto=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/model/feed/request/FeedLikeRequest.kt | 9 +++++++++ .../thip/data/model/feed/response/FeedLikeResponse.kt | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt create mode 100644 app/src/main/java/com/texthip/thip/data/model/feed/response/FeedLikeResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt b/app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt new file mode 100644 index 00000000..d3fb5c11 --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/feed/request/FeedLikeRequest.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.feed.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FeedLikeRequest( + @SerialName("type") val type: Boolean +) diff --git a/app/src/main/java/com/texthip/thip/data/model/feed/response/FeedLikeResponse.kt b/app/src/main/java/com/texthip/thip/data/model/feed/response/FeedLikeResponse.kt new file mode 100644 index 00000000..d0ac267e --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/feed/response/FeedLikeResponse.kt @@ -0,0 +1,10 @@ +package com.texthip.thip.data.model.feed.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FeedLikeResponse( + @SerialName("feedId") val feedId: Long, + @SerialName("isLiked") val isLiked: Boolean +) From e6f026ee7dc6104777727a083078449df1945718 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:15:02 +0900 Subject: [PATCH 07/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4,=20=EB=A0=88=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/data/repository/FeedRepository.kt | 11 ++++++++++- .../com/texthip/thip/data/service/FeedService.kt | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt index a747e8c4..f88872ab 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt @@ -4,10 +4,12 @@ import android.content.Context import android.net.Uri import com.texthip.thip.data.model.base.handleBaseResponse import com.texthip.thip.data.model.feed.request.CreateFeedRequest +import com.texthip.thip.data.model.feed.request.FeedLikeRequest import com.texthip.thip.data.model.feed.response.CreateFeedResponse import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.model.feed.response.FeedWriteInfoResponse import com.texthip.thip.data.model.feed.response.AllFeedResponse +import com.texthip.thip.data.model.feed.response.FeedLikeResponse import com.texthip.thip.data.model.feed.response.MyFeedResponse import com.texthip.thip.data.service.FeedService import dagger.hilt.android.qualifiers.ApplicationContext @@ -148,7 +150,7 @@ class FeedRepository @Inject constructor( } /** 피드 상세 조회 */ - suspend fun getFeedDetail(feedId: Int): Result = runCatching { + suspend fun getFeedDetail(feedId: Long): Result = runCatching { feedService.getFeedDetail(feedId) .handleBaseResponse() .getOrThrow() @@ -185,4 +187,11 @@ class FeedRepository @Inject constructor( .handleBaseResponse() .getOrThrow() } + + suspend fun changeFeedLike(feedId: Long, newLikeStatus: Boolean): Result = runCatching { + val request = FeedLikeRequest(type = newLikeStatus) + feedService.toggleFeedLike(feedId, request) + .handleBaseResponse() + .getOrThrow() + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt index 9febdc0c..bf66d591 100644 --- a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt @@ -1,15 +1,18 @@ package com.texthip.thip.data.service import com.texthip.thip.data.model.base.BaseResponse +import com.texthip.thip.data.model.feed.request.FeedLikeRequest import com.texthip.thip.data.model.feed.response.CreateFeedResponse import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.model.feed.response.FeedUsersInfoResponse import com.texthip.thip.data.model.feed.response.FeedUsersResponse import com.texthip.thip.data.model.feed.response.FeedWriteInfoResponse import com.texthip.thip.data.model.feed.response.AllFeedResponse +import com.texthip.thip.data.model.feed.response.FeedLikeResponse import com.texthip.thip.data.model.feed.response.MyFeedResponse import okhttp3.MultipartBody import okhttp3.RequestBody +import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Multipart @@ -48,7 +51,7 @@ interface FeedService { /** 피드 상세 조회 */ @GET("feeds/{feedId}") suspend fun getFeedDetail( - @Path("feedId") feedId: Int + @Path("feedId") feedId: Long ): BaseResponse @GET("feeds/users/{userId}/info") @@ -66,4 +69,11 @@ interface FeedService { suspend fun deleteFeed( @Path("feedId") feedId: Long ): BaseResponse + + /** 피드 좋아요 상태 변경 */ + @POST("feeds/{feedId}/likes") + suspend fun toggleFeedLike( + @Path("feedId") feedId: Long, + @Body request: FeedLikeRequest + ): BaseResponse } \ No newline at end of file From e1d7446ba5c19ef573795a49324124fd5d9301c6 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:16:13 +0900 Subject: [PATCH 08/25] =?UTF-8?q?[feat]:=20FeedId=20=ED=83=80=EC=9E=85=20i?= =?UTF-8?q?nt=20->=20Long=20=EB=B3=80=EA=B2=BD=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/ui/feed/component/MyFeedCard.kt | 4 ++-- .../thip/ui/feed/screen/FeedCommentScreen.kt | 2 +- .../com/texthip/thip/ui/feed/screen/FeedScreen.kt | 12 ++++++------ .../thip/ui/feed/viewmodel/FeedDetailViewModel.kt | 2 +- .../thip/ui/mypage/component/SavedFeedCard.kt | 4 ++-- .../com/texthip/thip/ui/mypage/mock/FeedItem.kt | 2 +- .../thip/ui/mypage/viewmodel/SavedFeedViewModel.kt | 14 +++++++------- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt index 297e75f3..8ad5203e 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt @@ -127,7 +127,7 @@ fun MyFeedCard( @Composable private fun MyFeedCardPrev() { val feed1 = FeedItem( - id = 1, + id = 1L, userProfileImage = "https://example.com/profile1.jpg", userName = "user.01", userRole = stringResource(R.string.influencer), @@ -143,7 +143,7 @@ private fun MyFeedCardPrev() { imageUrls = emptyList() ) val feed2 = FeedItem( - id = 2, + id = 2L, userProfileImage = "https://example.com/profile2.jpg", userName = "user.01", userRole = stringResource(R.string.influencer), 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 023620a9..ed0894a6 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 @@ -63,7 +63,7 @@ import com.texthip.thip.ui.group.note.mock.ReplyItem as FeedReplyItem @Composable fun FeedCommentScreen( modifier: Modifier = Modifier, - feedId: Int, + feedId: Long, onNavigateBack: () -> Unit = {}, currentUserId: Int = 1, currentUserName: String = "현재사용자", diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt index 614b6a03..85f9d27e 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt @@ -66,11 +66,11 @@ import kotlinx.coroutines.launch fun FeedScreen( onNavigateToMySubscription: () -> Unit = {}, onNavigateToFeedWrite: () -> Unit = {}, - onNavigateToFeedComment: (Int) -> Unit = {}, + onNavigateToFeedComment: (Long) -> Unit = {}, nickname: String = "", userRole: String = "", followerProfileImageUrls: List = emptyList(), - resultFeedId: Int? = null, + resultFeedId: Long? = null, onResultConsumed: () -> Unit = {}, feedViewModel: FeedViewModel = hiltViewModel(), mySubscriptionViewModel: MySubscriptionViewModel = hiltViewModel() @@ -267,7 +267,7 @@ fun FeedScreen( // MyFeedItem을 FeedItem으로 변환 val feedItem = FeedItem( - id = myFeed.feedId, + id = myFeed.feedId.toLong(), userProfileImage = null, userName = "", // 내 피드이므로 고정값 userRole = "", // 내 피드이므로 고정값 @@ -313,7 +313,7 @@ fun FeedScreen( itemsIndexed(feedUiState.allFeeds, key = { _, item -> item.feedId }) { index, allFeed -> // AllFeedItem을 FeedItem으로 변환 val feedItem = FeedItem( - id = allFeed.feedId, + id = allFeed.feedId.toLong(), userProfileImage = allFeed.creatorProfileImageUrl, userName = allFeed.creatorNickname, userRole = allFeed.aliasName, @@ -339,7 +339,7 @@ fun FeedScreen( // TODO: API 호출로 북마크 상태 변경 }, onLikeClick = { - // TODO: API 호출로 좋아요 상태 변경 + feedViewModel.changeFeedLike(feedItem.id) }, onContentClick = { onNavigateToFeedComment(feedItem.id) @@ -389,7 +389,7 @@ private fun FeedScreenPreview() { ThipTheme { val mockFeeds = List(5) { FeedItem( - id = it + 1, + id = (it + 1).toLong(), userProfileImage = "https://example.com/profile$it.jpg", userName = "user.$it", userRole = "문학 칭호", diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt index 0e96cd9c..74ad5429 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt @@ -33,7 +33,7 @@ class FeedDetailViewModel @Inject constructor( _uiState.value = update(_uiState.value) } - fun loadFeedDetail(feedId: Int) { + fun loadFeedDetail(feedId: Long) { viewModelScope.launch { updateState { it.copy(isLoading = true, error = null) } diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt b/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt index b9032072..85578dac 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt @@ -119,7 +119,7 @@ fun SavedFeedCard( @Composable private fun SavedFeedCardPrev() { val feed1 = FeedItem( - id = 1, + id = 1L, userProfileImage = "https://example.com/profile1.jpg", userName = "user.01", userRole = stringResource(R.string.influencer), @@ -135,7 +135,7 @@ private fun SavedFeedCardPrev() { ) val feed2 = FeedItem( - id = 2, + id = 2L, userProfileImage = "https://example.com/profile2.jpg", userName = "user.01", userRole = stringResource(R.string.influencer), diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/mock/FeedItem.kt b/app/src/main/java/com/texthip/thip/ui/mypage/mock/FeedItem.kt index 32c75942..0b28b534 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/mock/FeedItem.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/mock/FeedItem.kt @@ -1,7 +1,7 @@ package com.texthip.thip.ui.mypage.mock data class FeedItem( - val id: Int, + val id: Long, val userProfileImage: String? = null, val userName: String, val userRole: String, diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt index 7cb2229c..f5f1af2b 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/SavedFeedViewModel.kt @@ -10,7 +10,7 @@ open class SavedFeedViewModel: ViewModel() { private val _feeds = MutableStateFlow( listOf( FeedItem( - id = 1, + id = 1L, userProfileImage = "https://example.com/profile.jpg", userName = "user", userRole = "학생", @@ -24,7 +24,7 @@ open class SavedFeedViewModel: ViewModel() { isSaved = true ), FeedItem( - id = 2, + id = 2L, userProfileImage = "https://example.com/profile.jpg", userName = "user", userRole = "학생", @@ -39,7 +39,7 @@ open class SavedFeedViewModel: ViewModel() { imageUrls = emptyList() ), FeedItem( - id = 3, + id = 3L, userProfileImage = "https://example.com/profile.jpg", userName = "user", userRole = "학생", @@ -54,7 +54,7 @@ open class SavedFeedViewModel: ViewModel() { imageUrls = listOf("https://example.com/image.jpg") ), FeedItem( - id = 4, + id = 4L, userProfileImage = "https://example.com/profile.jpg", userName = "user", userRole = "학생", @@ -69,7 +69,7 @@ open class SavedFeedViewModel: ViewModel() { imageUrls = listOf("https://example.com/image.jpg") ), FeedItem( - id = 5, + id = 5L, userProfileImage = "https://example.com/profile.jpg", userName = "user", userRole = "학생", @@ -90,13 +90,13 @@ open class SavedFeedViewModel: ViewModel() { open val feeds: StateFlow> = _feeds - fun toggleBookmark(id: Int) { + fun toggleBookmark(id: Long) { _feeds.value = _feeds.value.map { if (it.id == id) it.copy(isSaved = !it.isSaved) else it } } - fun toggleLike(id: Int) { + fun toggleLike(id: Long) { _feeds.value = _feeds.value.map { if (it.id == id) it.copy(isLiked = !it.isLiked) else it } From b549e3c7f6d99fff9d11d4168b46dd3eefd26e56 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:16:35 +0900 Subject: [PATCH 09/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EB=B7=B0?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20->=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=B3=80=EA=B2=BD=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/viewmodel/FeedViewModel.kt | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt index 871c3809..b05bb1d2 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt @@ -7,9 +7,11 @@ import com.texthip.thip.data.model.feed.response.MyFeedItem import com.texthip.thip.data.model.users.response.RecentWriterList import com.texthip.thip.data.repository.FeedRepository import com.texthip.thip.data.repository.UserRepository +import com.texthip.thip.ui.feed.usecase.ChangeFeedLikeUseCase 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 @@ -44,7 +46,8 @@ data class FeedUiState( @HiltViewModel class FeedViewModel @Inject constructor( private val feedRepository: FeedRepository, - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val changeFeedLikeUseCase: ChangeFeedLikeUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(FeedUiState()) val uiState = _uiState.asStateFlow() @@ -284,4 +287,31 @@ class FeedViewModel @Inject constructor( } } } + + fun changeFeedLike(feedId: Long) { + viewModelScope.launch { + val currentFeeds = _uiState.value.allFeeds + val feedToUpdate = currentFeeds.find { it.feedId.toLong() == feedId } ?: return@launch + + //ui 먼저 변경 ( 낙관적 업데이트 ) + val newFeeds = currentFeeds.map { + if (it.feedId.toLong() == feedId) { + it.copy( + isLiked = !it.isLiked, + likeCount = if (it.isLiked) it.likeCount - 1 else it.likeCount + 1 + ) + } else { + it + } + } + _uiState.update { it.copy(allFeeds = newFeeds) } + + //api 호출 + val newLikeStatus = !feedToUpdate.isLiked + changeFeedLikeUseCase(feedId, newLikeStatus) + .onFailure { + _uiState.update { it.copy(allFeeds = currentFeeds)} + } + } + } } \ No newline at end of file From 189970aa17bbac77f1b39596c8dc9fc07e3e85cf Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:16:45 +0900 Subject: [PATCH 10/25] =?UTF-8?q?[feat]:=20=EB=8B=A4=EB=A5=B8=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=ED=94=BC=EB=93=9C=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?->=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=83=81=ED=83=9C=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=95=A8=EC=88=98=20=EC=83=9D=EC=84=B1=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/feed/viewmodel/FeedOthersViewModel.kt | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt index f8b4fd66..26fbcb41 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedOthersViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import com.texthip.thip.data.model.feed.response.FeedList import com.texthip.thip.data.model.feed.response.FeedUsersInfoResponse import com.texthip.thip.data.repository.FeedRepository +import com.texthip.thip.ui.feed.usecase.ChangeFeedLikeUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow @@ -25,9 +26,9 @@ data class FeedOthersUiState( @HiltViewModel class FeedOthersViewModel @Inject constructor( private val feedRepository: FeedRepository, + private val changeFeedLikeUseCase: ChangeFeedLikeUseCase, savedStateHandle: SavedStateHandle ) : ViewModel() { - private val userId: Long = requireNotNull(savedStateHandle["userId"]) private val _uiState = MutableStateFlow(FeedOthersUiState()) @@ -64,4 +65,31 @@ class FeedOthersViewModel @Inject constructor( } } } + + fun changeFeedLike(feedId: Long) { + viewModelScope.launch { + val currentFeeds = _uiState.value.feeds + val feedToUpdate = currentFeeds.find { it.feedId == feedId } ?: return@launch + + //ui 먼저 변경 ( 낙관적 업데이트 ) + val newFeeds = currentFeeds.map { + if (it.feedId == feedId) { + it.copy( + isLiked = !it.isLiked, + likeCount = if (it.isLiked) it.likeCount - 1 else it.likeCount + 1 + ) + } else { + it + } + } + _uiState.update { it.copy(feeds = newFeeds) } + + //api 호출 + val newLikeStatus = !feedToUpdate.isLiked + changeFeedLikeUseCase(feedId, newLikeStatus) + .onFailure { + _uiState.update { it.copy(feeds = currentFeeds) } + } + } + } } \ No newline at end of file From 98d3a659fe2757edd2f885e9d87aaad3e91a6c96 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:17:09 +0900 Subject: [PATCH 11/25] =?UTF-8?q?[feat]:=20=EB=8B=A4=EB=A5=B8=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=ED=94=BC=EB=93=9C=20screen=20->=20onLikeClick=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/ui/feed/screen/FeedOthersScreen.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt index 43a67cac..84d14767 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedOthersScreen.kt @@ -45,14 +45,16 @@ fun FeedOthersScreen( FeedOthersContent( uiState = uiState, - onNavigateBack = onNavigateBack + onNavigateBack = onNavigateBack, + onLikeClick = { feedId -> viewModel.changeFeedLike(feedId) } ) } @Composable fun FeedOthersContent( uiState: FeedOthersUiState, - onNavigateBack: () -> Unit + onNavigateBack: () -> Unit, + onLikeClick: (Long) -> Unit ) { val userInfo = uiState.userInfo @@ -134,7 +136,7 @@ fun FeedOthersContent( Spacer(modifier = Modifier.height(if (index == 0) 20.dp else 40.dp)) OthersFeedCard( feedItem = feed, - onLikeClick = { /* TODO: 좋아요 로직 연결 */ }, + onLikeClick = { onLikeClick(feed.feedId) }, onContentClick = { /* TODO: 피드 상세 댓글 화면으로 이동 */ } ) Spacer(modifier = Modifier.height(40.dp)) @@ -184,7 +186,8 @@ private fun FeedOthersScreenPrev() { userInfo = mockUserInfo, feeds = mockFeeds ), - onNavigateBack = {} + onNavigateBack = {}, + onLikeClick = {} ) } } \ No newline at end of file From 77274c9ff5853fedde08a90fe46717dc4a404951 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 22:17:35 +0900 Subject: [PATCH 12/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20FeedId=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=88=98=EC=A0=95=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/navigator/extensions/FeedNavigationExtensions.kt | 2 +- .../texthip/thip/ui/navigator/navigations/FeedNavigation.kt | 6 +++--- .../java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt index 85c3cff7..88f892c1 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/extensions/FeedNavigationExtensions.kt @@ -20,7 +20,7 @@ fun NavHostController.navigateToFeedWrite() { } // 피드 댓글으로 -fun NavHostController.navigateToFeedComment(feedId: Int) { +fun NavHostController.navigateToFeedComment(feedId: Long) { navigate(FeedRoutes.Comment(feedId)) } diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt index 16523728..cd34b053 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt @@ -21,7 +21,7 @@ import com.texthip.thip.ui.navigator.routes.MainTabRoutes // Feed fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBack: () -> Unit) { composable { backStackEntry -> - val resultFeedId = backStackEntry.savedStateHandle.get("feedId") + val resultFeedId = backStackEntry.savedStateHandle.get("feedId") FeedScreen( nickname = "ThipUser01", @@ -29,7 +29,7 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac followerProfileImageUrls = emptyList(), resultFeedId = resultFeedId, onResultConsumed = { - backStackEntry.savedStateHandle.remove("feedId") + backStackEntry.savedStateHandle.remove("feedId") }, onNavigateToMySubscription = { navController.navigateToMySubscription() @@ -93,7 +93,7 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac } composable { backStackEntry -> val route = backStackEntry.arguments?.let { - FeedRoutes.Comment(it.getInt("feedId")) + FeedRoutes.Comment(it.getLong("feedId")) } ?: return@composable FeedCommentScreen( diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt b/app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt index 55e0d1d5..9a7b267e 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/routes/FeedRoutes.kt @@ -7,7 +7,7 @@ sealed class FeedRoutes : Routes() { @Serializable data object MySubscription : FeedRoutes() - @Serializable data class Comment(val feedId: Int) : FeedRoutes() + @Serializable data class Comment(val feedId: Long) : FeedRoutes() @Serializable data class Write( From c8d00018aa21d364856b113affe708d18fc4df1a Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 23:10:00 +0900 Subject: [PATCH 13/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=9A=94=EC=B2=AD,=EC=9D=91=EB=8B=B5=20dto=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/model/feed/request/FeedSaveRequest.kt | 9 +++++++++ .../thip/data/model/feed/response/FeedSaveResponse.kt | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/data/model/feed/request/FeedSaveRequest.kt create mode 100644 app/src/main/java/com/texthip/thip/data/model/feed/response/FeedSaveResponse.kt diff --git a/app/src/main/java/com/texthip/thip/data/model/feed/request/FeedSaveRequest.kt b/app/src/main/java/com/texthip/thip/data/model/feed/request/FeedSaveRequest.kt new file mode 100644 index 00000000..e795813b --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/feed/request/FeedSaveRequest.kt @@ -0,0 +1,9 @@ +package com.texthip.thip.data.model.feed.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FeedSaveRequest( + @SerialName("type") val type: Boolean +) diff --git a/app/src/main/java/com/texthip/thip/data/model/feed/response/FeedSaveResponse.kt b/app/src/main/java/com/texthip/thip/data/model/feed/response/FeedSaveResponse.kt new file mode 100644 index 00000000..64ba310b --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/model/feed/response/FeedSaveResponse.kt @@ -0,0 +1,10 @@ +package com.texthip.thip.data.model.feed.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FeedSaveResponse( + @SerialName("feedId") val feedId: Long, + @SerialName("isSaved") val isSaved: Boolean +) From f623358d0c95e2b70e80b698c0976db827603de7 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 23:11:30 +0900 Subject: [PATCH 14/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20usecase=20=EC=83=9D=EC=84=B1=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt diff --git a/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt b/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt new file mode 100644 index 00000000..d90196bd --- /dev/null +++ b/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt @@ -0,0 +1,11 @@ +package com.texthip.thip.ui.feed.usecase + +import com.texthip.thip.data.repository.FeedRepository +import javax.inject.Inject + +class ChangeFeedSaveUseCase @Inject constructor( + private val feedRepository: FeedRepository +) { + suspend operator fun invoke(feedId: Long, newSaveStatus: Boolean) = + feedRepository.changeFeedLike(feedId, newSaveStatus) +} \ No newline at end of file From bd966265662e9d5fd80ac6c190a6d4130e189e6c Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 23:11:43 +0900 Subject: [PATCH 15/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../texthip/thip/data/repository/FeedRepository.kt | 13 ++++++++++++- .../com/texthip/thip/data/service/FeedService.kt | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt index bee99bfa..120f2803 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt @@ -5,11 +5,13 @@ import android.net.Uri import com.texthip.thip.data.model.base.handleBaseResponse import com.texthip.thip.data.model.feed.request.CreateFeedRequest import com.texthip.thip.data.model.feed.request.FeedLikeRequest +import com.texthip.thip.data.model.feed.request.FeedSaveRequest import com.texthip.thip.data.model.feed.response.AllFeedResponse import com.texthip.thip.data.model.feed.response.CreateFeedResponse import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.model.feed.response.FeedLikeResponse import com.texthip.thip.data.model.feed.response.FeedMineInfoResponse +import com.texthip.thip.data.model.feed.response.FeedSaveResponse import com.texthip.thip.data.model.feed.response.FeedWriteInfoResponse import com.texthip.thip.data.model.feed.response.MyFeedResponse import com.texthip.thip.data.model.feed.response.RelatedBooksResponse @@ -210,8 +212,17 @@ class FeedRepository @Inject constructor( suspend fun changeFeedLike(feedId: Long, newLikeStatus: Boolean): Result = runCatching { val request = FeedLikeRequest(type = newLikeStatus) - feedService.toggleFeedLike(feedId, request) + feedService.changeFeedLike(feedId, request) .handleBaseResponse() .getOrThrow() } + + /** 피드 저장 */ + suspend fun changeFeedSave(feedId: Long, newSaveStatus: Boolean): Result = runCatching { + val request = FeedSaveRequest(type = newSaveStatus) + feedService.changeFeedSave(feedId, request) + .handleBaseResponse() + .getOrThrow() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt index 5ae0ae80..a64325a8 100644 --- a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/FeedService.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.feed.request.FeedLikeRequest +import com.texthip.thip.data.model.feed.request.FeedSaveRequest import com.texthip.thip.data.model.feed.request.UpdateFeedRequest import com.texthip.thip.data.model.feed.response.AllFeedResponse import com.texthip.thip.data.model.feed.response.CreateFeedResponse import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.model.feed.response.FeedLikeResponse import com.texthip.thip.data.model.feed.response.FeedMineInfoResponse +import com.texthip.thip.data.model.feed.response.FeedSaveResponse import com.texthip.thip.data.model.feed.response.FeedUsersInfoResponse import com.texthip.thip.data.model.feed.response.FeedUsersResponse import com.texthip.thip.data.model.feed.response.FeedWriteInfoResponse @@ -88,7 +90,7 @@ interface FeedService { /** 피드 좋아요 상태 변경 */ @POST("feeds/{feedId}/likes") - suspend fun toggleFeedLike( + suspend fun changeFeedLike( @Path("feedId") feedId: Long, @Body request: FeedLikeRequest ): BaseResponse @@ -99,4 +101,11 @@ interface FeedService { @Path("feedId") feedId: Int, @Body request: UpdateFeedRequest ): BaseResponse + + /** 피드 저장 상태 변경 */ + @POST("feeds/{feedId}/saved") + suspend fun changeFeedSave( + @Path("feedId") feedId: Long, + @Body request: FeedSaveRequest + ): BaseResponse } \ No newline at end of file From 32b5ce89835c81b2a9debc49b87ee13e336c2310 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 23:35:29 +0900 Subject: [PATCH 16/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EB=B7=B0=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=88=98=EC=A0=95=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/viewmodel/FeedViewModel.kt | 36 ++++++++++++++++--- .../ui/feed/viewmodel/FeedWriteViewModel.kt | 15 ++++---- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt index 0887e329..3a2fa8bd 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt @@ -9,6 +9,7 @@ import com.texthip.thip.data.model.users.response.RecentWriterList import com.texthip.thip.data.repository.FeedRepository import com.texthip.thip.data.repository.UserRepository import com.texthip.thip.ui.feed.usecase.ChangeFeedLikeUseCase +import com.texthip.thip.ui.feed.usecase.ChangeFeedSaveUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -49,7 +50,8 @@ data class FeedUiState( class FeedViewModel @Inject constructor( private val feedRepository: FeedRepository, private val userRepository: UserRepository, - private val changeFeedLikeUseCase: ChangeFeedLikeUseCase + private val changeFeedLikeUseCase: ChangeFeedLikeUseCase, + private val changeFeedSaveUseCase: ChangeFeedSaveUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(FeedUiState()) val uiState = _uiState.asStateFlow() @@ -67,7 +69,7 @@ class FeedViewModel @Inject constructor( loadAllFeeds() fetchRecentWriters() } - + fun onTabSelected(index: Int) { updateState { it.copy(selectedTabIndex = index) } @@ -314,7 +316,7 @@ class FeedViewModel @Inject constructor( //ui 먼저 변경 ( 낙관적 업데이트 ) val newFeeds = currentFeeds.map { - if (it.feedId.toLong() == feedId) { + if (it.feedId.toLong() == feedId) { it.copy( isLiked = !it.isLiked, likeCount = if (it.isLiked) it.likeCount - 1 else it.likeCount + 1 @@ -323,13 +325,37 @@ class FeedViewModel @Inject constructor( it } } - _uiState.update { it.copy(allFeeds = newFeeds) } + _uiState.update { it.copy(allFeeds = newFeeds) } //api 호출 val newLikeStatus = !feedToUpdate.isLiked changeFeedLikeUseCase(feedId, newLikeStatus) .onFailure { - _uiState.update { it.copy(allFeeds = currentFeeds)} + _uiState.update { it.copy(allFeeds = currentFeeds) } + } + } + } + + fun changeFeedSave(feedId: Long) { + viewModelScope.launch { + val currentFeeds = _uiState.value.allFeeds + val feedToUpdate = currentFeeds.find { it.feedId.toLong() == feedId } ?: return@launch + + // (낙관적 업데이트) UI 즉시 변경 + val newFeeds = currentFeeds.map { + if (it.feedId.toLong() == feedId) { + it.copy(isSaved = !it.isSaved) // isSaved 상태 반전 + } else { + it + } + } + updateState { it.copy(allFeeds = newFeeds) } + + // API 호출 + val newSaveStatus = !feedToUpdate.isSaved + changeFeedSaveUseCase(feedId, newSaveStatus) + .onFailure { + updateState { it.copy(allFeeds = currentFeeds) } } } } diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt index fa262004..0b4cb315 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.onFailure @HiltViewModel class FeedWriteViewModel @Inject constructor( @@ -61,7 +62,7 @@ class FeedWriteViewModel @Inject constructor( } } - fun loadFeedForEdit(feedId: Int) { + fun loadFeedForEdit(feedId: Long) { viewModelScope.launch { updateState { it.copy(isLoading = true) } @@ -117,7 +118,7 @@ class FeedWriteViewModel @Inject constructor( } fun setEditData( - feedId: Int, + feedId: Long, isbn: String, bookTitle: String, bookAuthor: String, @@ -352,7 +353,7 @@ class FeedWriteViewModel @Inject constructor( } } - fun createOrUpdateFeed(onSuccess: (Int) -> Unit, onError: (String) -> Unit) { + fun createOrUpdateFeed(onSuccess: (Long) -> Unit, onError: (String) -> Unit) { val currentState = _uiState.value if (currentState.isEditMode && currentState.editingFeedId != null) { @@ -362,7 +363,7 @@ class FeedWriteViewModel @Inject constructor( } } - fun createFeed(onSuccess: (Int) -> Unit, onError: (String) -> Unit) { + fun createFeed(onSuccess: (Long) -> Unit, onError: (String) -> Unit) { val currentState = _uiState.value if (!currentState.isFormValid) { @@ -391,7 +392,7 @@ class FeedWriteViewModel @Inject constructor( result.onSuccess { response -> val feedId = response?.feedId if (feedId != null) { - onSuccess(feedId) + onSuccess(feedId.toLong()) } else { onError(stringResourceProvider.getString(R.string.error_feed_id_not_returned)) } @@ -415,7 +416,7 @@ class FeedWriteViewModel @Inject constructor( } } - private fun updateFeed(feedId: Int, onSuccess: (Int) -> Unit, onError: (String) -> Unit) { + private fun updateFeed(feedId: Long, onSuccess: (Long) -> Unit, onError: (String) -> Unit) { val currentState = _uiState.value if (!currentState.isFormValid) { @@ -437,7 +438,7 @@ class FeedWriteViewModel @Inject constructor( result.onSuccess { response -> val updatedFeedId = response?.feedId ?: feedId - onSuccess(updatedFeedId) + onSuccess(updatedFeedId.toLong()) }.onFailure { exception -> onError( exception.message From d142f9a712050824d5dacadd0b298bab83cbe79e Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 23:36:22 +0900 Subject: [PATCH 17/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20feedscreen=20=EC=A0=81=EC=9A=A9(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt index d8646d3c..4702da4e 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt @@ -337,7 +337,7 @@ fun FeedScreen( feedItem = feedItem, bottomTextColor = hexToColor(allFeed.aliasColor), onBookmarkClick = { - // TODO: API 호출로 북마크 상태 변경 + feedViewModel.changeFeedSave(feedItem.id) }, onLikeClick = { feedViewModel.changeFeedLike(feedItem.id) From 18203f367d519570bfc821b7b35f36b7b81bde4c Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Mon, 18 Aug 2025 23:47:02 +0900 Subject: [PATCH 18/25] =?UTF-8?q?[feat]:=20=ED=83=80=EC=9E=85=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/texthip/thip/data/service/FeedService.kt | 2 +- .../java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt | 2 +- .../com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt | 2 +- .../texthip/thip/ui/navigator/navigations/FeedNavigation.kt | 4 ++-- .../texthip/thip/ui/search/screen/SearchBookDetailScreen.kt | 4 ++-- .../texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt index a64325a8..8b93abfe 100644 --- a/app/src/main/java/com/texthip/thip/data/service/FeedService.kt +++ b/app/src/main/java/com/texthip/thip/data/service/FeedService.kt @@ -98,7 +98,7 @@ interface FeedService { /** 피드 수정 */ @PATCH("feeds/{feedId}") suspend fun updateFeed( - @Path("feedId") feedId: Int, + @Path("feedId") feedId: Long, @Body request: UpdateFeedRequest ): BaseResponse diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt index 2f485e1b..c4923e47 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt @@ -62,7 +62,7 @@ import com.texthip.thip.ui.theme.ThipTheme.typography fun FeedWriteScreen( modifier: Modifier = Modifier, onNavigateBack: () -> Unit, - onFeedCreated: (Int) -> Unit = {}, + onFeedCreated: (Long) -> Unit = {}, viewModel: FeedWriteViewModel = hiltViewModel() ) { val uiState by viewModel.uiState.collectAsState() diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt index 65780274..97bb7041 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt @@ -24,7 +24,7 @@ data class FeedWriteUiState( val isBookPreselected: Boolean = false, val isLoadingCategories: Boolean = false, val isEditMode: Boolean = false, - val editingFeedId: Int? = null + val editingFeedId: Long? = null ) { // 유효성 검사 로직 val isContentValid: Boolean diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt index deb20c27..270275c4 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt @@ -66,7 +66,7 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac route.bookAuthor != null ) { viewModel.setEditData( - feedId = route.feedId, + feedId = route.feedId.toLong(), isbn = route.isbn, bookTitle = route.bookTitle, bookAuthor = route.bookAuthor, @@ -77,7 +77,7 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac ) } else if (route.feedId != null) { // 수정 모드: 기존 방식 (API 호출) - viewModel.loadFeedForEdit(route.feedId) + viewModel.loadFeedForEdit(route.feedId.toLong()) } else if (route.isbn != null && route.bookTitle != null && route.bookAuthor != null && 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 2ea2b17e..41c14c10 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 @@ -73,7 +73,7 @@ fun SearchBookDetailScreen( onRightClick: () -> Unit = {}, onRecruitingGroupClick: () -> Unit = {}, onWriteFeedClick: () -> Unit = {}, - onFeedClick: (Int) -> Unit = {}, + onFeedClick: (Long) -> Unit = {}, onBookmarkClick: (String, Boolean) -> Unit = { _, _ -> }, viewModel: BookDetailViewModel = hiltViewModel() ) { @@ -120,7 +120,7 @@ private fun SearchBookDetailScreenContent( onRightClick: () -> Unit = {}, onRecruitingGroupClick: () -> Unit = {}, onWriteFeedClick: () -> Unit = {}, - onFeedClick: (Int) -> Unit = {}, + onFeedClick: (Long) -> Unit = {}, onBookmarkClick: (String, Boolean) -> Unit = { _, _ -> }, onSortChange: (String) -> Unit = {}, onLoadMore: () -> Unit = {} diff --git a/app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt b/app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt index f1c2235c..9984747c 100644 --- a/app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/search/viewmodel/BookDetailViewModel.kt @@ -178,7 +178,7 @@ class BookDetailViewModel @Inject constructor( // RelatedFeedItem을 FeedItem으로 변환하는 확장 함수 private fun RelatedFeedItem.toFeedItem(): FeedItem { return FeedItem( - id = this.feedId, + id = this.feedId.toLong(), userProfileImage = this.creatorProfileImageUrl, userName = this.creatorNickname, userRole = this.aliasName, From 12a5163baf21f381eb7ec20142afaa199f036857 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 00:05:41 +0900 Subject: [PATCH 19/25] =?UTF-8?q?[feat]:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,,=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/data/repository/FeedRepository.kt | 20 +++++++++++++++++++ .../texthip/thip/ui/feed/screen/FeedScreen.kt | 1 + .../ui/feed/usecase/ChangeFeedSaveUseCase.kt | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt index 120f2803..70af1774 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/FeedRepository.kt @@ -6,6 +6,7 @@ import com.texthip.thip.data.model.base.handleBaseResponse import com.texthip.thip.data.model.feed.request.CreateFeedRequest import com.texthip.thip.data.model.feed.request.FeedLikeRequest import com.texthip.thip.data.model.feed.request.FeedSaveRequest +import com.texthip.thip.data.model.feed.request.UpdateFeedRequest import com.texthip.thip.data.model.feed.response.AllFeedResponse import com.texthip.thip.data.model.feed.response.CreateFeedResponse import com.texthip.thip.data.model.feed.response.FeedDetailResponse @@ -178,6 +179,25 @@ class FeedRepository @Inject constructor( .getOrThrow() } + /** 피드 수정 */ + suspend fun updateFeed( + feedId: Long, + contentBody: String? = null, + isPublic: Boolean? = null, + tagList: List? = null, + remainImageUrls: List? = null + ): Result = runCatching { + val request = UpdateFeedRequest( + contentBody = contentBody, + isPublic = isPublic, + tagList = tagList, + remainImageUrls = remainImageUrls + ) + + feedService.updateFeed(feedId, request) + .handleBaseResponse() + .getOrThrow() + } /** 임시 파일들을 정리하는 함수 */ private fun cleanupTempFiles(tempFiles: List) { tempFiles.forEach { file -> diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt index 4702da4e..b2a5daa3 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.feed.screen +import android.util.Log import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing diff --git a/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt b/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt index d90196bd..d59c1106 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/usecase/ChangeFeedSaveUseCase.kt @@ -7,5 +7,5 @@ class ChangeFeedSaveUseCase @Inject constructor( private val feedRepository: FeedRepository ) { suspend operator fun invoke(feedId: Long, newSaveStatus: Boolean) = - feedRepository.changeFeedLike(feedId, newSaveStatus) + feedRepository.changeFeedSave(feedId, newSaveStatus) } \ No newline at end of file From 27ba048664af82cf70397ef251639c94e69b1e4f Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 01:33:12 +0900 Subject: [PATCH 20/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20ui=EC=97=90=20=EB=B0=94=EB=A1=9C=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=EB=90=98=EB=8F=84=EB=A1=9D=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/screen/FeedCommentScreen.kt | 13 +++++++++++++ .../texthip/thip/ui/feed/screen/FeedScreen.kt | 19 +++++++++++++++++-- .../thip/ui/feed/viewmodel/FeedViewModel.kt | 11 +++++++++++ .../navigator/navigations/FeedNavigation.kt | 7 +++++-- 4 files changed, 46 insertions(+), 4 deletions(-) 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 e4ced5ad..0f3827d3 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 @@ -38,6 +38,8 @@ 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.navigation.NavHostController +import androidx.navigation.compose.rememberNavController import coil.compose.AsyncImage import com.texthip.thip.R import com.texthip.thip.ui.common.CommentActionMode @@ -62,6 +64,7 @@ import kotlinx.coroutines.delay @Composable fun FeedCommentScreen( + navController: NavHostController, modifier: Modifier = Modifier, feedId: Long, onNavigateBack: () -> Unit = {}, @@ -72,6 +75,15 @@ fun FeedCommentScreen( val feedDetailUiState by feedDetailViewModel.uiState.collectAsState() val commentsUiState by commentsViewModel.uiState.collectAsState() + LaunchedEffect(feedDetailUiState.deleteSuccess) { + if (feedDetailUiState.deleteSuccess) { + navController.previousBackStackEntry + ?.savedStateHandle + ?.set("deleted_feed_id", feedId) + + onNavigateBack() + } + } LaunchedEffect(feedId) { feedDetailViewModel.loadFeedDetail(feedId) commentsViewModel.initialize(postId = feedId.toLong(), postType = "FEED") @@ -429,6 +441,7 @@ private fun FeedCommentScreenPrev() { ThipTheme { FeedCommentScreen( feedId = 1, + navController = rememberNavController() ) } } diff --git a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt index b2a5daa3..c4874a56 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt @@ -42,6 +42,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController import com.texthip.thip.R import com.texthip.thip.ui.common.buttons.FloatingButton import com.texthip.thip.ui.common.header.AuthorHeader @@ -71,6 +73,7 @@ fun FeedScreen( onNavigateToBookDetail: (String) -> Unit = {}, resultFeedId: Long? = null, onResultConsumed: () -> Unit = {}, + navController: NavHostController, feedViewModel: FeedViewModel = hiltViewModel(), ) { val feedUiState by feedViewModel.uiState.collectAsState() @@ -97,6 +100,16 @@ fun FeedScreen( lastVisibleIndex >= totalItems - 3 } } + LaunchedEffect(Unit) { + navController.currentBackStackEntry?.savedStateHandle?.let { handle -> + handle.getLiveData("deleted_feed_id").observeForever { deletedId -> + if (deletedId != null) { + feedViewModel.removeDeletedFeed(deletedId) + handle.remove("deleted_feed_id") + } + } + } + } LaunchedEffect(shouldLoadMore) { if (shouldLoadMore) { @@ -394,7 +407,8 @@ private fun FeedScreenPreview() { ThipTheme { FeedScreen( onNavigateToFeedWrite = { }, - onNavigateToBookDetail = { } + onNavigateToBookDetail = { }, + navController = rememberNavController() ) } } @@ -405,7 +419,8 @@ private fun FeedScreenWithoutDataPreview() { ThipTheme { FeedScreen( onNavigateToFeedWrite = { }, - onNavigateToBookDetail = { } + onNavigateToBookDetail = { }, + navController = rememberNavController() ) } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt index 3a2fa8bd..0de163b3 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt @@ -359,4 +359,15 @@ class FeedViewModel @Inject constructor( } } } + fun removeDeletedFeed(feedId: Long) { + val currentAllFeeds = _uiState.value.allFeeds.filterNot { it.feedId.toLong() == feedId } + val currentMyFeeds = _uiState.value.myFeeds.filterNot { it.feedId.toLong() == feedId } + + updateState { + it.copy( + allFeeds = currentAllFeeds, + myFeeds = currentMyFeeds + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt index 270275c4..df422805 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt @@ -25,6 +25,7 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac val resultFeedId = backStackEntry.savedStateHandle.get("feedId") FeedScreen( + navController = navController, resultFeedId = resultFeedId, onResultConsumed = { backStackEntry.savedStateHandle.remove("feedId") @@ -115,12 +116,14 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac ) } composable { backStackEntry -> - val route = backStackEntry.arguments?.let { + /*val route = backStackEntry.arguments?.let { FeedRoutes.Comment(it.getLong("feedId")) - } ?: return@composable + } ?: return@composable*/ + val route = backStackEntry.toRoute() FeedCommentScreen( feedId = route.feedId, + navController = navController, onNavigateBack = { navController.popBackStack() }, From 9efed102fcbad71875a5e6c15729a561ab4ba370 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 03:03:14 +0900 Subject: [PATCH 21/25] =?UTF-8?q?[ui]:=20=ED=94=BC=EB=93=9C=20=EC=9E=90?= =?UTF-8?q?=EC=84=B8=ED=9E=88=20=EB=B3=B4=EA=B8=B0=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A4=91=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/common/buttons/ActionBarButton.kt | 28 +++++++++++++------ .../thip/ui/feed/screen/FeedCommentScreen.kt | 3 ++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt b/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt index 5af726aa..9440c051 100644 --- a/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt +++ b/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBarButton.kt @@ -30,6 +30,7 @@ fun ActionBarButton( isSaveVisible: Boolean = false, isSaved: Boolean = false, isPinVisible: Boolean = false, + isLockIcon: Boolean = false, onLikeClick: () -> Unit = {}, onCommentClick: () -> Unit = {}, onBookmarkClick: () -> Unit = {}, @@ -88,12 +89,22 @@ fun ActionBarButton( } if (isSaveVisible) { - Icon( - modifier = Modifier.clickable { onBookmarkClick() }, - painter = painterResource(if (isSaved) R.drawable.ic_save_filled else R.drawable.ic_save), - contentDescription = null, - tint = Color.Unspecified - ) + if (isLockIcon) { + Icon( + painter = painterResource(R.drawable.ic_lock), + contentDescription = null, + tint = Color.Unspecified + ) + } else { + Icon( + modifier = Modifier.clickable { onBookmarkClick() }, + painter = painterResource( + if (isSaved) R.drawable.ic_save_filled else R.drawable.ic_save + ), + contentDescription = null, + tint = Color.Unspecified + ) + } } } } @@ -110,10 +121,11 @@ private fun ActionBarButtonPreview() { commentCount = 45, isSaveVisible = true, isSaved = isSaved, - isPinVisible = true, + isPinVisible = false, onLikeClick = { isLiked = !isLiked }, onCommentClick = {}, onBookmarkClick = { isSaved = !isSaved }, - onPinClick = {} + onPinClick = {}, + isLockIcon = true ) } \ No newline at end of file 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 0f3827d3..23d23a07 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 @@ -243,6 +243,9 @@ fun FeedCommentScreen( } } } + HorizontalDivider(color = colors.DarkGrey02, thickness = 1.dp) + + HorizontalDivider(color = colors.DarkGrey03, thickness = 10.dp) } } From 8ca06320ee5d8f847c6a38aba44e778a3b9e5d27 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 03:23:10 +0900 Subject: [PATCH 22/25] =?UTF-8?q?[ui]:=20=ED=94=BC=EB=93=9C=20=EC=9E=90?= =?UTF-8?q?=EC=84=B8=ED=9E=88=20=EB=B3=B4=EA=B8=B0=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A4=91=20-=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=95=B4=EC=95=BC=ED=95=A8=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/screen/FeedCommentScreen.kt | 17 +++++++++++++++++ .../ui/feed/viewmodel/FeedDetailViewModel.kt | 7 ++++++- 2 files changed, 23 insertions(+), 1 deletion(-) 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 23d23a07..3eaa2545 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 @@ -44,6 +44,7 @@ import coil.compose.AsyncImage import com.texthip.thip.R import com.texthip.thip.ui.common.CommentActionMode import com.texthip.thip.ui.common.bottomsheet.MenuBottomSheet +import com.texthip.thip.ui.common.buttons.ActionBarButton import com.texthip.thip.ui.common.buttons.ActionBookButton import com.texthip.thip.ui.common.buttons.OptionChipButton import com.texthip.thip.ui.common.forms.CommentTextField @@ -245,6 +246,22 @@ fun FeedCommentScreen( } HorizontalDivider(color = colors.DarkGrey02, thickness = 1.dp) + ActionBarButton( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 12.dp), + isLiked = feedDetail.isLiked, + likeCount = feedDetail.likeCount, + commentCount = feedDetail.commentCount, + isSaveVisible = true, + isSaved = feedDetail.isSaved, + isLockIcon = feedDetail.isLocked, + isPinVisible = true, + onLikeClick = { feedDetailViewModel.feed(feedDetail.feedId) }, + onCommentClick = { /* 스크롤 이동 or 포커스 처리 */ }, + onBookmarkClick = { feedDetailViewModel.toggleSave(feedDetail.feedId) }, + onPinClick = { /* TODO: pin 기능 */ } + ) HorizontalDivider(color = colors.DarkGrey03, thickness = 10.dp) } diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt index 74ad5429..b6114a0f 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.repository.FeedRepository +import com.texthip.thip.ui.feed.usecase.ChangeFeedLikeUseCase import com.texthip.thip.ui.feed.usecase.DeleteFeedUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -23,7 +24,8 @@ data class FeedDetailUiState( @HiltViewModel class FeedDetailViewModel @Inject constructor( private val feedRepository: FeedRepository, - private val deleteFeedUseCase: DeleteFeedUseCase + private val deleteFeedUseCase: DeleteFeedUseCase, + private val changeFeedLikeUseCase: ChangeFeedLikeUseCase, ) : ViewModel() { private val _uiState = MutableStateFlow(FeedDetailUiState()) @@ -76,6 +78,9 @@ class FeedDetailViewModel @Inject constructor( } } } + fun changeFeedLike(){ + + } fun clearError() { updateState { it.copy(error = null) } From 2fb865276cc07178bdddb7a5b317697a977091ef Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 11:23:21 +0900 Subject: [PATCH 23/25] =?UTF-8?q?[ui]:=20=ED=94=BC=EB=93=9C=20=EC=9E=90?= =?UTF-8?q?=EC=84=B8=ED=9E=88=20=EB=B3=B4=EA=B8=B0=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?->ActionBarButton=20=EC=B6=94=EA=B0=80=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/ui/feed/screen/FeedCommentScreen.kt | 380 +++++++++--------- 1 file changed, 193 insertions(+), 187 deletions(-) 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 3eaa2545..693b3b87 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 @@ -147,219 +147,225 @@ fun FeedCommentScreen( Box(modifier = Modifier.fillMaxSize()) { Box( - modifier = if (isBottomSheetVisible || showDialog) { - Modifier - .fillMaxSize() - .blur(5.dp) - } else { - Modifier.fillMaxSize() - } - // 바깥 터치 시 키보드 숨기기 - .pointerInput(Unit) { - detectTapGestures(onTap = { - focusManager.clearFocus() - selectedCommentId = null - }) + modifier = if (isBottomSheetVisible || showDialog) { + Modifier + .fillMaxSize() + .blur(5.dp) + } else { + Modifier.fillMaxSize() } - ) { - DefaultTopAppBar( - isRightIconVisible = true, - isTitleVisible = false, - onLeftClick = onNavigateBack, - onRightClick = { isBottomSheetVisible = true }, - ) - - LazyColumn( - modifier = modifier - .fillMaxWidth() - .padding(top = 56.dp), - contentPadding = PaddingValues(bottom = 20.dp) + // 바깥 터치 시 키보드 숨기기 + .pointerInput(Unit) { + detectTapGestures(onTap = { + focusManager.clearFocus() + selectedCommentId = null + }) + } ) { - // 상단 피드 - item { - Column { - ProfileBar( - modifier = Modifier.padding(20.dp), - profileImage = feedDetail.creatorProfileImageUrl ?: "", - topText = feedDetail.creatorNickname, - bottomText = feedDetail.aliasName, - showSubscriberInfo = false, - hoursAgo = feedDetail.postDate - ) - Column( - Modifier - .fillMaxWidth() - .padding(vertical = 16.dp, horizontal = 20.dp) - ) { - ActionBookButton( - bookTitle = feedDetail.bookTitle, - bookAuthor = feedDetail.bookAuthor, - onClick = {} + DefaultTopAppBar( + isRightIconVisible = true, + isTitleVisible = false, + onLeftClick = onNavigateBack, + onRightClick = { isBottomSheetVisible = true }, + ) + + LazyColumn( + modifier = modifier + .fillMaxWidth() + .padding(top = 56.dp), + contentPadding = PaddingValues(bottom = 20.dp) + ) { + // 상단 피드 + item { + Column { + ProfileBar( + modifier = Modifier.padding(20.dp), + profileImage = feedDetail.creatorProfileImageUrl ?: "", + topText = feedDetail.creatorNickname, + bottomText = feedDetail.aliasName, + showSubscriberInfo = false, + hoursAgo = feedDetail.postDate ) - } - Text( - text = feedDetail.contentBody, - style = typography.feedcopy_r400_s14_h20, - color = colors.White, - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 16.dp, start = 20.dp, end = 20.dp) - ) - if (images.isNotEmpty()) { - LazyRow( + Column( Modifier .fillMaxWidth() - .padding(start = 20.dp, bottom = 16.dp), - verticalAlignment = Alignment.CenterVertically + .padding(vertical = 16.dp, horizontal = 20.dp) ) { - itemsIndexed(images.take(3)) { index, imageUrl -> - AsyncImage( - model = imageUrl, - contentDescription = null, - modifier = Modifier - .padding(end = 16.dp) - .size(200.dp) - .clickable { - selectedImageIndex = index - showImageViewer = true - }, - contentScale = ContentScale.Crop - ) - } + ActionBookButton( + bookTitle = feedDetail.bookTitle, + bookAuthor = feedDetail.bookAuthor, + onClick = {} + ) } - } - if (feedDetail.tagList.isNotEmpty()) { - Row( - Modifier + Text( + text = feedDetail.contentBody, + style = typography.feedcopy_r400_s14_h20, + color = colors.White, + modifier = Modifier .fillMaxWidth() - .padding(bottom = 16.dp, start = 20.dp, end = 20.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - feedDetail.tagList.forEach { tag -> - OptionChipButton( - text = "#$tag", - isFilled = false, - isSelected = false, - onClick = {}) + .padding(bottom = 16.dp, start = 20.dp, end = 20.dp) + ) + if (images.isNotEmpty()) { + LazyRow( + Modifier + .fillMaxWidth() + .padding(start = 20.dp, bottom = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + itemsIndexed(images.take(3)) { index, imageUrl -> + AsyncImage( + model = imageUrl, + contentDescription = null, + modifier = Modifier + .padding(end = 16.dp) + .size(200.dp) + .clickable { + selectedImageIndex = index + showImageViewer = true + }, + contentScale = ContentScale.Crop + ) + } } } - } - HorizontalDivider(color = colors.DarkGrey02, thickness = 1.dp) - - ActionBarButton( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 12.dp), - isLiked = feedDetail.isLiked, - likeCount = feedDetail.likeCount, - commentCount = feedDetail.commentCount, - isSaveVisible = true, - isSaved = feedDetail.isSaved, - isLockIcon = feedDetail.isLocked, - isPinVisible = true, - onLikeClick = { feedDetailViewModel.feed(feedDetail.feedId) }, - onCommentClick = { /* 스크롤 이동 or 포커스 처리 */ }, - onBookmarkClick = { feedDetailViewModel.toggleSave(feedDetail.feedId) }, - onPinClick = { /* TODO: pin 기능 */ } - ) + if (feedDetail.tagList.isNotEmpty()) { + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + feedDetail.tagList.forEach { tag -> + OptionChipButton( + text = "#$tag", + isFilled = false, + isSelected = false, + onClick = {}) + } + } + } + HorizontalDivider( + modifier = Modifier.padding(horizontal = 20.dp, vertical = 16.dp), + color = colors.DarkGrey02, thickness = 1.dp + ) - HorizontalDivider(color = colors.DarkGrey03, thickness = 10.dp) - } - } - when { - commentsUiState.isLoading -> { - item { - Box( + ActionBarButton( modifier = Modifier .fillMaxWidth() - .padding(vertical = 40.dp), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator(color = colors.White) - } + .padding(horizontal = 20.dp), + isLiked = feedDetail.isLiked, + likeCount = feedDetail.likeCount, + commentCount = feedDetail.commentCount, + isSaveVisible = true, + isSaved = feedDetail.isSaved, + isPinVisible = false, + onLikeClick = { feedDetailViewModel.changeFeedLike() }, + onCommentClick = { /* 스크롤 이동 or 포커스 처리 */ }, + onBookmarkClick = { feedDetailViewModel.changeFeedSave() }, + onPinClick = { /* TODO: pin 기능 */ } + ) + + HorizontalDivider( + modifier = Modifier.padding(top = 16.dp), + color = colors.DarkGrey03, + thickness = 10.dp + ) } } - // 댓글 없음 - commentsUiState.comments.isEmpty() -> { - item { - Column( - modifier = Modifier - .fillMaxWidth() - .height(400.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = stringResource(R.string.no_comments_yet), - style = typography.smalltitle_sb600_s18_h24, - color = colors.White - ) - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = stringResource(R.string.no_comment_subtext), - style = typography.copy_r400_s14, - color = colors.Grey - ) + when { + commentsUiState.isLoading -> { + item { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 40.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = colors.White) + } } } - } - - else -> { - items( - items = commentsUiState.comments, - key = { comment -> comment.commentId ?: comment.hashCode() } - ) { commentItem -> - CommentSection( - commentItem = commentItem, - actionMode = CommentActionMode.POPUP, - selectedCommentId = selectedCommentId, - onEvent = commentsViewModel::onEvent, - onReplyClick = { commentId, nickname -> - replyingToCommentId = commentId - replyingToNickname = nickname - selectedCommentId = null - }, - onCommentLongPress = { comment -> - selectedCommentId = comment.commentId - }, - onReplyLongPress = { reply -> - selectedCommentId = reply.commentId - }, - onDismissPopup = { - selectedCommentId = null + // 댓글 없음 + commentsUiState.comments.isEmpty() -> { + item { + Column( + modifier = Modifier + .fillMaxWidth() + .height(400.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(R.string.no_comments_yet), + style = typography.smalltitle_sb600_s18_h24, + color = colors.White + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.no_comment_subtext), + style = typography.copy_r400_s14, + color = colors.Grey + ) } - ) + } + } + + else -> { + items( + items = commentsUiState.comments, + key = { comment -> comment.commentId ?: comment.hashCode() } + ) { commentItem -> + CommentSection( + commentItem = commentItem, + actionMode = CommentActionMode.POPUP, + selectedCommentId = selectedCommentId, + onEvent = commentsViewModel::onEvent, + onReplyClick = { commentId, nickname -> + replyingToCommentId = commentId + replyingToNickname = nickname + selectedCommentId = null + }, + onCommentLongPress = { comment -> + selectedCommentId = comment.commentId + }, + onReplyLongPress = { reply -> + selectedCommentId = reply.commentId + }, + onDismissPopup = { + selectedCommentId = null + } + ) + } } } } - } - // 댓글 입력창 - CommentTextField( - modifier = Modifier.align(Alignment.BottomCenter), - input = commentInput, - hint = stringResource(R.string.reply_to), - onInputChange = { commentInput = it }, - onSendClick = { - if (commentInput.isNotBlank()) { - commentsViewModel.onEvent( - CommentsEvent.CreateComment( - content = commentInput, - parentId = replyingToCommentId + // 댓글 입력창 + CommentTextField( + modifier = Modifier.align(Alignment.BottomCenter), + input = commentInput, + hint = stringResource(R.string.reply_to), + onInputChange = { commentInput = it }, + onSendClick = { + if (commentInput.isNotBlank()) { + commentsViewModel.onEvent( + CommentsEvent.CreateComment( + content = commentInput, + parentId = replyingToCommentId + ) ) - ) - commentInput = "" + commentInput = "" + replyingToCommentId = null + replyingToNickname = null + focusManager.clearFocus() + } + }, + replyTo = replyingToNickname, + onCancelReply = { replyingToCommentId = null replyingToNickname = null - focusManager.clearFocus() } - }, - replyTo = replyingToNickname, - onCancelReply = { - replyingToCommentId = null - replyingToNickname = null - } - ) + ) } // 신고 완료 토스트 From dacf9c955e6c65741a51328bab5be81a283fe1e7 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 11:23:41 +0900 Subject: [PATCH 24/25] =?UTF-8?q?[feat]:=20=ED=94=BC=EB=93=9C=20=EC=9E=90?= =?UTF-8?q?=EC=84=B8=ED=9E=88=20=EB=B3=B4=EA=B8=B0=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?->=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=EC=97=90=20changeFeedSave=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/feed/viewmodel/FeedDetailViewModel.kt | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt index b6114a0f..e2543135 100644 --- a/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.texthip.thip.data.model.feed.response.FeedDetailResponse import com.texthip.thip.data.repository.FeedRepository import com.texthip.thip.ui.feed.usecase.ChangeFeedLikeUseCase +import com.texthip.thip.ui.feed.usecase.ChangeFeedSaveUseCase import com.texthip.thip.ui.feed.usecase.DeleteFeedUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -26,6 +27,7 @@ class FeedDetailViewModel @Inject constructor( private val feedRepository: FeedRepository, private val deleteFeedUseCase: DeleteFeedUseCase, private val changeFeedLikeUseCase: ChangeFeedLikeUseCase, + private val changeFeedSaveUseCase: ChangeFeedSaveUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(FeedDetailUiState()) @@ -78,8 +80,39 @@ class FeedDetailViewModel @Inject constructor( } } } - fun changeFeedLike(){ + fun changeFeedLike() { + viewModelScope.launch { + val originalFeed = _uiState.value.feedDetail ?: return@launch + + val updatedFeed = originalFeed.copy( + isLiked = !originalFeed.isLiked, + likeCount = if (originalFeed.isLiked) originalFeed.likeCount - 1 else originalFeed.likeCount + 1 + ) + updateState { it.copy(feedDetail = updatedFeed) } + + val newLikeStatus = !originalFeed.isLiked + changeFeedLikeUseCase(originalFeed.feedId.toLong(), newLikeStatus) + .onFailure { + updateState { it.copy(feedDetail = originalFeed) } + } + } + } + fun changeFeedSave() { + viewModelScope.launch { + val originalFeed = _uiState.value.feedDetail ?: return@launch + + val updatedFeed = originalFeed.copy( + isSaved = !originalFeed.isSaved + ) + updateState { it.copy(feedDetail = updatedFeed) } + + val newSaveStatus = !originalFeed.isSaved + changeFeedSaveUseCase(originalFeed.feedId.toLong(), newSaveStatus) + .onFailure { + updateState { it.copy(feedDetail = originalFeed) } + } + } } fun clearError() { From 4a4786df38f3f298e086ed01d1c737d122670c35 Mon Sep 17 00:00:00 2001 From: JJUYAAA Date: Tue, 19 Aug 2025 11:24:07 +0900 Subject: [PATCH 25/25] =?UTF-8?q?[feat]:=20savedfeedcard=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20->=20=EC=9E=A0=EA=B8=88=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt b/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt index 2599450c..59cab1b8 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/component/SavedFeedCard.kt @@ -109,6 +109,7 @@ fun SavedFeedCard( commentCount = feedItem.commentCount, isSaveVisible = true, isSaved = feedItem.isSaved, + isLockIcon = feedItem.isLocked, onLikeClick = onLikeClick, onCommentClick = onCommentClick, onBookmarkClick = onBookmarkClick