"use client"; import { useState, useCallback } from "react"; import { Modal, Steps, Button, Space, Checkbox, Table, Select, Row, Col, Progress, Typography, Empty, } from "antd"; import { ReloadOutlined } from "@ant-design/icons"; import type { Config } from "@/types"; import type { ParsedProject } from "@/services/project"; const { Text } = Typography; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- interface PublishVersion { key: string; mc_version: string; artifact: string | null; sources: string | null; // version label that matches the platform's versioning label: string; // already exists on this platform? existsModrinth: boolean; existsCurseforge: boolean; } interface PublishModalProps { open: boolean; config: Config; project: ParsedProject; onClose: () => void; } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function PublishModal({ open, config, project, onClose, }: PublishModalProps) { const [currentStep, setCurrentStep] = useState(0); // Version selection const [selectedVersions, setSelectedVersions] = useState>( new Set(), ); // Publish settings const [changelog, setChangelog] = useState(""); const [versionType, setVersionType] = useState<"release" | "beta" | "alpha">( "release", ); // Progress const [publishing, setPublishing] = useState(false); const [progress, setProgress] = useState<{ modrinth: { done: number; total: number }; curseforge: { done: number; total: number }; }>({ modrinth: { done: 0, total: 0 }, curseforge: { done: 0, total: 0 } }); // Platform existing versions — loaded when modal opens const [loadingExisting, setLoadingExisting] = useState(false); const [existingModrinth, setExistingModrinth] = useState([]); const [existingCurseforge, setExistingCurseforge] = useState([]); const loadExisting = useCallback(async () => { setLoadingExisting(true); try { // TODO: Fetch existing versions from both platforms via tRPC/Server Action // For now, placeholder setExistingModrinth([]); setExistingCurseforge([]); } catch { // ignore } finally { setLoadingExisting(false); } }, []); // Build publish version rows from project artifacts const publishVersions: PublishVersion[] = project.artifacts.map((a) => ({ key: a.mc_version, mc_version: a.mc_version, artifact: a.artifact, sources: a.sources, label: "", // TODO: compute from version template existsModrinth: existingModrinth.includes(a.mc_version), existsCurseforge: existingCurseforge.includes(a.mc_version), })); // --------------- Steps handling --------------- const handleNext = useCallback(() => { if (currentStep === 0) { // Init selection with all non-existing entries const preselected = new Set(); for (const v of publishVersions) { if (!v.existsModrinth || !v.existsCurseforge) { preselected.add(v.key); } } setSelectedVersions(preselected); } setCurrentStep((prev) => Math.min(prev + 1, 1)); }, [currentStep, publishVersions]); const handlePrev = useCallback(() => { setCurrentStep((prev) => Math.max(prev - 1, 0)); }, []); const handleConfirmPublish = useCallback(async () => { setPublishing(true); // TODO: Call tRPC mutation to publish setPublishing(false); }, []); const handleClose = useCallback(() => { if (publishing) return; setCurrentStep(0); setSelectedVersions(new Set()); setChangelog(""); setVersionType("release"); onClose(); }, [publishing, onClose]); // --------------- Render: Step 1 — Version Selection --------------- const renderStep1 = () => { const modrinthColumns = [ { title: "MC 版本", dataIndex: "mc_version", key: "mc_version", width: 100, }, { title: "构建产物", dataIndex: "artifact", key: "artifact", ellipsis: true, }, { title: "", key: "select", width: 50, render: (_: unknown, record: PublishVersion) => ( { const next = new Set(selectedVersions); if (e.target.checked) next.add(record.key); else next.delete(record.key); setSelectedVersions(next); }} /> ), }, ]; const curseforgeColumns = [ { title: "MC 版本", dataIndex: "mc_version", key: "mc_version", width: 100, }, { title: "构建产物", dataIndex: "artifact", key: "artifact", ellipsis: true, }, { title: "", key: "select", width: 50, render: (_: unknown, record: PublishVersion) => ( { const next = new Set(selectedVersions); if (e.target.checked) next.add(record.key); else next.delete(record.key); setSelectedVersions(next); }} /> ), }, ]; return (
选择要发布的版本
{loadingExisting ? (
加载中…
) : ( Modrinth {publishVersions.length > 0 ? ( ) : ( )} CurseForge {publishVersions.length > 0 ? (
) : ( )} )} ); }; // --------------- Render: Step 2 — Publish Settings --------------- const renderStep2 = () => { const hasModrinth = publishVersions.some( (v) => !v.existsModrinth && (selectedVersions.has(v.key) || v.existsModrinth), ); const hasCurseforge = publishVersions.some( (v) => !v.existsCurseforge && (selectedVersions.has(v.key) || v.existsCurseforge), ); return (
{/* Changelog */}
Changelog (Markdown) {/* TODO: Replace with @uiw/react-md-editor */}