Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 118 additions & 12 deletions code/components/room-components/connected-users.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,135 @@
"use client";

import React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import Avatar from "boring-avatars";
import { Avatar as AvatarUI, AvatarFallback } from "@/components/ui/avatar";
import { WeaveConnectedUsersChanged } from "@inditextech/weavejs-sdk";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useCollaborationRoom } from "@/store/store";
import { Users } from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

type ConnectionStatusProps = {
connectedUsers: WeaveConnectedUsersChanged;
};

export const ConnectedUsers = ({ connectedUsers }: Readonly<ConnectionStatusProps>) => {
export const ConnectedUsers = ({
connectedUsers,
}: Readonly<ConnectionStatusProps>) => {
const user = useCollaborationRoom((state) => state.user);

const connectedUserKey = React.useMemo(() => {
const filterOwnUser = Object.keys(connectedUsers).filter(
(actUser) => actUser === user?.name
);
return filterOwnUser?.[0];
}, [user, connectedUsers]);

const { showUsers, restUsers } = React.useMemo(() => {
const filterOwnUser = Object.keys(connectedUsers).filter(
(actUser) => actUser !== user?.name
);
return {
showUsers: filterOwnUser.slice(0, 6),
restUsers: filterOwnUser.slice(6),
};
}, [user, connectedUsers]);

if (Object.keys(connectedUsers).length === 0) {
return null;
}

return (
<div className="pointer-events-none flex gap-5 justify-start items-center">
{Object.keys(connectedUsers).map((user) => {
const userInfo = connectedUsers[user];
return (
<Avatar key={user} className="w-[32px] h-[32px]">
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>{userInfo.name.slice(0, 2)}</AvatarFallback>
</Avatar>
);
})}
<div className="w-full min-h-[40px] flex gap-1 justify-start items-center">
<TooltipProvider delayDuration={300}>
{connectedUserKey && (
<Tooltip>
<TooltipTrigger asChild>
<button className="cursor-pointer">
<AvatarUI className="w-[32px] h-[32px]">
<AvatarFallback>
<Avatar name={user?.name} variant="beam" />
</AvatarFallback>
</AvatarUI>
</button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p className="font-noto-sans-mono text-sm">{user?.name}</p>
</TooltipContent>
</Tooltip>
)}
{/* <div className="w-[1px] h-[20px] bg-zinc-200"></div> */}
{showUsers.map((user) => {
const userInfo = connectedUsers[user];
return (
<Tooltip key={user}>
<TooltipTrigger asChild>
<button className="cursor-pointer">
<AvatarUI className="w-[32px] h-[32px]">
<AvatarFallback>
<Avatar name={userInfo?.name} variant="beam" />
</AvatarFallback>
</AvatarUI>
</button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p className="font-noto-sans-mono text-sm">{userInfo.name}</p>
</TooltipContent>
</Tooltip>
);
})}
{restUsers.length > 0 && (
<>
<div className="w-[1px] mx-1 h-[20px] bg-zinc-200"></div>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenu>
<DropdownMenuTrigger className="rounded-none cursor-pointer p-2 hover:bg-zinc-200 focus:outline-none">
<Users className="rounded-none" />
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
side="bottom"
alignOffset={-6}
sideOffset={8}
className="font-noto-sans-mono rounded-none"
>
{restUsers.map((user) => {
const userInfo = connectedUsers[user];
return (
<DropdownMenuItem
key={user}
className="text-foreground focus:bg-white hover:rounded-none"
>
<AvatarUI className="w-[32px] h-[32px]">
<AvatarFallback>
<Avatar name={userInfo?.name} variant="beam" />
</AvatarFallback>
</AvatarUI>
{userInfo?.name}
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</TooltipTrigger>
<TooltipContent side="bottom">
<p className="font-noto-sans-mono text-sm">More users</p>
</TooltipContent>
</Tooltip>
</>
)}
</TooltipProvider>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function MultiuseOverlay() {
return (
<div
className={
"pointer-events-none absolute top-[calc(50px+8px+4px)] right-2 bottom-[calc(50px+8px+4px)] flex flex-col gap-5 justify-center items-center"
"pointer-events-none absolute top-[calc(99px+12px)] right-2 bottom-[calc(50px+8px+4px)] flex flex-col gap-5 justify-center items-center"
}
>
<div className="w-[320px] p-0 h-full bg-white border border-light-border-3 shadow-xs flex justify-start items-center gap-3 overflow-hidden">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import {
import { Logo } from "@/components/utils/logo";
import { Image as ImageIcon, FileText, Ellipsis, LogOut } from "lucide-react";
import { WeaveExportStageActionParams } from "@inditextech/weavejs-sdk";
import { ConnectionStatus } from "../connection-status";

export function RoomInformationOverlay() {
const router = useRouter();

const instance = useWeave((state) => state.instance);
const weaveConnectionStatus = useWeave((state) => state.connection.status);

const room = useCollaborationRoom((state) => state.room);

Expand Down Expand Up @@ -54,6 +56,7 @@ export function RoomInformationOverlay() {
<div className="p-1 pl-3 bg-white border border-zinc-200 shadow-xs flex justify-start items-center gap-2">
<Logo kind="small" />
<div className="w-[1px] h-4 mx-2 bg-zinc-200"></div>
<ConnectionStatus weaveConnectionStatus={weaveConnectionStatus} />
<div className="flex justify-start items-center font-noto-sans-mono text-foreground !normal-case min-h-[32px] pr-2">
{room}
</div>
Expand Down
13 changes: 8 additions & 5 deletions code/components/room-components/overlay/room-status-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

import React from "react";
import { ConnectedUsers } from "./../connected-users";
import { ConnectionStatus } from "./../connection-status";
import { useWeave } from "@inditextech/weavejs-react";

export function RoomStatusOverlay() {
const weaveConnectionStatus = useWeave((state) => state.connection.status);
const connectedUsers = useWeave((state) => state.users);

return (
<div className="absolute top-2 right-2 flex flex-col gap-1 justify-center items-center">
<div className="w-[320px] p-2 bg-white border border-zinc-200 shadow-xs flex flex-col justify-start items-center">
<div className="w-full flex justify-between items-center gap-4">
<div className="w-[320px] min-h-[50px] p-2 bg-white border border-zinc-200 shadow-xs flex flex-col justify-start items-center">
<div className="w-full min-h-[40px] h-full flex flex-col justify-between items-center gap-2">
<ConnectedUsers connectedUsers={connectedUsers} />
<ConnectionStatus weaveConnectionStatus={weaveConnectionStatus} />
<div className="w-full flex justify-center gap-2 items-center text-center font-noto-sans-mono text-xs border-t border-zinc-200 pt-1">
<div className="px-2 mt-1 py-1 bg-accent">
{Object.keys(connectedUsers).length}
</div>
<div>user(s) connected</div>
</div>
</div>
</div>
</div>
Expand Down
24 changes: 18 additions & 6 deletions code/components/room-components/toolbar/toolbar-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import React from "react";
import { cn } from "@/lib/utils";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";

type ToolbarButtonProps = {
icon: React.ReactNode;
Expand All @@ -13,18 +18,25 @@ type ToolbarButtonProps = {
tooltipSide?: "top" | "bottom" | "left" | "right";
};

export function ToolbarButton({ icon, label = "tool", onClick, disabled = false, active = false, tooltipSide = "right" }: Readonly<ToolbarButtonProps>) {
export function ToolbarButton({
icon,
label = "tool",
onClick,
disabled = false,
active = false,
tooltipSide = "right",
}: Readonly<ToolbarButtonProps>) {
return (
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<button
<button
className={cn(
"pointer-events-auto cursor-pointer hover:bg-zinc-200 px-2 py-2 flex justify-center items-center",
{
["bg-zinc-200"]: active,
["pointer-events-none cursor-default opacity-50"]: disabled,
},
}
)}
disabled={disabled}
onClick={onClick}
Expand All @@ -37,5 +49,5 @@ export function ToolbarButton({ icon, label = "tool", onClick, disabled = false,
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
};
);
}
6 changes: 6 additions & 0 deletions code/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@tanstack/react-query": "^5.67.1",
"boring-avatars": "^1.11.2",
"canvas": "^3.1.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand Down