v4: a profile's lists of dumps can be narrowed to a category — /@name/dumps and /@name/upvoted showed every dump, with no way to see only those filed under one category, as the index can. The title row of each list now has the index's category menu, and the category picked goes in the URL as ?category=<slug>, which the other list keeps and every other tab drops (a path segment would collide with /@name/dumps/<slug>, a dump's own page). GET /api/users/:key/dumps and /upvoted take ?category= as the followed feed does, an unknown slug answering an empty list; resolveCategoryFilter moves to category-service.ts for both. useUserDumpFeed passes everything the socket changes through the category, so a dump edited out of it leaves and an upvote outside it doesn't come in. CategorySwitcher now reports the category picked to its owner (onSelect) instead of navigating to the index's path itself, and the divider after it in the index's tab row is drawn by CSS, so the profile gets none. useTabParam's setter takes the query string to go to, the current one by default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,13 +10,15 @@
|
||||
* - `GET /api/users?q=` — public. Answers up to 8 users whose name starts
|
||||
* with `q`, for @mention suggestions.
|
||||
* - `GET /api/users/:key` — public. Answers a user's profile.
|
||||
* - `GET /api/users/:key/dumps?page=&limit=` — optional sign-in. Answers a
|
||||
* page of their dumps, the private ones only to themselves.
|
||||
* - `GET /api/users/:key/dumps?page=&limit=&category=` — optional sign-in.
|
||||
* Answers a page of their dumps, the private ones only to themselves,
|
||||
* optionally in one category.
|
||||
* - `GET /api/users/:key/dumps/:slug` — optional sign-in. Answers their dump
|
||||
* whose slug is or was `slug`, with its current slug; a private one 404s
|
||||
* but for its poster.
|
||||
* - `GET /api/users/:key/upvoted?page=&limit=` — optional sign-in. Answers a
|
||||
* page of the dumps they upvoted, private ones only to themselves.
|
||||
* - `GET /api/users/:key/upvoted?page=&limit=&category=` — optional sign-in.
|
||||
* Answers a page of the dumps they upvoted, private ones only to
|
||||
* themselves, optionally in one category.
|
||||
* - `GET /api/users/:key/playlists?page=&limit=` — optional sign-in. Answers a
|
||||
* page of the playlists they made, the private ones only to themselves.
|
||||
* - `GET /api/users/:key/playlists/:slug` — optional sign-in. Answers their
|
||||
@@ -95,6 +97,7 @@ import {
|
||||
getFollowedPlaylistsByUser,
|
||||
getFollowedUsersByUser,
|
||||
} from "../services/follow-service.ts";
|
||||
import { resolveCategoryFilter } from "../services/category-service.ts";
|
||||
|
||||
/**
|
||||
* Mounted by `api/main.ts` behind the error, Open Graph and CORS middleware.
|
||||
@@ -154,14 +157,13 @@ router.get("/:key", (ctx) => {
|
||||
router.get("/:key/dumps", async (ctx) => {
|
||||
const user = resolveUserKey(ctx.params.key);
|
||||
const requestingUserId = await parseOptionalAuth(ctx);
|
||||
const { page, limit } = parsePagination(ctx.request.url.searchParams);
|
||||
const params = ctx.request.url.searchParams;
|
||||
const { page, limit } = parsePagination(params);
|
||||
const categoryId = resolveCategoryFilter(params);
|
||||
const includePrivate = requestingUserId === user.id;
|
||||
const { items, hasMore } = getDumpsByUser(
|
||||
user.id,
|
||||
page,
|
||||
limit,
|
||||
includePrivate,
|
||||
);
|
||||
const { items, hasMore } = categoryId === "missing"
|
||||
? { items: [], hasMore: false }
|
||||
: getDumpsByUser(user.id, page, limit, includePrivate, categoryId);
|
||||
const responseBody: APIResponse<PaginatedData<Dump>> = {
|
||||
success: true,
|
||||
data: { items, hasMore },
|
||||
@@ -182,13 +184,12 @@ router.get("/:key/dumps/:slug", async (ctx) => {
|
||||
router.get("/:key/upvoted", async (ctx) => {
|
||||
const user = resolveUserKey(ctx.params.key);
|
||||
const requestingUserId = await parseOptionalAuth(ctx);
|
||||
const { page, limit } = parsePagination(ctx.request.url.searchParams);
|
||||
const { items, hasMore } = getVotedDumpsByUser(
|
||||
user.id,
|
||||
page,
|
||||
limit,
|
||||
requestingUserId,
|
||||
);
|
||||
const params = ctx.request.url.searchParams;
|
||||
const { page, limit } = parsePagination(params);
|
||||
const categoryId = resolveCategoryFilter(params);
|
||||
const { items, hasMore } = categoryId === "missing"
|
||||
? { items: [], hasMore: false }
|
||||
: getVotedDumpsByUser(user.id, page, limit, requestingUserId, categoryId);
|
||||
const responseBody: APIResponse<PaginatedData<Dump>> = {
|
||||
success: true,
|
||||
data: { items, hasMore },
|
||||
|
||||
Reference in New Issue
Block a user