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/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/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 +) 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 +) 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 fc112ef7..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 @@ -4,14 +4,18 @@ 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.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.FeedWriteInfoResponse +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.RelatedBooksResponse -import com.texthip.thip.data.model.feed.response.AllFeedResponse +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 import com.texthip.thip.data.service.FeedService import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -169,7 +173,7 @@ class FeedRepository @Inject constructor( } /** 피드 상세 조회 */ - suspend fun getFeedDetail(feedId: Int): Result = runCatching { + suspend fun getFeedDetail(feedId: Long): Result = runCatching { feedService.getFeedDetail(feedId) .handleBaseResponse() .getOrThrow() @@ -177,7 +181,7 @@ class FeedRepository @Inject constructor( /** 피드 수정 */ suspend fun updateFeed( - feedId: Int, + feedId: Long, contentBody: String? = null, isPublic: Boolean? = null, tagList: List? = null, @@ -194,7 +198,6 @@ class FeedRepository @Inject constructor( .handleBaseResponse() .getOrThrow() } - /** 임시 파일들을 정리하는 함수 */ private fun cleanupTempFiles(tempFiles: List) { tempFiles.forEach { file -> @@ -219,4 +222,27 @@ class FeedRepository @Inject constructor( .handleBaseResponse() .getOrThrow() } + + /** 피드 삭제 */ + suspend fun deleteFeed(feedId: Long): Result = runCatching { + feedService.deleteFeed(feedId) + .handleBaseResponse() + .getOrThrow() + } + + suspend fun changeFeedLike(feedId: Long, newLikeStatus: Boolean): Result = runCatching { + val request = FeedLikeRequest(type = newLikeStatus) + 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 26aa7fd7..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 @@ -1,19 +1,24 @@ 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 -import com.texthip.thip.data.model.feed.response.FeedMineInfoResponse -import com.texthip.thip.data.model.feed.response.RelatedBooksResponse -import com.texthip.thip.data.model.feed.response.AllFeedResponse import com.texthip.thip.data.model.feed.response.MyFeedResponse -import com.texthip.thip.data.model.feed.request.UpdateFeedRequest +import com.texthip.thip.data.model.feed.response.RelatedBooksResponse import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Multipart import retrofit2.http.PATCH @@ -64,7 +69,7 @@ interface FeedService { /** 피드 상세 조회 */ @GET("feeds/{feedId}") suspend fun getFeedDetail( - @Path("feedId") feedId: Int + @Path("feedId") feedId: Long ): BaseResponse @GET("feeds/users/{userId}/info") @@ -77,10 +82,30 @@ interface FeedService { @Path("userId") userId: Long ): BaseResponse + /** 피드 삭제 */ + @DELETE("feeds/{feedId}") + suspend fun deleteFeed( + @Path("feedId") feedId: Long + ): BaseResponse + + /** 피드 좋아요 상태 변경 */ + @POST("feeds/{feedId}/likes") + suspend fun changeFeedLike( + @Path("feedId") feedId: Long, + @Body request: FeedLikeRequest + ): BaseResponse + /** 피드 수정 */ @PATCH("feeds/{feedId}") suspend fun updateFeed( - @Path("feedId") feedId: Int, + @Path("feedId") feedId: Long, @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 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/component/MyFeedCard.kt b/app/src/main/java/com/texthip/thip/ui/feed/component/MyFeedCard.kt index a000e02a..1f1b46e9 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 @@ -128,7 +128,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), @@ -144,7 +144,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 ab0e5498..3a94d614 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,10 +38,13 @@ 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 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 @@ -63,8 +66,9 @@ import kotlinx.coroutines.delay @Composable fun FeedCommentScreen( + navController: NavHostController, modifier: Modifier = Modifier, - feedId: Int, + feedId: Long, onNavigateBack: () -> Unit = {}, onNavigateToFeedEdit: (Int) -> Unit = {}, onNavigateToUserProfile: (userId: Long) -> Unit = {}, @@ -74,6 +78,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") @@ -239,7 +252,31 @@ fun FeedCommentScreen( } } } - HorizontalDivider(color = colors.DarkGrey03, thickness = 10.dp) + HorizontalDivider( + modifier = Modifier.padding(horizontal = 20.dp, vertical = 16.dp), + color = colors.DarkGrey02, thickness = 1.dp + ) + + ActionBarButton( + modifier = Modifier + .fillMaxWidth() + .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 } } when { @@ -349,7 +386,6 @@ fun FeedCommentScreen( .zIndex(2f) ) } - } if (isBottomSheetVisible) { val menuItems = if (feedDetail.isWriter) { @@ -386,36 +422,31 @@ fun FeedCommentScreen( ) ) } + MenuBottomSheet( + items = menuItems, + onDismiss = { isBottomSheetVisible = false } + ) + } - MenuBottomSheet( - items = menuItems, - onDismiss = { isBottomSheetVisible = false } - ) - } - - if (showDialog) { - Box( - Modifier - .fillMaxSize() - .clickable { showDialog = false }) { - Box(Modifier.align(Alignment.Center)) { - DialogPopup( - title = stringResource(R.string.delete_feed_dialog_title), - description = stringResource(R.string.delete_feed_dialog_description), - onConfirm = { - showDialog = false - isBottomSheetVisible = false - // TODO: 피드 삭제 API 호출 - }, - onCancel = { - showDialog = false - isBottomSheetVisible = false - } - ) - } - } - } - + if (showDialog) { + Box( + Modifier + .fillMaxSize() + .clickable { showDialog = false }) { + Box(Modifier.align(Alignment.Center)) { + DialogPopup( + title = stringResource(R.string.delete_feed_dialog_title), + description = stringResource(R.string.delete_feed_dialog_description), + onConfirm = { + showDialog = false + isBottomSheetVisible = false + feedDetailViewModel.deleteFeed(feedId) + }, + onCancel = { + showDialog = false + isBottomSheetVisible = false + } + ) LaunchedEffect(showToast) { if (showToast) { delay(3000) @@ -439,6 +470,7 @@ private fun FeedCommentScreenPrev() { ThipTheme { FeedCommentScreen( feedId = 1, + navController = rememberNavController() ) } } 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 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 e1ee0d57..01f71cf4 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 @@ -41,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 @@ -65,11 +68,12 @@ import kotlinx.coroutines.launch fun FeedScreen( onNavigateToMySubscription: () -> Unit = {}, onNavigateToFeedWrite: () -> Unit = {}, - onNavigateToFeedComment: (Int) -> Unit = {}, + onNavigateToFeedComment: (Long) -> Unit = {}, onNavigateToBookDetail: (String) -> Unit = {}, + resultFeedId: Long? = null, onNavigateToUserProfile: (userId: Long) -> Unit = {}, - resultFeedId: Int? = null, onResultConsumed: () -> Unit = {}, + navController: NavHostController, feedViewModel: FeedViewModel = hiltViewModel(), ) { val feedUiState by feedViewModel.uiState.collectAsState() @@ -96,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) { @@ -210,7 +224,7 @@ fun FeedScreen( // 내 피드 item { Spacer(modifier = Modifier.height(32.dp)) - + val myFeedInfo = feedUiState.myFeedInfo AuthorHeader( profileImage = myFeedInfo?.profileImageUrl, @@ -265,7 +279,7 @@ fun FeedScreen( // MyFeedItem을 FeedItem으로 변환 val feedItem = FeedItem( - id = myFeed.feedId, + id = myFeed.feedId.toLong(), userProfileImage = null, userName = "", // 내 피드이므로 고정값 userRole = "", // 내 피드이므로 고정값 @@ -314,7 +328,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, @@ -337,10 +351,10 @@ fun FeedScreen( feedItem = feedItem, bottomTextColor = hexToColor(allFeed.aliasColor), onBookmarkClick = { - // TODO: API 호출로 북마크 상태 변경 + feedViewModel.changeFeedSave(feedItem.id) }, onLikeClick = { - // TODO: API 호출로 좋아요 상태 변경 + feedViewModel.changeFeedLike(feedItem.id) }, onContentClick = { onNavigateToFeedComment(feedItem.id) @@ -396,7 +410,8 @@ private fun FeedScreenPreview() { ThipTheme { FeedScreen( onNavigateToFeedWrite = { }, - onNavigateToBookDetail = { } + onNavigateToBookDetail = { }, + navController = rememberNavController() ) } } @@ -407,7 +422,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/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/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 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..d59c1106 --- /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.changeFeedSave(feedId, newSaveStatus) +} \ No newline at end of file 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 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..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 @@ -4,6 +4,9 @@ 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.ChangeFeedSaveUseCase +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 +17,17 @@ 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, + private val changeFeedLikeUseCase: ChangeFeedLikeUseCase, + private val changeFeedSaveUseCase: ChangeFeedSaveUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(FeedDetailUiState()) @@ -29,7 +37,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) } @@ -54,6 +62,58 @@ 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 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() { updateState { it.copy(error = null) } 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 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 0e7d0d1d..c158b0d3 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 @@ -8,9 +8,12 @@ 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 com.texthip.thip.ui.feed.usecase.ChangeFeedSaveUseCase 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 @@ -46,7 +49,9 @@ data class FeedUiState( @HiltViewModel class FeedViewModel @Inject constructor( private val feedRepository: FeedRepository, - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val changeFeedLikeUseCase: ChangeFeedLikeUseCase, + private val changeFeedSaveUseCase: ChangeFeedSaveUseCase ) : ViewModel() { private val _uiState = MutableStateFlow(FeedUiState()) @@ -305,4 +310,66 @@ 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) } + } + } + } + + 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) } + } + } + } + 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/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/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 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 ac5d937f..232eb249 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 @@ -111,6 +111,7 @@ fun SavedFeedCard( commentCount = feedItem.commentCount, isSaveVisible = true, isSaved = feedItem.isSaved, + isLockIcon = feedItem.isLocked, onLikeClick = onLikeClick, onCommentClick = onCommentClick, onBookmarkClick = onBookmarkClick @@ -122,7 +123,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), @@ -138,7 +139,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 } 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 906166ca..df3bd6c2 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 @@ -23,12 +23,13 @@ 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( + navController = navController, resultFeedId = resultFeedId, onResultConsumed = { - backStackEntry.savedStateHandle.remove("feedId") + backStackEntry.savedStateHandle.remove("feedId") }, onNavigateToMySubscription = { navController.navigateToMySubscription() @@ -70,7 +71,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, @@ -81,7 +82,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 && @@ -119,12 +120,14 @@ fun NavGraphBuilder.feedNavigation(navController: NavHostController, navigateBac ) } composable { backStackEntry -> - val route = backStackEntry.arguments?.let { - FeedRoutes.Comment(it.getInt("feedId")) - } ?: return@composable + /*val route = backStackEntry.arguments?.let { + FeedRoutes.Comment(it.getLong("feedId")) + } ?: return@composable*/ + val route = backStackEntry.toRoute() FeedCommentScreen( feedId = route.feedId, + navController = navController, onNavigateBack = { navController.popBackStack() }, 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 e70faa67..fd9037f5 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( 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,