Skip to content

Commit

Permalink
Merge pull request #50 from jarrisondev/fix-multicursor-bugs
Browse files Browse the repository at this point in the history
Fix multicursor bugs
  • Loading branch information
afordigital authored Jul 31, 2024
2 parents 04fdb81 + e133062 commit d0d0449
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 251 deletions.
22 changes: 22 additions & 0 deletions app/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import React from 'react'
import { Nav } from './Nav'
import { WelcomeHero } from './WelcomeHero'
import { Sponsors } from './Sponsors'

export default function Hero() {
return (
<div className="flex mb-32 flex-col gap-[72px]">
<div className="bg-caBlurBoxes absolute left-[-100px] top-80 h-[200px] w-[200px] blur-[200px] sm:h-[300px] sm:w-[300px]"></div>
<div className="bg-pattern relative flex w-full flex-col gap-[74px] overflow-hidden px-2 pt-4">
<div className="w-full absolute -bottom-20 h-[200px]"></div>
<div className="bg-caBlurBoxes absolute right-[-100px] top-20 h-[400px] w-[200px] blur-[200px] sm:h-[300px] sm:w-[300px]"></div>
<Nav />
<WelcomeHero />
<Sponsors />
</div>
</div>

)
}
34 changes: 33 additions & 1 deletion app/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<nav className="relative px-2 w-full max-w-6xl h-16 mx-auto flex items-center justify-between">
<a href="/" className="flex items-center gap-1 cursor-pointer">
<Logo />
<h1 className="text-2xl text-white font-semibold">Aforshow</h1>
</a>
<Button>Inscribirse</Button>
<div className="flex items-center gap-5 h-full">
<div
className={cn('group flex -space-x-3 hover:cursor-pointer overflow-hidden p-2', {"opacity-70": disabled})}
onClick={() => {
if (setDisabled) setDisabled((prev) => !prev);
}}>
{
cursorsSlice.map((cursor) => (
<div key={cursor.id} className={imgCircleClass}>
<img className="scale-[1.7] absolute top-0 left-0" src={cursor.flagUrl} alt="" />
</div>
))
}
{
cursors.length > slice && (
<div className={cn(imgCircleClass, 'flex items-center justify-center bg-[#121112]')}>
<span className="text-white font-semibold">+{cursors.length - slice}</span>
</div>
)
}
</div>
<Button>Inscribirse</Button>
</div>
</nav>
);
};
6 changes: 3 additions & 3 deletions app/components/WelcomeHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { Countdown } from "./common/Countdown";

export const WelcomeHero = () => {
return (
<section className="mx-auto max-w-6xl w-full h-full">
<div className="relative z-20 flex flex-col gap-12 items-center text-center">
<div className="flex flex-col ">
<section className="mx-auto mt-5 max-w-6xl w-full h-full">
<div className="relative z-20 flex flex-col gap-14 items-center text-center">
<div className="flex flex-col gap-2">
<h2 className="text-caTextSecondary text-lg md:text-[24px]">
El evento de programación del año
</h2>
Expand Down
2 changes: 1 addition & 1 deletion app/components/common/Countdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const Countdown = ({ className, startFrom }: CountDownProps) => {
/* Use cn to override the wraper if need it */

return (
<div className={cn("flex gap-6 md:gap-16", className)}>
<div className={cn("mt-10 flex gap-6 md:gap-16", className)}>
{time.map(({ key, label }, index) => (
<section key={index} className="text-center text-shadow-sm">
<span className="font-bold text-3xl xl:text-6xl">
Expand Down
79 changes: 42 additions & 37 deletions app/cursors-context.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,56 @@
"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;
y: number;
pointer: "mouse" | "touch";
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<Cursor>
disabled: boolean | null
setDisabled: Dispatch<SetStateAction<boolean>> | null
}

export const CursorsContext = createContext<CursorsContextType>({
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<Position | null>(null);
const [disabled, setDisabled] = useState<boolean>(false);
const [dimensions, setDimensions] = useState<{
width: number;
height: number;
Expand All @@ -49,6 +62,24 @@ export default function CursorsContextProvider(props: {
});
const [others, setOthers] = useState<OtherCursorsMap>({});

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"]) => {
Expand Down Expand Up @@ -109,42 +140,16 @@ export default function CursorsContextProvider(props: {
pointer: "mouse",
} as Position;
socket.send(JSON.stringify(position));
setSelf(position);
};
window.addEventListener("mousemove", onMouseMove);

// Also listen for touch events
const onTouchMove = (e: TouchEvent) => {
if (!socket) return;
if (!dimensions.width || !dimensions.height) return;
e.preventDefault();
const position = {
x: e.touches[0].clientX / dimensions.width,
y: e.touches[0].clientY / dimensions.height,
pointer: "touch",
} as Position;
socket.send(JSON.stringify(position));
setSelf(position);
};
window.addEventListener("touchmove", onTouchMove);

// Catch the end of touch events
const onTouchEnd = (e: TouchEvent) => {
if (!socket) return;
socket.send(JSON.stringify({}));
setSelf(null);
};
window.addEventListener("touchend", onTouchEnd);
window.addEventListener("mousemove", onMouseMove);

return () => {
window.removeEventListener("mousemove", onMouseMove);
window.removeEventListener("touchmove", onTouchMove);
window.removeEventListener("touchend", onTouchEnd);
};
}, [socket, dimensions]);

return (
<CursorsContext.Provider value={{ others: others, self: self }}>
<CursorsContext.Provider value={{ cursors, disabled, setDisabled }}>
{props.children}
</CursorsContext.Provider>
);
Expand Down
42 changes: 42 additions & 0 deletions app/cursors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client";

import { useEffect, useState } from "react";
import { useCursors } from "./cursors-context";
import OtherCursor from "./other-cursor";

export default function Cursors() {
const { cursors, disabled } = useCursors();
const [windowDimensions, setWindowDimensions] = useState({
width: 0,
height: 0,
});

useEffect(() => {
const onResize = () => {
setWindowDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener("resize", onResize);
onResize();
return () => {
window.removeEventListener("resize", onResize);
};
}, []);


const cursorsSliced = disabled ? [] : cursors.slice(-15);

return (
<div className="absolute h-full w-full pointer-events-none overflow-hidden">
{cursorsSliced.map((cursor) => (
<OtherCursor
key={cursor.id}
cursor={cursor}
windowDimensions={windowDimensions}
/>
))}
</div>
);
}
Loading

0 comments on commit d0d0449

Please sign in to comment.