-
Notifications
You must be signed in to change notification settings - Fork 3
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
647e348
commit a02bacc
Showing
7 changed files
with
229 additions
and
203 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
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
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,56 @@ | ||
"use client" | ||
|
||
import { useRouter } from "next/navigation"; | ||
import { toast } from "sonner"; | ||
import { LogOut } from "lucide-react"; | ||
import { PopoverContent, PopoverTrigger } from "@radix-ui/react-popover"; | ||
|
||
import { Avatar, AvatarImage } from "@/components/ui/avatar"; | ||
import { Popover } from "@/components/ui/popover"; | ||
import { Button } from "@/components/ui/button"; | ||
import { SessionReturnType } from "@/lib/customHooks/ReturnType"; | ||
import getInitials from "@/helpers/getInitials"; | ||
|
||
import { LogoutAction } from "../actions"; | ||
|
||
type ProfileBtnPropType = Pick<SessionReturnType, "name" | "image"> | ||
export default function ProfileBtn({ name, image }: ProfileBtnPropType) { | ||
const router = useRouter(); | ||
|
||
const logout = async () => { | ||
const response = await LogoutAction(); | ||
if (response.success) { | ||
toast.success("Successfully logged out"); | ||
router.push("/api/auth/signin"); | ||
} else { | ||
toast.error(response.error); | ||
} | ||
}; | ||
return ( | ||
<Popover> | ||
<PopoverTrigger> | ||
<div className="relative flex justify-center items-center bg-blue-50 size-8 rounded-full ring-blue-100 hover:ring"> | ||
<p>{getInitials(name ?? "X")}</p> | ||
</div> | ||
{image ? ( | ||
<Avatar className="size-8 absolute transform -translate-y-full"> | ||
<AvatarImage src={image} /> | ||
</Avatar> | ||
) : (<></>)} | ||
</PopoverTrigger> | ||
<PopoverContent | ||
className="flex flex-col p-0 py-1 text-left w-min bg-white shadow-md" | ||
> | ||
<Button | ||
// id={item.title} | ||
variant="ghost" | ||
className="gap-2 justify-start" | ||
onClick={logout} | ||
> | ||
<LogOut size={20} color="#48acf9" strokeWidth={1.5} /> | ||
<p className="text-neutral-600">Logout</p> | ||
</Button> | ||
</PopoverContent> | ||
</Popover> | ||
) | ||
} |
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
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
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,19 +1,145 @@ | ||
"use client" | ||
|
||
import { useEffect, useState, useCallback } from "react"; | ||
import { useParams } from "next/navigation"; | ||
import { useEditor } from "@tiptap/react"; | ||
import { toast } from "sonner"; | ||
import type { Document} from "@prisma/client"; | ||
import html2canvas from "html2canvas"; | ||
import Collaboration from "@tiptap/extension-collaboration"; | ||
import CollaborationCursor from "@tiptap/extension-collaboration-cursor"; | ||
|
||
import { extensions, props } from "./editorConfig"; | ||
import { getRandomColor } from "@/helpers/getRandomColor"; | ||
import useDebounce from "@/lib/customHooks/useDebounce"; | ||
|
||
type EditorType = { | ||
docData?: string; | ||
debouncedSaveDoc: (editor: any) => void; | ||
}; | ||
import { ydoc, provider, extensions, props } from "./editorConfig"; | ||
import { GetDocDetails, UpdateDocData, UpdateThumbnail } from "../actions"; | ||
|
||
type EditorPropType = { | ||
setIsSaving: React.Dispatch<React.SetStateAction<boolean>>; | ||
} | ||
export const Editor = ({ setIsSaving }: EditorPropType) => { | ||
const params = useParams(); | ||
|
||
const [isFirstLoad, setIsFirstLoad] = useState(true); | ||
const [name, setName] = useState(""); | ||
const [docData, setDocData] = useState<Document | undefined>(undefined); | ||
const [status, setStatus] = useState("connecting"); | ||
console.log(status); | ||
|
||
useEffect(() => { | ||
setName(localStorage.getItem("name") || ""); | ||
setIsFirstLoad(false); | ||
}, []) | ||
|
||
useEffect(() => { | ||
// Update status changes | ||
const statusHandler = (event: any) => { | ||
setStatus(event.status); | ||
}; | ||
|
||
provider.on("status", statusHandler); | ||
|
||
return () => { | ||
provider.off("status", statusHandler); | ||
}; | ||
}, []); | ||
|
||
// Doc data fetching | ||
useEffect(() => { | ||
(async () => { | ||
const response = await GetDocDetails(params.id); | ||
if (response.success) { | ||
setDocData(response.data); | ||
} else { | ||
toast.error(response.error); | ||
} | ||
})(); | ||
}, [params.id]); | ||
|
||
export const Editor = ({ docData, debouncedSaveDoc }: EditorType) => { | ||
return useEditor({ | ||
extensions: extensions, | ||
const createDocThumbnail = useCallback(async () => { | ||
try { | ||
const page = document.getElementsByClassName("tiptap")[0]; | ||
if (!page) return; | ||
// @ts-ignore | ||
const canvas = await html2canvas(page, { scale: 1 }); | ||
|
||
const thumbnail = canvas | ||
.toDataURL(`${docData?.id}thumbnail/png`) | ||
.replace(/^data:image\/\w+;base64,/, ""); | ||
|
||
await UpdateThumbnail(params.id, thumbnail); | ||
} catch (e) { | ||
console.log(e); | ||
toast.error("Something went wrong"); | ||
} | ||
}, [docData?.id, params.id]); | ||
|
||
const debounce = useDebounce(async (editor: any) => { | ||
setIsSaving(true); | ||
|
||
const response = await UpdateDocData( | ||
params.id, | ||
JSON.stringify(editor.getJSON()), | ||
); | ||
if (response.success) { | ||
setIsSaving(false); | ||
return createDocThumbnail(); | ||
} | ||
setIsSaving(false); | ||
toast.error(response.error); | ||
}, 1000); | ||
|
||
// Editor instance | ||
const editor = useEditor({ | ||
onCreate: ({ editor: currentEditor }) => { | ||
provider.on("synced", () => { | ||
if (currentEditor.isEmpty) { | ||
currentEditor.commands.setContent(""); | ||
} | ||
}); | ||
}, | ||
extensions: [ | ||
...extensions, | ||
Collaboration.configure({ | ||
document: ydoc, | ||
}), | ||
CollaborationCursor.configure({ | ||
provider, | ||
user: { | ||
name, | ||
color: getRandomColor(), | ||
}, | ||
}), | ||
], | ||
editorProps: props, | ||
content: docData, | ||
content: "", | ||
onUpdate({ editor }) { | ||
debouncedSaveDoc(editor); | ||
if (!isFirstLoad) { | ||
debounce(editor); | ||
} | ||
}, | ||
}); | ||
|
||
// Save current user to localStorage and emit to editor | ||
useEffect(() => { | ||
if (editor) { | ||
editor | ||
.chain() | ||
.focus() | ||
.updateUser({ name }) | ||
.run(); | ||
} | ||
}, [editor, name]); | ||
|
||
// Set content of the doc | ||
useEffect(() => { | ||
if (editor && docData) { | ||
editor.commands.setContent( | ||
docData?.data ? JSON.parse(docData?.data) : "", | ||
); | ||
} | ||
}, [editor, docData, docData?.data]); | ||
|
||
return { editor, docData }; | ||
}; |
Oops, something went wrong.