Better collection pages including more view options (#1137)

## Description
This is a large update to the collection page!

- Show the collection's details including overview & backdrop
- Separate collection by type (Movies, TV, etc) into rows
- View options are now shared across all collections
- Filter & sort are still saved per collection

By default, the collection will be separated by type, but there is a
view option to restore the combined grid.

### Related issues
Closes #527 
Closes #1088
Fixes
https://github.com/damontecres/Wholphin/issues/877#issuecomment-3889013124
Related to #737

### Testing
Emulator mostly

## Screenshots
![collection_page
Large](https://github.com/user-attachments/assets/2b9a8740-5610-491a-b139-018dabd5f2f1)

### Separated types into rows
![collection_rows
Large](https://github.com/user-attachments/assets/34556f64-6089-4d65-b243-6df01bae5423)

### Combined type into grid
![collection_grid
Large](https://github.com/user-attachments/assets/feea14ef-2b7a-4c09-bba7-d146ec92eb83)

## AI or LLM usage
None
This commit is contained in:
Ray 2026-03-28 10:31:03 -04:00 committed by GitHub
parent b8a9cb5027
commit c35b5cdd0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1853 additions and 59 deletions

View file

@ -0,0 +1,82 @@
package com.github.damontecres.wholphin.services
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton
/**
* Gets/saves a serializable object by name
*/
@Singleton
class KeyValueService
@Inject
constructor(
val dataStore: DataStore<Preferences>,
) {
val json =
Json {
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
}
inline fun <reified T> get(key: String): Flow<T?> =
dataStore.data.map { preferences ->
preferences[stringPreferencesKey(key)]?.let {
json.decodeFromString<T>(serializer<T>(), it)
}
}
inline fun <reified T> get(
key: String,
defaultValue: T,
): Flow<T> =
dataStore.data.map { preferences ->
preferences[stringPreferencesKey(key)]?.let {
json.decodeFromString<T>(serializer<T>(), it)
} ?: defaultValue
}
inline fun <reified T> get(
userId: UUID,
key: String,
defaultValue: T,
): Flow<T> =
dataStore.data.map { preferences ->
preferences[stringPreferencesKey("${userId}_$key")]?.let {
json.decodeFromString<T>(serializer<T>(), it)
} ?: defaultValue
}
suspend inline fun <reified T> save(
key: String,
value: T,
) {
dataStore.updateData { preferences ->
val valueStr = json.encodeToString(value)
preferences.toMutablePreferences().apply {
set(stringPreferencesKey(key), valueStr)
}
}
}
suspend inline fun <reified T> save(
userId: UUID,
key: String,
value: T,
) {
dataStore.updateData { preferences ->
val valueStr = json.encodeToString(value)
preferences.toMutablePreferences().apply {
set(stringPreferencesKey("${userId}_$key"), valueStr)
}
}
}
}

View file

@ -5,6 +5,9 @@ import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStoreFile
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.room.Room
import com.github.damontecres.wholphin.data.AppDatabase
import com.github.damontecres.wholphin.data.ItemPlaybackDao
@ -85,4 +88,13 @@ object DatabaseModule {
produceNewData = { AppPreferences.getDefaultInstance() },
),
)
@Provides
@Singleton
fun keyValueDataStore(
@ApplicationContext context: Context,
): DataStore<Preferences> =
PreferenceDataStoreFactory.create(
produceFile = { context.preferencesDataStoreFile("key_value") },
)
}