Fix improper caching of tags fetching

This commit is contained in:
Jared Schoeny
2026-03-29 00:50:30 -10:00
parent 82886ec2bb
commit 99f2e05627
13 changed files with 159 additions and 67 deletions

View File

@@ -11,10 +11,12 @@ import { updateHack, saveHackCovers, presignCoverUpload } from "@/app/hack/actio
import SortableCovers from "@/components/Hack/SortableCovers";
import Select from "@/components/Primitives/Select";
import type { Database } from "@/types/db";
import type { CatalogTagRow } from "@/types/catalogTag";
import { FiExternalLink } from "react-icons/fi";
interface HackEditFormProps {
slug: string;
catalogTags: CatalogTagRow[];
initial: {
title: string;
summary: string;
@@ -36,7 +38,7 @@ interface HackEditFormProps {
};
}
export default function HackEditForm({ slug, initial }: HackEditFormProps) {
export default function HackEditForm({ slug, initial, catalogTags }: HackEditFormProps) {
const supabase = createClient();
const MAX_COVERS = 10;
const [title, setTitle] = React.useState(initial.title);
@@ -363,7 +365,7 @@ export default function HackEditForm({ slug, initial }: HackEditFormProps) {
)}
</div>
<div className={`rounded-md ring-1 ring-inset ${tagsChanged ? 'ring-[var(--ring)] bg-[var(--surface-2)]' : 'ring-transparent'} p-1`}>
<TagSelector value={tags} onChange={setTags} />
<TagSelector value={tags} onChange={setTags} catalogTags={catalogTags} />
</div>
</div>
</div>

View File

@@ -1,6 +1,7 @@
"use client";
import React from "react";
import type { CatalogTagRow } from "@/types/catalogTag";
import HackSubmitForm from "@/components/Hack/HackSubmitForm";
import HackEditForm from "@/components/Hack/HackEditForm";
@@ -12,12 +13,14 @@ interface HackFormCreateProps {
isArchive?: boolean;
permissionFrom?: string;
customCreator?: string;
catalogTags: CatalogTagRow[];
}
interface HackFormEditProps {
mode: "edit";
slug: string;
initial: React.ComponentProps<typeof HackEditForm>["initial"];
catalogTags: CatalogTagRow[];
}
export type HackFormProps = HackFormCreateProps | HackFormEditProps;
@@ -29,9 +32,10 @@ export default function HackForm(props: HackFormProps) {
isArchive={props.isArchive}
permissionFrom={props.permissionFrom}
customCreator={props.customCreator}
catalogTags={props.catalogTags}
/>;
}
return <HackEditForm slug={props.slug} initial={props.initial} />;
return <HackEditForm slug={props.slug} initial={props.initial} catalogTags={props.catalogTags} />;
}

View File

@@ -25,6 +25,7 @@ import BPS from "rom-patcher-js/rom-patcher-js/modules/RomPatcher.format.bps.js"
import { sha1Hex } from "@/utils/hash";
import { platformAccept, setDraftCovers, getDraftCovers, deleteDraftCovers } from "@/utils/idb";
import { slugify, sortOrderedTags } from "@/utils/format";
import type { CatalogTagRow } from "@/types/catalogTag";
function SortableCoverItem({ id, index, url, filename, onRemove }: { id: string; index: number; url: string; filename: string; onRemove: () => void }) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
@@ -66,6 +67,7 @@ interface HackSubmitFormProps {
isArchive?: boolean;
permissionFrom?: string;
customCreator?: string;
catalogTags: CatalogTagRow[];
}
export default function HackSubmitForm({
@@ -73,6 +75,7 @@ export default function HackSubmitForm({
isArchive = false,
permissionFrom = undefined,
customCreator = undefined,
catalogTags,
}: HackSubmitFormProps) {
const MAX_COVERS = 10;
const { profile, user } = useAuthContext();
@@ -899,7 +902,7 @@ export default function HackSubmitForm({
<div className="grid gap-2">
<label className="text-sm text-foreground/80">Tags <span className="text-red-500">*</span></label>
<TagSelector value={tags} onChange={setTags} />
<TagSelector value={tags} onChange={setTags} catalogTags={catalogTags} />
</div>
<div className="grid gap-1">

View File

@@ -1,10 +1,19 @@
"use client";
import React from "react";
import type { CatalogTagRow } from "@/types/catalogTag";
import HackForm from "@/components/Hack/HackForm";
import ArchiveModeSelector from "@/components/Submit/ArchiveModeSelector";
export default function SubmitPageClient({ canCreateArchive, dummy }: { canCreateArchive: boolean; dummy: boolean }) {
export default function SubmitPageClient({
canCreateArchive,
dummy,
catalogTags,
}: {
canCreateArchive: boolean;
dummy: boolean;
catalogTags: CatalogTagRow[];
}) {
const [showModeSelector, setShowModeSelector] = React.useState(canCreateArchive);
const [customCreator, setCustomCreator] = React.useState<string | undefined>(undefined);
const [permissionFrom, setPermissionFrom] = React.useState<string | undefined>(undefined);
@@ -27,5 +36,6 @@ export default function SubmitPageClient({ canCreateArchive, dummy }: { canCreat
isArchive={isArchive}
permissionFrom={permissionFrom}
customCreator={customCreator}
catalogTags={catalogTags}
/>;
}

View File

@@ -1,6 +1,7 @@
"use client";
import React from "react";
import type { CatalogTagRow } from "@/types/catalogTag";
import { createClient } from "@/utils/supabase/client";
import { MdTune } from "react-icons/md";
import { CATEGORY_ICONS, getCategoryIcon } from "@/components/Icons/tagCategories";
@@ -19,16 +20,13 @@ import {
import { SortableContext, arrayMove, useSortable, rectSortingStrategy } from "@dnd-kit/sortable";
import { RxDragHandleDots2 } from "react-icons/rx";
type TagRow = {
id: number;
name: string;
category: string | null;
popularity: number;
};
type TagRow = CatalogTagRow;
export interface TagSelectorProps {
value: string[];
onChange: (next: string[]) => void;
/** When set, skips client Supabase fetch (use server-cached catalog). */
catalogTags?: CatalogTagRow[];
}
type CategoryIconType = React.ComponentType<React.SVGProps<SVGSVGElement>> | null;
@@ -114,11 +112,11 @@ function SortableSelectedTag({
);
}
export default function TagSelector({ value, onChange }: TagSelectorProps) {
export default function TagSelector({ value, onChange, catalogTags }: TagSelectorProps) {
const supabase = createClient();
const [query, setQuery] = React.useState("");
const [allTags, setAllTags] = React.useState<TagRow[]>([]);
const [loading, setLoading] = React.useState(false);
const [allTags, setAllTags] = React.useState<TagRow[]>(() => catalogTags ?? []);
const [loading, setLoading] = React.useState(() => catalogTags === undefined);
const [activeCategory, _setActiveCategory] = React.useState<string | "advanced" | null>(null);
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
const categoryRefs = React.useRef<Record<string, HTMLDivElement | null>>({});
@@ -143,6 +141,8 @@ export default function TagSelector({ value, onChange }: TagSelectorProps) {
}, []);
React.useEffect(() => {
if (catalogTags !== undefined) return;
let cancelled = false;
(async () => {
try {
setLoading(true);
@@ -156,12 +156,15 @@ export default function TagSelector({ value, onChange }: TagSelectorProps) {
popularity: t.usage?.[0]?.count || 0,
}));
rows.sort((a, b) => (b.popularity - a.popularity) || a.name.localeCompare(b.name));
setAllTags(rows);
if (!cancelled) setAllTags(rows);
} finally {
setLoading(false);
if (!cancelled) setLoading(false);
}
})();
}, [supabase]);
return () => {
cancelled = true;
};
}, [catalogTags, supabase]);
const grouped = React.useMemo(() => {
const map = new Map<string, TagRow[]>();