diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index 9a189973..812c3343 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -87,11 +87,12 @@ fun HomePage( val context = LocalContext.current var firstLoad by rememberSaveable { mutableStateOf(true) } LaunchedEffect(Unit) { - viewModel.init(preferences).join() + viewModel.init(firstLoad, preferences).join() firstLoad = false } val loading by viewModel.loadingState.observeAsState(LoadingState.Loading) - val homeRows by viewModel.homeRows.observeAsState(listOf()) + val watchingRows by viewModel.watchingRows.observeAsState(listOf()) + val latestRows by viewModel.latestRows.observeAsState(listOf()) LaunchedEffect(loading) { val state = loading if (!firstLoad && state is LoadingState.Error) { @@ -118,7 +119,7 @@ fun HomePage( var showPlaylistDialog by remember { mutableStateOf(null) } val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending) HomePageContent( - homeRows, + watchingRows + latestRows, onClickItem = { viewModel.navigationManager.navigateTo(it.destination()) }, @@ -277,7 +278,7 @@ fun HomePageContent( -> { Column( verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = modifier, + modifier = Modifier.animateItem(), ) { Text( text = r.title, @@ -295,7 +296,7 @@ fun HomePageContent( is HomeRowLoadingState.Error -> { Column( verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = modifier, + modifier = Modifier.animateItem(), ) { Text( text = r.title, @@ -323,7 +324,10 @@ fun HomePageContent( } }, onLongClickItem = onLongClickItem, - modifier = Modifier.fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth() + .animateItem(), cardContent = { index, item, cardModifier, onClick, onLongClick -> // TODO better aspect ration handling? BannerCard( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt index 623a5bb2..cf8668a6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt @@ -12,6 +12,7 @@ import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem +import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.HomeRowLoadingState import com.github.damontecres.wholphin.util.LoadingExceptionHandler @@ -51,12 +52,18 @@ class HomeViewModel val navDrawerItemRepository: NavDrawerItemRepository, ) : ViewModel() { val loadingState = MutableLiveData(LoadingState.Pending) - val homeRows = MutableLiveData>() + val watchingRows = MutableLiveData>(listOf()) + val latestRows = MutableLiveData>(listOf()) private lateinit var preferences: UserPreferences - fun init(preferences: UserPreferences): Job { - loadingState.value = LoadingState.Loading + fun init( + firstLoad: Boolean, + preferences: UserPreferences, + ): Job { + if (firstLoad) { + loadingState.value = LoadingState.Loading + } this.preferences = preferences val prefs = preferences.appPreferences.homePagePreferences val limit = prefs.maxItemsPerRow @@ -84,9 +91,7 @@ class HomeViewModel prefs.enableRewatchingNextUp, prefs.combineContinueNext, ) - val latest = getLatest(userDto, limit, includedIds) - - val homeRows = + val watching = buildList { if (resume.isNotEmpty()) { add( @@ -104,12 +109,19 @@ class HomeViewModel ), ) } - addAll(latest) } + + val latest = getLatest(userDto, limit, includedIds) + val pendingLatest = latest.map { HomeRowLoadingState.Loading(it.title) } + withContext(Dispatchers.Main) { - this@HomeViewModel.homeRows.value = homeRows + this@HomeViewModel.watchingRows.value = watching + if (firstLoad) { + this@HomeViewModel.latestRows.value = pendingLatest + } loadingState.value = LoadingState.Success } + loadLatest(latest) } } } @@ -140,7 +152,7 @@ class HomeViewModel .getResumeItems(request) .content .items - .map { BaseItem.Companion.from(it, api, true) } + .map { BaseItem.from(it, api, true) } return items } @@ -174,70 +186,71 @@ class HomeViewModel user: UserDto, limit: Int, includedIds: List, - ): List { - val latestMediaIncludes = - user.configuration - ?.orderedViews - .orEmpty() - .toMutableList() - .apply { - removeAll(user.configuration?.latestItemsExcludes.orEmpty()) - }.filter { includedIds.contains(it) } - + ): List { + val excluded = user.configuration?.latestItemsExcludes.orEmpty() val views by api.userViewsApi.getUserViews() - val rows = - latestMediaIncludes - .mapNotNull { viewId -> views.items.firstOrNull { it.id == viewId } } - .filter { it.collectionType in supportedLatestCollectionTypes } - .mapNotNull { view -> + val latestData = + views.items + .filter { + it.id in includedIds && it.id !in excluded && + it.collectionType in supportedLatestCollectionTypes + }.mapNotNull { view -> val title = if (view.collectionType == CollectionType.LIVETV) { context.getString(R.string.recently_recorded) } else { view.name?.let { context.getString(R.string.recently_added_in, it) } } ?: context.getString(R.string.recently_added) - try { - val viewId = - if (view.collectionType == CollectionType.LIVETV) { - api.liveTvApi - .getRecordingFolders( - userId = user.id, - ).content.items - .firstOrNull() - ?.id - } else { - view.id - } - viewId?.let { - val request = - GetLatestMediaRequest( - fields = SlimItemFields, - imageTypeLimit = 1, - parentId = viewId, - groupItems = true, - limit = limit, - isPlayed = null, // Server will handle user's preference - ) - val latest = - api.userLibraryApi - .getLatestMedia(request) - .content - .map { BaseItem.from(it, api, true) } - HomeRowLoadingState.Success( - title = title, - items = latest, - ) + val viewId = + if (view.collectionType == CollectionType.LIVETV) { + api.liveTvApi + .getRecordingFolders( + userId = user.id, + ).content.items + .firstOrNull() + ?.id + } else { + view.id } - } catch (ex: Exception) { - Timber.e(ex, "Exception fetching %s", title) - HomeRowLoadingState.Error( - title = title, - exception = ex, - ) + viewId?.let { + val request = + GetLatestMediaRequest( + fields = SlimItemFields, + imageTypeLimit = 1, + parentId = viewId, + groupItems = true, + limit = limit, + isPlayed = null, // Server will handle user's preference + ) + LatestData(title, request) } } - return rows + return latestData + } + + private suspend fun loadLatest(latestData: List) { + val rows = + latestData.map { (title, request) -> + try { + val latest = + api.userLibraryApi + .getLatestMedia(request) + .content + .map { BaseItem.from(it, api, true) } + HomeRowLoadingState.Success( + title = title, + items = latest, + ) + } catch (ex: Exception) { + Timber.e(ex, "Exception fetching %s", title) + HomeRowLoadingState.Error( + title = title, + exception = ex, + ) + } + } + latestRows.setValueOnMain(rows) } fun setWatched( @@ -250,7 +263,7 @@ class HomeViewModel api.playStateApi.markUnplayedItem(itemId) } withContext(Dispatchers.Main) { - init(preferences) + init(false, preferences) } } @@ -264,7 +277,7 @@ class HomeViewModel api.userLibraryApi.unmarkFavoriteItem(itemId) } withContext(Dispatchers.Main) { - init(preferences) + init(false, preferences) } } } @@ -275,3 +288,8 @@ val supportedLatestCollectionTypes = CollectionType.TVSHOWS, CollectionType.LIVETV, ) + +data class LatestData( + val title: String, + val request: GetLatestMediaRequest, +) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/DestinationContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/DestinationContent.kt index 72d3fdd0..099f4816 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/DestinationContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/DestinationContent.kt @@ -148,6 +148,20 @@ fun DestinationContent( BaseItemKind.USER_VIEW -> when (destination.item?.data?.collectionType) { + CollectionType.TVSHOWS -> + CollectionFolderTv( + preferences, + destination, + modifier, + ) + + CollectionType.MOVIES -> + CollectionFolderMovie( + preferences, + destination, + modifier, + ) + CollectionType.LIVETV -> CollectionFolderLiveTv( preferences,