diff --git a/app/components/Nav.tsx b/app/components/Nav.tsx index 3ea8b50..d9cc9b2 100644 --- a/app/components/Nav.tsx +++ b/app/components/Nav.tsx @@ -1,14 +1,46 @@ +/* eslint-disable @next/next/no-img-element */ +import { useCursors } from "app/cursors-context"; import { Button } from "./ui/button"; import Logo from "app/components/icons/Logo"; +import { cn } from "./utils"; export const Nav = () => { + const { cursors, disabled, setDisabled } = useCursors(); + + const slice = 3; + const cursorsSlice = cursors.slice(-slice); + + const imgCircleClass = cn('relative rounded-full h-8 w-8 ring-2 ring-white overflow-hidden group-hover:ring-[3px]') + return ( ); }; diff --git a/app/cursors-context.tsx b/app/cursors-context.tsx index 3612efe..292cea5 100644 --- a/app/cursors-context.tsx +++ b/app/cursors-context.tsx @@ -1,7 +1,8 @@ "use client"; -import { useState, useEffect, useContext, createContext, useRef } from "react"; +import { useState, useEffect, useContext, createContext, useRef, Dispatch, SetStateAction } from "react"; import usePartySocket from "partysocket/react"; +import twemoji from "twemoji"; type Position = { x: number; @@ -9,35 +10,47 @@ type Position = { pointer: "mouse"; }; -type Cursor = Position & { +export type Cursor = Position & { + id: string; country: string | null; + flag: string; + flagUrl: string; lastUpdate: number; }; type OtherCursorsMap = { [id: string]: Cursor; }; - interface CursorsContextType { - others: OtherCursorsMap; - self: Position | null; + cursors: Array + disabled: boolean | null + setDisabled: Dispatch> | null } export const CursorsContext = createContext({ - others: {}, - self: null, + cursors: [], + disabled: null, + setDisabled: null, }); export function useCursors() { return useContext(CursorsContext); } +function getFlagEmoji(countryCode: string) { + const codePoints = countryCode + .toUpperCase() + .split("") + .map((char) => 127397 + char.charCodeAt(0)); + return String.fromCodePoint(...codePoints); +} + export default function CursorsContextProvider(props: { host: string; room: string; children: React.ReactNode; }) { - const [self, setSelf] = useState(null); + const [disabled, setDisabled] = useState(false); const [dimensions, setDimensions] = useState<{ width: number; height: number; @@ -49,6 +62,24 @@ export default function CursorsContextProvider(props: { }); const [others, setOthers] = useState({}); + const cursors: Cursor[] = Object.entries(others).map(([id, cursor]): Cursor => { + const flag = cursor.country ? getFlagEmoji(cursor.country) : ""; + + const flagAsImage = twemoji.parse(flag, + { + base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/' + }) + + const flagUrl = flagAsImage.match(/src="([^"]+)"/)?.[1] || ""; + + return { + ...cursor, + id, + flag: flagAsImage, + flagUrl: flagUrl, + } + }); + useEffect(() => { if (socket) { const onMessage = (evt: WebSocketEventMap["message"]) => { @@ -109,7 +140,6 @@ export default function CursorsContextProvider(props: { pointer: "mouse", } as Position; socket.send(JSON.stringify(position)); - setSelf(position); }; window.addEventListener("mousemove", onMouseMove); @@ -119,7 +149,7 @@ export default function CursorsContextProvider(props: { }, [socket, dimensions]); return ( - + {props.children} ); diff --git a/app/cursors.tsx b/app/cursors.tsx index 6e141fe..1cede2e 100644 --- a/app/cursors.tsx +++ b/app/cursors.tsx @@ -5,7 +5,7 @@ import { useCursors } from "./cursors-context"; import OtherCursor from "./other-cursor"; export default function Cursors() { - const { others, self } = useCursors(); + const { cursors, disabled } = useCursors(); const [windowDimensions, setWindowDimensions] = useState({ width: 0, height: 0, @@ -26,28 +26,16 @@ export default function Cursors() { }, []); - // get the last 30 cursors - const cursors = Object.keys(others).slice(-30); - const count = Object.keys(others).length + (self ? 1 : 0); - + const cursorsSliced = disabled ? [] : cursors.slice(-15); return (
-
- {count > 0 && ( -
- {count}× - 🐁 -
- )} -
- - {cursors.map((id) => ( - + {cursorsSliced.map((cursor) => ( + ))}
); diff --git a/app/other-cursor.tsx b/app/other-cursor.tsx index f409e54..ac2d97e 100644 --- a/app/other-cursor.tsx +++ b/app/other-cursor.tsx @@ -1,40 +1,18 @@ -import twemoji from "twemoji"; -import { useCursors } from "./cursors-context"; +import { Cursor } from "./cursors-context"; // NOTE // The pointer SVG is from https://github.com/daviddarnes/mac-cursors // The license is the Apple User Agreement -function getFlagEmoji(countryCode: string) { - const codePoints = countryCode - .toUpperCase() - .split("") - .map((char) => 127397 + char.charCodeAt(0)); - return String.fromCodePoint(...codePoints); -} - export default function OtherCursor(props: { - id: string; + cursor: Cursor windowDimensions: { width: number; height: number }; }) { - const { id, windowDimensions } = props; - const { others } = useCursors(); - const cursor = others[id]; - if (!cursor) { - return null; - } + const { cursor, windowDimensions } = props; const left = cursor.x * windowDimensions.width; const top = cursor.y * windowDimensions.height; - const flag = cursor.country ? getFlagEmoji(cursor.country) : ""; - - const flagAsImage = twemoji.parse(flag, - { - base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/', - className:'twemoji', - }) - return (