A few more home page fixes (#967)

## Description
A few more home page fixes
* Focus issue between settings & preset buttons
* Fix default genre card size for "Wholphin Default" preset
* Cache genre image urls for 2 hours so that they don't change on every
refresh

### Related issues
Related to #399

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Ray 2026-02-24 12:37:21 -05:00 committed by GitHub
parent c4cd1fbfd3
commit a14fdac852
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 35 additions and 3 deletions

View file

@ -569,6 +569,7 @@ class HomeSettingsService
userDto: UserDto, userDto: UserDto,
libraries: List<Library>, libraries: List<Library>,
limit: Int = prefs.maxItemsPerRow, limit: Int = prefs.maxItemsPerRow,
isRefresh: Boolean,
): HomeRowLoadingState = ): HomeRowLoadingState =
when (row) { when (row) {
is HomeRowConfig.ContinueWatching -> { is HomeRowConfig.ContinueWatching -> {
@ -649,12 +650,14 @@ class HomeSettingsService
val genreImages = val genreImages =
getGenreImageMap( getGenreImageMap(
api = api, api = api,
userId = serverRepository.currentUser.value?.id,
scope = scope, scope = scope,
imageUrlService = imageUrlService, imageUrlService = imageUrlService,
genres = genreIds, genres = genreIds,
parentId = row.parentId, parentId = row.parentId,
includeItemTypes = null, includeItemTypes = null,
cardWidthPx = null, cardWidthPx = null,
useCache = isRefresh,
) )
val library = val library =
libraries libraries

View file

@ -35,6 +35,7 @@ import com.github.damontecres.wholphin.util.GetGenresRequestHandler
import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler
import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler
import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.LoadingState
import com.mayakapps.kache.InMemoryKache
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
@ -55,8 +56,10 @@ import org.jellyfin.sdk.model.api.ItemFields
import org.jellyfin.sdk.model.api.ItemSortBy import org.jellyfin.sdk.model.api.ItemSortBy
import org.jellyfin.sdk.model.api.request.GetGenresRequest import org.jellyfin.sdk.model.api.request.GetGenresRequest
import org.jellyfin.sdk.model.api.request.GetItemsRequest import org.jellyfin.sdk.model.api.request.GetItemsRequest
import timber.log.Timber
import java.util.UUID import java.util.UUID
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.time.Duration.Companion.hours
@HiltViewModel(assistedFactory = GenreViewModel.Factory::class) @HiltViewModel(assistedFactory = GenreViewModel.Factory::class)
class GenreViewModel class GenreViewModel
@ -110,6 +113,7 @@ class GenreViewModel
val genreToUrl = val genreToUrl =
getGenreImageMap( getGenreImageMap(
api = api, api = api,
userId = serverRepository.currentUser.value?.id,
scope = viewModelScope, scope = viewModelScope,
imageUrlService = imageUrlService, imageUrlService = imageUrlService,
genres = genres.map { it.id }, genres = genres.map { it.id },
@ -141,15 +145,35 @@ class GenreViewModel
} }
} }
data class GenreCacheKey(
val userId: UUID?,
val parentId: UUID,
)
private val genreCache by lazy {
InMemoryKache<GenreCacheKey, Map<UUID, String?>>(8) {
expireAfterWriteDuration = 2.hours
}
}
suspend fun getGenreImageMap( suspend fun getGenreImageMap(
api: ApiClient, api: ApiClient,
userId: UUID?,
scope: CoroutineScope, scope: CoroutineScope,
imageUrlService: ImageUrlService, imageUrlService: ImageUrlService,
genres: List<UUID>, genres: List<UUID>,
parentId: UUID, parentId: UUID,
includeItemTypes: List<BaseItemKind>?, includeItemTypes: List<BaseItemKind>?,
cardWidthPx: Int?, cardWidthPx: Int?,
useCache: Boolean = true,
): Map<UUID, String?> { ): Map<UUID, String?> {
val key = GenreCacheKey(userId, parentId)
if (useCache) {
genreCache.getIfAvailable(key)?.let {
Timber.v("Got cached entry")
return it
}
}
val genreToUrl = ConcurrentHashMap<UUID, String?>() val genreToUrl = ConcurrentHashMap<UUID, String?>()
val semaphore = Semaphore(4) val semaphore = Semaphore(4)
genres genres
@ -161,6 +185,7 @@ suspend fun getGenreImageMap(
.execute( .execute(
api, api,
GetItemsRequest( GetItemsRequest(
userId = userId,
parentId = parentId, parentId = parentId,
recursive = true, recursive = true,
limit = 1, limit = 1,
@ -189,6 +214,7 @@ suspend fun getGenreImageMap(
} }
} }
}.awaitAll() }.awaitAll()
genreCache.put(key, genreToUrl)
return genreToUrl return genreToUrl
} }

View file

@ -78,6 +78,8 @@ class HomeViewModel
// Refreshing if a load has already occurred and the rows haven't significantly changed // Refreshing if a load has already occurred and the rows haven't significantly changed
val refresh = val refresh =
state.loadingState == LoadingState.Success && state.settings == settings state.loadingState == LoadingState.Success && state.settings == settings
Timber.v("refresh=$refresh, state.loadingState=${state.loadingState}")
_state.update { it.copy(settings = settings) }
val semaphore = Semaphore(4) val semaphore = Semaphore(4)
@ -102,6 +104,7 @@ class HomeViewModel
userDto = userDto, userDto = userDto,
libraries = libraries, libraries = libraries,
limit = prefs.maxItemsPerRow, limit = prefs.maxItemsPerRow,
isRefresh = refresh,
) )
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.e(ex, "Error on row %s", row) Timber.e(ex, "Error on row %s", row)

View file

@ -19,7 +19,6 @@ import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.preferences.PrefContentScale import com.github.damontecres.wholphin.preferences.PrefContentScale
import com.github.damontecres.wholphin.ui.AspectRatio import com.github.damontecres.wholphin.ui.AspectRatio
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType import com.github.damontecres.wholphin.ui.components.ViewOptionImageType
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import org.jellyfin.sdk.model.api.CollectionType import org.jellyfin.sdk.model.api.CollectionType
@ -81,7 +80,7 @@ data class HomeRowPresets(
contentScale = PrefContentScale.FIT, contentScale = PrefContentScale.FIT,
), ),
liveTv = HomeRowViewOptions.liveTvDefault, liveTv = HomeRowViewOptions.liveTvDefault,
genreSize = Cards.HEIGHT_2X3_DP, genreSize = HomeRowViewOptions.genreDefault.heightDp,
) )
} }

View file

@ -137,7 +137,7 @@ fun HomeSettingsRowList(
position = 2 position = 2
onClickPresets.invoke() onClickPresets.invoke()
}, },
modifier = Modifier.focusRequester(focusRequesters[1]), modifier = Modifier.focusRequester(focusRequesters[2]),
) )
} }
item { item {

View file

@ -133,6 +133,7 @@ class HomeSettingsViewModel
userDto = userDto, userDto = userDto,
libraries = state.libraries, libraries = state.libraries,
limit = limit, limit = limit,
isRefresh = false,
) )
} }
} }