-
Notifications
You must be signed in to change notification settings - Fork 0
[feat] #70 포즈 API 연동 및 스크랩 기능 구현 #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
474ade5
[feat] #70: Pose API 레이어 구현
ikseong00 d42572c
[feat] #70: 포즈 목록 화면 API 연동
ikseong00 92d595b
[feat] #70: 랜덤 포즈 화면 API 연동
ikseong00 f4f5ff3
[refactor] #70: BottomSheet이 중간에 걸치지 않도록 수정
ikseong00 12b564f
[feat] #70: 포즈 목록 Paging 적용
ikseong00 63f00d4
[build] #70: detekt UnusedPrivateProperty suppress 추가
ikseong00 3fe975d
[refactor] #70: PoseDetailResponse를 PoseItemResponse로 이름 변경
ikseong00 a956c89
[refactor] #70: PagingConfig 상수 위치 변경 및 설정 추가
ikseong00 3df5f84
[refactor] #70: 포즈 랜덤 화면 플로팅 바 패딩 수정
ikseong00 323d245
[refactor] #70: 포즈 상세 조회 API 응답 모델에서 'scrap' 필드 제거
ikseong00 3d32488
[refactor] #70: 포즈 상세화면 진입 시 Pose 객체 대신 ID를 전달하도록 변경
ikseong00 40a158a
[feat] #70: 랜덤 포즈 좌우 이동 및 프리패치 구현
ikseong00 523ebf2
[feat] #70: 포즈 상세/랜덤 응답에 scrap 필드 반영
ikseong00 6855722
[feat] #70: 포즈 상세화면 스크랩 API 연동
ikseong00 cd699b1
[feat] #70: 랜덤 포즈 스크랩 API 연동 및 UI 개선
ikseong00 f8aa796
[fix] #70: detekt SpacingAroundCurly 린트 오류 수정
ikseong00 176e224
[refactor] #70: 사용하지 않는 `getPoses` 함수 제거
ikseong00 95deaa9
[fix] #70: updateScrap 반환값 수정 및 스크랩 조건 로직 개선
ikseong00 a35534d
[fix] #70: handleMoveNext 인덱스 이동 및 프리패치 로직 버그 수정
ikseong00 e94b2ea
[fix] #70: RandomPoseScreen ImageLoader remember 처리
ikseong00 d7a27e3
[feat] #70랜덤 포즈 조회 시 인원 수 필터링 기능 추가
ikseong00 9271d37
[refactor] #70: 포즈 스크랩 실패 시 스크랩 상태 롤백 로직 수정
ikseong00 6126a61
Merge branch 'develop' into feat/#70-pose-api
Ojongseok File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
core/data-api/src/main/java/com/neki/android/core/dataapi/repository/PoseRepository.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.neki.android.core.dataapi.repository | ||
|
|
||
| import androidx.paging.PagingData | ||
| import com.neki.android.core.model.PeopleCount | ||
| import com.neki.android.core.model.Pose | ||
| import com.neki.android.core.model.SortOrder | ||
| import kotlinx.coroutines.flow.Flow | ||
|
|
||
| interface PoseRepository { | ||
|
|
||
| fun getPosesFlow( | ||
| headCount: PeopleCount? = null, | ||
| sortOrder: SortOrder = SortOrder.DESC, | ||
| ): Flow<PagingData<Pose>> | ||
|
|
||
| suspend fun getPose(poseId: Long): Result<Pose> | ||
|
|
||
| suspend fun getRandomPose(headCount: PeopleCount): Result<Pose> | ||
|
|
||
| suspend fun updateScrap(poseId: Long, scrap: Boolean): Result<Unit> | ||
| } |
43 changes: 43 additions & 0 deletions
43
core/data/src/main/java/com/neki/android/core/data/paging/PosePagingSource.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package com.neki.android.core.data.paging | ||
|
|
||
| import androidx.paging.PagingSource | ||
| import androidx.paging.PagingState | ||
| import com.neki.android.core.data.remote.api.PoseService | ||
| import com.neki.android.core.model.PeopleCount | ||
| import com.neki.android.core.model.Pose | ||
| import com.neki.android.core.model.SortOrder | ||
|
|
||
| class PosePagingSource( | ||
| private val poseService: PoseService, | ||
| private val headCount: PeopleCount?, | ||
| private val sortOrder: SortOrder, | ||
| ) : PagingSource<Int, Pose>() { | ||
|
|
||
| override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Pose> { | ||
| return try { | ||
| val page = params.key ?: 0 | ||
| val response = poseService.getPoses( | ||
| page = page, | ||
| size = params.loadSize, | ||
| headCount = headCount?.name, | ||
| sortOrder = sortOrder.name, | ||
| ) | ||
| val poses = response.data.toModels() | ||
|
|
||
| LoadResult.Page( | ||
| data = poses, | ||
| prevKey = if (page == 0) null else page - 1, | ||
| nextKey = if (poses.isEmpty()) null else page + 1, | ||
| ) | ||
| } catch (e: Exception) { | ||
| LoadResult.Error(e) | ||
| } | ||
| } | ||
|
|
||
| override fun getRefreshKey(state: PagingState<Int, Pose>): Int? { | ||
| return state.anchorPosition?.let { anchorPosition -> | ||
| state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) | ||
| ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) | ||
| } | ||
| } | ||
| } |
52 changes: 52 additions & 0 deletions
52
core/data/src/main/java/com/neki/android/core/data/remote/api/PoseService.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package com.neki.android.core.data.remote.api | ||
|
|
||
| import com.neki.android.core.data.remote.model.request.UpdateScrapRequest | ||
| import com.neki.android.core.data.remote.model.response.BasicNullableResponse | ||
| import com.neki.android.core.data.remote.model.response.BasicResponse | ||
| import com.neki.android.core.data.remote.model.response.PoseDetailResponse | ||
| import com.neki.android.core.data.remote.model.response.PoseResponse | ||
| import io.ktor.client.HttpClient | ||
| import io.ktor.client.call.body | ||
| import io.ktor.client.request.get | ||
| import io.ktor.client.request.parameter | ||
| import io.ktor.client.request.patch | ||
| import io.ktor.client.request.setBody | ||
| import javax.inject.Inject | ||
|
|
||
| class PoseService @Inject constructor( | ||
| private val client: HttpClient, | ||
| ) { | ||
| // 포즈 목록 조회 | ||
| suspend fun getPoses( | ||
| page: Int = 0, | ||
| size: Int = 20, | ||
| headCount: String? = null, | ||
| sortOrder: String = "DESC", | ||
| ): BasicResponse<PoseResponse> { | ||
| return client.get("/api/poses") { | ||
| parameter("page", page) | ||
| parameter("size", size) | ||
| parameter("headCount", headCount) | ||
| parameter("sortOrder", sortOrder) | ||
| }.body() | ||
| } | ||
|
|
||
| // 포즈 상세 조회 | ||
| suspend fun getPose(poseId: Long): BasicResponse<PoseDetailResponse> { | ||
| return client.get("/api/poses/$poseId").body() | ||
| } | ||
|
|
||
| // 랜덤 포즈 조회 | ||
| suspend fun getRandomPose(headCount: String): BasicResponse<PoseDetailResponse> { | ||
| return client.get("/api/poses/random") { | ||
| parameter("headCount", headCount) | ||
| }.body() | ||
| } | ||
|
|
||
| // 스크랩 업데이트 | ||
| suspend fun updateScrap(poseId: Long, scrap: Boolean): BasicNullableResponse<Unit> { | ||
| return client.patch("/api/poses/$poseId/scrap") { | ||
| setBody(UpdateScrapRequest(scrap)) | ||
| }.body() | ||
| } | ||
| } |
9 changes: 9 additions & 0 deletions
9
.../data/src/main/java/com/neki/android/core/data/remote/model/request/UpdateScrapRequest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.neki.android.core.data.remote.model.request | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class UpdateScrapRequest( | ||
| @SerialName("scrap") val scrap: Boolean, | ||
| ) |
23 changes: 23 additions & 0 deletions
23
...data/src/main/java/com/neki/android/core/data/remote/model/response/PoseDetailResponse.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.neki.android.core.data.remote.model.response | ||
|
|
||
| import com.neki.android.core.model.PeopleCount | ||
| import com.neki.android.core.model.Pose | ||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class PoseDetailResponse( | ||
| @SerialName("poseId") val poseId: Long, | ||
| @SerialName("headCount") val headCount: String, | ||
| @SerialName("imageUrl") val imageUrl: String, | ||
| @SerialName("scrap") val scrap: Boolean, | ||
| @SerialName("contentType") val contentType: String, | ||
| @SerialName("createdAt") val createdAt: String, | ||
| ) { | ||
| internal fun toModel() = Pose( | ||
| id = poseId, | ||
| isScrapped = scrap, | ||
| poseImageUrl = imageUrl, | ||
| peopleCount = PeopleCount.entries.find { it.name == headCount }?.value ?: 1, | ||
| ) | ||
| } |
29 changes: 29 additions & 0 deletions
29
core/data/src/main/java/com/neki/android/core/data/remote/model/response/PoseResponse.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.neki.android.core.data.remote.model.response | ||
|
|
||
| import com.neki.android.core.model.PeopleCount | ||
| import com.neki.android.core.model.Pose | ||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class PoseResponse( | ||
| @SerialName("hasNext") val hasNext: Boolean, | ||
| @SerialName("items") val items: List<Item>, | ||
| ) { | ||
| @Serializable | ||
| data class Item( | ||
| @SerialName("poseId") val poseId: Long, | ||
| @SerialName("headCount") val headCount: String, | ||
| @SerialName("imageUrl") val imageUrl: String, | ||
| @SerialName("contentType") val contentType: String, | ||
| @SerialName("createdAt") val createdAt: String, | ||
| ) { | ||
| internal fun toModel() = Pose( | ||
| id = poseId, | ||
| poseImageUrl = imageUrl, | ||
| peopleCount = PeopleCount.entries.find { it.name == headCount }?.value ?: 1, | ||
| ) | ||
| } | ||
|
|
||
| fun toModels() = items.map { it.toModel() } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
core/data/src/main/java/com/neki/android/core/data/repository/impl/PoseRepositoryImpl.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| package com.neki.android.core.data.repository.impl | ||
|
|
||
| import androidx.paging.Pager | ||
| import androidx.paging.PagingConfig | ||
| import androidx.paging.PagingData | ||
| import com.neki.android.core.data.paging.PosePagingSource | ||
| import com.neki.android.core.data.remote.api.PoseService | ||
| import com.neki.android.core.data.util.runSuspendCatching | ||
| import com.neki.android.core.dataapi.repository.PoseRepository | ||
| import com.neki.android.core.model.PeopleCount | ||
| import com.neki.android.core.model.Pose | ||
| import com.neki.android.core.model.SortOrder | ||
| import kotlinx.coroutines.flow.Flow | ||
| import javax.inject.Inject | ||
|
|
||
| private const val PAGE_SIZE = 20 | ||
| private const val PREFETCH_DISTANCE = 10 | ||
|
|
||
| class PoseRepositoryImpl @Inject constructor( | ||
| private val poseService: PoseService, | ||
| ) : PoseRepository { | ||
|
|
||
| override fun getPosesFlow( | ||
| headCount: PeopleCount?, | ||
| sortOrder: SortOrder, | ||
| ): Flow<PagingData<Pose>> { | ||
| return Pager( | ||
| config = PagingConfig( | ||
| pageSize = PAGE_SIZE, | ||
| initialLoadSize = PAGE_SIZE, | ||
| prefetchDistance = PREFETCH_DISTANCE, | ||
| enablePlaceholders = false, | ||
| ), | ||
| pagingSourceFactory = { | ||
| PosePagingSource( | ||
| poseService = poseService, | ||
| headCount = headCount, | ||
| sortOrder = sortOrder, | ||
| ) | ||
| }, | ||
| ).flow | ||
| } | ||
|
|
||
| override suspend fun getPose(poseId: Long): Result<Pose> = runSuspendCatching { | ||
| poseService.getPose(poseId).data.toModel() | ||
| } | ||
|
|
||
| override suspend fun getRandomPose(headCount: PeopleCount): Result<Pose> = runSuspendCatching { | ||
| poseService.getRandomPose(headCount = headCount.name).data.toModel() | ||
| } | ||
|
|
||
| override suspend fun updateScrap(poseId: Long, scrap: Boolean): Result<Unit> = runSuspendCatching { | ||
| poseService.updateScrap(poseId, scrap) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.