Skip to content
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

[FIX] 서버 통신에 성공한 경우에만 스크랩 갱신시키도록 변경 #320

Merged
merged 18 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
555fee1
[FIX] #314 코스 발견 페이지에서 스크랩 서버 통신에 성공했을 때만 뷰를 갱신시키도록 변경
leeeha Jan 28, 2024
65413b0
[UI] #314 상세페이지 스크랩 개수 텍스트 크기 변경
leeeha Jan 28, 2024
5344fa1
[FIX] #314 상세페이지에서 스크랩 서버 통신에 성공한 경우에만 뷰 갱신
leeeha Jan 28, 2024
781d10f
[CHORE] #314 라이브데이터 var -> val 변경
leeeha Jan 28, 2024
ffda301
[FIX] #314 코스 검색 시 나오는 리스트에도 동일한 스크랩 로직 반영
leeeha Jan 28, 2024
ebb56b8
[FIX] #314 프래그먼트 LifecycleOwner 변경 (액티비티 말고 프래그먼트 뷰로)
leeeha Jan 29, 2024
62e239c
[FIX] #314 LayoutParams 타입 캐스팅 안되면 null 반환하도록 변경
leeeha Jan 29, 2024
4455231
[MOD] #314 내가 스크랩한 코스 조회하는 서버 통신 코드 변경
leeeha Jan 29, 2024
26ca341
[MOD] #314 데이터 클래스의 패키지 위치 변경 (data -> domain)
leeeha Jan 29, 2024
9a1fa3b
[MOD] #314 내가 스크랩한 코스 조회 후 뷰에 바인딩 하는 코드 변경
leeeha Jan 29, 2024
3a1bb9e
[MOD] #314 리스트의 아이템 개수에 따라 뷰 갱신
leeeha Jan 29, 2024
254bf78
[REFACTOR] #314 리사이클러뷰를 감싸고 있는 네스티드 뷰 삭제
leeeha Jan 29, 2024
c0d4150
[CHORE] #314 함수명에서 오타 수정
leeeha Jan 29, 2024
1545b38
[MOD] #314 서버 통신에 성공했을 때만 스크랩 리스트로부터 아이템 삭제하도록 변경
leeeha Jan 29, 2024
da67bfb
Merge branch 'develop' of https://github.com/Runnect/Runnect-Android …
leeeha Feb 10, 2024
b8852f2
[MERGE] #314 디벨롭 브랜치와의 머지
leeeha Feb 10, 2024
c7b1f90
[MOD] #314 스크랩 서버통신이 로딩 상태일 때는 로딩 프로그레스바 띄우도록 변경
leeeha Feb 14, 2024
b3b728b
Merge branch 'develop' of https://github.com/Runnect/Runnect-Android …
leeeha Feb 14, 2024
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
@@ -1,55 +1,55 @@
package com.runnect.runnect.data.dto.response


import com.runnect.runnect.domain.entity.MyScrapCourse
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseGetMyScrapCourse(
@SerialName("data")
val data: Data,
@SerialName("message")
val message: String,
@SerialName("status")
val status: Int,
@SerialName("success")
val success: Boolean,
@SerialName("user")
val user: User,
@SerialName("scraps")
val scraps: List<Scrap>,
) {
@Serializable
data class Data(
@SerialName("Scraps")
val scraps: List<Scrap>,
@SerialName("user")
val user: User,
data class User(
@SerialName("userId")
val id: Int,
)

@Serializable
data class Scrap(
@SerialName("courseId")
val courseId: Int,
@SerialName("departure")
val departure: Departure,
@SerialName("id")
val id: Int,
@SerialName("image")
val image: String,
@SerialName("publicCourseId")
val publicCourseId: Int,
@SerialName("title")
val title: String,
) {
@Serializable
data class Scrap(
@SerialName("courseId")
val courseId: Int,
@SerialName("departure")
val departure: Departure,
@SerialName("id")
val id: Int,
@SerialName("image")
val image: String,
@SerialName("publicCourseId")
val publicCourseId: Int,
@SerialName("title")
val title: String,
) {
@Serializable
data class Departure(
@SerialName("city")
val city: String,
@SerialName("region")
val region: String,
)
}
data class Departure(
@SerialName("city")
val city: String,
@SerialName("region")
val region: String,
)
}

@Serializable
data class User(
@SerialName("id")
val id: Int,
fun toMyScrapCourses(): List<MyScrapCourse> = scraps.map { course ->
MyScrapCourse(
courseId = course.courseId,
id = course.id,
publicCourseId = course.publicCourseId,
image = course.image,
city = course.departure.city,
region = course.departure.region,
title = course.title
)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.runnect.runnect.data.repository

import com.runnect.runnect.data.dto.MyScrapCourse
import com.runnect.runnect.data.dto.request.RequestPutMyDrawCourse
import com.runnect.runnect.data.dto.response.ResponseGetMyScrapCourse
import com.runnect.runnect.domain.entity.MyScrapCourse
import com.runnect.runnect.data.dto.response.ResponsePutMyDrawCourse
import com.runnect.runnect.data.dto.response.toMyDrawCourse
import com.runnect.runnect.data.source.remote.RemoteStorageDataSource
Expand All @@ -21,24 +20,8 @@ class StorageRepositoryImpl @Inject constructor(private val remoteStorageDataSou
return remoteStorageDataSource.deleteMyDrawCourse(deleteCourseList = deleteCourseList)
}

override suspend fun getMyScrapCourse(): MutableList<MyScrapCourse> {
return changeMyScrapData(
data = remoteStorageDataSource.getMyScrapCourse().body()!!.data.scraps
).toMutableList()
}

private fun changeMyScrapData(data: List<ResponseGetMyScrapCourse.Data.Scrap>): List<MyScrapCourse> {
val changedData = data.map {
MyScrapCourse(
courseId = it.courseId,
id = it.id,
publicCourseId = it.publicCourseId,
image = it.image,
city = it.departure.city,
region = it.departure.region,
title = it.title
)
override suspend fun getMyScrapCourse(): Result<List<MyScrapCourse>?> =
runCatching {
remoteStorageDataSource.getMyScrapCourse().data?.toMyScrapCourses()
}
return changedData
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ import com.runnect.runnect.data.dto.request.RequestPostCourseScrap
import com.runnect.runnect.data.dto.request.RequestPostPublicCourse
import com.runnect.runnect.data.dto.request.RequestPostRunningHistory
import com.runnect.runnect.data.dto.request.RequestPutMyDrawCourse
import com.runnect.runnect.data.dto.response.*
import com.runnect.runnect.data.dto.response.ResponseGetCourseDetail
import com.runnect.runnect.data.dto.response.ResponseGetDiscoverMarathon
import com.runnect.runnect.data.dto.response.ResponseGetDiscoverPick
import com.runnect.runnect.data.dto.response.ResponseGetDiscoverRecommend
import com.runnect.runnect.data.dto.response.ResponseGetDiscoverSearch
import com.runnect.runnect.data.dto.response.ResponseGetMyDrawCourse
import com.runnect.runnect.data.dto.response.ResponseGetMyDrawDetail
import com.runnect.runnect.data.dto.response.ResponseGetMyScrapCourse
import com.runnect.runnect.data.dto.response.ResponsePatchPublicCourse
import com.runnect.runnect.data.dto.response.ResponsePostDiscoverUpload
import com.runnect.runnect.data.dto.response.ResponsePostMyDrawCourse
import com.runnect.runnect.data.dto.response.ResponsePostMyHistory
import com.runnect.runnect.data.dto.response.ResponsePostScrap
import com.runnect.runnect.data.dto.response.ResponsePutMyDrawCourse
import com.runnect.runnect.data.dto.response.base.BaseResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
Expand Down Expand Up @@ -38,8 +51,7 @@ interface CourseService {
): BaseResponse<ResponseGetCourseDetail>

@GET("/api/course/private/user")
suspend fun getMyCourseLoad(
): ResponseGetDiscoverPick
suspend fun getMyCourseLoad(): ResponseGetDiscoverPick

@POST("/api/public-course")
suspend fun postUploadMyCourse(
Expand All @@ -61,13 +73,11 @@ interface CourseService {

//보관함 내가 그린 코스 가져오기
@GET("/api/course/user")
suspend fun getCourseList(
): BaseResponse<ResponseGetMyDrawCourse>
suspend fun getDrawCourseList(): BaseResponse<ResponseGetMyDrawCourse>

//보관함 스크랩 코스 가져오기
@GET("/api/scrap/user")
suspend fun getScrapList(
): Response<ResponseGetMyScrapCourse>
suspend fun getScrapCourseList(): BaseResponse<ResponseGetMyScrapCourse>

//내가 그린 코스 Detail 가져오기
@GET("/api/course/detail/{courseId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import javax.inject.Inject

class RemoteStorageDataSource @Inject constructor(private val courseService: CourseService) {
suspend fun getMyDrawCourse(): BaseResponse<ResponseGetMyDrawCourse> =
courseService.getCourseList()
courseService.getDrawCourseList()

suspend fun deleteMyDrawCourse(deleteCourseList: RequestPutMyDrawCourse): Response<ResponsePutMyDrawCourse> =
courseService.deleteMyDrawCourse(deleteCourseList)

suspend fun getMyScrapCourse(): Response<ResponseGetMyScrapCourse> =
courseService.getScrapList()
suspend fun getMyScrapCourse(): BaseResponse<ResponseGetMyScrapCourse> =
courseService.getScrapCourseList()
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.runnect.runnect.data.dto
package com.runnect.runnect.domain.entity


import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class MyScrapCourse(
val courseId: Int?,
val id: Int?,
val courseId: Int,
val id: Int,
val publicCourseId: Int,
val image: String?,
Comment on lines +9 to 11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이제 보니 id가 3개나 되니까 각각이 뭔지 잘 모르겠고 너무 헷갈리는군여

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 처음에는 헷갈렸는데 어느정도 패턴이 있어서 적응이 되더라구요 😅

val city: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.runnect.runnect.domain.repository

import com.runnect.runnect.data.dto.MyScrapCourse
import com.runnect.runnect.domain.entity.MyScrapCourse
import com.runnect.runnect.data.dto.request.RequestPutMyDrawCourse
import com.runnect.runnect.data.dto.response.ResponsePutMyDrawCourse
import com.runnect.runnect.domain.entity.MyDrawCourse
Expand All @@ -9,5 +9,5 @@ import retrofit2.Response
interface StorageRepository {
suspend fun getMyDrawCourse(): Result<List<MyDrawCourse>?>
suspend fun deleteMyDrawCourse(deleteCourseList: RequestPutMyDrawCourse) : Response<ResponsePutMyDrawCourse>
suspend fun getMyScrapCourse(): MutableList<MyScrapCourse>?
suspend fun getMyScrapCourse(): Result<List<MyScrapCourse>?>
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main
}

fun updateStorageScrapScreen() {
storageScrapFragment?.getCourse()
storageScrapFragment?.getMyScrapCourses()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,7 @@ class CourseDetailActivity :
return@setOnClickListener
}

it.isSelected = !it.isSelected
viewModel.postCourseScrap(publicCourseId, it.isSelected)
viewModel.postCourseScrap(publicCourseId, !it.isSelected)
}
}

Expand Down Expand Up @@ -581,8 +580,18 @@ class CourseDetailActivity :

private fun setupCourseScrapStateObserver() {
viewModel.courseScrapState.observe(this) { state ->
if (state is UiStateV2.Failure) {
showSnackbar(binding.root, state.msg)
when (state) {
is UiStateV2.Success -> {
val response = state.data ?: return@observe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 null 처리 setupMyScrapCourseGetStateObserver()처럼 뷰모델에서 하는 건 어떨까요? 통일이 되면 좋을 것 같습니다.

Copy link
Member Author

@leeeha leeeha Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

응답값이 널일 때 어떻게 처리할 것인지에 대한 논의가 미리 진행되지 않아서, 이런 식으로 그냥 리턴을 해버리는 코드도 상당히 많은 거 같아요..! 이런 코드들도 우남님이 말하신 것처럼 뷰모델에서 널체크 하고 UiState Failure 처리하는 것으로 수정이 필요할 거 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CourseDetailViewModel에서 대부분의 서버통신 코드가 널처리를 따로 하지 않고 그냥 nullable한 응답값을 프래그먼트로 보내는 방식이어서, 코드의 수정사항이 많을 것으로 예상됩니다. 배포 후에 별도 브랜치에서 리팩토링 진행할게요!

binding.tvCourseDetailScrapCount.text = response.scrapCount.toString()
binding.ivCourseDetailScrap.isSelected = response.scrapTF
}

is UiStateV2.Failure -> {
showSnackbar(binding.root, state.msg)
}

else -> {}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ class CourseDetailViewModel @Inject constructor(
private val courseRepository: CourseRepository, private val userRepository: UserRepository
) : ViewModel() {
// 서버통신 코드
private var _courseGetState = MutableLiveData<UiStateV2<CourseDetail?>>()
private val _courseGetState = MutableLiveData<UiStateV2<CourseDetail?>>()
val courseGetState: LiveData<UiStateV2<CourseDetail?>>
get() = _courseGetState

private var _coursePatchState = MutableLiveData<UiStateV2<EditableCourseDetail?>>()
private val _coursePatchState = MutableLiveData<UiStateV2<EditableCourseDetail?>>()
val coursePatchState: LiveData<UiStateV2<EditableCourseDetail?>>
get() = _coursePatchState

private var _courseDeleteState = MutableLiveData<UiStateV2<ResponseDeleteUploadCourse?>>()
private val _courseDeleteState = MutableLiveData<UiStateV2<ResponseDeleteUploadCourse?>>()
val courseDeleteState: LiveData<UiStateV2<ResponseDeleteUploadCourse?>>
get() = _courseDeleteState

private var _courseScrapState = MutableLiveData<UiStateV2<ResponsePostScrap?>>()
private val _courseScrapState = MutableLiveData<UiStateV2<ResponsePostScrap?>>()
val courseScrapState: LiveData<UiStateV2<ResponsePostScrap?>>
get() = _courseScrapState

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,12 +432,24 @@ class DiscoverFragment : BindingFragment<FragmentDiscoverBinding>(R.layout.fragm

private fun setupCourseScrapStateObserver() {
viewModel.courseScrapState.observe(viewLifecycleOwner) { state ->
if (state is UiStateV2.Failure) {
context?.showSnackbar(
anchorView = binding.root,
message = state.msg,
gravity = Gravity.TOP
)
when (state) {
is UiStateV2.Success -> {
val response = state.data ?: return@observe
multiViewAdapter.updateCourseScrap(
publicCourseId = response.publicCourseId.toInt(),
scrap = response.scrapTF
)
}

is UiStateV2.Failure -> {
context?.showSnackbar(
anchorView = binding.root,
message = state.msg,
gravity = Gravity.TOP
)
}

else -> {}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ class DiscoverMarathonAdapter(
}
}

fun updateMarathonCourseScrap(
targetIndex: Int,
scrap: Boolean
) {
currentList[targetIndex].scrap = scrap
notifyItemChanged(targetIndex)
}

companion object {
private val diffUtil = ItemDiffCallback<DiscoverMultiViewItem.MarathonCourse>(
onItemsTheSame = { old, new -> old.id == new.id },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ class DiscoverRecommendAdapter(
return@setOnClickListener
}

view.isSelected = !view.isSelected
onHeartButtonClick.invoke(course.id, view.isSelected)
onHeartButtonClick.invoke(course.id, !view.isSelected)
}
}

Expand Down Expand Up @@ -92,6 +91,14 @@ class DiscoverRecommendAdapter(
}
}

fun updateRecommendCourseScrap(
targetIndex: Int,
scrap: Boolean
) {
currentList[targetIndex].scrap = scrap
notifyItemChanged(targetIndex)
}

fun addRecommendCourseNextPage(nextPageItems: List<DiscoverMultiViewItem.RecommendCourse>) {
Timber.d("before item count : $itemCount")

Expand Down
Loading
Loading