mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Customize home page (#803)
## Description This PR adds the ability to customize the home page in-app ### Features - Add, remove, & reorder rows - Persist the configuration locally - Save the configuration to the server, allowing to pull down on other devices - Adjust view options for rows such as card height, image type, preferring series images, or aspect ratio (similar to libraries) - Pull down the web client's home rows (no plugins are supported yet!) - Preview of the home page which is usable & updated as changes are made ### Row options These are row types that can be added in-app via the UI: - Continue watching - Next up - Combined continue waiting & next up - Recently added in a library - Recently released in a library - Genres in a library - Suggestions for a library (movie & TV show libraries only) - Favorite movies, tv shows, episodes, etc Additionally, there are more row types that don't have a UI to add them (yet): - Simple query to get items from a parent ID such as a collection or playlist - Complex query to get arbitrary items via the `/Items` API endpoint ### Dev notes Settings are loaded in order: 1. Locally saved 2. Remote saved 3. Fallback to default similar to Wholphin's original home rows The remote saved settings are stored via the display preferences API. I know some server admins would prefer to push a default setup to their clients. This PR does not have that ability, but it does define a straightforward API for defining the settings. Something like the potential server plugin work started in #625 could be slimmed down to expose a URL to be added in the load order. I'm also investigating integration with popular home page plugins to allow for further customization, but will take more time. ### Related issues Closes #399 Closes #361 Closes #282 Related to #340
This commit is contained in:
parent
b7679b3aa1
commit
fcba1c7444
35 changed files with 4297 additions and 321 deletions
|
|
@ -0,0 +1,150 @@
|
|||
package com.github.damontecres.wholphin.test
|
||||
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||
import com.github.damontecres.wholphin.preferences.PrefContentScale
|
||||
import com.github.damontecres.wholphin.services.HomeSettingsService
|
||||
import com.github.damontecres.wholphin.ui.AspectRatio
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import io.mockk.mockk
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jellyfin.sdk.model.UUID
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class TestHomeRowSamples {
|
||||
companion object {
|
||||
val SAMPLES =
|
||||
listOf(
|
||||
HomeRowConfig.RecentlyAdded(
|
||||
parentId = UUID.randomUUID(),
|
||||
viewOptions =
|
||||
HomeRowViewOptions(
|
||||
heightDp = 100,
|
||||
spacing = 8,
|
||||
contentScale = PrefContentScale.CROP,
|
||||
aspectRatio = AspectRatio.FOUR_THREE,
|
||||
imageType = ViewOptionImageType.THUMB,
|
||||
showTitles = false,
|
||||
useSeries = false,
|
||||
),
|
||||
),
|
||||
HomeRowConfig.RecentlyReleased(
|
||||
parentId = UUID.randomUUID(),
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.Genres(
|
||||
parentId = UUID.randomUUID(),
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.ContinueWatching(
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.NextUp(
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.ContinueWatchingCombined(
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.ByParent(
|
||||
parentId = UUID.randomUUID(),
|
||||
recursive = true,
|
||||
sort = SortAndDirection(ItemSortBy.CRITIC_RATING, SortOrder.ASCENDING),
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.GetItems(
|
||||
name = "Episodes by date created",
|
||||
getItems =
|
||||
GetItemsRequest(
|
||||
parentId = UUID.randomUUID(),
|
||||
recursive = true,
|
||||
isFavorite = true,
|
||||
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
||||
sortBy = listOf(ItemSortBy.DATE_CREATED),
|
||||
sortOrder = listOf(SortOrder.DESCENDING),
|
||||
),
|
||||
viewOptions = HomeRowViewOptions(),
|
||||
),
|
||||
HomeRowConfig.Favorite(kind = BaseItemKind.SERIES),
|
||||
HomeRowConfig.Recordings(),
|
||||
HomeRowConfig.TvPrograms(),
|
||||
HomeRowConfig.Suggestions(parentId = UUID.randomUUID()),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Check all types have a sample`() {
|
||||
// This ensures there is a sample for each possible HomeRowConfig type
|
||||
val foundTypes = mutableSetOf<KClass<out HomeRowConfig>>()
|
||||
SAMPLES.forEach {
|
||||
when (it) {
|
||||
is HomeRowConfig.ContinueWatching -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.ContinueWatchingCombined -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.Genres -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.NextUp -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.RecentlyAdded -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.RecentlyReleased -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.ByParent -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.GetItems -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.Favorite -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.Recordings -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.TvPrograms -> foundTypes.add(it::class)
|
||||
is HomeRowConfig.Suggestions -> foundTypes.add(it::class)
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(HomeRowConfig::class.sealedSubclasses.size, foundTypes.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Print sample JSON`() {
|
||||
// This just prints out the JSON of the samples so developers can review
|
||||
val json =
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
prettyPrint = true
|
||||
}
|
||||
val string = json.encodeToString(SAMPLES)
|
||||
println(string)
|
||||
json.decodeFromString<List<HomeRowConfig>>(string)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Parse list of rows with an unknown type`() {
|
||||
val service =
|
||||
HomeSettingsService(
|
||||
context = mockk(),
|
||||
api = mockk(),
|
||||
userPreferencesService = mockk(),
|
||||
navDrawerItemRepository = mockk(),
|
||||
latestNextUpService = mockk(),
|
||||
imageUrlService = mockk(),
|
||||
suggestionService = mockk(),
|
||||
)
|
||||
|
||||
val str = """{
|
||||
"type": "HomePageSettings",
|
||||
"version": 1,
|
||||
"rows": [
|
||||
{
|
||||
"type": "RecentlyAdded",
|
||||
"parentId": "1dd1c2fd-2e1b-48e4-ba94-17a2350fe9cf"
|
||||
},
|
||||
{
|
||||
"type": "Does not exist",
|
||||
"viewOptions": {}
|
||||
}
|
||||
]
|
||||
}"""
|
||||
|
||||
val jsonElement = service.jsonParser.parseToJsonElement(str)
|
||||
val settings = service.decode(jsonElement)
|
||||
Assert.assertEquals(1, settings.rows.size)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue