Treat a custom author name alone as a display name, not an upload on someone's behalf (#103)
Some checks failed
Deploy Supabase Migrations to Production / migrate (push) Has been cancelled

Creators who were given a custom original_author (e.g. a team name) without a
permission source were asked for "Where the creator gave permission" in Listing
health, and their details sheet refused to save. Only hacks with permission_from
are on someone's behalf now.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jared Schoeny
2026-10-03 04:54:35 -06:00
committed by GitHub
parent 25d9bf910c
commit 904206013a
3 changed files with 5 additions and 5 deletions

View File

@@ -97,7 +97,8 @@ export async function updateHack(args: {
if (args.box_art !== undefined) updatePayload.box_art = args.box_art;
if (args.social_links !== undefined) updatePayload.social_links = args.social_links;
if (args.original_author !== undefined || args.permission_from !== undefined) {
if (!hack.original_author) {
// A custom display name alone (original_author without permission_from) isn't an upload on someone's behalf.
if (!hack.permission_from) {
return { ok: false, error: "This hack was not uploaded on someone else's behalf" } as const;
}
if (args.original_author?.trim() === "" || args.permission_from?.trim() === "") {

View File

@@ -318,7 +318,7 @@ export async function createDraft(formData: FormData) {
export async function getDraftChecklist(slug: string) {
const supabase = await createClient();
const [{ data: hack }, { count: covers }, { count: tags }] = await Promise.all([
supabase.from("hacks").select("base_rom,summary,description,completion_status,language,original_author,permission_from,ai_disclosed_at,current_patch").eq("slug", slug).maybeSingle(),
supabase.from("hacks").select("base_rom,summary,description,completion_status,language,ai_disclosed_at,current_patch").eq("slug", slug).maybeSingle(),
supabase.from("hack_covers").select("id", { count: "exact", head: true }).eq("hack_slug", slug),
supabase.from("hack_tags").select("tag_id", { count: "exact", head: true }).eq("hack_slug", slug),
]);
@@ -334,7 +334,6 @@ export async function getDraftChecklist(slug: string) {
{ key: "shots", label: "At least one screenshot", done: (covers ?? 0) > 0 },
{ key: "tags", label: "At least one tag", done: (tags ?? 0) > 0 },
{ key: "ai", label: "AI label filled in", done: !!hack.ai_disclosed_at },
...(hack.original_author ? [{ key: "permission", label: "Where the creator gave permission", done: !!hack.permission_from }] : []),
];
const recommended = [
{ key: "tags3", label: "Add at least 3 tags so players can find it", done: (tags ?? 0) >= 3 },

View File

@@ -17,8 +17,8 @@ export interface DraftDetailsValues {
completion_status: Completion | null;
box_art: string | null;
social_links: Social | null;
/** Set on hacks uploaded on someone else's behalf; shows the creator fields. */
original_author: string | null;
/** Set on hacks uploaded on someone else's behalf; shows the creator fields. An original_author alone is just a custom display name. */
permission_from: string | null;
/** Left out once the hack is listed, since admins only read it during review. */
verification_contact_info?: string | null;
@@ -99,7 +99,7 @@ export default function DraftDetails({ values, baseLocked, children }: DraftDeta
<DetailsSheet
initial={applied}
baseLocked={baseLocked}
behalf={values.original_author !== null}
behalf={values.permission_from !== null}
askContact={values.verification_contact_info !== undefined}
onApplied={(form) => {
setApplied(form);