Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface PoseRepository {
sortOrder: SortOrder = SortOrder.DESC,
): Flow<PagingData<Pose>>

fun getScrappedPosesFlow(
fun getBookmarkedPosesFlow(
sortOrder: SortOrder = SortOrder.DESC,
): Flow<PagingData<Pose>>

Expand All @@ -30,5 +30,5 @@ interface PoseRepository {
poseSize: Int,
): Result<List<Pose>>

suspend fun updateScrap(poseId: Long, scrap: Boolean): Result<Unit>
suspend fun updateBookmark(poseId: Long, bookmark: Boolean): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import com.neki.android.core.data.remote.api.PoseService
import com.neki.android.core.model.Pose
import com.neki.android.core.model.SortOrder

class ScrapPosePagingSource(
class BookmarkPosePagingSource(
private val poseService: PoseService,
private val sortOrder: SortOrder,
) : PagingSource<Int, Pose>() {
Comment thread
ikseong00 marked this conversation as resolved.

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Pose> {
return try {
val page = params.key ?: 0
val poses = poseService.getScrappedPoses(
val poses = poseService.getBookmarkedPoses(
page = page,
size = params.loadSize,
sortOrder = sortOrder.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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.request.UpdateBookmarkRequest
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
Expand Down Expand Up @@ -44,8 +44,8 @@ class PoseService @Inject constructor(
}.body()
}

// 스크랩된 포즈 목록 조회
suspend fun getScrappedPoses(
// 북마크된 포즈 목록 조회
suspend fun getBookmarkedPoses(
page: Int = 0,
size: Int = 20,
sortOrder: String = "DESC",
Expand All @@ -57,10 +57,10 @@ class PoseService @Inject constructor(
}.body()
}

// 스크랩 업데이트
suspend fun updateScrap(poseId: Long, scrap: Boolean): BasicNullableResponse<Unit> {
// 북마크 업데이트
suspend fun updateBookmark(poseId: Long, bookmark: Boolean): BasicNullableResponse<Unit> {
return client.patch("/api/poses/$poseId/scrap") {
setBody(UpdateScrapRequest(scrap))
setBody(UpdateBookmarkRequest(bookmark))
}.body()
}
Comment thread
ikseong00 marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class UpdateScrapRequest(
@SerialName("scrap") val scrap: Boolean,
data class UpdateBookmarkRequest(
@SerialName("scrap") val bookmark: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ data class PoseDetailResponse(
@SerialName("poseId") val poseId: Long,
@SerialName("headCount") val headCount: String,
@SerialName("imageUrl") val imageUrl: String,
@SerialName("scrap") val scrap: Boolean,
@SerialName("scrap") val bookmark: Boolean,
@SerialName("contentType") val contentType: String,
@SerialName("createdAt") val createdAt: String,
) {
internal fun toModel() = Pose(
id = poseId,
isScrapped = scrap,
isBookmarked = bookmark,
poseImageUrl = imageUrl,
peopleCount = PeopleCount.entries.find { it.name == headCount }?.value ?: 1,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ data class PoseResponse(
@SerialName("poseId") val poseId: Long,
@SerialName("headCount") val headCount: String,
@SerialName("imageUrl") val imageUrl: String,
@SerialName("scrap") val scrap: Boolean,
@SerialName("scrap") val bookmark: Boolean,
@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,
isScrapped = scrap,
isBookmarked = bookmark,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.paging.PagingData
import com.neki.android.core.common.exception.ApiErrorCode.NO_MORE_RANDOM_POSE
import com.neki.android.core.common.exception.NoMorePoseException
import com.neki.android.core.data.paging.PosePagingSource
import com.neki.android.core.data.paging.ScrapPosePagingSource
import com.neki.android.core.data.paging.BookmarkPosePagingSource
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
Expand Down Expand Up @@ -45,7 +45,7 @@ class PoseRepositoryImpl @Inject constructor(
).flow
}

override fun getScrappedPosesFlow(
override fun getBookmarkedPosesFlow(
sortOrder: SortOrder,
): Flow<PagingData<Pose>> {
return Pager(
Expand All @@ -56,7 +56,7 @@ class PoseRepositoryImpl @Inject constructor(
enablePlaceholders = false,
),
pagingSourceFactory = {
ScrapPosePagingSource(
BookmarkPosePagingSource(
poseService = poseService,
sortOrder = sortOrder,
)
Expand Down Expand Up @@ -115,7 +115,7 @@ class PoseRepositoryImpl @Inject constructor(
return@runSuspendCatching result
}

override suspend fun updateScrap(poseId: Long, scrap: Boolean): Result<Unit> = runSuspendCatching {
poseService.updateScrap(poseId, scrap)
override suspend fun updateBookmark(poseId: Long, bookmark: Boolean): Result<Unit> = runSuspendCatching {
poseService.updateBookmark(poseId, bookmark)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private fun NekiEndActionBarPreview() {
onClick = {},
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.icon_scrap_stroked),
imageVector = ImageVector.vectorResource(R.drawable.icon_bookmark_stroked),
contentDescription = null,
tint = NekiTheme.colorScheme.gray500,
)
Expand Down Expand Up @@ -156,7 +156,7 @@ private fun NekiBothSidesActionBarPreview() {
onClick = {},
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.icon_scrap_stroked),
imageVector = ImageVector.vectorResource(R.drawable.icon_bookmark_stroked),
contentDescription = null,
tint = NekiTheme.colorScheme.gray500,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import kotlinx.serialization.Serializable
data class Pose(
val id: Long = 0L,
val poseImageUrl: String = "",
val isScrapped: Boolean = false,
val isBookmarked: Boolean = false,
val peopleCount: Int = 0,
)
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private fun FilterBarDefaultPreview() {
isDownIconChipSelected = false,
isDefaultChipSelected = false,
downIconChipDisplayText = "인원수",
defaultChipDisplayText = "스크랩",
defaultChipDisplayText = "북마크",
)
}
}
Expand All @@ -145,7 +145,7 @@ private fun FilterBarSelectedPreview() {
isDownIconChipSelected = true,
isDefaultChipSelected = true,
downIconChipDisplayText = "2인",
defaultChipDisplayText = "스크랩",
defaultChipDisplayText = "북마크",
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.neki.android.feature.pose.api

sealed interface PoseResult {
data class ScrapChanged(val poseId: Long, val isScrapped: Boolean) : PoseResult
data class BookmarkChanged(val poseId: Long, val isBookmarked: Boolean) : PoseResult
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import com.neki.android.core.model.Pose
data class PoseDetailState(
val isLoading: Boolean = false,
val pose: Pose = Pose(),
val committedScrap: Boolean = false,
val committedBookmark: Boolean = false,
)

sealed interface PoseDetailIntent {
data object EnterPoseDetailScreen : PoseDetailIntent
data object ClickBackIcon : PoseDetailIntent
data object ClickScrapIcon : PoseDetailIntent
data class ScrapCommitted(val newScrap: Boolean) : PoseDetailIntent
data class RevertScrap(val originalScrap: Boolean) : PoseDetailIntent
data object ClickBookmarkIcon : PoseDetailIntent
data class BookmarkCommitted(val newBookmark: Boolean) : PoseDetailIntent
data class RevertBookmark(val originalBookmark: Boolean) : PoseDetailIntent
}

sealed interface PoseDetailSideEffect {
data object NavigateBack : PoseDetailSideEffect
data class ShowToast(val message: String) : PoseDetailSideEffect
data class NotifyScrapChanged(val poseId: Long, val isScrapped: Boolean) : PoseDetailSideEffect
data class NotifyBookmarkChanged(val poseId: Long, val isBookmarked: Boolean) : PoseDetailSideEffect
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ internal fun PoseDetailRoute(
nekiToast.showToast(sideEffect.message)
}

is PoseDetailSideEffect.NotifyScrapChanged -> {
is PoseDetailSideEffect.NotifyBookmarkChanged -> {
resultEventBus.sendResult(
result = PoseResult.ScrapChanged(sideEffect.poseId, sideEffect.isScrapped),
result = PoseResult.BookmarkChanged(sideEffect.poseId, sideEffect.isBookmarked),
allowDuplicate = false,
)
}
Expand Down Expand Up @@ -83,8 +83,8 @@ internal fun PoseDetailScreen(
color = NekiTheme.colorScheme.gray75,
)
PoseActionBar(
isScrapped = uiState.pose.isScrapped,
onClickScrap = { onIntent(PoseDetailIntent.ClickScrapIcon) },
isBookmarked = uiState.pose.isBookmarked,
onClickBookmark = { onIntent(PoseDetailIntent.ClickBookmarkIcon) },
)
}
}
Expand All @@ -98,7 +98,7 @@ private fun PoseDetailScreenPreview() {
pose = Pose(
id = 1,
poseImageUrl = "https://picsum.photos/400/600",
isScrapped = false,
isBookmarked = false,
),
),
)
Expand All @@ -107,14 +107,14 @@ private fun PoseDetailScreenPreview() {

@DevicePreview
@Composable
private fun PoseDetailScreenScrappedPreview() {
private fun PoseDetailScreenBookmarkedPreview() {
NekiTheme {
PoseDetailScreen(
uiState = PoseDetailState(
pose = Pose(
id = 1,
poseImageUrl = "https://picsum.photos/400/600",
isScrapped = true,
isBookmarked = true,
),
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class PoseDetailViewModel @AssistedInject constructor(
@ApplicationScope private val applicationScope: CoroutineScope,
) : ViewModel() {

private val scrapRequests = MutableSharedFlow<Boolean>(extraBufferCapacity = 64)
private val bookmarkRequests = MutableSharedFlow<Boolean>(extraBufferCapacity = 64)

@AssistedFactory
interface Factory {
Expand All @@ -41,19 +41,19 @@ class PoseDetailViewModel @AssistedInject constructor(

init {
viewModelScope.launch {
scrapRequests
bookmarkRequests
.debounce(500)
.collect { newScrap ->
val committedScrap = store.uiState.value.committedScrap
if (committedScrap != newScrap) {
poseRepository.updateScrap(id, newScrap)
.collect { newBookmark ->
val committedBookmark = store.uiState.value.committedBookmark
if (committedBookmark != newBookmark) {
poseRepository.updateBookmark(id, newBookmark)
.onSuccess {
Timber.d("updateScrap success")
store.onIntent(PoseDetailIntent.ScrapCommitted(newScrap))
Timber.d("updateBookmark success")
store.onIntent(PoseDetailIntent.BookmarkCommitted(newBookmark))
}
.onFailure { error ->
Timber.e(error, "updateScrap failed")
store.onIntent(PoseDetailIntent.RevertScrap(committedScrap))
Timber.e(error, "updateBookmark failed")
store.onIntent(PoseDetailIntent.RevertBookmark(committedBookmark))
}
}
}
Expand All @@ -69,29 +69,29 @@ class PoseDetailViewModel @AssistedInject constructor(
when (intent) {
PoseDetailIntent.EnterPoseDetailScreen -> fetchPoseData(reduce)
PoseDetailIntent.ClickBackIcon -> postSideEffect(PoseDetailSideEffect.NavigateBack)
PoseDetailIntent.ClickScrapIcon -> handleScrapToggle(state, reduce)
is PoseDetailIntent.ScrapCommitted -> {
reduce { copy(committedScrap = intent.newScrap) }
postSideEffect(PoseDetailSideEffect.NotifyScrapChanged(id, intent.newScrap))
PoseDetailIntent.ClickBookmarkIcon -> handleBookmarkToggle(state, reduce)
is PoseDetailIntent.BookmarkCommitted -> {
reduce { copy(committedBookmark = intent.newBookmark) }
postSideEffect(PoseDetailSideEffect.NotifyBookmarkChanged(id, intent.newBookmark))
}
is PoseDetailIntent.RevertScrap -> reduce { copy(pose = pose.copy(isScrapped = intent.originalScrap)) }
is PoseDetailIntent.RevertBookmark -> reduce { copy(pose = pose.copy(isBookmarked = intent.originalBookmark)) }
}
}

private fun handleScrapToggle(
private fun handleBookmarkToggle(
state: PoseDetailState,
reduce: (PoseDetailState.() -> PoseDetailState) -> Unit,
) {
val newScrapStatus = !state.pose.isScrapped
viewModelScope.launch { scrapRequests.emit(newScrapStatus) }
reduce { copy(pose = pose.copy(isScrapped = newScrapStatus)) }
val newBookmarkStatus = !state.pose.isBookmarked
viewModelScope.launch { bookmarkRequests.emit(newBookmarkStatus) }
reduce { copy(pose = pose.copy(isBookmarked = newBookmarkStatus)) }
}

private fun fetchPoseData(reduce: (PoseDetailState.() -> PoseDetailState) -> Unit) {
viewModelScope.launch {
poseRepository.getPose(poseId = id)
.onSuccess { data ->
reduce { copy(pose = data, committedScrap = data.isScrapped) }
reduce { copy(pose = data, committedBookmark = data.isBookmarked) }
}
.onFailure { error ->
Timber.e(error)
Expand All @@ -102,12 +102,12 @@ class PoseDetailViewModel @AssistedInject constructor(
override fun onCleared() {
super.onCleared()

val currentScrap = store.uiState.value.pose.isScrapped
val committedScrap = store.uiState.value.committedScrap
val currentBookmark = store.uiState.value.pose.isBookmarked
val committedBookmark = store.uiState.value.committedBookmark

if (currentScrap != committedScrap) {
if (currentBookmark != committedBookmark) {
applicationScope.launch {
poseRepository.updateScrap(id, currentScrap)
poseRepository.updateBookmark(id, currentBookmark)
}
}
}
Expand Down
Loading