diff --git a/app/components/Hero.tsx b/app/components/Hero.tsx new file mode 100644 index 0000000..da9e497 --- /dev/null +++ b/app/components/Hero.tsx @@ -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 ( +
+
+
+
+
+
+
+ + ) +} 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/components/WelcomeHero.tsx b/app/components/WelcomeHero.tsx index 2505042..a235d1f 100644 --- a/app/components/WelcomeHero.tsx +++ b/app/components/WelcomeHero.tsx @@ -4,9 +4,9 @@ import { Countdown } from "./common/Countdown"; export const WelcomeHero = () => { return ( -
-
-
+
+
+

El evento de programación del año

diff --git a/app/components/common/Countdown.tsx b/app/components/common/Countdown.tsx index 56ca661..9907b9a 100644 --- a/app/components/common/Countdown.tsx +++ b/app/components/common/Countdown.tsx @@ -65,7 +65,7 @@ export const Countdown = ({ className, startFrom }: CountDownProps) => { /* Use cn to override the wraper if need it */ return ( -
+
{time.map(({ key, label }, index) => (
diff --git a/app/cursors-context.tsx b/app/cursors-context.tsx index f7e9e20..292cea5 100644 --- a/app/cursors-context.tsx +++ b/app/cursors-context.tsx @@ -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 + 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,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 ( - + {props.children} ); diff --git a/app/cursors.tsx b/app/cursors.tsx new file mode 100644 index 0000000..1cede2e --- /dev/null +++ b/app/cursors.tsx @@ -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 ( +
+ {cursorsSliced.map((cursor) => ( + + ))} +
+ ); +} diff --git a/app/other-cursor.tsx b/app/other-cursor.tsx index 06540e4..ac2d97e 100644 --- a/app/other-cursor.tsx +++ b/app/other-cursor.tsx @@ -1,97 +1,36 @@ -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; - fill: string; + cursor: Cursor windowDimensions: { width: number; height: number }; }) { - const { id, windowDimensions } = props; - const { fill } = props ?? "#000"; - const { others } = useCursors(); - const cursor = others[id]; - if (!cursor) { - return null; - } - - /* - if (cursor.lastUpdated < Date.now() - 10000) { - return null - } - */ - - const offset = 10; - const left = cursor.x * windowDimensions.width - offset; - const top = cursor.y * windowDimensions.height - offset; - - 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 pointer = cursor.pointer ?? "mouse"; + const { cursor, windowDimensions } = props; - const lastUpdate = new Date(cursor.lastUpdate).toLocaleTimeString(); + const left = cursor.x * windowDimensions.width; + const top = cursor.y * windowDimensions.height; return ( -
- {pointer === "touch" ? ( - - - - - - - ) : ( - - - - - - - )} -
+ + + + -
+
); } diff --git a/app/page.tsx b/app/page.tsx index f783bdd..3eb2656 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,4 +1,3 @@ -import SharedSpace from "./shared-space"; import CursorsContextProvider from "./cursors-context"; import { Contributors } from "./components/common/Contributors"; import { Divider } from "./components/common/Divider"; @@ -6,6 +5,8 @@ import { Schedule } from "./components/Schedule"; import { FAQ } from "./components/FAQ"; import { Footer } from "./components/Footer"; import { TicketDownload } from "./components/TicketDownload"; +import Hero from "./components/Hero"; +import Cursors from "./cursors"; export default function Home({ searchParams, @@ -16,17 +17,18 @@ export default function Home({ const room = typeof searchParams?.partyroom === "string" ? searchParams.partyroom - : "voronoi-room"; + : "aforshow-room"; const host = typeof searchParams?.partyhost === "string" ? searchParams.partyhost - : "voronoi-party.genmon.partykit.dev"; + : "aforshow-2024-party.jarrisondev.partykit.dev"; return ( -
- - + +
+ +
@@ -37,7 +39,7 @@ export default function Home({
- -
+
+ ); } diff --git a/app/self-cursor.tsx b/app/self-cursor.tsx deleted file mode 100644 index ea10a45..0000000 --- a/app/self-cursor.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useCursors } from "./cursors-context"; - -export default function SelfCursor(props: { - windowDimensions: { width: number; height: number }; -}) { - const { windowDimensions } = props; - const { self } = useCursors(); - const fill = "#000"; - - if (!self) { - return null; - } - - const offset = 10; - const left = self.x * windowDimensions.width - offset; - const top = self.y * windowDimensions.height - offset; - - return ( -
- - - - - - -
- ); -} diff --git a/app/shared-space.tsx b/app/shared-space.tsx deleted file mode 100644 index 31a2d63..0000000 --- a/app/shared-space.tsx +++ /dev/null @@ -1,78 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; - -import { useCursors } from "./cursors-context"; -import OtherCursor from "./other-cursor"; -import SelfCursor from "./self-cursor"; -import { WelcomeHero } from "./components/WelcomeHero"; - -import { Nav } from "./components/Nav"; -import { Sponsors } from "./components/Sponsors"; - -export default function SharedSpace() { - const { others, self } = 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); - }; - }, []); - - useEffect(() => { - // Scroll to top - window.scrollTo(0, 0); - return () => { - document.body.classList.remove("overflow-hidden"); - }; - }, []); - - const count = Object.keys(others).length + (self ? 1 : 0); - - return ( -
-
- {count > 0 && ( -
- {count}× - 🐁 -
- )} -
- -
-
-
-
-
- - {Object.keys(others).map((id) => ( -
- -
- ))} - {self?.pointer === "touch" && ( - - )} -
- ); -} diff --git a/partykit.json b/partykit.json index 426c63e..5228e09 100644 --- a/partykit.json +++ b/partykit.json @@ -2,5 +2,5 @@ "$schema": "https://www.partykit.io/schema.json", "name": "aforshow-2024-party", "main": "party/server.ts", - "compatibilityDate": "2024-07-16" -} + "compatibilityDate": "2024-07-30" +} \ No newline at end of file