working basic live collab

This commit is contained in:
Ishaan Dey 2024-05-03 14:58:56 -07:00
parent a18bcf9c14
commit 17af48fe2c
6 changed files with 65 additions and 19 deletions

View File

@ -57,7 +57,8 @@ io.use((socket, next) => __awaiter(void 0, void 0, void 0, function* () {
return; return;
} }
const sandbox = dbUserJSON.sandbox.find((s) => s.id === sandboxId); const sandbox = dbUserJSON.sandbox.find((s) => s.id === sandboxId);
if (!sandbox) { const sharedSandboxes = dbUserJSON.usersToSandboxes.find((uts) => uts.sandboxId === sandboxId);
if (!sandbox && !sharedSandboxes) {
console.log("Invalid credentials."); console.log("Invalid credentials.");
next(new Error("Invalid credentials.")); next(new Error("Invalid credentials."));
return; return;

View File

@ -63,8 +63,11 @@ io.use(async (socket, next) => {
} }
const sandbox = dbUserJSON.sandbox.find((s) => s.id === sandboxId) const sandbox = dbUserJSON.sandbox.find((s) => s.id === sandboxId)
const sharedSandboxes = dbUserJSON.usersToSandboxes.find(
(uts) => uts.sandboxId === sandboxId
)
if (!sandbox) { if (!sandbox && !sharedSandboxes) {
console.log("Invalid credentials.") console.log("Invalid credentials.")
next(new Error("Invalid credentials.")) next(new Error("Invalid credentials."))
return return

View File

@ -106,4 +106,39 @@
padding: 2px 4px; padding: 2px 4px;
border-radius: 4px; border-radius: 4px;
margin-left: 56px; margin-left: 56px;
}
.yRemoteSelection {
opacity: 0.5;
background-color: var(--user-color);
margin-right: -1px;
}
/* Cursor caret */
.yRemoteSelectionHead {
position: absolute;
box-sizing: border-box;
height: 100%;
border-left: 2px solid var(--user-color);
}
/* Cursor name */
.yRemoteSelectionHead::after {
position: absolute;
top: -1.4em;
left: -2px;
padding: 2px 6px;
background: var(--user-color);
color: #fff;
border: 0;
border-radius: 6px;
border-bottom-left-radius: 0;
line-height: normal;
white-space: nowrap;
font-size: 14px;
font-style: normal;
font-weight: 600;
pointer-events: none;
user-select: none;
z-index: 1000;
} }

View File

@ -42,7 +42,6 @@ import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
import { User } from "@/lib/types" import { User } from "@/lib/types"
import { processFileType, validateName } from "@/lib/utils" import { processFileType, validateName } from "@/lib/utils"
import { Cursors } from "./live/cursors" import { Cursors } from "./live/cursors"
import { Avatars } from "./live/avatars"
export default function CodeEditor({ export default function CodeEditor({
userData, userData,
@ -271,26 +270,31 @@ export default function CodeEditor({
}) })
useEffect(() => { useEffect(() => {
if (!editorRef.current) return let yProvider: any
let yDoc: Y.Doc
let binding: MonacoBinding
const yDoc = new Y.Doc() if (editorRef.current) {
const yText = yDoc.getText("monaco") yDoc = new Y.Doc()
const yProvider: any = new LiveblocksProvider(room, yDoc) const yText = yDoc.getText("monaco")
setProvider(yProvider) yProvider = new LiveblocksProvider(room, yDoc)
setProvider(yProvider)
const binding = new MonacoBinding( // Attach Yjs to Monaco
yText, binding = new MonacoBinding(
editorRef.current.getModel() as monaco.editor.ITextModel, yText,
new Set([editorRef.current]), editorRef.current.getModel() as monaco.editor.ITextModel,
yProvider.awareness as Awareness new Set([editorRef.current]),
) yProvider.awareness as Awareness
)
}
return () => { return () => {
yDoc.destroy() yDoc?.destroy()
yProvider.destroy() yProvider?.destroy()
binding.destroy() binding?.destroy()
} }
}, [editorRef, room]) }, [editorRef.current, room])
// connection/disconnection effect + resizeobserver // connection/disconnection effect + resizeobserver
useEffect(() => { useEffect(() => {
@ -490,7 +494,6 @@ export default function CodeEditor({
{tab.name} {tab.name}
</Tab> </Tab>
))} ))}
<Avatars />
</div> </div>
<div <div
ref={editorContainerRef} ref={editorContainerRef}

View File

@ -19,6 +19,7 @@ export function Cursors({ yProvider }: { yProvider: TypedLiveblocksProvider }) {
// On changes, update `awarenessUsers` // On changes, update `awarenessUsers`
function setUsers() { function setUsers() {
console.log("SET USERS")
setAwarenessUsers( setAwarenessUsers(
Array.from(yProvider.awareness.getStates()) as AwarenessList Array.from(yProvider.awareness.getStates()) as AwarenessList
) )

View File

@ -10,6 +10,7 @@ import { Button } from "@/components/ui/button"
import { useState } from "react" import { useState } from "react"
import EditSandboxModal from "./edit" import EditSandboxModal from "./edit"
import ShareSandboxModal from "./share" import ShareSandboxModal from "./share"
import { Avatars } from "../live/avatars"
export default function Navbar({ export default function Navbar({
userData, userData,
@ -58,6 +59,8 @@ export default function Navbar({
</div> </div>
</div> </div>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
<Avatars />
<Button variant="outline" onClick={() => setIsShareOpen(true)}> <Button variant="outline" onClick={() => setIsShareOpen(true)}>
<Users className="w-4 h-4 mr-2" /> <Users className="w-4 h-4 mr-2" />
Share Share