forked from akai/readest
The Series sort comparator compared only seriesIndex, ignoring the series name. When used as the within-group order for "group by Author → sort by Series", this ranked every book #1 across all series as a block, then every #2, etc., scattering each series instead of keeping it consecutive. Compare series name first, then index, so all books of one series appear together in series order before the next series begins. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1184,6 +1184,47 @@ describe('secondary sort - createWithinGroupSorter (drilled-in group)', () => {
|
||||
expect(sorted.map((b) => b.metadata?.seriesIndex)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('keeps each series together (in series order) when an author has multiple series', () => {
|
||||
const books = [
|
||||
createMockBook({
|
||||
hash: 'b1',
|
||||
title: 'B One',
|
||||
metadata: { series: 'Series B', seriesIndex: 1 },
|
||||
}),
|
||||
createMockBook({
|
||||
hash: 'a2',
|
||||
title: 'A Two',
|
||||
metadata: { series: 'Series A', seriesIndex: 2 },
|
||||
}),
|
||||
createMockBook({
|
||||
hash: 'a1',
|
||||
title: 'A One',
|
||||
metadata: { series: 'Series A', seriesIndex: 1 },
|
||||
}),
|
||||
createMockBook({
|
||||
hash: 'b2',
|
||||
title: 'B Two',
|
||||
metadata: { series: 'Series B', seriesIndex: 2 },
|
||||
}),
|
||||
];
|
||||
const sorter = createWithinGroupSorter(
|
||||
LibraryGroupByType.Author,
|
||||
LibrarySortByType.Title,
|
||||
'en',
|
||||
true,
|
||||
LibrarySortByType.Series,
|
||||
);
|
||||
const sorted = [...books].sort(sorter);
|
||||
// All of Series A consecutively in index order, then all of Series B —
|
||||
// not interleaved by index across series (#1s together, #2s together).
|
||||
expect(sorted.map((b) => `${b.metadata?.series} #${b.metadata?.seriesIndex}`)).toEqual([
|
||||
'Series A #1',
|
||||
'Series A #2',
|
||||
'Series B #1',
|
||||
'Series B #2',
|
||||
]);
|
||||
});
|
||||
|
||||
it('falls back to primary when secondary ties (same series index)', () => {
|
||||
const books = [
|
||||
createMockBook({
|
||||
|
||||
@@ -196,8 +196,16 @@ const compareBookByKey = (a: Book, b: Book, sortBy: string, uiLanguage: string):
|
||||
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||||
case LibrarySortByType.Format:
|
||||
return a.format.localeCompare(b.format, uiLanguage || navigator.language);
|
||||
case LibrarySortByType.Series:
|
||||
case LibrarySortByType.Series: {
|
||||
// Group by series name first so books of the same series stay consecutive,
|
||||
// then order within a series by index. Comparing index alone would interleave
|
||||
// series (all #1s, then all #2s) when this key is used as a secondary sort.
|
||||
const aSeries = a.metadata?.series || '';
|
||||
const bSeries = b.metadata?.series || '';
|
||||
const bySeries = aSeries.localeCompare(bSeries, uiLanguage || navigator.language);
|
||||
if (bySeries !== 0) return bySeries;
|
||||
return (a.metadata?.seriesIndex || 0) - (b.metadata?.seriesIndex || 0);
|
||||
}
|
||||
case LibrarySortByType.Published: {
|
||||
const aPublished = a.metadata?.published || '0001-01-01';
|
||||
const bPublished = b.metadata?.published || '0001-01-01';
|
||||
@@ -227,8 +235,8 @@ const compareBookByKey = (a: Book, b: Book, sortBy: string, uiLanguage: string):
|
||||
|
||||
/**
|
||||
* @param secondarySortBy - Optional tiebreaker key applied when the primary
|
||||
* comparison returns 0. Pass `'none'` (or omit) to disable. Series-index ties
|
||||
* on a Series secondary fall through to the underlying primary tie.
|
||||
* comparison returns 0. Pass `'none'` (or omit) to disable. A Series secondary
|
||||
* orders by series name then index; ties on both fall through to the primary tie.
|
||||
*/
|
||||
export const createBookSorter =
|
||||
(sortBy: string, uiLanguage: string, secondarySortBy: LibrarySecondarySortByType = 'none') =>
|
||||
|
||||
Reference in New Issue
Block a user