-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c6a3c30
commit eb4da7d
Showing
5 changed files
with
158 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,5 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/* eslint-disable react-hooks/exhaustive-deps */ | ||
"use client"; | ||
import DocumentPage from "@/template/DocumentPage"; | ||
|
||
import { useState, useEffect } from "react"; | ||
import socket from "@/lib/socket"; | ||
import dynamic from "next/dynamic"; | ||
import API from "@/lib/api"; | ||
import useDebounce from "@/hook/useDebounce"; | ||
|
||
import "react-quill/dist/quill.snow.css"; | ||
|
||
const QuillNoSSRWrapper = dynamic(() => import("react-quill"), { | ||
ssr: false, | ||
loading: () => <p>Loading...</p>, | ||
}); | ||
|
||
export default function DocumentPage({ params }: { params: { id: string } }) { | ||
const { id } = params; | ||
const [content, setContent] = useState<string>(""); | ||
const [versions, setVersions] = useState<any[]>([]); | ||
const debouncedContent = useDebounce(content, 500); | ||
|
||
const fetchDocument = async () => { | ||
try { | ||
const { data } = await API.get(`/api/document/${id}`); | ||
setContent(data.content); | ||
} catch (error) { | ||
console.error("Failed to fetch document:", error); | ||
} | ||
}; | ||
|
||
const fetchVersionsOfDocument = async () => { | ||
try { | ||
const { data } = await API.get(`/api/documents/${id}/versions`); | ||
setVersions(data); | ||
} catch (error) { | ||
console.error("Failed to fetch versions:", error); | ||
} | ||
}; | ||
|
||
const restoreVersionOfDocument = async (index: number) => { | ||
try { | ||
await API.put(`/api/documents/${id}/restore`, { versionIndex: index }); | ||
const { data } = await API.get(`/api/documents/${id}`); | ||
setContent(data.content); | ||
} catch (error) { | ||
console.error("Failed to restore version:", error); | ||
} | ||
}; | ||
|
||
const handleChangeContent = (value: string) => { | ||
setContent(value); | ||
socket.emit("edit-document", id, value); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!id) return; | ||
|
||
fetchDocument(); | ||
fetchVersionsOfDocument(); | ||
|
||
socket.connect(); | ||
socket.emit("join-document", id); | ||
|
||
socket.on("receive-edit", (updatedContent: string) => { | ||
setContent(updatedContent); | ||
}); | ||
|
||
return () => { | ||
socket.disconnect(); | ||
}; | ||
}, [id]); | ||
|
||
useEffect(() => { | ||
if (debouncedContent) { | ||
API.put(`/api/document/${id}`, { content: debouncedContent }).catch( | ||
(error) => console.error("Failed to save document", error) | ||
); | ||
} | ||
}, [debouncedContent]); | ||
|
||
return ( | ||
<div className="p-4"> | ||
<div className="flex justify-between items-center mb-4"> | ||
<h1 className="text-2xl font-bold">Document ID: {id}</h1> | ||
<div className="mt-4"> | ||
<QuillNoSSRWrapper | ||
theme="snow" | ||
value={content} | ||
onChange={handleChangeContent} | ||
className="bg-white p-2" | ||
/> | ||
</div> | ||
<div className="mt-4"> | ||
<h2 className="text-lg font-bold">Version History</h2> | ||
{versions.map((version, index) => ( | ||
<div key={index} className="mt-2"> | ||
<button | ||
className="bg-gray-200 p-1 rounded hover:bg-gray-300 " | ||
onClick={() => restoreVersionOfDocument(index)} | ||
> | ||
Restore Version {index + 1} | ||
</button> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
export default function Page(props) { | ||
return <DocumentPage {...props} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
"use client"; | ||
|
||
import dynamic from "next/dynamic"; | ||
import "react-quill/dist/quill.snow.css"; | ||
|
||
const QuillNoSSRWrapper = dynamic(() => import("react-quill"), { | ||
ssr: false, | ||
loading: () => <p>Loading...</p>, | ||
}); | ||
|
||
interface DocumentEditorProps { | ||
content: string; | ||
onChange: (value: string) => void; | ||
} | ||
|
||
export default function DocumentEditor({ | ||
content, | ||
onChange, | ||
}: DocumentEditorProps) { | ||
return ( | ||
<div className="mt-4"> | ||
<QuillNoSSRWrapper | ||
theme="snow" | ||
value={content} | ||
onChange={onChange} | ||
className="bg-white p-2" | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
|
||
interface VersionHistoryProps { | ||
versions: any[]; | ||
onRestore: (index: number) => void; | ||
} | ||
|
||
export default function VersionHistory({ | ||
versions, | ||
onRestore, | ||
}: VersionHistoryProps) { | ||
return ( | ||
<div className="mt-4"> | ||
<h2 className="text-lg font-bold">Version History</h2> | ||
{versions.map((version, index) => ( | ||
<div key={index} className="mt-2"> | ||
<button | ||
className="bg-gray-200 p-1 rounded hover:bg-gray-300" | ||
onClick={() => onRestore(index)} | ||
> | ||
Restore Version {index + 1} | ||
</button> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* eslint-disable react-hooks/exhaustive-deps */ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
"use client"; | ||
|
||
import { useState, useEffect } from "react"; | ||
|
||
import DocumentEditor from "@/components/DocumentEditor"; | ||
import VersionHistory from "@/components/VersionHistory"; | ||
import useDebounce from "@/hook/useDebounce"; | ||
import socket from "@/lib/socket"; | ||
import API from "@/lib/api"; | ||
|
||
interface DocumentPageProps { | ||
params: { | ||
id: string; | ||
}; | ||
} | ||
|
||
export default function DocumentPage({ params }: DocumentPageProps) { | ||
const { id } = params; | ||
|
||
const [content, setContent] = useState<string>(""); | ||
const [versions, setVersions] = useState<any[]>([]); | ||
const debouncedContent = useDebounce(content, 500); | ||
|
||
const fetchDocument = async () => { | ||
try { | ||
const { data } = await API.get(`/api/document/${id}`); | ||
setContent(data.content); | ||
} catch (error) { | ||
console.error("Failed to fetch document: ", error); | ||
} | ||
}; | ||
|
||
const fetchVersionsOfDocument = async () => { | ||
try { | ||
const { data } = await API.get(`/api/documents/${id}/versions`); | ||
setVersions(data); | ||
} catch (error) { | ||
console.error("Failed to fetch versions: ", error); | ||
} | ||
}; | ||
|
||
const restoreVersionOfDocument = async (index: number) => { | ||
try { | ||
await API.put(`/api/documents/${id}/restore`, { versionIndex: index }); | ||
const { data } = await API.get(`/api/documents/${id}`); | ||
setContent(data.content); | ||
} catch (error) { | ||
console.error("Failed to restore version:", error); | ||
} | ||
}; | ||
|
||
const handleChangeContent = (value: string) => { | ||
setContent(value); | ||
socket.emit("edit-document", id, value); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!id) return; | ||
|
||
fetchDocument(); | ||
fetchVersionsOfDocument(); | ||
|
||
socket.connect(); | ||
socket.emit("join-document", id); | ||
|
||
socket.on("receive-edit", (updatedContent: string) => { | ||
setContent(updatedContent); | ||
}); | ||
|
||
return () => { | ||
socket.disconnect(); | ||
}; | ||
}, [id]); | ||
|
||
useEffect(() => { | ||
if (debouncedContent) { | ||
API.put(`/api/document/${id}`, { content: debouncedContent }).catch( | ||
(error) => console.error("Failed to save document", error) | ||
); | ||
} | ||
}, [debouncedContent]); | ||
|
||
return ( | ||
<div className="p-4"> | ||
<div className="flex justify-between items-center mb-4"> | ||
<h1 className="text-2xl font-bold">Document ID: {id}</h1> | ||
</div> | ||
<DocumentEditor content={content} onChange={handleChangeContent} /> | ||
<VersionHistory | ||
versions={versions} | ||
onRestore={restoreVersionOfDocument} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters