Skip to content

Commit 8f00ccb

Browse files
authored
merge #102 -> develop
[Feat/#102] liked api
2 parents 92560b0 + 81793b7 commit 8f00ccb

File tree

14 files changed

+174
-87
lines changed

14 files changed

+174
-87
lines changed

app/src/main/java/com/paw/key/core/designsystem/component/CourseDetail.kt

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,24 @@ import coil.request.ImageRequest
4040
import com.paw.key.R
4141
import com.paw.key.core.designsystem.theme.PawKeyTheme
4242
import com.paw.key.core.designsystem.theme.Gray100
43+
import com.paw.key.core.util.noRippleClickable
4344
import com.paw.key.domain.model.entity.walklist.CategoryTop3Entity
45+
import kotlinx.serialization.json.JsonNull.content
4446

4547
@Composable
4648
fun CourseDetail(
4749
title : String,
4850
petName : String,
4951
date : String,
5052
location : String,
51-
isLike : Boolean,
52-
content : String,
53+
onClickLike: (Boolean) -> Unit,
5354
petProfileImage : String,
5455
routeMapImageUrl : String,
5556
categorySummary : List<String>,
5657
categoryTop3 : List<CategoryTop3Entity>,
5758
totalReviewCount : Int,
5859
walkingImageUrls : List<String>,
59-
60+
content: String,
6061
onImageClick: (String) -> Unit,
6162
modifier: Modifier = Modifier
6263
) {
@@ -110,16 +111,18 @@ fun CourseDetail(
110111
)
111112

112113
Icon(
113-
imageVector = if (isLiked.value) {
114-
ImageVector.vectorResource(id = R.drawable.ic_eye_linear_gray_valid)
115-
} else {
116-
ImageVector.vectorResource(id = R.drawable.ic_eye_linear_gray_invalid)
117-
},
114+
imageVector = if (isLiked.value)
115+
ImageVector.vectorResource(id = R.drawable.ic_heart_filled)
116+
else
117+
ImageVector.vectorResource(id = R.drawable.ic_heart_default),
118118
contentDescription = "좋아요",
119119
tint = Color.Unspecified,
120-
modifier = Modifier.clickable {
121-
isLiked.value = !isLiked.value
122-
}
120+
modifier = Modifier
121+
.size(24.dp)
122+
.noRippleClickable {
123+
isLiked.value = !isLiked.value // 로컬 상태 먼저 변경
124+
onClickLike(!isLiked.value) // 변경된 값을 넘김
125+
}
123126
)
124127
}
125128

@@ -324,43 +327,22 @@ fun CourseDetailPreview() {
324327
petName = "핑구",
325328
date = "2025/06/30",
326329
location = "홍대입구역",
327-
isLike = true,
328-
content = "산책로가 깨끗하고 벚꽃이 예뻐요!",
330+
onClickLike = {},
329331
petProfileImage = "https://pawkey-server.com/image/profile.png",
330332
routeMapImageUrl = "https://pawkey-server.com/image/map.png",
331333
categoryTop3 = listOf(
332-
CategoryTop3Entity(
333-
rank = 1,
334-
optionText = "산책로가 어쩌구 저꾸",
335-
percentage = 42,
336-
categoryName = "산책로가 어쩌구 저꾸",
337-
categoryOptionId = 1,
338-
categoryId = 2
339-
),
340-
CategoryTop3Entity(
341-
rank = 2,
342-
optionText = "산책로가 어쩌구 저꾸",
343-
percentage = 37,
344-
categoryName = "산책로가 어쩌구 저꾸",
345-
categoryOptionId = 1,
346-
categoryId = 2
347-
),
348-
CategoryTop3Entity(
349-
rank = 3,
350-
optionText = "산책로가 어쩌구 저꾸",
351-
percentage = 35,
352-
categoryName = "산책로가 어쩌구 저꾸",
353-
categoryOptionId = 1,
354-
categoryId = 2
355-
)
334+
CategoryTop3Entity(rank = 1, optionText = "산책로가 어쩌구 저꾸", percentage = 42, categoryName = "", categoryOptionId = 1, categoryId = 2),
335+
CategoryTop3Entity(rank = 2, optionText = "풍경이 예뻐요", percentage = 37, categoryName = "", categoryOptionId = 1, categoryId = 2),
336+
CategoryTop3Entity(rank = 3, optionText = "깨끗해요", percentage = 35, categoryName = "", categoryOptionId = 1, categoryId = 2)
356337
),
357338
totalReviewCount = 42,
358339
walkingImageUrls = listOf(
359340
"https://pawkey-server.com/image/walk1.jpg",
360341
"https://pawkey-server.com/image/walk2.jpg"
361342
),
362343
categorySummary = listOf("안전", "편리성"),
363-
onImageClick = {},
344+
content = "산책로가 깨끗하고 벚꽃이 예뻐요!",
345+
onImageClick = {}
364346
)
365347
}
366348
}

app/src/main/java/com/paw/key/data/di/RepositoryModule.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.paw.key.data.di
22

33
import com.paw.key.data.repositoryimpl.ArchivedListRepositoryImpl
44
import com.paw.key.data.repositoryimpl.DummyRepositoryImpl
5+
import com.paw.key.data.repositoryimpl.LikeRepositoryImpl
56
import com.paw.key.data.repositoryimpl.PetProfileRepositoryImpl
67
import com.paw.key.data.repositoryimpl.onboarding.OnboardingInfoRepositoryImpl
78
import com.paw.key.data.repositoryimpl.onboarding.OnboardingRegionRepositoryImpl
@@ -19,6 +20,7 @@ import com.paw.key.data.repositoryimpl.walklist.WalkListDetailRepositoryImpl
1920
import com.paw.key.data.repositoryimpl.walkreview.WalkReviewRepositoryImpl
2021
import com.paw.key.domain.repository.ArchivedListRepository
2122
import com.paw.key.domain.repository.DummyRepository
23+
import com.paw.key.domain.repository.LikeRepository
2224
import com.paw.key.domain.repository.onboarding.OnboardingInfoRepository
2325
import com.paw.key.domain.repository.onboarding.OnboardingRegionRepository
2426
import com.paw.key.domain.repository.onboarding.OnboardingRepository
@@ -124,6 +126,12 @@ interface RepositoryModule {
124126
impl: ArchivedListRepositoryImpl
125127
): ArchivedListRepository
126128

129+
@Binds
130+
@Singleton
131+
fun bindLikeRepository(
132+
impl: LikeRepositoryImpl
133+
): LikeRepository
134+
127135
@Binds
128136
@Singleton
129137
fun bindWalkReviewRepository(

app/src/main/java/com/paw/key/data/di/ServiceModule.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.paw.key.data.di
22

33
import com.paw.key.data.service.ArchivedListService
44
import com.paw.key.data.service.DummyService
5+
import com.paw.key.data.service.LikeService
56
import com.paw.key.data.service.PetProfileService
67
import com.paw.key.data.service.onboarding.OnboardingInfoService
78
import com.paw.key.data.service.onboarding.OnboardingPetsService
@@ -89,6 +90,11 @@ object ServiceModule {
8990
fun provideArchivedListService(retrofit: Retrofit): ArchivedListService =
9091
retrofit.create()
9192

93+
@Provides
94+
@Singleton
95+
fun provideLikeService(retrofit: Retrofit): LikeService =
96+
retrofit.create()
97+
9298
@Provides
9399
@Singleton
94100
fun provideWalkReviewService(retrofit: Retrofit): WalkReviewService =

app/src/main/java/com/paw/key/data/dto/response/ArchivedListResponseDto.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ data class ArchivedDto(
3636
val descriptionTags: List<String>
3737
) {
3838
fun toEntity() = ArchivedListEntity(
39-
postId = postId.toLong(),
39+
postId = postId,
4040
createdAt = createdAt,
4141
isLiked = isLike,
4242
title = title,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.paw.key.data.repositoryimpl
2+
3+
import com.paw.key.data.remote.datasource.LikeDataSource
4+
import com.paw.key.domain.repository.LikeRepository
5+
import javax.inject.Inject
6+
7+
class LikeRepositoryImpl @Inject constructor(
8+
private val dataSource: LikeDataSource
9+
) : LikeRepository {
10+
11+
override suspend fun likeCourse(userId: Int, courseId: Int): Result<Unit> {
12+
return try {
13+
dataSource.likeCourse(userId, courseId)
14+
Result.success(Unit)
15+
} catch (e: Exception) {
16+
Result.failure(e)
17+
}
18+
}
19+
20+
override suspend fun unlikeCourse(userId: Int, courseId: Int): Result<Unit> {
21+
return try {
22+
dataSource.unlikeCourse(userId, courseId)
23+
Result.success(Unit)
24+
} catch (e: Exception) {
25+
Result.failure(e)
26+
}
27+
}
28+
}

app/src/main/java/com/paw/key/data/service/LikeService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import retrofit2.http.POST
77
import retrofit2.http.Path
88

99
interface LikeService {
10+
1011
@POST("/api/v1/likes/{courseId}")
1112
suspend fun likeCourse(
1213
@Header("X-USER-ID") userId: Int,

app/src/main/java/com/paw/key/domain/model/entity/archivedlist/ArchivedListEntity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ data class ArchivedListPostsEntity(
55
)
66

77
data class ArchivedListEntity(
8-
val postId: Long,
8+
val postId: Int,
99
val createdAt: String,
1010
val isLiked: Boolean,
1111
val title: String,

app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/TabListScreen.kt

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,14 @@ package com.paw.key.presentation.ui.course.entire.tab.map.List
33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.border
55
import androidx.compose.foundation.clickable
6-
import androidx.compose.foundation.layout.Arrangement
7-
import androidx.compose.foundation.layout.Box
8-
import androidx.compose.foundation.layout.Column
9-
import androidx.compose.foundation.layout.Row
10-
import androidx.compose.foundation.layout.fillMaxSize
11-
import androidx.compose.foundation.layout.fillMaxWidth
12-
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.foundation.layout.*
137
import androidx.compose.foundation.lazy.LazyColumn
148
import androidx.compose.foundation.lazy.items
159
import androidx.compose.foundation.shape.RoundedCornerShape
1610
import androidx.compose.material3.CircularProgressIndicator
1711
import androidx.compose.material3.Icon
1812
import androidx.compose.material3.Text
19-
import androidx.compose.runtime.Composable
20-
import androidx.compose.runtime.getValue
21-
import androidx.compose.runtime.mutableStateOf
22-
import androidx.compose.runtime.remember
23-
import androidx.compose.runtime.setValue
13+
import androidx.compose.runtime.*
2414
import androidx.compose.ui.Alignment
2515
import androidx.compose.ui.Modifier
2616
import androidx.compose.ui.graphics.Color
@@ -41,7 +31,8 @@ import com.paw.key.presentation.ui.course.entire.tab.map.List.viewmodel.TapListV
4131
private fun PreviewTabListScreen() {
4232
PawKeyTheme {
4333
TabListScreen(
44-
navigateToDetail = {}
34+
navigateToDetail = {},
35+
onClickLike = { _, _ -> }
4536
)
4637
}
4738
}
@@ -55,7 +46,10 @@ fun TapListRoute(
5546
TabListScreen(
5647
modifier = modifier,
5748
navigateToDetail = navigateToDetail,
58-
viewModel = viewModel
49+
viewModel = viewModel,
50+
onClickLike = { postId, isLiked ->
51+
viewModel.toggleLike(postId = postId, isLiked = isLiked)
52+
}
5953
)
6054
}
6155

@@ -64,13 +58,13 @@ fun TabListScreen(
6458
navigateToDetail: () -> Unit,
6559
modifier: Modifier = Modifier,
6660
viewModel: TapListViewModel = hiltViewModel(),
61+
onClickLike: (postId: Int, isLiked: Boolean) -> Unit
6762
) {
6863
var showBottomSheet by remember { mutableStateOf(false) }
6964
val listState by viewModel.state.collectAsStateWithLifecycle()
7065

7166
Column(
72-
modifier = modifier
73-
.fillMaxSize()
67+
modifier = modifier.fillMaxSize()
7468
) {
7569
Row(
7670
horizontalArrangement = Arrangement.spacedBy(8.dp),
@@ -84,10 +78,9 @@ fun TabListScreen(
8478
imageVector = ImageVector.vectorResource(R.drawable.ic_course_optin_filter),
8579
contentDescription = "filter",
8680
tint = Color.Unspecified,
87-
modifier = Modifier
88-
.noRippleClickable {
89-
showBottomSheet = true
90-
}
81+
modifier = Modifier.noRippleClickable {
82+
showBottomSheet = true
83+
}
9184
)
9285
OptionChip(
9386
text = if (viewModel.isFilterApplied()) "필터 적용됨" else "선택한 옵션이 없어요",
@@ -101,10 +94,6 @@ fun TabListScreen(
10194
.background(PawKeyTheme.colors.white2)
10295
.padding(bottom = 36.dp)
10396
) {
104-
105-
// Todo : 나중에 서버용 리스트로 변경
106-
107-
// 로딩 상태 표시
10897
if (listState.isLoading) {
10998
item {
11099
Box(
@@ -134,6 +123,7 @@ fun TabListScreen(
134123
isLiked = post.isLike,
135124
onClickLike = { isLiked ->
136125
viewModel.toggleLike(post.postId, isLiked)
126+
137127
},
138128
onClickItem = { navigateToDetail() }
139129
)

app/src/main/java/com/paw/key/presentation/ui/course/entire/tab/map/List/viewmodel/TapListViewModel.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,20 @@ class TapListViewModel @Inject constructor(
314314
fun isFilterApplied(): Boolean {
315315
return isAllOptionsSelected()
316316
}
317+
318+
fun toggleLike(postId: Int, isLiked: Boolean) {
319+
viewModelScope.launch {
320+
_state.update { state ->
321+
val updatedPosts = state.postsResult?.posts?.map {
322+
if (it.postId == postId) it.copy(isLike = isLiked) else it
323+
} ?: emptyList()
324+
325+
val updatedPostsResult = state.postsResult?.copy(posts = updatedPosts)
326+
327+
state.copy(
328+
postsResult = updatedPostsResult
329+
)
330+
}
331+
}
332+
}
317333
}

0 commit comments

Comments
 (0)