From e4bf6cca5955219c30ccb8253bec8b8482a2e66f Mon Sep 17 00:00:00 2001 From: soopeach Date: Thu, 4 Jan 2024 13:25:18 +0900 Subject: [PATCH 1/2] =?UTF-8?q?:sparkles:=20LikePostUseCase=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/src/main/java/com/whyranoid/data/API.kt | 2 ++ .../src/main/java/com/whyranoid/data/Utils.kt | 12 +++++++++++ .../community/CommunityDataSource.kt | 6 ++++++ .../community/CommunityDataSourceImpl.kt | 20 +++++++++++++++++++ .../datasource/community/CommunityService.kt | 15 ++++++++++++++ .../datasource/post/PostDataSourceImpl.kt | 2 +- .../community/request/PostLikeRequest.kt | 8 ++++++++ .../model/community/response/LikerProfile.kt | 10 ++++++++++ .../community/response/PostLikeResponse.kt | 10 ++++++++++ .../whyranoid/data/model/post/PostResponse.kt | 3 ++- .../repository/CommunityRepositoryImpl.kt | 12 +++++++++++ .../com/whyranoid/domain/model/post/Post.kt | 2 ++ .../domain/repository/CommunityRepository.kt | 6 ++++++ .../domain/usecase/LikePostUseCase.kt | 16 +++++++++++++++ 14 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 data/src/main/java/com/whyranoid/data/Utils.kt create mode 100644 data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSource.kt create mode 100644 data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSourceImpl.kt create mode 100644 data/src/main/java/com/whyranoid/data/datasource/community/CommunityService.kt create mode 100644 data/src/main/java/com/whyranoid/data/model/community/request/PostLikeRequest.kt create mode 100644 data/src/main/java/com/whyranoid/data/model/community/response/LikerProfile.kt create mode 100644 data/src/main/java/com/whyranoid/data/model/community/response/PostLikeResponse.kt create mode 100644 data/src/main/java/com/whyranoid/data/repository/CommunityRepositoryImpl.kt create mode 100644 domain/src/main/java/com/whyranoid/domain/repository/CommunityRepository.kt create mode 100644 domain/src/main/java/com/whyranoid/domain/usecase/LikePostUseCase.kt diff --git a/data/src/main/java/com/whyranoid/data/API.kt b/data/src/main/java/com/whyranoid/data/API.kt index 728d52d3..4b069a5e 100644 --- a/data/src/main/java/com/whyranoid/data/API.kt +++ b/data/src/main/java/com/whyranoid/data/API.kt @@ -25,6 +25,8 @@ object API { const val SEARCH = "api/community/search-nickname" + const val SEND_LIKE = "api/community/send-like" + object WalkingControl { const val RUNNING_START = "api/walk/start" diff --git a/data/src/main/java/com/whyranoid/data/Utils.kt b/data/src/main/java/com/whyranoid/data/Utils.kt new file mode 100644 index 00000000..b4cc2046 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/Utils.kt @@ -0,0 +1,12 @@ +package com.whyranoid.data + +import retrofit2.Response + +fun Response.getResult(transform: (T) -> R): R { + if (this.isSuccessful.not()) + throw Exception(this.errorBody().toString()) + else if (this.body() == null) + throw Exception(this.message()) + + return requireNotNull(this.body()?.let(transform) ?: throw Exception("empty response")) +} \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSource.kt b/data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSource.kt new file mode 100644 index 00000000..e42aba80 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSource.kt @@ -0,0 +1,6 @@ +package com.whyranoid.data.datasource.community + +interface CommunityDataSource { + + suspend fun likePost(postId: Long, likerId: Long): Result +} \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSourceImpl.kt b/data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSourceImpl.kt new file mode 100644 index 00000000..9548f6b4 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/datasource/community/CommunityDataSourceImpl.kt @@ -0,0 +1,20 @@ +package com.whyranoid.data.datasource.community + +import com.whyranoid.data.getResult +import com.whyranoid.data.model.community.request.PostLikeRequest + +class CommunityDataSourceImpl( + private val service: CommunityService +) : CommunityDataSource { + override suspend fun likePost(postId: Long, likerId: Long): Result { + + return kotlin.runCatching { + val request = PostLikeRequest(likerId, postId) + val response = service.likePost(request) + + response.getResult { + it.likerCount + } + } + } +} \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/datasource/community/CommunityService.kt b/data/src/main/java/com/whyranoid/data/datasource/community/CommunityService.kt new file mode 100644 index 00000000..de950e61 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/datasource/community/CommunityService.kt @@ -0,0 +1,15 @@ +package com.whyranoid.data.datasource.community + +import com.whyranoid.data.API +import com.whyranoid.data.model.community.request.PostLikeRequest +import com.whyranoid.data.model.community.response.PostLikeResponse +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +interface CommunityService { + + @POST(API.SEND_LIKE) + suspend fun likePost(@Body postLikeRequest: PostLikeRequest): Response + +} \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/datasource/post/PostDataSourceImpl.kt b/data/src/main/java/com/whyranoid/data/datasource/post/PostDataSourceImpl.kt index d516c58c..e597f174 100644 --- a/data/src/main/java/com/whyranoid/data/datasource/post/PostDataSourceImpl.kt +++ b/data/src/main/java/com/whyranoid/data/datasource/post/PostDataSourceImpl.kt @@ -93,7 +93,7 @@ class PostDataSourceImpl(private val postService: PostService) : PostDataSource override suspend fun getMyFollowingsPost(uid: Long): Result> { return kotlin.runCatching { val posts = requireNotNull(postService.getPosts(uid).body()) - posts.map { it.toPost() } + posts.map { it.toPost(uid) } } } } diff --git a/data/src/main/java/com/whyranoid/data/model/community/request/PostLikeRequest.kt b/data/src/main/java/com/whyranoid/data/model/community/request/PostLikeRequest.kt new file mode 100644 index 00000000..cf192fd3 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/model/community/request/PostLikeRequest.kt @@ -0,0 +1,8 @@ +package com.whyranoid.data.model.community.request + +import com.google.gson.annotations.SerializedName + +data class PostLikeRequest( + @SerializedName("likerId") val likerId: Long, + @SerializedName("postId") val postId: Long, +) diff --git a/data/src/main/java/com/whyranoid/data/model/community/response/LikerProfile.kt b/data/src/main/java/com/whyranoid/data/model/community/response/LikerProfile.kt new file mode 100644 index 00000000..26cdc7e9 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/model/community/response/LikerProfile.kt @@ -0,0 +1,10 @@ +package com.whyranoid.data.model.community.response + +import com.google.gson.annotations.SerializedName + +data class LikerProfile( + @SerializedName("nickname") val nickname: String, + @SerializedName("profileImg") val profileImg: String, + @SerializedName("status") val status: String, + @SerializedName("walkieId") val walkieId: Long +) \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/model/community/response/PostLikeResponse.kt b/data/src/main/java/com/whyranoid/data/model/community/response/PostLikeResponse.kt new file mode 100644 index 00000000..8149cc76 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/model/community/response/PostLikeResponse.kt @@ -0,0 +1,10 @@ +package com.whyranoid.data.model.community.response + +import com.google.gson.annotations.SerializedName + +data class PostLikeResponse( + @SerializedName("likerCount") val likerCount: Long, + @SerializedName("likerId") val likerId: Long, + @SerializedName("likerProfiles") val likerProfiles: List, + @SerializedName("postId") val postId: Long +) \ No newline at end of file diff --git a/data/src/main/java/com/whyranoid/data/model/post/PostResponse.kt b/data/src/main/java/com/whyranoid/data/model/post/PostResponse.kt index 0cc1ffb3..5369e3d6 100644 --- a/data/src/main/java/com/whyranoid/data/model/post/PostResponse.kt +++ b/data/src/main/java/com/whyranoid/data/model/post/PostResponse.kt @@ -36,13 +36,14 @@ data class PostResponse( ) } - fun toPost(): Post { + fun toPost(myUid: Long): Post { return Post( id = this.postId, imageUrl = this.photo, likeCount = this.likers.size, contents = this.content, author = this.poster.toUser(), + isLiked = this.likers.firstOrNull { it.uid == myUid } != null, date = dateFormatter.parse(this.date.replace("T", " ")).time, ) } diff --git a/data/src/main/java/com/whyranoid/data/repository/CommunityRepositoryImpl.kt b/data/src/main/java/com/whyranoid/data/repository/CommunityRepositoryImpl.kt new file mode 100644 index 00000000..4e20c6c8 --- /dev/null +++ b/data/src/main/java/com/whyranoid/data/repository/CommunityRepositoryImpl.kt @@ -0,0 +1,12 @@ +package com.whyranoid.data.repository + +import com.whyranoid.data.datasource.community.CommunityDataSource +import com.whyranoid.domain.repository.CommunityRepository + +class CommunityRepositoryImpl( + private val communityDataSource: CommunityDataSource +): CommunityRepository { + override suspend fun likePost(postId: Long, likerId: Long): Result { + return communityDataSource.likePost(postId, likerId) + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/whyranoid/domain/model/post/Post.kt b/domain/src/main/java/com/whyranoid/domain/model/post/Post.kt index 20e7bb22..c717b32b 100644 --- a/domain/src/main/java/com/whyranoid/domain/model/post/Post.kt +++ b/domain/src/main/java/com/whyranoid/domain/model/post/Post.kt @@ -6,6 +6,7 @@ data class Post( val id: Long, val imageUrl: String, val likeCount: Int, + val isLiked: Boolean, val contents: String, val author: User, val date: Long = 0L, @@ -17,6 +18,7 @@ data class Post( likeCount = 3, contents = "오늘도 상쾌한 달리기~", author = User.DUMMY, + isLiked = false ) } } diff --git a/domain/src/main/java/com/whyranoid/domain/repository/CommunityRepository.kt b/domain/src/main/java/com/whyranoid/domain/repository/CommunityRepository.kt new file mode 100644 index 00000000..faa32e20 --- /dev/null +++ b/domain/src/main/java/com/whyranoid/domain/repository/CommunityRepository.kt @@ -0,0 +1,6 @@ +package com.whyranoid.domain.repository + +interface CommunityRepository { + + suspend fun likePost(postId: Long, likerId: Long): Result +} \ No newline at end of file diff --git a/domain/src/main/java/com/whyranoid/domain/usecase/LikePostUseCase.kt b/domain/src/main/java/com/whyranoid/domain/usecase/LikePostUseCase.kt new file mode 100644 index 00000000..c853ee3a --- /dev/null +++ b/domain/src/main/java/com/whyranoid/domain/usecase/LikePostUseCase.kt @@ -0,0 +1,16 @@ +package com.whyranoid.domain.usecase + +import com.whyranoid.domain.repository.AccountRepository +import com.whyranoid.domain.repository.CommunityRepository +import kotlinx.coroutines.flow.first + +class LikePostUseCase( + private val communityRepository: CommunityRepository, + private val accountRepository: AccountRepository +) { + + suspend operator fun invoke(postId: Long): Result { + val uid = requireNotNull(accountRepository.uId.first()) + return communityRepository.likePost(postId, uid) + } +} From c3e8bb708c977bd21a004cf94c68db6499ad54c1 Mon Sep 17 00:00:00 2001 From: soopeach Date: Thu, 4 Jan 2024 13:25:58 +0900 Subject: [PATCH 2/2] =?UTF-8?q?:sparkles:=20=EC=A2=8B=EC=95=84=EC=9A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/whyranoid/walkie/KoinModules.kt | 13 +++++++- .../component/community/PostContentItem.kt | 16 ++++++---- .../component/community/PostItem.kt | 9 ++++-- .../presentation/screens/CommunityScreen.kt | 4 ++- .../viewmodel/CommunityScreenViewModel.kt | 32 +++++++++++++++++-- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt index 49e1f949..aeeaddf2 100644 --- a/app/src/main/java/com/whyranoid/walkie/KoinModules.kt +++ b/app/src/main/java/com/whyranoid/walkie/KoinModules.kt @@ -11,6 +11,9 @@ import com.whyranoid.data.datasource.OtherUserPagingSource import com.whyranoid.data.datasource.UserDataSourceImpl import com.whyranoid.data.datasource.account.AccountDataSourceImpl import com.whyranoid.data.datasource.account.AccountService +import com.whyranoid.data.datasource.community.CommunityDataSource +import com.whyranoid.data.datasource.community.CommunityDataSourceImpl +import com.whyranoid.data.datasource.community.CommunityService import com.whyranoid.data.datasource.follow.FollowDataSourceImpl import com.whyranoid.data.datasource.follow.FollowService import com.whyranoid.data.datasource.post.PostDataSourceImpl @@ -19,6 +22,7 @@ import com.whyranoid.data.datasource.running.RunningControlDataSourceImpl import com.whyranoid.data.datasource.running.RunningService import com.whyranoid.data.repository.AccountRepositoryImpl import com.whyranoid.data.repository.ChallengeRepositoryImpl +import com.whyranoid.data.repository.CommunityRepositoryImpl import com.whyranoid.data.repository.FollowRepositoryImpl import com.whyranoid.data.repository.GpsRepositoryImpl import com.whyranoid.data.repository.NetworkRepositoryImpl @@ -35,6 +39,7 @@ import com.whyranoid.domain.datasource.RunningControlDataSource import com.whyranoid.domain.datasource.UserDataSource import com.whyranoid.domain.repository.AccountRepository import com.whyranoid.domain.repository.ChallengeRepository +import com.whyranoid.domain.repository.CommunityRepository import com.whyranoid.domain.repository.FollowRepository import com.whyranoid.domain.repository.GpsRepository import com.whyranoid.domain.repository.NetworkRepository @@ -53,6 +58,7 @@ import com.whyranoid.domain.usecase.GetPostUseCase import com.whyranoid.domain.usecase.GetUserBadgesUseCase import com.whyranoid.domain.usecase.GetUserDetailUseCase import com.whyranoid.domain.usecase.GetUserPostPreviewsUseCase +import com.whyranoid.domain.usecase.LikePostUseCase import com.whyranoid.domain.usecase.SignOutUseCase import com.whyranoid.domain.usecase.UploadPostUseCase import com.whyranoid.domain.usecase.broadcast.AddGpsListener @@ -106,7 +112,7 @@ val viewModelModule = module { factory { AddPostViewModel(get()) } factory { SearchFriendViewModel(get(), get(), get()) } factory { DialogViewModel(get(), get(), get(), get(), get(), get()) } - factory { CommunityScreenViewModel(get(), get()) } + factory { CommunityScreenViewModel(get(), get(), get()) } } val repositoryModule = module { @@ -120,6 +126,7 @@ val repositoryModule = module { single { NetworkRepositoryImpl(get()) } single { GpsRepositoryImpl(get()) } single { OtherUserRepositoryImpl(OtherUserPagingSource()) } + single { CommunityRepositoryImpl(get()) } } val dataSourceModule = module { @@ -129,6 +136,7 @@ val dataSourceModule = module { single { AccountDataSourceImpl(get()) } single { FollowDataSourceImpl(get()) } single { RunningControlDataSourceImpl(get()) } + single { CommunityDataSourceImpl(get()) } } val useCaseModule = module { @@ -157,6 +165,7 @@ val useCaseModule = module { single { UnFollowUseCase(get(), get()) } single { GetMyFollowingUseCase(get(), get()) } single { GetFollowingsPostsUseCase(get(), get()) } + single { LikePostUseCase(get(), get()) } } val databaseModule = module { @@ -222,4 +231,6 @@ val networkModule = module { single { get().create(PostService::class.java) } single { get().create(RunningService::class.java) } + + single { get().create(CommunityService::class.java) } } diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/community/PostContentItem.kt b/presentation/src/main/java/com/whyranoid/presentation/component/community/PostContentItem.kt index d77243af..b044ee68 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/community/PostContentItem.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/community/PostContentItem.kt @@ -17,10 +17,15 @@ import androidx.compose.ui.unit.dp import com.whyranoid.domain.model.post.Post import com.whyranoid.presentation.icons.buttoniconpack.CommentButtonIcon import com.whyranoid.presentation.icons.buttoniconpack.HeartButtonIcon +import com.whyranoid.presentation.theme.WalkieColor import com.whyranoid.presentation.theme.WalkieTypography @Composable -fun PostContentItem(post: Post) { +fun PostContentItem( + post: Post, + onLikeClicked: (Long) -> Unit = {} +) { + Column( modifier = Modifier .fillMaxWidth() @@ -43,11 +48,11 @@ fun PostContentItem(post: Post) { modifier = Modifier .size(20.dp) .clickable { - - } - , + onLikeClicked(post.id) + }, imageVector = HeartButtonIcon, contentDescription = "좋아요 버튼", + tint = if (post.isLiked) WalkieColor.Primary else WalkieColor.GrayBorder ) Spacer(modifier = Modifier.size(16.dp)) @@ -57,8 +62,7 @@ fun PostContentItem(post: Post) { .size(20.dp) .clickable { - } - , + }, imageVector = CommentButtonIcon, contentDescription = "댓글 버튼", ) diff --git a/presentation/src/main/java/com/whyranoid/presentation/component/community/PostItem.kt b/presentation/src/main/java/com/whyranoid/presentation/component/community/PostItem.kt index 461c8d14..ff8c5203 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/component/community/PostItem.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/component/community/PostItem.kt @@ -14,7 +14,10 @@ import coil.compose.AsyncImage import com.whyranoid.domain.model.post.Post @Composable -fun PostItem(post: Post) { +fun PostItem( + post: Post, + onLikeClicked: (Long) -> Unit = {}, +) { Column( modifier = Modifier .fillMaxHeight() @@ -36,7 +39,9 @@ fun PostItem(post: Post) { contentScale = ContentScale.Crop ) - PostContentItem(post) + PostContentItem(post) { + onLikeClicked(it) + } } } \ No newline at end of file diff --git a/presentation/src/main/java/com/whyranoid/presentation/screens/CommunityScreen.kt b/presentation/src/main/java/com/whyranoid/presentation/screens/CommunityScreen.kt index 28af8cdd..c74d412d 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/screens/CommunityScreen.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/screens/CommunityScreen.kt @@ -95,7 +95,9 @@ fun CommunityScreen( state.posts.getDataOrNull()?.forEach { post -> item { - PostItem(post = post) + PostItem(post = post) { postId -> + viewModel.likePost(postId) + } } } } diff --git a/presentation/src/main/java/com/whyranoid/presentation/viewmodel/CommunityScreenViewModel.kt b/presentation/src/main/java/com/whyranoid/presentation/viewmodel/CommunityScreenViewModel.kt index 6ccebf5f..b87764fe 100644 --- a/presentation/src/main/java/com/whyranoid/presentation/viewmodel/CommunityScreenViewModel.kt +++ b/presentation/src/main/java/com/whyranoid/presentation/viewmodel/CommunityScreenViewModel.kt @@ -5,6 +5,7 @@ import com.whyranoid.domain.model.post.Post import com.whyranoid.domain.model.user.User import com.whyranoid.domain.usecase.GetMyFollowingUseCase import com.whyranoid.domain.usecase.GetFollowingsPostsUseCase +import com.whyranoid.domain.usecase.LikePostUseCase import com.whyranoid.presentation.model.UiState import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.intent @@ -22,7 +23,8 @@ data class CommunityScreenState( class CommunityScreenViewModel( private val getMyFollowingUseCase: GetMyFollowingUseCase, - private val getFollowingsPostsUseCase: GetFollowingsPostsUseCase + private val getFollowingsPostsUseCase: GetFollowingsPostsUseCase, + private val likePostUseCase: LikePostUseCase ) : ViewModel(), ContainerHost { override val container = @@ -44,7 +46,6 @@ class CommunityScreenViewModel( fun getPosts() = intent { val result = getFollowingsPostsUseCase() - println("결과 : $result") result.onSuccess { posts -> reduce { state.copy( @@ -56,4 +57,31 @@ class CommunityScreenViewModel( } } } + + fun likePost(postId: Long) = intent { + val result = likePostUseCase(postId) + + result.onSuccess { updatedLikeCount -> + + reduce { + state.copy( + posts = UiState.Success( + state.posts.getDataOrNull()?.map { + if (it.id == postId) { + it.copy( + likeCount = if (updatedLikeCount == -1L) it.likeCount - 1 else updatedLikeCount.toInt(), + isLiked = it.isLiked.not() + ) + } else { + it + } + } ?: emptyList() + ) + ) + } + }.onFailure { + // TODO: Error handling + } + + } } \ No newline at end of file