feat(glossary) : allow shared shortcuts and show item images
This commit is contained in:
@@ -43,14 +43,14 @@ export function GlossaryManager({
|
|||||||
const [item, setItem] = useState<CatalogItem | null>(null);
|
const [item, setItem] = useState<CatalogItem | null>(null);
|
||||||
const [alias, setAlias] = useState("");
|
const [alias, setAlias] = useState("");
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const itemNames = useMemo(() => new Map(
|
const itemsByKey = useMemo(() => new Map(
|
||||||
Object.entries(catalogs).flatMap(([catalogKind, items]) =>
|
Object.entries(catalogs).flatMap(([catalogKind, items]) =>
|
||||||
items.map((catalogItem) => [`${catalogKind}:${catalogItem.key}`, catalogItem.name] as const),
|
items.map((catalogItem) => [`${catalogKind}:${catalogItem.key}`, catalogItem] as const),
|
||||||
),
|
),
|
||||||
), [catalogs]);
|
), [catalogs]);
|
||||||
const search = searchItems(aliases, query,
|
const search = searchItems(aliases, query,
|
||||||
(entry) => `${entry.alias} ${itemNames.get(`${entry.kind}:${entry.catalogKey}`) ?? entry.catalogKey}`,
|
(entry) => `${entry.alias} ${itemsByKey.get(`${entry.kind}:${entry.catalogKey}`)?.name ?? entry.catalogKey}`,
|
||||||
(entry) => [entry.alias, itemNames.get(`${entry.kind}:${entry.catalogKey}`) ?? ""]);
|
(entry) => [entry.alias, itemsByKey.get(`${entry.kind}:${entry.catalogKey}`)?.name ?? ""]);
|
||||||
const visibleAliases = search.items;
|
const visibleAliases = search.items;
|
||||||
|
|
||||||
const submit = () => startTransition(async () => {
|
const submit = () => startTransition(async () => {
|
||||||
@@ -147,9 +147,18 @@ export function GlossaryManager({
|
|||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
{visibleAliases.map((entry) => {
|
{visibleAliases.map((entry) => {
|
||||||
const name = itemNames.get(`${entry.kind}:${entry.catalogKey}`);
|
const catalogItem = itemsByKey.get(`${entry.kind}:${entry.catalogKey}`);
|
||||||
|
const name = catalogItem?.name;
|
||||||
return (
|
return (
|
||||||
<div key={entry.id} className="flex items-center gap-3 rounded-lg border p-3">
|
<div key={entry.id} className="flex items-center gap-3 rounded-lg border p-3">
|
||||||
|
{catalogItem ? (
|
||||||
|
<span className={cn(
|
||||||
|
"relative size-10 shrink-0 overflow-hidden rounded-md bg-muted",
|
||||||
|
rarityGradientClass(catalogItem.rarity),
|
||||||
|
)}>
|
||||||
|
<Image src={catalogItem.imageUrl} alt="" fill sizes="40px" className="object-contain" />
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<p className="font-medium">{entry.alias}</p>
|
<p className="font-medium">{entry.alias}</p>
|
||||||
<p className="truncate text-sm text-muted-foreground">{name ?? entry.catalogKey}</p>
|
<p className="truncate text-sm text-muted-foreground">{name ?? entry.catalogKey}</p>
|
||||||
|
|||||||
+2
-1
@@ -256,8 +256,9 @@ export const glossaryAliases = guidesSchema.table(
|
|||||||
...timestamps,
|
...timestamps,
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
uniqueIndex("glossary_alias_kind_normalized_unique").on(
|
uniqueIndex("glossary_alias_item_normalized_unique").on(
|
||||||
table.kind,
|
table.kind,
|
||||||
|
table.catalogKey,
|
||||||
table.normalizedAlias,
|
table.normalizedAlias,
|
||||||
),
|
),
|
||||||
index("glossary_alias_item_idx").on(table.kind, table.catalogKey),
|
index("glossary_alias_item_idx").on(table.kind, table.catalogKey),
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
DROP INDEX "guides"."glossary_alias_kind_normalized_unique";--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX "glossary_alias_item_normalized_unique" ON "guides"."glossary_alias" USING btree ("kind","catalog_key","normalized_alias");
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -309,6 +309,13 @@
|
|||||||
"when": 1789199401241,
|
"when": 1789199401241,
|
||||||
"tag": "0043_eager_killmonger",
|
"tag": "0043_eager_killmonger",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 44,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1789283972474,
|
||||||
|
"tag": "0044_plain_mystique",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,36 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { getTableConfig } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
|
import { glossaryAliases } from "../db/schema";
|
||||||
import { aliasesByItem, normalizeGlossaryAlias } from "./glossary";
|
import { aliasesByItem, normalizeGlossaryAlias } from "./glossary";
|
||||||
|
import { searchItems } from "./search";
|
||||||
|
|
||||||
describe("glossary aliases", () => {
|
describe("glossary aliases", () => {
|
||||||
|
it("limits duplicate shortcuts to the same catalog item", () => {
|
||||||
|
const uniqueIndexes = getTableConfig(glossaryAliases).indexes.filter((index) => index.config.unique);
|
||||||
|
expect(uniqueIndexes).toHaveLength(1);
|
||||||
|
expect(uniqueIndexes[0].config.columns.map((column) => "name" in column ? column.name : undefined)).toEqual([
|
||||||
|
"kind",
|
||||||
|
"catalog_key",
|
||||||
|
"normalized_alias",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("finds multiple weapons sharing the same shortcut", () => {
|
||||||
|
const weapons = [
|
||||||
|
{ key: "ballad", name: "Ballad of the Fjords" },
|
||||||
|
{ key: "favonius", name: "Favonius Greatsword" },
|
||||||
|
{ key: "homa", name: "Staff of Homa" },
|
||||||
|
];
|
||||||
|
const grouped = aliasesByItem([
|
||||||
|
{ kind: "weapon", catalogKey: "ballad", alias: "bp" },
|
||||||
|
{ kind: "weapon", catalogKey: "favonius", alias: "bp" },
|
||||||
|
]);
|
||||||
|
const names = (weapon: (typeof weapons)[number]) => [weapon.name, ...(grouped.weapon.get(weapon.key) ?? [])];
|
||||||
|
expect(searchItems(weapons, "BP", (weapon) => names(weapon).join(" "), names).items)
|
||||||
|
.toEqual(weapons.slice(0, 2));
|
||||||
|
});
|
||||||
|
|
||||||
it("normalizes case, spacing, and unicode", () => {
|
it("normalizes case, spacing, and unicode", () => {
|
||||||
expect(normalizeGlossaryAlias(" HoMa ")).toBe("homa");
|
expect(normalizeGlossaryAlias(" HoMa ")).toBe("homa");
|
||||||
expect(normalizeGlossaryAlias("ATK")).toBe("atk");
|
expect(normalizeGlossaryAlias("ATK")).toBe("atk");
|
||||||
|
|||||||
Reference in New Issue
Block a user