A Jellypod podcast listed its episodes alphabetically. The store pinned SortBy=SortName onto every drill-down, which overrode the order the channel plugin returns — and since Jellypod prefixes played episodes with "[Played]", the name sort also clumped every heard episode at the top. Which order a container's children take is domain knowledge, so it moves to Rust: the caller names the container (GetItemsOptions.parentKind) and default_listing_sort answers with the sort. A channel folder is PremiereDate descending, every other container keeps SortName ascending, and a caller naming no container still gets no SortBy, so the paths that rely on the server's own order keep it. An explicit sort always wins. ChannelFolderItem with is_folder now maps to MediaKind::ChannelFolder instead of collapsing into Folder — while both were Folder there was nothing to key the rule on. The offline leg of the cache/server race applies the same order, so the cached list no longer flashes in name order before the server's arrives. TRACES: UR-007 | DR-257 | UT-229, UT-230, UT-231
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
/**
|
|
* What order a container's children come back in.
|
|
*
|
|
* The store used to pin `sortBy: "SortName"` onto every drill-down, which is
|
|
* where the podcast bug came from: a Jellypod channel folder lists its episodes
|
|
* newest-first, and an alphabetical sort not only lost that order but clumped
|
|
* every "[Played] …" title at the top. The store now says *what the container
|
|
* is* and lets Rust say how it orders — the same division as `SearchScope`.
|
|
*
|
|
* TRACES: UR-007 | DR-257 | UT-231
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
|
|
const getItemsMock = vi.fn();
|
|
|
|
vi.mock("@tauri-apps/api/event", () => ({
|
|
listen: vi.fn(async () => () => {}),
|
|
}));
|
|
|
|
vi.mock("./auth", () => ({
|
|
auth: {
|
|
getRepository: () => ({ getItems: getItemsMock }),
|
|
},
|
|
}));
|
|
|
|
import { library } from "./library";
|
|
|
|
describe("library.loadItems ordering", () => {
|
|
beforeEach(() => {
|
|
getItemsMock.mockReset();
|
|
getItemsMock.mockResolvedValue({ items: [], totalRecordCount: 0 });
|
|
});
|
|
|
|
it("names the container rather than a sort field", async () => {
|
|
await library.loadItems("podcast-1", { parentKind: "channelFolder" });
|
|
|
|
const options = getItemsMock.mock.calls[0][1];
|
|
expect(options.parentKind).toBe("channelFolder");
|
|
// Naming a sort field here would put the ordering rule back in the
|
|
// presentation layer, which is the leak this fix removes.
|
|
expect(options.sortBy).toBeUndefined();
|
|
expect(options.sortOrder).toBeUndefined();
|
|
});
|
|
|
|
it("falls back to a plain folder when the caller names no container", async () => {
|
|
await library.loadItems("library-1");
|
|
|
|
const options = getItemsMock.mock.calls[0][1];
|
|
expect(options.parentKind).toBe("folder");
|
|
expect(options.sortBy).toBeUndefined();
|
|
});
|
|
});
|