api+admin: let the curator take a photo off the landing, not just move it
Uploads still land in the community strip with consent on, by the uploader's own tick — that default stays. What was missing is the curator's removal: the slot select only offered the other three live placements, so "off the strip" meant publishing the photo somewhere else or deleting the uploader's row. `off` is a fifth slot value. The reel draws slot === 'strip' and each live slot draws its own, so an `off` photo renders nowhere on the landing, while its owner still has it in MY PHOTOS.
This commit is contained in:
@@ -83,8 +83,11 @@ CREATE INDEX IF NOT EXISTS idx_events_at ON events(at);
|
||||
|
||||
// Where a curated photo is allowed to appear on the landing page: the community
|
||||
// strip, the live tester's preview, the creator lab's preview, or the QR card.
|
||||
// One is picked at random out of its slot on every page load.
|
||||
export const PHOTO_SLOTS = ['strip', 'tester', 'creator', 'qr'] as const;
|
||||
// One is picked at random out of its slot on every page load. `off` is the
|
||||
// curator's "take it off the landing, keep the row" — the reel and the three
|
||||
// live slots all draw by exact slot, so an `off` photo shows up nowhere, while
|
||||
// its owner still has it in MY PHOTOS.
|
||||
export const PHOTO_SLOTS = ['strip', 'tester', 'creator', 'qr', 'off'] as const;
|
||||
export type PhotoSlot = (typeof PHOTO_SLOTS)[number];
|
||||
export const isPhotoSlot = (v: unknown): v is PhotoSlot =>
|
||||
typeof v === 'string' && (PHOTO_SLOTS as readonly string[]).includes(v);
|
||||
|
||||
@@ -304,6 +304,16 @@ try {
|
||||
'the slot is public, the owner is not',
|
||||
((await actor().req('/photos')).body?.photos ?? []).find((r) => r.id === id)?.slot === 'qr',
|
||||
);
|
||||
// `off` is the curator's removal: the reel draws slot === 'strip' and the
|
||||
// live slots draw their own, so the row shows up nowhere — but the uploader
|
||||
// keeps it in their folder.
|
||||
const off = await patch(`/admin/photos/${id}`, { slot: 'off' });
|
||||
check('an admin takes a photo off the landing', off.status === 200 && off.body?.slot === 'off', JSON.stringify(off.body));
|
||||
check(
|
||||
'an off photo is on no landing slot',
|
||||
((await actor().req('/photos')).body?.photos ?? []).find((r) => r.id === id)?.slot === 'off',
|
||||
);
|
||||
check('its owner still has it in the folder', ((await user.req('/photos/mine')).body?.photos ?? []).some((p) => p.id === id));
|
||||
check('an unknown slot is refused', (await patch(`/admin/photos/${id}`, { slot: 'nope' })).status === 400);
|
||||
check('placing an unknown photo is a 404', (await patch('/admin/photos/999999', { slot: 'qr' })).status === 404);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user