Fix alphabet highlight for names starting with "The " (#1788)

## Description
The alphabet scroll bar highlighted the letter of an item's *display*
name while the grid sorted by the server's article-stripped `SortName`,
so "The Beatles" sat under **B** but highlighted **T**.

- **Artists:** the Artists tab never requested `SortName`, so the
highlight silently fell back to `Name`. It now asks for `SlimItemFields`
(which already includes `SORT_NAME`), matching the sibling `/Items`
branch. The count-only query used for letter positions passes `fields =
null`, since a `limit = 0` request has no use for them.
- **Genres and studios:** these grids bucketed on the display name
unconditionally. The sort-name fallback is extracted as
`BaseItemDto.alphabetSortName` (`sortName ?: name ?: ""`) and carried
onto the `Genre` and `Studio` grid items, so all three grids now derive
the letter from one value.

### Related issues
https://github.com/damontecres/Wholphin/issues/1787

### Testing
Two unit tests added:
- `CollectionFolderViewModelTest` pins that the artist pager actually
requests `SORT_NAME` — the field whose absence caused the bug.
- `AlphabetSortNameTest` covers both branches of the `alphabetSortName`
fallback.

Manually: select a music library → Artists tab → put the cursor on an
artist starting with "The " and confirm the highlighted letter matches
the grid position. Same check on the genre and studio grids.

## AI or LLM usage
I used Cursor for debugging and implementing the fix, and Claude Code
for a follow-up cleanup pass over the diff.
This commit is contained in:
Stefan Bullones 2026-08-11 03:52:43 +02:00 • committed by GitHub
parent 454c0212b6
commit a0966dd05a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 73 additions and 5 deletions

View file

@ -399,6 +399,7 @@ class CollectionFolderViewModel
GetArtistsRequest(
parentId = item?.id,
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB),
fields = SlimItemFields,
),
)
}
@ -477,6 +478,7 @@ class CollectionFolderViewModel
GetArtistsHandler.countMatching(
createGetArtistsRequest(filter).copy(
enableImageTypes = null,
fields = null,
nameLessThan = letter.toString(),
limit = 0,
enableTotalRecordCount = true,

View file

@ -20,6 +20,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.alphabetSortName
import com.github.damontecres.wholphin.data.model.createGenreDestination
import com.github.damontecres.wholphin.services.ImageUrlService
import com.github.damontecres.wholphin.services.NavigationManager
@ -104,7 +105,12 @@ class GenreViewModel
.execute(api, request)
.content.items
.map {
Genre(it.id, it.name ?: "", null)
Genre(
id = it.id,
name = it.name ?: "",
imageUrl = null,
sortName = it.alphabetSortName,
)
}
_state.update {
it.copy(
@ -243,10 +249,10 @@ data class Genre(
val id: UUID,
val name: String,
val imageUrl: String?,
override val sortName: String = name,
) : CardGridItem {
override val gridId: String get() = id.toString()
override val playable: Boolean = false
override val sortName: String get() = name
}
/**

View file

@ -20,6 +20,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.alphabetSortName
import com.github.damontecres.wholphin.data.model.createStudioDestination
import com.github.damontecres.wholphin.services.ImageUrlService
import com.github.damontecres.wholphin.services.NavigationManager
@ -97,7 +98,12 @@ class StudioViewModel
imageType = ImageType.THUMB,
fillWidth = cardWidthPx,
)
Studio(it.id, it.name ?: "", imageUrl)
Studio(
id = it.id,
name = it.name ?: "",
imageUrl = imageUrl,
sortName = it.alphabetSortName,
)
}
_state.update {
it.copy(
@ -133,10 +139,10 @@ data class Studio(
val id: UUID,
val name: String,
val imageUrl: String?,
override val sortName: String = name,
) : CardGridItem {
override val gridId: String get() = id.toString()
override val playable: Boolean = false
override val sortName: String get() = name
}
data class StudioGridState(