diff --git a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt index 8b30e0ec9..4d316faf7 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt @@ -1,12 +1,12 @@ package com.festago.festago.data.di.singletonscope import com.festago.festago.data.repository.DefaultArtistRepository +import com.festago.festago.data.repository.DefaultBookmarkRepository import com.festago.festago.data.repository.DefaultFestivalRepository import com.festago.festago.data.repository.DefaultRecentSearchRepository import com.festago.festago.data.repository.DefaultSchoolRepository import com.festago.festago.data.repository.DefaultSearchRepository import com.festago.festago.data.repository.DefaultUserRepository -import com.festago.festago.data.repository.FakeBookmarkRepository import com.festago.festago.domain.repository.ArtistRepository import com.festago.festago.domain.repository.BookmarkRepository import com.festago.festago.domain.repository.FestivalRepository @@ -45,7 +45,7 @@ interface RepositoryModule { @Binds @Singleton - fun bindsBookmarkRepository(bookmarkRepository: FakeBookmarkRepository): BookmarkRepository + fun bindsBookmarkRepository(bookmarkRepository: DefaultBookmarkRepository): BookmarkRepository @Binds @Singleton diff --git a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt index 9ea7f788d..a3414d88d 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt @@ -51,7 +51,7 @@ object ServiceModule { @Provides @Singleton fun providesBookmarkRetrofitService( - @NormalRetrofitQualifier retrofit: Retrofit, + @AuthRetrofitQualifier retrofit: Retrofit, ): BookmarkRetrofitService { return retrofit.create(BookmarkRetrofitService::class.java) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultBookmarkRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultBookmarkRepository.kt index 2d1f72434..d76321bdb 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultBookmarkRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultBookmarkRepository.kt @@ -39,9 +39,11 @@ class DefaultBookmarkRepository @Inject constructor( } override suspend fun deleteFestivalBookmark(festivalId: Long): Result { - return runCatchingResponse { + val result = runCatchingResponse { bookmarkRetrofitService.deleteBookmark(festivalId, BookmarkType.FESTIVAL) } + result.onFailure { if (it.message?.contains("204") == true) return Result.success(Unit) } + return result } override suspend fun addSchoolBookmark(schoolId: Long): Result { @@ -59,9 +61,11 @@ class DefaultBookmarkRepository @Inject constructor( } override suspend fun deleteSchoolBookmark(schoolId: Long): Result { - return runCatchingResponse { + val result = runCatchingResponse { bookmarkRetrofitService.deleteBookmark(schoolId, BookmarkType.SCHOOL) } + result.onFailure { if (it.message?.contains("204") == true) return Result.success(Unit) } + return result } override suspend fun addArtistBookmark(artistId: Long): Result { @@ -81,8 +85,10 @@ class DefaultBookmarkRepository @Inject constructor( } override suspend fun deleteArtistBookmark(artistId: Long): Result { - return runCatchingResponse { + val result = runCatchingResponse { bookmarkRetrofitService.deleteBookmark(artistId, BookmarkType.ARTIST) } + result.onFailure { if (it.message?.contains("204") == true) return Result.success(Unit) } + return result } } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailEvent.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailEvent.kt index 4d8f51934..cee6df76a 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailEvent.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailEvent.kt @@ -4,4 +4,6 @@ sealed interface ArtistDetailEvent { class ShowArtistDetail(val artistId: Long) : ArtistDetailEvent class ShowFestivalDetail(val festivalId: Long) : ArtistDetailEvent + + class FailedToFetchBookmarkList(val message: String) : ArtistDetailEvent } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailFragment.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailFragment.kt index b8f361e4a..82e241791 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailFragment.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -53,9 +54,7 @@ class ArtistDetailFragment : Fragment() { requireActivity().onBackPressedDispatcher.onBackPressed() } - binding.cvBookmark.setOnClickListener { - binding.ivBookmark.isSelected = !binding.ivBookmark.isSelected - } + binding.cvBookmark.isSelected } private fun initObserve() { @@ -81,6 +80,8 @@ class ArtistDetailFragment : Fragment() { private fun handleSuccess(uiState: ArtistDetailUiState.Success) { binding.successUiState = uiState + binding.ivBookmark.isSelected = uiState.bookMarked + val items: List = if (uiState.isLast) { uiState.festivals } else { @@ -117,6 +118,11 @@ class ArtistDetailFragment : Fragment() { ), ) } + + is ArtistDetailEvent.FailedToFetchBookmarkList -> { + Toast.makeText(requireContext(), "최대 북마크 갯수를 초과했습니다", Toast.LENGTH_SHORT) + .show() + } } private fun startBrowser(url: String) { diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt index 1fecab1b9..a5f94cca4 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt @@ -6,6 +6,8 @@ import com.festago.festago.common.analytics.AnalyticsHelper import com.festago.festago.common.analytics.logNetworkFailure import com.festago.festago.domain.model.festival.FestivalsPage import com.festago.festago.domain.repository.ArtistRepository +import com.festago.festago.domain.repository.BookmarkRepository +import com.festago.festago.presentation.ui.artistdetail.ArtistDetailEvent.FailedToFetchBookmarkList import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistDetailUiState import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistUiState import com.festago.festago.presentation.ui.artistdetail.uistate.FestivalItemUiState @@ -24,6 +26,7 @@ import javax.inject.Inject @HiltViewModel class ArtistDetailViewModel @Inject constructor( private val artistRepository: ArtistRepository, + private val bookmarkRepository: BookmarkRepository, private val analyticsHelper: AnalyticsHelper, ) : ViewModel() { private val _event: MutableSharedFlow = MutableSharedFlow() @@ -40,12 +43,17 @@ class ArtistDetailViewModel @Inject constructor( runCatching { val deferredArtistDetail = async { artistRepository.loadArtistDetail(id) } val deferredFestivals = async { artistRepository.loadArtistFestivals(id, 10) } + val deferredBookmarks = async { bookmarkRepository.getArtistBookmarks() } + val artist = deferredArtistDetail.await().getOrThrow() val festivalPage = deferredFestivals.await().getOrThrow() + val artistBookmarks = deferredBookmarks.await().getOrThrow() _uiState.value = ArtistDetailUiState.Success( - deferredArtistDetail.await().getOrThrow(), - festivalPage.toUiState(), - festivalPage.isLastPage, + artist = artist, + bookMarked = artistBookmarks.firstOrNull { it.artist.id == artist.id.toLong() } != null, + festivals = festivalPage.toUiState(), + isLast = festivalPage.isLastPage, + onBookmarkClick = ::toggleArtistBookmark, ) if (festivalPage.festivals.isEmpty()) { @@ -85,6 +93,21 @@ class ArtistDetailViewModel @Inject constructor( } } + private fun toggleArtistBookmark(artistId: Int) { + viewModelScope.launch { + val uiState = uiState.value as? ArtistDetailUiState.Success ?: return@launch + + if (uiState.bookMarked) { + bookmarkRepository.deleteArtistBookmark(artistId.toLong()) + .onSuccess { _uiState.value = uiState.copy(bookMarked = false) } + .onFailure { _event.emit(FailedToFetchBookmarkList("최대 북마크 갯수를 초과했습니다")) } + } else { + bookmarkRepository.addArtistBookmark(artistId.toLong()) + .onSuccess { _uiState.value = uiState.copy(bookMarked = true) } + .onFailure { _event.emit(FailedToFetchBookmarkList("최대 북마크 갯수를 초과했습니다")) } + } + } + } private fun handleFailure(key: String, throwable: Throwable) { _uiState.value = ArtistDetailUiState.Error { _uiState.value = ArtistDetailUiState.Loading diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/ArtistDetailUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/ArtistDetailUiState.kt index 2998f978a..3f1199fdc 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/ArtistDetailUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/ArtistDetailUiState.kt @@ -7,8 +7,10 @@ sealed interface ArtistDetailUiState { data class Success( val artist: ArtistDetail, + val bookMarked: Boolean, val festivals: List, val isLast: Boolean, + val onBookmarkClick: (Int) -> Unit, ) : ArtistDetailUiState class Error(val refresh: (id: Long) -> Unit) : ArtistDetailUiState diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailEvent.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailEvent.kt index 86bd8e1cd..01719142c 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailEvent.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailEvent.kt @@ -3,4 +3,5 @@ package com.festago.festago.presentation.ui.festivaldetail sealed interface FestivalDetailEvent { class ShowArtistDetail(val artistId: Long) : FestivalDetailEvent class ShowSchoolDetail(val schoolId: Long) : FestivalDetailEvent + class FailedToFetchBookmarkList(val message: String) : FestivalDetailEvent } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailFragment.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailFragment.kt index f67871ba7..cb8a60a04 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailFragment.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailFragment.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import android.widget.Toast import androidx.appcompat.content.res.AppCompatResources import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -56,9 +57,6 @@ class FestivalDetailFragment : Fragment() { binding.ivBack.setOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } - binding.cvBookmark.setOnClickListener { - binding.ivBookmark.isSelected = !binding.ivBookmark.isSelected - } } private fun initObserve() { @@ -85,6 +83,7 @@ class FestivalDetailFragment : Fragment() { private fun handleSuccess(uiState: FestivalDetailUiState.Success) { binding.successUiState = uiState + binding.ivBookmark.isSelected = uiState.bookmarked binding.tvFestivalDDay.setFestivalDDay(uiState.festival.startDate, uiState.festival.endDate) binding.ivFestivalBackground.setColorFilter(Color.parseColor("#66000000")) adapter.submitList(uiState.stages) @@ -160,6 +159,10 @@ class FestivalDetailFragment : Fragment() { ), ) } + + is FestivalDetailEvent.FailedToFetchBookmarkList -> { + Toast.makeText(requireContext(), event.message, Toast.LENGTH_SHORT).show() + } } } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailViewModel.kt index 168e50620..d571b1674 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/FestivalDetailViewModel.kt @@ -7,7 +7,9 @@ import com.festago.festago.common.analytics.logNetworkFailure import com.festago.festago.domain.model.artist.Artist import com.festago.festago.domain.model.festival.FestivalDetail import com.festago.festago.domain.model.stage.Stage +import com.festago.festago.domain.repository.BookmarkRepository import com.festago.festago.domain.repository.FestivalRepository +import com.festago.festago.presentation.ui.festivaldetail.FestivalDetailEvent.FailedToFetchBookmarkList import com.festago.festago.presentation.ui.festivaldetail.uiState.ArtistItemUiState import com.festago.festago.presentation.ui.festivaldetail.uiState.FestivalDetailUiState import com.festago.festago.presentation.ui.festivaldetail.uiState.FestivalUiState @@ -25,6 +27,7 @@ import javax.inject.Inject @HiltViewModel class FestivalDetailViewModel @Inject constructor( private val festivalRepository: FestivalRepository, + private val bookmarkRepository: BookmarkRepository, private val analyticsHelper: AnalyticsHelper, ) : ViewModel() { @@ -38,8 +41,15 @@ class FestivalDetailViewModel @Inject constructor( if (!refresh && _uiState.value is FestivalDetailUiState.Success) return viewModelScope.launch { - festivalRepository.loadFestivalDetail(festivalId).onSuccess { festivalDetail -> + val deferredFestivalDetail = festivalRepository.loadFestivalDetail(festivalId) + val deferredBookmarks = bookmarkRepository.getFestivalBookmarkIds() + + runCatching { + val festivalDetail = deferredFestivalDetail.getOrThrow() + val festivalBookmarkIds = deferredBookmarks.getOrThrow() + _uiState.value = festivalDetail.toSuccessUiState() + .copy(bookmarked = festivalBookmarkIds.contains(festivalId)) }.onFailure { _uiState.value = FestivalDetailUiState.Error { festivalId -> _uiState.value = FestivalDetailUiState.Loading @@ -53,20 +63,37 @@ class FestivalDetailViewModel @Inject constructor( } } - private fun FestivalDetail.toSuccessUiState() = - FestivalDetailUiState.Success( - FestivalUiState( - id = id, - name = name, - startDate = startDate, - endDate = endDate, - posterImageUrl = posterImageUrl, - school = school, - onSchoolClick = ::showSchoolDetail, - socialMedias = socialMedias, - ), - stages = stages.map { it.toUiState() }, - ) + private fun FestivalDetail.toSuccessUiState() = FestivalDetailUiState.Success( + festival = FestivalUiState( + id = id, + name = name, + startDate = startDate, + endDate = endDate, + posterImageUrl = posterImageUrl, + school = school, + onSchoolClick = ::showSchoolDetail, + socialMedias = socialMedias, + ), + bookmarked = false, + stages = stages.map { it.toUiState() }, + onBookmarkClick = { festivalId -> toggleFestivalBookmark(festivalId) }, + ) + + private fun toggleFestivalBookmark(festivalId: Long) { + viewModelScope.launch { + val uiState = _uiState.value as? FestivalDetailUiState.Success ?: return@launch + + if (uiState.bookmarked) { + bookmarkRepository.deleteFestivalBookmark(festivalId) + .onSuccess { _uiState.value = uiState.copy(bookmarked = false) } + .onFailure { _event.emit(FailedToFetchBookmarkList("최대 북마크 갯수를 초과했습니다")) } + } else { + bookmarkRepository.addFestivalBookmark(festivalId) + .onSuccess { _uiState.value = uiState.copy(bookmarked = true) } + .onFailure { _event.emit(FailedToFetchBookmarkList("최대 북마크 갯수를 초과했습니다")) } + } + } + } private fun Stage.toUiState() = StageItemUiState( id = id, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/uiState/FestivalDetailUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/uiState/FestivalDetailUiState.kt index 4e4953457..7ec8e78d7 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/uiState/FestivalDetailUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/festivaldetail/uiState/FestivalDetailUiState.kt @@ -5,7 +5,9 @@ interface FestivalDetailUiState { data class Success( val festival: FestivalUiState, + val bookmarked: Boolean, val stages: List, + val onBookmarkClick: (Long) -> Unit, ) : FestivalDetailUiState class Error(val refresh: (id: Long) -> Unit) : FestivalDetailUiState diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/bookmarklist/festivalbookmark/FestivalBookmarkViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/bookmarklist/festivalbookmark/FestivalBookmarkViewModel.kt index a94c3db92..2a4fb630a 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/bookmarklist/festivalbookmark/FestivalBookmarkViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/bookmarklist/festivalbookmark/FestivalBookmarkViewModel.kt @@ -32,17 +32,21 @@ class FestivalBookmarkViewModel @Inject constructor( fun fetchBookmarkList() { viewModelScope.launch { _uiState.value = FestivalBookmarkUiState.Loading - bookmarkRepository.getFestivalBookmarkIds().onSuccess { bookmarkIds -> - bookmarkRepository.getFestivalBookmarks(bookmarkIds, FestivalBookmarkOrder.FESTIVAL) - .onSuccess { festivalBookmarks -> - _uiState.value = - FestivalBookmarkUiState.Success(festivalBookmarks.map { it.toUiState() }) - }.onFailure { - _uiState.value = FestivalBookmarkUiState.Error - } - }.onFailure { - _uiState.value = FestivalBookmarkUiState.Error + val bookmarkIds = bookmarkRepository.getFestivalBookmarkIds() + .getOrElse { _uiState.value = FestivalBookmarkUiState.Error; return@launch } + + if (bookmarkIds.isEmpty()) { + _uiState.value = FestivalBookmarkUiState.Success(emptyList()) + return@launch } + + bookmarkRepository.getFestivalBookmarks(bookmarkIds, FestivalBookmarkOrder.FESTIVAL) + .onSuccess { festivalBookmarks -> + _uiState.value = + FestivalBookmarkUiState.Success(festivalBookmarks.map { it.toUiState() }) + }.onFailure { + _uiState.value = FestivalBookmarkUiState.Error + } } } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailEvent.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailEvent.kt index 02b0420c3..1b57bfc73 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailEvent.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailEvent.kt @@ -4,4 +4,6 @@ sealed interface SchoolDetailEvent { class ShowArtistDetail(val artistId: Long) : SchoolDetailEvent class ShowFestivalDetail(val festivalId: Long) : SchoolDetailEvent + + class FailedToFetchBookmarkList(val message: String) : SchoolDetailEvent } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFragment.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFragment.kt index 81e170e46..de150ecf9 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFragment.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFragment.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -66,9 +67,6 @@ class SchoolDetailFragment : Fragment() { binding.ivBack.setOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } - binding.cvBookmark.setOnClickListener { - binding.ivBookmark.isSelected = !binding.ivBookmark.isSelected - } } private fun updateUi(uiState: SchoolDetailUiState) { @@ -81,6 +79,9 @@ class SchoolDetailFragment : Fragment() { private fun handleSuccess(uiState: SchoolDetailUiState.Success) { binding.successUiState = uiState + + binding.ivBookmark.isSelected = uiState.bookmarked + binding.ivSchoolBackground.setColorFilter(Color.parseColor("#66000000")) val items: List = if (uiState.isLast) { @@ -121,6 +122,10 @@ class SchoolDetailFragment : Fragment() { ) ) } + + is SchoolDetailEvent.FailedToFetchBookmarkList -> { + Toast.makeText(requireContext(), event.message, Toast.LENGTH_SHORT).show() + } } private fun startBrowser(url: String) { diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt index 4e72ba619..dc627b300 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt @@ -5,7 +5,9 @@ import androidx.lifecycle.viewModelScope import com.festago.festago.common.analytics.AnalyticsHelper import com.festago.festago.common.analytics.logNetworkFailure import com.festago.festago.domain.model.festival.Festival +import com.festago.festago.domain.repository.BookmarkRepository import com.festago.festago.domain.repository.SchoolRepository +import com.festago.festago.presentation.ui.schooldetail.SchoolDetailEvent.FailedToFetchBookmarkList import com.festago.festago.presentation.ui.schooldetail.uistate.ArtistUiState import com.festago.festago.presentation.ui.schooldetail.uistate.FestivalItemUiState import com.festago.festago.presentation.ui.schooldetail.uistate.SchoolDetailUiState @@ -24,6 +26,7 @@ import javax.inject.Inject @HiltViewModel class SchoolDetailViewModel @Inject constructor( private val schoolRepository: SchoolRepository, + private val bookmarkRepository: BookmarkRepository, private val analyticsHelper: AnalyticsHelper, ) : ViewModel() { @@ -37,15 +40,19 @@ class SchoolDetailViewModel @Inject constructor( viewModelScope.launch { val deferredSchoolInfo = async { schoolRepository.loadSchoolInfo(schoolId) } val deferredFestivalPage = async { schoolRepository.loadSchoolFestivals(schoolId) } + val deferredBookmarks = async { bookmarkRepository.getSchoolBookmarks() } runCatching { val schoolInfo = deferredSchoolInfo.await().getOrThrow() val festivalPage = deferredFestivalPage.await().getOrThrow() + val schoolBookmarks = deferredBookmarks.await().getOrThrow() _uiState.value = SchoolDetailUiState.Success( schoolInfo = schoolInfo, + bookmarked = schoolBookmarks.firstOrNull { it.school.id == schoolInfo.id.toLong() } != null, festivals = festivalPage.festivals.map { it.toUiState() }, isLast = festivalPage.isLastPage, + onBookmarkClick = { schoolId -> toggleSchoolBookmark(schoolId) }, ) if (festivalPage.festivals.isEmpty()) { loadMoreSchoolFestivals(schoolId) @@ -84,6 +91,22 @@ class SchoolDetailViewModel @Inject constructor( } } + private fun toggleSchoolBookmark(schoolId: Int) { + val uiState = uiState.value as? SchoolDetailUiState.Success ?: return + + viewModelScope.launch { + if (uiState.bookmarked) { + bookmarkRepository.deleteSchoolBookmark(schoolId.toLong()) + .onSuccess { _uiState.value = uiState.copy(bookmarked = false) } + .onFailure { _event.emit(FailedToFetchBookmarkList("최대 북마크 갯수를 초과했습니다")) } + } else { + bookmarkRepository.addSchoolBookmark(uiState.schoolInfo.id.toLong()) + .onSuccess { _uiState.value = uiState.copy(bookmarked = true) } + .onFailure { _event.emit(FailedToFetchBookmarkList("최대 북마크 갯수를 초과했습니다")) } + } + } + } + private fun handleFailure(key: String, throwable: Throwable) { _uiState.value = SchoolDetailUiState.Error { _uiState.value = SchoolDetailUiState.Loading diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/SchoolDetailUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/SchoolDetailUiState.kt index 8d23053e0..2c85eefad 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/SchoolDetailUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/SchoolDetailUiState.kt @@ -7,8 +7,10 @@ sealed interface SchoolDetailUiState { data class Success( val schoolInfo: SchoolInfo, + val bookmarked: Boolean, val festivals: List, val isLast: Boolean, + val onBookmarkClick: (Int) -> Unit, ) : SchoolDetailUiState class Error(val refresh: (schoolId: Long) -> Unit) : SchoolDetailUiState diff --git a/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml b/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml index c1ec166d5..752ac1672 100644 --- a/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml +++ b/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml @@ -92,6 +92,7 @@