mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
refactor(home): extract refresh side effects
Centralize home refresh orchestration so future refresh side effects can be added through Hilt multibindings instead of expanding AppViewModel.
This commit is contained in:
@@ -12,6 +12,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||||||
import hu.bbara.purefin.core.data.DownloadMediaSourceResolver
|
import hu.bbara.purefin.core.data.DownloadMediaSourceResolver
|
||||||
import hu.bbara.purefin.core.data.OfflineCatalogStore
|
import hu.bbara.purefin.core.data.OfflineCatalogStore
|
||||||
import hu.bbara.purefin.core.data.SmartDownloadStore
|
import hu.bbara.purefin.core.data.SmartDownloadStore
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -230,6 +231,8 @@ class MediaDownloadManager @Inject constructor(
|
|||||||
for (seriesId in enabledSeriesIds) {
|
for (seriesId in enabledSeriesIds) {
|
||||||
try {
|
try {
|
||||||
syncSmartDownloadsForSeries(seriesId)
|
syncSmartDownloadsForSeries(seriesId)
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag(TAG).e(e, "Smart download sync failed for series $seriesId")
|
Timber.tag(TAG).e(e, "Smart download sync failed for series $seriesId")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import hu.bbara.purefin.core.data.HomeRepository
|
|||||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.core.data.NetworkMonitor
|
import hu.bbara.purefin.core.data.NetworkMonitor
|
||||||
import hu.bbara.purefin.core.data.UserSessionRepository
|
import hu.bbara.purefin.core.data.UserSessionRepository
|
||||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
import hu.bbara.purefin.core.feature.browse.home.refresh.HomeRefreshCoordinator
|
||||||
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
|
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
|
||||||
import hu.bbara.purefin.core.settings.SettingsRepository
|
import hu.bbara.purefin.core.settings.SettingsRepository
|
||||||
import hu.bbara.purefin.core.model.EpisodeUiModel
|
import hu.bbara.purefin.core.model.EpisodeUiModel
|
||||||
@@ -41,7 +41,7 @@ class AppViewModel @Inject constructor(
|
|||||||
private val userSessionRepository: UserSessionRepository,
|
private val userSessionRepository: UserSessionRepository,
|
||||||
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
|
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val homeRefreshCoordinator: HomeRefreshCoordinator,
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
networkMonitor: NetworkMonitor,
|
networkMonitor: NetworkMonitor,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@@ -253,35 +253,16 @@ class AppViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun onResumed() {
|
fun onResumed() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
homeRefreshCoordinator.onResumed()
|
||||||
homeRepository.refreshHomeData()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Refresh is best-effort; don't crash on failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
mediaDownloadManager.syncSmartDownloads()
|
|
||||||
} catch (_: Exception) { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRefresh() {
|
fun onRefresh() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_isRefreshing.value = true
|
homeRefreshCoordinator.onRefresh { isRefreshing ->
|
||||||
try {
|
_isRefreshing.value = isRefreshing
|
||||||
homeRepository.refreshHomeData()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Refresh is best-effort; don't crash on failure
|
|
||||||
} finally {
|
|
||||||
_isRefreshing.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
mediaDownloadManager.syncSmartDownloads()
|
|
||||||
} catch (_: Exception) { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openSearch() {
|
fun openSearch() {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package hu.bbara.purefin.core.feature.browse.home.refresh
|
||||||
|
|
||||||
|
import hu.bbara.purefin.core.data.HomeRepository
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class HomeRefreshCoordinator @Inject constructor(
|
||||||
|
private val homeRepository: HomeRepository,
|
||||||
|
private val sideEffects: Set<@JvmSuppressWildcards HomeRefreshSideEffect>,
|
||||||
|
) {
|
||||||
|
private val sideEffectsMutex = Mutex()
|
||||||
|
|
||||||
|
suspend fun onResumed() {
|
||||||
|
refreshHomeData()
|
||||||
|
runSideEffects()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun onRefresh(setRefreshing: (Boolean) -> Unit) {
|
||||||
|
refreshHomeData(setRefreshing)
|
||||||
|
runSideEffects()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun refreshHomeData(
|
||||||
|
setRefreshing: ((Boolean) -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
setRefreshing?.invoke(true)
|
||||||
|
try {
|
||||||
|
homeRepository.refreshHomeData()
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Refresh is best-effort; don't crash on failure
|
||||||
|
} finally {
|
||||||
|
setRefreshing?.invoke(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun runSideEffects() {
|
||||||
|
sideEffectsMutex.withLock {
|
||||||
|
coroutineScope {
|
||||||
|
sideEffects.forEach { sideEffect ->
|
||||||
|
launch {
|
||||||
|
try {
|
||||||
|
sideEffect.run()
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package hu.bbara.purefin.core.feature.browse.home.refresh
|
||||||
|
|
||||||
|
interface HomeRefreshSideEffect {
|
||||||
|
suspend fun run()
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package hu.bbara.purefin.core.feature.browse.home.refresh
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import dagger.multibindings.IntoSet
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
abstract class HomeRefreshSideEffectModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun bindSyncSmartDownloadsHomeRefreshSideEffect(
|
||||||
|
impl: SyncSmartDownloadsHomeRefreshSideEffect
|
||||||
|
): HomeRefreshSideEffect
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package hu.bbara.purefin.core.feature.browse.home.refresh
|
||||||
|
|
||||||
|
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SyncSmartDownloadsHomeRefreshSideEffect @Inject constructor(
|
||||||
|
private val mediaDownloadController: MediaDownloadController,
|
||||||
|
) : HomeRefreshSideEffect {
|
||||||
|
|
||||||
|
override suspend fun run() {
|
||||||
|
mediaDownloadController.syncSmartDownloads()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user