Add Modrinth and CurseForge project ID lookup to field validation

This commit is contained in:
CPTProgrammer 2026-07-14 14:29:56 +08:00
parent 9658773fa2
commit 52ef5bf927
No known key found for this signature in database
2 changed files with 63 additions and 59 deletions

View File

@ -13,7 +13,6 @@ import {
App, App,
} from "antd"; } from "antd";
import { PlusOutlined, DeleteOutlined } from "@ant-design/icons"; import { PlusOutlined, DeleteOutlined } from "@ant-design/icons";
import { useDebouncedCallback } from "use-debounce";
import { createConfig, updateConfig } from "@/services/config"; import { createConfig, updateConfig } from "@/services/config";
import { import {
getModrinthLoaders, getModrinthLoaders,
@ -25,6 +24,8 @@ import {
readProjectVersion, readProjectVersion,
checkArtifactFiles, checkArtifactFiles,
checkSourceFiles, checkSourceFiles,
lookupModrinthProject,
lookupCurseforgeProject,
} from "@/services/project"; } from "@/services/project";
import { import {
useFieldValidations, useFieldValidations,
@ -137,6 +138,32 @@ const STATIC_DEFS: StaticValidationDef<Config>[] = [
: { status: "error", help: r.error }; : { status: "error", help: r.error };
}, },
}, },
// 5. Modrinth 项目 ID
{
target: ["modrinth", "project_id"],
deps: [["modrinth", "project_id"]],
async validate(getVal) {
const id = getVal(["modrinth", "project_id"]) as string | undefined;
if (!id) return null;
const r = await lookupModrinthProject(id);
return r.ok
? { status: "success", help: `项目:${r.name}` }
: { status: "error", help: r.error };
},
},
// 6. CurseForge 项目 ID
{
target: ["curseforge", "project_id"],
deps: [["curseforge", "project_id"]],
async validate(getVal) {
const id = getVal(["curseforge", "project_id"]) as number | undefined;
if (id == null || id === 0) return null;
const r = await lookupCurseforgeProject(id);
return r.ok
? { status: "success", help: `项目:${r.name}` }
: { status: "error", help: r.error };
},
},
]; ];
const LIST_DEFS: ListValidationDef<Config>[] = []; const LIST_DEFS: ListValidationDef<Config>[] = [];
@ -299,29 +326,6 @@ export function ConfigModal({
[mode, initialName, onSaved, message], [mode, initialName, onSaved, message],
); );
// --------------- Debounced project name lookup ---------------
const [modrinthProjectName, setModrinthProjectName] = useState<string | null>(null);
const [curseforgeProjectName, setCurseforgeProjectName] = useState<string | null>(null);
const lookupModrinthProject = useDebouncedCallback(async (projectId: string) => {
if (!projectId) {
setModrinthProjectName(null);
return;
}
// TODO: call Modrinth API to get project name
setModrinthProjectName(null);
}, 800);
const lookupCurseforgeProject = useDebouncedCallback(async (projectId: number | null) => {
if (projectId == null) {
setCurseforgeProjectName(null);
return;
}
// TODO: call CurseForge API to get project name
setCurseforgeProjectName(null);
}, 800);
// --------------- Render --------------- // --------------- Render ---------------
return ( return (
@ -351,10 +355,6 @@ export function ConfigModal({
<ConfigModalForm <ConfigModalForm
mode={mode} mode={mode}
initialConfig={initialConfig} initialConfig={initialConfig}
modrinthProjectName={modrinthProjectName}
curseforgeProjectName={curseforgeProjectName}
lookupModrinthProject={lookupModrinthProject}
lookupCurseforgeProject={lookupCurseforgeProject}
onSave={handleSave} onSave={handleSave}
/> />
</Modal> </Modal>
@ -368,20 +368,12 @@ export function ConfigModal({
interface ConfigModalFormProps { interface ConfigModalFormProps {
mode: "add" | "edit"; mode: "add" | "edit";
initialConfig: Config | null; initialConfig: Config | null;
modrinthProjectName: string | null;
curseforgeProjectName: string | null;
lookupModrinthProject: (projectId: string) => void;
lookupCurseforgeProject: (projectId: number | null) => void;
onSave: (values: ConfigValues) => void; onSave: (values: ConfigValues) => void;
} }
function ConfigModalForm({ function ConfigModalForm({
mode, mode,
initialConfig, initialConfig,
modrinthProjectName,
curseforgeProjectName,
lookupModrinthProject,
lookupCurseforgeProject,
onSave, onSave,
}: ConfigModalFormProps) { }: ConfigModalFormProps) {
const [form] = Form.useForm<ConfigValues>(); const [form] = Form.useForm<ConfigValues>();
@ -527,23 +519,15 @@ function ConfigModalForm({
label: "Modrinth", label: "Modrinth",
children: ( children: (
<> <>
<Form.Item <ValidatedFormItem
name={["modrinth", "project_id"]} name={["modrinth", "project_id"]}
label="项目 ID" label="项目 ID"
rules={[ rules={[
{ required: true, message: "请输入 Modrinth 项目 ID" }, { required: true, message: "请输入 Modrinth 项目 ID" },
]} ]}
extra={
modrinthProjectName
? `项目名称: ${modrinthProjectName}`
: undefined
}
> >
<Input <Input placeholder="8 位 base62" />
placeholder="8 位 base62" </ValidatedFormItem>
onChange={(e) => lookupModrinthProject(e.target.value)}
/>
</Form.Item>
<Form.Item <Form.Item
name={["modrinth", "version_name"]} name={["modrinth", "version_name"]}
@ -658,7 +642,7 @@ function ConfigModalForm({
label: "CurseForge", label: "CurseForge",
children: ( children: (
<> <>
<Form.Item <ValidatedFormItem
name={["curseforge", "project_id"]} name={["curseforge", "project_id"]}
label="项目 ID" label="项目 ID"
rules={[ rules={[
@ -667,22 +651,12 @@ function ConfigModalForm({
message: "请输入 CurseForge 项目 ID", message: "请输入 CurseForge 项目 ID",
}, },
]} ]}
extra={
curseforgeProjectName
? `项目名称: ${curseforgeProjectName}`
: undefined
}
> >
<InputNumber <InputNumber
style={{ width: "100%" }} style={{ width: "100%" }}
placeholder="数字 ID" placeholder="数字 ID"
onChange={(val) =>
lookupCurseforgeProject(
typeof val === "number" ? val : null,
)
}
/> />
</Form.Item> </ValidatedFormItem>
<Form.Item <Form.Item
name={["curseforge", "version_name"]} name={["curseforge", "version_name"]}

View File

@ -250,3 +250,33 @@ export async function checkSourceFiles(
} }
return { ok: true, matched }; return { ok: true, matched };
} }
// ---------------------------------------------------------------------------
// 平台项目查询
// ---------------------------------------------------------------------------
import { getProject } from "@/lib/modrinth/project";
import { getMod } from "@/lib/curseforge/mod";
import { modrinthClient, curseforgeClient } from "./clients";
export async function lookupModrinthProject(
projectId: string,
): Promise<OkResult<{ name: string }> | ErrResult> {
try {
const project = await getProject(modrinthClient, projectId);
return { ok: true, name: project.title };
} catch (err) {
return { ok: false, error: err instanceof Error ? err.message : "查询 Modrinth 项目失败" };
}
}
export async function lookupCurseforgeProject(
projectId: number,
): Promise<OkResult<{ name: string }> | ErrResult> {
try {
const mod = await getMod(curseforgeClient, projectId);
return { ok: true, name: mod.data.name };
} catch (err) {
return { ok: false, error: err instanceof Error ? err.message : "查询 CurseForge 项目失败" };
}
}