清除业务契约双读:抽奖/草稿/时间戳/邮件只认新格式,并删除 legacy 短代码改写。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -69,8 +69,6 @@ type ComposeDraft = {
|
||||
tags: string;
|
||||
boardId: number;
|
||||
postType?: string;
|
||||
/** @deprecated 旧草稿仅有 JSON 字符串;新草稿用 typeForm */
|
||||
typeMeta?: string;
|
||||
typeForm?: TypeFormSnapshot;
|
||||
updatedAt: number;
|
||||
};
|
||||
@@ -90,70 +88,12 @@ function serializeTags(tags: string[]): string {
|
||||
return tags.join(", ");
|
||||
}
|
||||
|
||||
/** 从旧版 typeMeta JSON 尽量还原类型表单(不完整则用默认) */
|
||||
function typeFormFromMeta(
|
||||
postType: PostTypeValue,
|
||||
raw: string | undefined
|
||||
): Partial<TypeFormSnapshot> {
|
||||
if (!raw?.trim()) return {};
|
||||
try {
|
||||
const m = JSON.parse(raw) as Record<string, unknown>;
|
||||
const out: Partial<TypeFormSnapshot> = {};
|
||||
const endsAt = typeof m.ends_at === "string" ? m.ends_at : "";
|
||||
const expireDays = Number(m.expire_days);
|
||||
if (endsAt) {
|
||||
const t = Date.parse(endsAt);
|
||||
if (Number.isFinite(t)) {
|
||||
out.deadlineMode = "custom";
|
||||
out.customEndsLocal = toLocalInputValue(new Date(t));
|
||||
}
|
||||
} else if (Number.isFinite(expireDays) && expireDays > 0) {
|
||||
out.deadlineMode = "preset";
|
||||
}
|
||||
if (postType === "poll") {
|
||||
if (Array.isArray(m.options)) {
|
||||
const opts = m.options.map((o) => String(o ?? ""));
|
||||
out.pollOptions = opts.length >= 2 ? opts : ["", ""];
|
||||
}
|
||||
out.pollMulti = !!m.multi;
|
||||
out.pollAnonymous = m.anonymous !== false;
|
||||
if ((POLL_EXPIRE_DAYS as readonly number[]).includes(expireDays)) {
|
||||
out.pollExpireDays = expireDays;
|
||||
}
|
||||
} else if (postType === "bounty") {
|
||||
const p = Number(m.points);
|
||||
out.bountyPoints = p > 0 ? Math.floor(p) : "";
|
||||
if ((BOUNTY_EXPIRE_DAYS as readonly number[]).includes(expireDays)) {
|
||||
out.bountyExpireDays = expireDays;
|
||||
}
|
||||
} else if (postType === "lottery") {
|
||||
const prizes = Array.isArray(m.prizes)
|
||||
? (m.prizes as { name?: string; count?: number }[]).map((p) => ({
|
||||
name: String(p.name || "").slice(0, 32),
|
||||
count:
|
||||
typeof p.count === "number" && p.count >= 1
|
||||
? Math.floor(p.count)
|
||||
: ("" as const),
|
||||
}))
|
||||
: [];
|
||||
out.lotteryPrizes =
|
||||
prizes.length > 0 ? prizes : [{ name: "", count: "" as const }];
|
||||
if ((LOTTERY_EXPIRE_DAYS as readonly number[]).includes(expireDays)) {
|
||||
out.lotteryExpireDays = expireDays;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function draftHasContent(d: ComposeDraft): boolean {
|
||||
if (d.title.trim() || d.content.trim() || d.tags.trim()) return true;
|
||||
if (d.boardId > 0) return true;
|
||||
const pt = normalizePostType(d.postType);
|
||||
if (pt !== "discussion") return true;
|
||||
if (d.typeForm || (d.typeMeta && d.typeMeta.trim())) return true;
|
||||
if (d.typeForm) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -177,8 +117,14 @@ function readDraft(): ComposeDraft | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(DRAFT_KEY);
|
||||
if (!raw) return null;
|
||||
const d = JSON.parse(raw) as ComposeDraft;
|
||||
const d = JSON.parse(raw) as ComposeDraft & { typeMeta?: string };
|
||||
if (!d || typeof d !== "object") return null;
|
||||
// 仅含旧 typeMeta、无 typeForm 的草稿已废弃
|
||||
if (d.typeMeta && !d.typeForm) {
|
||||
clearDraft();
|
||||
return null;
|
||||
}
|
||||
delete d.typeMeta;
|
||||
return d;
|
||||
} catch {
|
||||
return null;
|
||||
@@ -295,7 +241,6 @@ export default function PostComposer(props: PostComposerProps) {
|
||||
try {
|
||||
const raw = JSON.parse(props.initialTypeMeta || "{}") as {
|
||||
prizes?: { name?: string; count?: number }[];
|
||||
slots?: number;
|
||||
};
|
||||
if (Array.isArray(raw.prizes) && raw.prizes.length > 0) {
|
||||
return raw.prizes.map((p) => ({
|
||||
@@ -303,9 +248,6 @@ export default function PostComposer(props: PostComposerProps) {
|
||||
count: Math.max(1, Number(p.count) || 1),
|
||||
}));
|
||||
}
|
||||
// 旧数据仅有 slots:数量可回填,名称留空让作者补全
|
||||
const slots = Number(raw.slots);
|
||||
if (slots > 0) return [{ name: "", count: slots }];
|
||||
return [{ name: "", count: "" }];
|
||||
} catch {
|
||||
return [{ name: "", count: "" }];
|
||||
@@ -573,9 +515,7 @@ export default function PostComposer(props: PostComposerProps) {
|
||||
const nextType = normalizePostType(draftBanner.postType);
|
||||
setPostType(nextType);
|
||||
|
||||
const fromMeta = typeFormFromMeta(nextType, draftBanner.typeMeta);
|
||||
const snap: Partial<TypeFormSnapshot> = {
|
||||
...fromMeta,
|
||||
...(draftBanner.typeForm || {}),
|
||||
};
|
||||
|
||||
@@ -615,9 +555,7 @@ export default function PostComposer(props: PostComposerProps) {
|
||||
}
|
||||
if (snap.customEndsLocal) setCustomEndsLocal(snap.customEndsLocal);
|
||||
|
||||
// 编辑态用的 typeMeta 字符串一并同步,避免后续误用空串
|
||||
if (draftBanner.typeMeta) setTypeMeta(draftBanner.typeMeta);
|
||||
else if (draftBanner.typeForm) setTypeMeta("");
|
||||
if (draftBanner.typeForm) setTypeMeta("");
|
||||
|
||||
setDraftBanner(null);
|
||||
skipDraftSave.current = false;
|
||||
|
||||
Reference in New Issue
Block a user