ui + shared user improvements

This commit is contained in:
Ishaan Dey
2024-05-05 14:33:09 -07:00
parent dd400b1d2a
commit 09ead6073b
17 changed files with 363 additions and 234 deletions

View File

@ -11,12 +11,7 @@ import * as Y from "yjs"
import LiveblocksProvider from "@liveblocks/yjs"
import { MonacoBinding } from "y-monaco"
import { Awareness } from "y-protocols/awareness"
import {
TypedLiveblocksProvider,
useRoom,
AwarenessList,
useSelf,
} from "@/liveblocks.config"
import { TypedLiveblocksProvider, useRoom } from "@/liveblocks.config"
import {
ResizableHandle,
@ -27,6 +22,7 @@ import {
ChevronLeft,
ChevronRight,
FileJson,
Loader2,
Plus,
RotateCw,
Shell,
@ -355,15 +351,20 @@ export default function CodeEditor({
setFiles(files)
}
socket.on("connect", onConnect)
const onRateLimit = (message: string) => {
toast.error(message)
}
socket.on("connect", onConnect)
socket.on("disconnect", onDisconnect)
socket.on("loaded", onLoadedEvent)
socket.on("rateLimit", onRateLimit)
return () => {
socket.off("connect", onConnect)
socket.off("disconnect", onDisconnect)
socket.off("loaded", onLoadedEvent)
socket.off("rateLimit", onRateLimit)
}
}, [])
@ -547,7 +548,7 @@ export default function CodeEditor({
>
{!activeId ? (
<>
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-secondary select-none">
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
<FileJson className="w-6 h-6 mr-3" />
No file selected.
</div>
@ -591,7 +592,12 @@ export default function CodeEditor({
value={activeFile ?? ""}
/>
</>
) : null}
) : (
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
<Loader2 className="animate-spin w-6 h-6 mr-3" />
Waiting for Clerk to load...
</div>
)}
</div>
</ResizablePanel>
<ResizableHandle />
@ -629,27 +635,36 @@ export default function CodeEditor({
minSize={20}
className="p-2 flex flex-col"
>
<div className="h-10 w-full flex gap-2 shrink-0">
<Tab selected>
<SquareTerminal className="w-4 h-4 mr-2" />
Shell
</Tab>
<Button
onClick={() => {
if (terminals.length >= 4) {
toast.error("You reached the maximum # of terminals.")
}
}}
size="smIcon"
variant={"secondary"}
className={`font-normal select-none text-muted-foreground`}
>
<Plus className="w-4 h-4" />
</Button>
</div>
<div className="w-full relative grow h-full overflow-hidden rounded-md bg-secondary">
{socket ? <EditorTerminal socket={socket} /> : null}
</div>
{isOwner ? (
<>
<div className="h-10 w-full flex gap-2 shrink-0">
<Tab selected>
<SquareTerminal className="w-4 h-4 mr-2" />
Shell
</Tab>
<Button
onClick={() => {
if (terminals.length >= 4) {
toast.error("You reached the maximum # of terminals.")
}
}}
size="smIcon"
variant={"secondary"}
className={`font-normal select-none text-muted-foreground`}
>
<Plus className="w-4 h-4" />
</Button>
</div>
<div className="w-full relative grow h-full overflow-hidden rounded-md bg-secondary">
{socket ? <EditorTerminal socket={socket} /> : null}
</div>
</>
) : (
<div className="w-full h-full flex items-center justify-center text-lg font-medium text-muted-foreground/50 select-none">
<TerminalSquare className="w-4 h-4 mr-2" />
No terminal access.
</div>
)}
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>

View File

@ -1,33 +1,39 @@
"use client"
import { colorClasses, colors } from "@/lib/colors"
import { useOthers } from "@/liveblocks.config"
import { useState } from "react"
const classNames = {
red: "w-8 h-8 leading-none font-mono rounded-full ring-1 ring-red-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-red-950 to-red-600 flex items-center justify-center text-xs font-medium",
orange:
"w-8 h-8 leading-none font-mono rounded-full ring-1 ring-orange-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-orange-950 to-orange-600 flex items-center justify-center text-xs font-medium",
yellow:
"w-8 h-8 leading-none font-mono rounded-full ring-1 ring-yellow-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-yellow-950 to-yellow-600 flex items-center justify-center text-xs font-medium",
green:
"w-8 h-8 leading-none font-mono rounded-full ring-1 ring-green-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-green-950 to-green-600 flex items-center justify-center text-xs font-medium",
blue: "w-8 h-8 leading-none font-mono rounded-full ring-1 ring-blue-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-blue-950 to-blue-600 flex items-center justify-center text-xs font-medium",
purple:
"w-8 h-8 leading-none font-mono rounded-full ring-1 ring-purple-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-purple-950 to-purple-600 flex items-center justify-center text-xs font-medium",
pink: "w-8 h-8 leading-none font-mono rounded-full ring-1 ring-pink-700 ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr from-pink-950 to-pink-600 flex items-center justify-center text-xs font-medium",
}
export function Avatars() {
const users = useOthers()
const colorNames = Object.keys(colors)
const [activeColors, setActiveColors] = useState([])
return (
<div className="flex space-x-2">
{users.map(({ connectionId, info }) => {
const c = colorNames[
connectionId % colorNames.length
] as keyof typeof colors
return (
<div
className={`w-8 h-8 font-mono rounded-full ring-1 ${colorClasses[c].ring} ring-offset-2 ring-offset-background overflow-hidden bg-gradient-to-tr ${colorClasses[c].bg} flex items-center justify-center text-xs font-medium`}
>
{info.name
.split(" ")
.slice(0, 2)
.map((letter) => letter[0].toUpperCase())}
</div>
)
})}
</div>
<>
<div className="flex space-x-2">
{users.map(({ connectionId, info }) => {
return (
<div className={classNames[info.color]}>
{info.name
.split(" ")
.slice(0, 2)
.map((letter) => letter[0].toUpperCase())}
</div>
)
})}
</div>
<div className="h-full w-[1px] bg-border mx-2" />
</>
)
}

View File

@ -5,11 +5,14 @@ import {
UserAwareness,
useSelf,
} from "@/liveblocks.config"
import { colors } from "@/lib/colors"
export function Cursors({ yProvider }: { yProvider: TypedLiveblocksProvider }) {
// Get user info from Liveblocks authentication endpoint
const userInfo = useSelf((me) => me.info)
if (!userInfo) return null
const [awarenessUsers, setAwarenessUsers] = useState<AwarenessList>([])
useEffect(() => {
@ -40,7 +43,7 @@ export function Cursors({ yProvider }: { yProvider: TypedLiveblocksProvider }) {
cursorStyles += `
.yRemoteSelection-${clientId},
.yRemoteSelectionHead-${clientId} {
--user-color: ${"#FF0000"};
--user-color: ${colors[client.user.color]};
}
.yRemoteSelectionHead-${clientId}::after {

View File

@ -2,6 +2,7 @@
import { RoomProvider } from "@/liveblocks.config"
import { ClientSideSuspense } from "@liveblocks/react"
import Loading from "../loading"
export function Room({
id,
@ -17,9 +18,9 @@ export function Room({
cursor: null,
}}
>
<ClientSideSuspense fallback={<div>Loading!!!!</div>}>
{() => children}
</ClientSideSuspense>
{/* <ClientSideSuspense fallback={<Loading />}> */}
{children}
{/* </ClientSideSuspense> */}
</RoomProvider>
)
}

View File

@ -0,0 +1,41 @@
import Image from "next/image"
import Logo from "@/assets/logo.svg"
import { Skeleton } from "../ui/skeleton"
import { Loader, Loader2 } from "lucide-react"
export default function Loading() {
return (
<div className="overflow-hidden overscroll-none w-screen flex flex-col h-screen bg-background">
<div className="h-14 px-2 w-full flex items-center justify-between border-b border-border">
<div className="flex items-center space-x-4">
<Image src={Logo} alt="Logo" width={36} height={36} />
<Skeleton className="w-[100px] h-[24px] rounded-md" />
</div>
<div className="flex items-center space-x-4">
<Skeleton className="w-[64px] h-[36px] rounded-md" />
<Skeleton className="w-[36px] h-[36px] rounded-full" />
</div>
</div>
<div className="grow flex w-screen">
<div className="h-full w-56 select-none flex flex-col text-sm items-start p-2">
<div className="flex w-full items-center justify-between h-8 mb-1 ">
<div className="text-muted-foreground">Explorer</div>
<div className="flex space-x-1">
<Skeleton className="w-6 h-6 rounded-md" />
<Skeleton className="w-6 h-6 rounded-md" />
</div>
</div>
<div className="w-full mt-1 flex flex-col">
<div className="w-full flex justify-center">
<Loader2 className="w-4 h-4 animate-spin" />
</div>
</div>
</div>
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-secondary select-none">
<Loader2 className="w-6 h-6 mr-3 animate-spin" />
Loading...
</div>{" "}
</div>
</div>
)
}

View File

@ -62,7 +62,7 @@ export default function Navbar({
) : null}
</div>
</div>
<div className="flex items-center space-x-4">
<div className="flex items-center h-full space-x-4">
<Avatars />
{isOwner ? (

View File

@ -8,7 +8,7 @@ import { useState } from "react"
import New from "./new"
import { Socket } from "socket.io-client"
import Button from "@/components/ui/customButton"
import Toggle from "@/components/ui/customToggle"
import { Switch } from "@/components/ui/switch"
export default function Sidebar({
files,
@ -104,12 +104,20 @@ export default function Sidebar({
)}
</div>
</div>
{/* <div className="flex items-center"> */}
<Toggle value={ai} setValue={setAi} className="w-full">
<Sparkles className="h-3 w-3 mr-2" />
AI Copilot
</Toggle>
{/* </div> */}
<div className="flex items-center justify-between w-full">
<div className="flex items-center">
<Sparkles
className={`h-4 w-4 mr-2 ${
ai ? "text-indigo-500" : "text-muted-foreground"
}`}
/>
Copilot{" "}
<span className="font-mono text-muted-foreground inline-block ml-1.5 text-xs leading-none border border-b-2 border-muted-foreground py-1 px-1.5 rounded-md">
G
</span>
</div>
<Switch checked={ai} onCheckedChange={setAi} />
</div>
</div>
)
}