feat(frontend): 进来即写——落地页「直接开始写」直达空白编辑器,敲字后延迟落库
Phase 0(写作工作台重构):新增 /write 草稿编辑器入口,只有敲入实质正文并停顿后 才懒建占位「未命名草稿」项目并种入首章草稿(useDeferredDraft,双重幂等闸 + 草稿落库 非致命降级),随即 replace 换入完整工作台;空进空出不落库,杜绝孤儿草稿。 落地页 CTA 改为「直接开始写」为主、「先立项」(立项向导)为辅。
This commit is contained in:
58
apps/web/components/start/DraftComposer.tsx
Normal file
58
apps/web/components/start/DraftComposer.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { TextArea } from "@/components/ui/TextArea";
|
||||
import { useDeferredDraft } from "@/lib/start/useDeferredDraft";
|
||||
|
||||
// 敲入正文后停顿多久才懒建项目(落库)——在停顿点落库,快照即所见,避免打断连续输入。
|
||||
const CREATE_DEBOUNCE_MS = 700;
|
||||
|
||||
// 进来即写:空白编辑器直达。敲入实质正文并停顿后懒建占位项目 + 落首章草稿,随即换入完整工作台。
|
||||
// 空进空出不落库(从不敲字 → 从不 POST)。落库逻辑在 useDeferredDraft(已单测),此处只管交互。
|
||||
export function DraftComposer() {
|
||||
const router = useRouter();
|
||||
const { ensureCreated } = useDeferredDraft();
|
||||
const [text, setText] = useState("");
|
||||
const textRef = useRef("");
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const redirectingRef = useRef(false);
|
||||
|
||||
const tryCreate = async (): Promise<void> => {
|
||||
if (redirectingRef.current) return;
|
||||
const snapshot = textRef.current;
|
||||
if (snapshot.trim().length === 0) return;
|
||||
const id = await ensureCreated(snapshot);
|
||||
if (id && !redirectingRef.current) {
|
||||
redirectingRef.current = true;
|
||||
router.replace(`/projects/${id}/write`);
|
||||
}
|
||||
};
|
||||
|
||||
const onChange = (value: string): void => {
|
||||
setText(value);
|
||||
textRef.current = value;
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
timerRef.current = setTimeout(() => void tryCreate(), CREATE_DEBOUNCE_MS);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
<div className="mx-auto flex min-h-[calc(100vh-var(--chrome,4rem))] max-w-3xl flex-col px-6 py-10 sm:px-8">
|
||||
<p className="mb-4 text-sm text-ink-soft">
|
||||
直接开始写——落笔即为你建立作品,正文自动保存。书名、设定、大纲都可在写作中随时补齐。
|
||||
</p>
|
||||
<TextArea
|
||||
value={text}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="min-h-[60vh] flex-1 resize-none text-lg leading-8"
|
||||
placeholder="从这里开始写你的第一章……"
|
||||
autoFocus
|
||||
aria-label="正文"
|
||||
/>
|
||||
</div>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user