diff --git a/apps/www/components/LaunchWeek/13/MetalTicket.tsx b/apps/www/components/LaunchWeek/13/MetalTicket.tsx index ea6181e925d..e0a83224aa7 100644 --- a/apps/www/components/LaunchWeek/13/MetalTicket.tsx +++ b/apps/www/components/LaunchWeek/13/MetalTicket.tsx @@ -9,45 +9,50 @@ import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js' import { useParams } from 'common' import { cn } from 'ui' -const ThreeCanvas: React.FC<{ username: string; className?: string }> = ({ - username = 'Francesco Sansalvadore', - className, -}) => { +const ThreeCanvas: React.FC<{ + username: string + className?: string + ticketPosition?: 'left' | 'right' +}> = ({ username = 'Francesco Sansalvadore', className, ticketPosition = 'right' }) => { const params = useParams() const canvasRef = useRef(null) const ticketRef = useRef(null) - const orbitRef = useRef(null) const animationFrameRef = useRef() const targetRotation = useRef<{ x: number; y: number }>({ x: 0, y: 0 }) const textLines = params?.username?.split(' ').reverse() || username?.split(' ').reverse() || [] const LINE_HEIGHT = 1.4 + const MIN_CANVAS_HEIGHT = 600 + const positionRight = ticketPosition === 'right' useEffect(() => { if (!canvasRef.current) return - const isDesktop = () => window.innerWidth > 1024 - const desktopWidth = () => (window.innerWidth / 2) * 3 - 140 - const canvasWidth = isDesktop() ? desktopWidth() : window.innerWidth - const canvasHeight = window.innerHeight - 65 + const isDesktop = (width: number) => width > 1024 + const calculateDesktopWidth = () => window.innerWidth + const initialCanvasWidth = window.innerWidth + const initialCanvasHeight = + window.innerHeight < MIN_CANVAS_HEIGHT ? MIN_CANVAS_HEIGHT - 65 : window.innerHeight - 65 + const getTicketScale = (width: number) => (isDesktop(width) ? 0.35 : 0.3) + const getTicketXPosition = (width: number, isRight: boolean) => + isDesktop(width) ? (isRight ? 5 : -5) : 0 + const ticketYIdleRotation = isDesktop(window.innerWidth) ? 0 : 0 // Initialize scene, camera, and renderer const scene = new THREE.Scene() - const camera = new THREE.PerspectiveCamera(75, canvasWidth / canvasHeight, 0.1, 1000) + const camera = new THREE.PerspectiveCamera( + 25, + initialCanvasWidth / initialCanvasHeight, + 0.1, + 1000 + ) const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) - renderer.setSize(canvasWidth, canvasHeight) + renderer.setSize(initialCanvasWidth, initialCanvasHeight) renderer.shadowMap.enabled = true canvasRef.current.appendChild(renderer.domElement) - // Create orbit object for camera rotation - const orbit = new THREE.Object3D() - orbit.rotation.order = 'YXZ' - scene.add(orbit) - orbitRef.current = orbit - // Camera setup - const cameraDistance = 30 + const cameraDistance = 10 camera.position.z = cameraDistance - orbit.add(camera) // Rest of your existing setup code (loader, materials, etc.) const loader = new GLTFLoader() @@ -70,6 +75,9 @@ const ThreeCanvas: React.FC<{ username: string; className?: string }> = ({ let ticket3DImport: THREE.Mesh const ticketGroup = new THREE.Group() + ticketGroup.position.x = getTicketXPosition(window.innerWidth, positionRight) + const ticketScale = getTicketScale(window.innerWidth) + ticketGroup.scale.set(ticketScale, ticketScale, ticketScale) loader.load('/images/launchweek/13/ticket/3D-ticket.glb', (gltf) => { ticket3DImport = gltf.scene.children[0] as THREE.Mesh @@ -140,11 +148,12 @@ const ThreeCanvas: React.FC<{ username: string; className?: string }> = ({ textMesh.castShadow = true ticketGroup.add(textMesh) }) + + camera.position.x = 0 }) }) camera.position.z = 30 - ticketGroup.scale.set(1.1, 1.1, 1.1) const getTicketScreenPosition = () => { if (!ticketRef.current) return null @@ -153,22 +162,19 @@ const ThreeCanvas: React.FC<{ username: string; className?: string }> = ({ ticketRef.current.getWorldPosition(vector) vector.project(camera) - const x = (vector.x * 0.5 + 0.5) * canvasWidth - const y = -(vector.y * 0.5 - 0.5) * canvasHeight + const x = (vector.x * 0.5 + 0.5) * initialCanvasWidth + const y = -(vector.y * 0.5 - 0.5) * initialCanvasHeight return { x, y } } // Animation function with smooth transitions const animate = () => { - if (orbitRef.current) { - // Smooth interpolation - orbitRef.current.rotation.x += - (targetRotation.current.x - orbitRef.current.rotation.x) * 0.1 - orbitRef.current.rotation.y += - (targetRotation.current.y - orbitRef.current.rotation.y) * 0.1 - orbitRef.current.rotation.z = 0 - } + // Smooth interpolation + ticketGroup.rotation.x += (targetRotation.current.x - ticketGroup.rotation.x) * 0.1 + ticketGroup.rotation.y += + ticketYIdleRotation + (targetRotation.current.y - ticketGroup.rotation.y) * 0.1 + ticketGroup.rotation.z = 0 renderer.render(scene, camera) animationFrameRef.current = requestAnimationFrame(animate) @@ -179,7 +185,7 @@ const ThreeCanvas: React.FC<{ username: string; className?: string }> = ({ // Mouse movement handler with distance-based sensitivity const handleMouseMove = (e: MouseEvent) => { - if (!canvasRef.current || !orbitRef.current) return + if (!canvasRef.current) return // Get canvas bounds const canvasRect = canvasRef.current.getBoundingClientRect() @@ -215,10 +221,18 @@ const ThreeCanvas: React.FC<{ username: string; className?: string }> = ({ // Handle window resize const handleResize = () => { - const newWidth = isDesktop() ? desktopWidth() : window.innerWidth - camera.aspect = newWidth / (window.innerHeight - 65) + const newWidth = isDesktop(window.innerWidth) ? calculateDesktopWidth() : window.innerWidth + ticketGroup.position.x = getTicketXPosition(window.innerWidth, positionRight) + const tickeScale = getTicketScale(window.innerWidth) + ticketGroup.scale.set(tickeScale, tickeScale, tickeScale) + camera.aspect = + newWidth / + (window.innerHeight < MIN_CANVAS_HEIGHT ? MIN_CANVAS_HEIGHT - 65 : window.innerHeight - 65) camera.updateProjectionMatrix() - renderer.setSize(newWidth, window.innerHeight - 65) + renderer.setSize( + newWidth, + window.innerHeight < MIN_CANVAS_HEIGHT ? MIN_CANVAS_HEIGHT - 65 : window.innerHeight - 65 + ) } // Event listeners diff --git a/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx b/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx index d22c5437713..9408151f5a7 100644 --- a/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx +++ b/apps/www/components/LaunchWeek/13/Multiplayer/CanvasPartyMode.tsx @@ -47,7 +47,7 @@ interface CellState { export const INTERACTIVE_GRID_COLORS = (isDark: boolean) => ({ GRID_STROKE: isDark ? '#242424' : '#EDEDED', - CURRENT_USER_HOVER: isDark ? '#242424' : '#d3d3d3', + CURRENT_USER_HOVER: isDark ? '#00311D' : '#72E3AD', }) // Generate a random user id diff --git a/apps/www/pages/launch-week/tickets/[username].tsx b/apps/www/pages/launch-week/tickets/[username].tsx index cd30ef4dc1a..a8e0db8bb00 100644 --- a/apps/www/pages/launch-week/tickets/[username].tsx +++ b/apps/www/pages/launch-week/tickets/[username].tsx @@ -6,24 +6,19 @@ import dynamic from 'next/dynamic' import Link from 'next/link' import Error from 'next/error' import { createClient, Session } from '@supabase/supabase-js' -import { Button, Divider, Separator } from 'ui' -import { SITE_URL, LW_URL, LW12_DATE } from '~/lib/constants' +import { Button } from 'ui' +import { SITE_URL, LW_URL } from '~/lib/constants' import supabase from '~/lib/supabase' import { Database } from '~/lib/database.types' import { AnimatePresence, m, LazyMotion, domAnimation } from 'framer-motion' import { DEFAULT_TRANSITION, INITIAL_BOTTOM, getAnimation } from '~/lib/animations' +import MetalTicket from '~/components/LaunchWeek/13/MetalTicket' import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' -import LW12Background from '~/components/LaunchWeek/12/LW12Background' -import { useTheme } from 'next-themes' -import TicketCopy from '~/components/LaunchWeek/12/Ticket/TicketCopy' - -const LW12TicketContainer = dynamic( - () => import('~/components/LaunchWeek/12/Ticket/TicketContainer') -) -const CTABanner = dynamic(() => import('~/components/CTABanner')) +import CanvasSingleMode from '~/components/LaunchWeek/13/Multiplayer/CanvasSingleMode' +import classNames from 'classnames' interface Props { user: UserData @@ -37,12 +32,12 @@ export default function UsernamePage({ user, ogImageUrl }: Props) { const DISPLAY_NAME = name || username const FIRST_NAME = DISPLAY_NAME?.split(' ')[0] const TITLE = `${DISPLAY_NAME ? DISPLAY_NAME.split(' ')[0] + '’s' : 'Get your'} Launch Week Ticket` - const DESCRIPTION = `Claim your Supabase Launch Week 12 ticket for a chance to win supa swag.` + const DESCRIPTION = `Claim your Supabase Launch Week 13 ticket for a chance to win supa swag.` const PAGE_URL = `${LW_URL}/tickets/${username}` const [session] = useState(null) const [ticketState, setTicketState] = useState('ticket') - const { resolvedTheme, setTheme } = useTheme() + // const { resolvedTheme, setTheme } = useTheme() // const isDark = resolvedTheme?.includes('dark') // const isDarkTheme = resolvedTheme === 'dark' @@ -93,15 +88,8 @@ export default function UsernamePage({ user, ogImageUrl }: Props) { setTicketState, }} > - - + +
@@ -110,25 +98,25 @@ export default function UsernamePage({ user, ogImageUrl }: Props) { initial={initial} animate={animate} exit={exit} - className="w-full flex-1 min-h-[400px] h-full flex flex-col xl:flex-row items-center xl:justify-center xl:items-center gap-8 md:gap-10 xl:gap-32 text-foreground text-center md:text-left" + className="w-full flex-1 h-full flex flex-col lg:flex-row items-center lg:justify-center lg:items-center gap-8 md:gap-10 lg:gap-32 text-foreground text-center md:text-left" > -
- - -
-
-
+
+
+

{DISPLAY_NAME?.split(' ')[0]}'s Ticket

- Join {FIRST_NAME} for Supabase Launch Week 12. Claim your ticket for a + Join {FIRST_NAME} for Supabase Launch Week 13. Claim your ticket for a chance to win limited swag.
@@ -138,8 +126,13 @@ export default function UsernamePage({ user, ogImageUrl }: Props) {
- + + @@ -163,14 +156,14 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { // stores the og images in supabase storage fetch(`${SITE_URL}/api-v2/ticket-og?username=${encodeURIComponent(username ?? '')}`) - // supabaseAdmin.functions.invoke(`lw12-ticket-og?username=${encodeURIComponent(username ?? '')}`) + // supabaseAdmin.functions.invoke(`lw13-ticket-og?username=${encodeURIComponent(username ?? '')}`) // fetch a specific user if (username) { const { data } = await supabaseAdmin! .from('tickets_view') .select('name, username, ticket_number, metadata, platinum, secret, role, company, location') - .eq('launch_week', 'lw12') + .eq('launch_week', 'lw13') .eq('username', username) .single() @@ -189,7 +182,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { } const ticketType = user?.secret ? 'secret' : user?.platinum ? 'platinum' : 'regular' - const ogImageUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/images/launch-week/lw12/og/${ticketType}/${username}.png?t=${dayjs(new Date()).format('DHHmmss')}` + const ogImageUrl = `${process.env.NEXT_PUBLIC_SUPABASE_URL}/storage/v1/object/public/images/launch-week/lw13/og/${ticketType}/${username}.png?t=${dayjs(new Date()).format('DHHmmss')}` return { props: {