commit
This commit is contained in:
+100
-1
@@ -1,7 +1,106 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import "./App.css";
|
||||
import { Client } from "./ot/client";
|
||||
import { generateOperations } from "./ot/helpers";
|
||||
import { getDemoServerInstance } from "./ot/server";
|
||||
|
||||
function Editor({ name }: { name: string }) {
|
||||
const clientRef = useRef<Client | null>(null);
|
||||
const [doc, setDoc] = useState<string>("");
|
||||
const prevDocRef = useRef<string>("");
|
||||
const [enableSync, setEnableSync] = useState<boolean>(true);
|
||||
|
||||
// 初始化client和文档状态
|
||||
useLayoutEffect(() => {
|
||||
if (!clientRef.current) {
|
||||
clientRef.current = new Client();
|
||||
clientRef.current.name = name;
|
||||
const initialDoc = clientRef.current.document || "";
|
||||
prevDocRef.current = initialDoc;
|
||||
setDoc(initialDoc);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!clientRef.current) return;
|
||||
// 定期同步client的文档状态到UI
|
||||
const timer = setInterval(() => {
|
||||
const currentDoc = clientRef.current!.document || "";
|
||||
if (currentDoc !== prevDocRef.current) {
|
||||
setDoc(currentDoc);
|
||||
prevDocRef.current = currentDoc;
|
||||
}
|
||||
}, 100);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
if (!clientRef.current) return;
|
||||
const newValue = e.target.value;
|
||||
// 使用client当前文档状态作为旧值
|
||||
const oldValue = clientRef.current.document || "";
|
||||
if (newValue !== oldValue) {
|
||||
// 生成操作集合
|
||||
const operations = generateOperations(oldValue, newValue);
|
||||
// 按顺序应用操作
|
||||
operations.forEach((op) => {
|
||||
clientRef.current!.applyClient(op);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleSync = () => {
|
||||
const newValue = !enableSync;
|
||||
setEnableSync(newValue);
|
||||
if (clientRef.current) {
|
||||
clientRef.current.setEnableSync(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ border: "1px solid #ccc", padding: 10 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
<p style={{ margin: 0 }}>Document:</p>
|
||||
<label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
|
||||
<input type="checkbox" checked={enableSync} onChange={handleToggleSync} />
|
||||
<span>同步到服务端</span>
|
||||
</label>
|
||||
</div>
|
||||
<textarea value={doc} onChange={handleChange} rows={5} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return <div>he</div>;
|
||||
const [serverDoc, setServerDoc] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setServerDoc(getDemoServerInstance().document || "");
|
||||
}, 100);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ border: "1px solid #ccc", padding: 10 }}>
|
||||
<p>Server Document:</p>
|
||||
<p>{serverDoc}</p>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 10, marginTop: 20 }}>
|
||||
<Editor name="A" />
|
||||
<Editor name="B" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
Reference in New Issue
Block a user