mirror of
https://github.com/artegoser/AnoPaper.git
synced 2024-11-06 04:53:56 +03:00
feat: collaborative editing
This commit is contained in:
parent
112db5c6ac
commit
536c847375
6 changed files with 106 additions and 2 deletions
25
index.js
25
index.js
|
@ -17,6 +17,31 @@ if (!fs.existsSync("./notes")) {
|
||||||
fs.mkdirSync("./notes");
|
fs.mkdirSync("./notes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io.on("connection", (socket) => {
|
||||||
|
socket.on("nameChanged", ({ name, room }) => {
|
||||||
|
socket.to(room).emit("nameChanged", {
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("textChanged", ({ text, room }) => {
|
||||||
|
socket.to(room).emit("textChanged", {
|
||||||
|
text,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("joinRoom", (room) => {
|
||||||
|
let rooms = Array.from(io.sockets.adapter.sids.get(socket.id));
|
||||||
|
|
||||||
|
for (let room of rooms) {
|
||||||
|
if (socket.id != room) {
|
||||||
|
socket.leave(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.join(room);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
app.post("/publish", function (req, res) {
|
app.post("/publish", function (req, res) {
|
||||||
|
|
|
@ -12,7 +12,6 @@ import PubNoteSafe from "./pages/pubNoteSafe";
|
||||||
import RenderMarkdown from "./components/markdown";
|
import RenderMarkdown from "./components/markdown";
|
||||||
import socket from "./components/socket";
|
import socket from "./components/socket";
|
||||||
import Settings from "./pages/settings";
|
import Settings from "./pages/settings";
|
||||||
import Chat from "./pages/chat";
|
|
||||||
import Locales from "./localisation/main";
|
import Locales from "./localisation/main";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ function App() {
|
||||||
<Route path="/pubNotesSafe/:id" element={<PubNoteSafe />} />
|
<Route path="/pubNotesSafe/:id" element={<PubNoteSafe />} />
|
||||||
<Route path="/pubError" element={<PubError />} />
|
<Route path="/pubError" element={<PubError />} />
|
||||||
<Route path="/settings" element={<Settings />} />
|
<Route path="/settings" element={<Settings />} />
|
||||||
<Route path="/chat" element={<Chat />} />
|
|
||||||
<Route
|
<Route
|
||||||
path="/about"
|
path="/about"
|
||||||
element={
|
element={
|
||||||
|
|
|
@ -43,6 +43,9 @@ let en = {
|
||||||
NoNotesYet: "No notes yet",
|
NoNotesYet: "No notes yet",
|
||||||
AIComplete: "Continue Note (AI)",
|
AIComplete: "Continue Note (AI)",
|
||||||
AdditionalFeatures: "Additional features",
|
AdditionalFeatures: "Additional features",
|
||||||
|
CollabEdit: "Collaborative editing",
|
||||||
|
Password: "Password",
|
||||||
|
CollabEditPassword: "Password for collaborative editing",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|
|
@ -44,6 +44,9 @@ let ru = {
|
||||||
NoNotesYet: "Заметок пока нет",
|
NoNotesYet: "Заметок пока нет",
|
||||||
AIComplete: "Продолжить заметку (ИИ)",
|
AIComplete: "Продолжить заметку (ИИ)",
|
||||||
AdditionalFeatures: "Дополнительные функции",
|
AdditionalFeatures: "Дополнительные функции",
|
||||||
|
CollabEdit: "Совместное редактрование",
|
||||||
|
Password: "Пароль",
|
||||||
|
CollabEditPassword: "Пароль для совместного редактирования",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ru;
|
export default ru;
|
||||||
|
|
|
@ -22,6 +22,26 @@ import {
|
||||||
import { inputStyle } from "../components/styles";
|
import { inputStyle } from "../components/styles";
|
||||||
import { Complete } from "../components/openai";
|
import { Complete } from "../components/openai";
|
||||||
|
|
||||||
|
function nameUpdate(e) {
|
||||||
|
if (Date.now() - window.lastSocketUpdate > window.socketTimeout) {
|
||||||
|
socket.emit("nameChanged", {
|
||||||
|
name: e.target.value,
|
||||||
|
room: settings.CollabEditPassword,
|
||||||
|
});
|
||||||
|
window.lastSocketUpdate = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function textUpdate(e) {
|
||||||
|
if (Date.now() - window.lastSocketUpdate > window.socketTimeout) {
|
||||||
|
socket.emit("textChanged", {
|
||||||
|
text: e.target.value,
|
||||||
|
room: settings.CollabEditPassword,
|
||||||
|
});
|
||||||
|
window.lastSocketUpdate = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function CreateNote() {
|
function CreateNote() {
|
||||||
const [preview, setPreview] = useState(false);
|
const [preview, setPreview] = useState(false);
|
||||||
const [publicState, setPublicState] = useState(settings.publicNote);
|
const [publicState, setPublicState] = useState(settings.publicNote);
|
||||||
|
@ -42,6 +62,32 @@ function CreateNote() {
|
||||||
md = md.value.trim();
|
md = md.value.trim();
|
||||||
|
|
||||||
localStorage.setItem("NoteText", md);
|
localStorage.setItem("NoteText", md);
|
||||||
|
|
||||||
|
if (settings.CollabEdit === true) {
|
||||||
|
socket.emit("textChanged", {
|
||||||
|
text: md,
|
||||||
|
room: settings.CollabEditPassword,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.CollabEdit === true) {
|
||||||
|
if (!window.alreadyConnected) {
|
||||||
|
socket.emit("joinRoom", settings.CollabEditPassword);
|
||||||
|
window.alreadyConnected = true;
|
||||||
|
window.lastSocketUpdate = Date.now();
|
||||||
|
window.socketTimeout = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on("textChanged", (data) => {
|
||||||
|
setText(data.text);
|
||||||
|
localStorage.setItem("NoteText", data.text);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("nameChanged", (data) => {
|
||||||
|
setName(data.name);
|
||||||
|
localStorage.setItem("NoteName", data.name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -73,6 +119,13 @@ function CreateNote() {
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setName(e.target.value);
|
setName(e.target.value);
|
||||||
localStorage.setItem("NoteName", e.target.value);
|
localStorage.setItem("NoteName", e.target.value);
|
||||||
|
|
||||||
|
if (settings.CollabEdit === true) {
|
||||||
|
nameUpdate(e);
|
||||||
|
setTimeout(() => {
|
||||||
|
nameUpdate(e);
|
||||||
|
}, window.socketTimeout);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -87,6 +140,13 @@ function CreateNote() {
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setText(e.target.value);
|
setText(e.target.value);
|
||||||
localStorage.setItem("NoteText", e.target.value);
|
localStorage.setItem("NoteText", e.target.value);
|
||||||
|
|
||||||
|
if (settings.CollabEdit === true) {
|
||||||
|
textUpdate(e);
|
||||||
|
setTimeout(() => {
|
||||||
|
textUpdate(e);
|
||||||
|
}, window.socketTimeout);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
value={localStorage.getItem("NoteText") || ""}
|
value={localStorage.getItem("NoteText") || ""}
|
||||||
></textarea>
|
></textarea>
|
||||||
|
@ -148,6 +208,10 @@ function CreateNote() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<SettingsCheckBox
|
||||||
|
label={locals.CollabEdit}
|
||||||
|
settingName="CollabEdit"
|
||||||
|
/>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -51,6 +51,17 @@ function Settings() {
|
||||||
label={locals.AdditionalFeatures}
|
label={locals.AdditionalFeatures}
|
||||||
settingName="additionalFeatures"
|
settingName="additionalFeatures"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SettingsCheckBox label={locals.CollabEdit} settingName="CollabEdit" />
|
||||||
|
|
||||||
|
<SettingsTextInput
|
||||||
|
placeholder={locals.Password}
|
||||||
|
label={locals.CollabEditPassword}
|
||||||
|
settingName="CollabEditPassword"
|
||||||
|
onChange={(e) => {
|
||||||
|
window.alreadyConnected = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
<SettingsSection name={locals.Interface}>
|
<SettingsSection name={locals.Interface}>
|
||||||
|
|
Loading…
Reference in a new issue