diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx index 043fdeb0f83..27caa65f6e1 100644 --- a/apps/docs/pages/_app.tsx +++ b/apps/docs/pages/_app.tsx @@ -11,7 +11,7 @@ import { AuthProvider, ThemeProvider, useTelemetryProps, useThemeSandbox } from import Head from 'next/head' import { useRouter } from 'next/router' import { useCallback, useEffect, useState, type PropsWithChildren } from 'react' -import { PortalToast, TabsProvider } from 'ui' +import { PortalToast, PromoToast, TabsProvider } from 'ui' import { CommandMenuProvider } from 'ui-patterns/Cmdk' import { useConsent } from 'ui-patterns/ConsentToast' @@ -229,6 +229,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
+
diff --git a/apps/docs/public/favicon/browserconfig.xml b/apps/docs/public/favicon/browserconfig.xml index c5541482230..1a8136def88 100644 --- a/apps/docs/public/favicon/browserconfig.xml +++ b/apps/docs/public/favicon/browserconfig.xml @@ -1,2 +1,2 @@ -#ffffff \ No newline at end of file +#ffffff \ No newline at end of file diff --git a/apps/studio/public/favicon/browserconfig.xml b/apps/studio/public/favicon/browserconfig.xml index c5541482230..1a8136def88 100644 --- a/apps/studio/public/favicon/browserconfig.xml +++ b/apps/studio/public/favicon/browserconfig.xml @@ -1,2 +1,2 @@ -#ffffff \ No newline at end of file +#ffffff \ No newline at end of file diff --git a/apps/www/components/Footer/index.tsx b/apps/www/components/Footer/index.tsx index f14c03d8f50..38f57d975d0 100644 --- a/apps/www/components/Footer/index.tsx +++ b/apps/www/components/Footer/index.tsx @@ -20,7 +20,7 @@ const Footer = (props: Props) => { const { resolvedTheme } = useTheme() const { pathname } = useRouter() - const isLaunchWeek = pathname.includes('launch-week') + const isLaunchWeek = pathname.includes('launch-week') || pathname.includes('special-announcement') const forceDark = isLaunchWeek || pathname === '/' if (props.hideFooter) { diff --git a/apps/www/components/Hero/Hero.tsx b/apps/www/components/Hero/Hero.tsx index a6082ccfe61..492834269e2 100644 --- a/apps/www/components/Hero/Hero.tsx +++ b/apps/www/components/Hero/Hero.tsx @@ -6,6 +6,7 @@ import gaEvents from '~/lib/gaEvents' import { Button, IconBookOpen } from 'ui' import SectionContainer from '~/components/Layouts/SectionContainer' import HeroFrameworks from './HeroFrameworks' +import AnnouncementBadge from '../Announcement/Badge' const Hero = () => { const router = useRouter() @@ -22,13 +23,13 @@ const Hero = () => {
- {/*
+
-
*/} +

Build in a weekend diff --git a/apps/www/components/LaunchWeek/11/Countdown.tsx b/apps/www/components/LaunchWeek/11/Countdown.tsx new file mode 100644 index 00000000000..d0a61eb6c30 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Countdown.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import Countdown from 'react-countdown' +import { CountdownWidget } from 'ui-patterns/CountdownWidget' + +const CountdownComponent = ({ + date, + showCard = true, + className, + size = 'small', +}: { + date: string | number | Date + showCard?: boolean + className?: string + size?: 'small' | 'large' +}) => { + if (!date) return null + + const renderer = ({ days, hours, minutes, seconds, completed }: any) => { + if (completed) { + // Render a completed state + return null + } else { + // Render countdown + return ( + + ) + } + } + + return +} + +export default CountdownComponent diff --git a/apps/www/components/LaunchWeek/11/GALogo.tsx b/apps/www/components/LaunchWeek/11/GALogo.tsx new file mode 100644 index 00000000000..808721e3534 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/GALogo.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { cn } from 'ui' + +interface Props { + className?: string +} + +const GALogo = ({ className }: Props) => { + return ( + + + + + + + ) +} + +export default GALogo diff --git a/apps/www/components/LaunchWeek/11/HackathonCallout.tsx b/apps/www/components/LaunchWeek/11/HackathonCallout.tsx new file mode 100644 index 00000000000..e65aae08570 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/HackathonCallout.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { TextLink, cn } from 'ui' + +const HackathonCallout = ({ className }: { className?: string }) => { + return ( +
+
+ Join the Hackathon +
+
+ +
+
+ ) +} + +export default HackathonCallout diff --git a/apps/www/components/LaunchWeek/11/LW11Background/Dot.js b/apps/www/components/LaunchWeek/11/LW11Background/Dot.js new file mode 100644 index 00000000000..489ad673d4b --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LW11Background/Dot.js @@ -0,0 +1,20 @@ +export function Dot(x, y, w, h, opacity, animationConfig, color = '255,255,255') { + this.x = x + this.y = y + this.w = w + this.h = h + this.opacity = opacity + this.anim = animationConfig + this.isVert = this.anim?.direction === 'vertical' + + this.draw = function (c) { + c.fillRect(this.x, this.y, this.w, this.h) + c.fillStyle = `rgba(${color},${this.opacity})` + + c.fill() + } + + this.update = function (c, clock) { + this.draw(c, clock) + } +} diff --git a/apps/www/components/LaunchWeek/11/LW11Background/index.tsx b/apps/www/components/LaunchWeek/11/LW11Background/index.tsx new file mode 100644 index 00000000000..ed55f8475da --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LW11Background/index.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useState } from 'react' +import anime from 'animejs' +import { useWindowSize } from 'react-use' +import { isBrowser } from 'common' +import { Dot } from './Dot' +import { cn } from 'ui' + +const defaultConfig = { + dotGrid: 40, + percentageLarge: 0.98, + percentageAnimated: 0.6, + randomizeLargeDots: 3, + randomizeSmallDots: 0.5, + minSpeed: 1, + maxSpeed: 2, + minOscillation: 1, + maxOscillation: 12, + minDelay: -3000, + maxDelay: 15000, + minDuration: 200, + maxDuration: 10000, +} + +const LW11Background = ({ className }: { className?: string }) => { + const canvasRef = React.useRef(null) + const [size, setSize] = useState({ w: 1200, h: 800 }) + const [config] = useState(defaultConfig) + const [hasInitialized, setHasInitialized] = useState(false) + const Window = useWindowSize() + + const DOT_AREA = config.dotGrid + let c = canvasRef.current?.getContext('2d') + let dotsArray: any[] = [] + + function init() { + if (!c) return + + setHasInitialized(true) + c = canvasRef.current?.getContext('2d')! + c.clearRect(0, 0, size.w, size.h) + dotsArray = [] + const GRID_COLS = Math.floor(size.w / DOT_AREA) + const GRID_ROWS = Math.floor(size.h / DOT_AREA) + + for (let i = 0; i < GRID_COLS; i++) { + for (let j = 0; j < GRID_ROWS; j++) { + const isLarge = Math.random() > config.percentageLarge + const isAnimated = isLarge || Math.random() > config.percentageAnimated + const direction = isAnimated ? (Math.random() > 0.5 ? 'vertical' : 'horizontal') : undefined + const speed = isAnimated ? anime.random(config.minSpeed, config.maxSpeed) : undefined + const opacity = isLarge ? 1 : anime.random(0.4, 1) + const isReverse = isAnimated ? Math.random() > 0.5 : undefined + const oscillation = isAnimated + ? anime.random(config.minOscillation, config.maxOscillation).toFixed() + : undefined + const dotSize = isLarge + ? Math.random() * config.randomizeLargeDots + : Math.random() * config.randomizeSmallDots + const endPos = { + x: anime + .random( + DOT_AREA * 1 - DOT_AREA / 2 + dotSize / 2, + DOT_AREA * 10 - DOT_AREA / 2 + dotSize / 2 + ) + .toFixed(), + y: anime + .random( + DOT_AREA * 1 - DOT_AREA / 2 + dotSize / 2, + DOT_AREA * 10 - DOT_AREA / 2 + dotSize / 2 + ) + .toFixed(), + } + const delay = anime.random(config.minDelay, config.maxDelay) + const duration = anime.random(config.minDuration, config.maxDuration) + const animationConfig: any = isAnimated + ? { + direction, + speed, + isReverse, + oscillation, + endPos, + delay, + duration, + } + : undefined + const x = (size.w / GRID_COLS) * i + DOT_AREA / 2 - dotSize / 2 + const y = (size.h / GRID_ROWS) * j + DOT_AREA / 2 - dotSize / 2 + const w = dotSize + const h = dotSize + + dotsArray.push(new Dot(x, y, w, h, opacity, animationConfig)) + } + } + + animate(0) + } + + function animate(clock?: number) { + if (!isBrowser) return + + for (let i = 0; i < dotsArray.length; i++) { + dotsArray[i].update(c, clock) + } + + const tl = anime + .timeline({ + targets: dotsArray.filter((dot) => dot.anim), + loop: true, + direction: 'alternate', + autoplay: true, + update: renderParticule, + }) + .add( + { + x: (p: any) => + !p.isVert ? `${p.anim?.isReverse ? '+' : '-'}=${DOT_AREA * p.anim.oscillation}` : p.x, + y: (p: any) => + p.isVert ? `${p.anim?.isReverse ? '+' : '-'}=${DOT_AREA * p.anim.oscillation}` : p.y, + duration: (p: any) => p.anim?.duration, + delay: (p: any) => p.anim?.delay - 1000, + easing: 'easeInOutExpo', + }, + '-=1000' + ) + + tl.play() + } + + function renderParticule(anim: any) { + if (!isBrowser) return + + c?.clearRect(0, 0, size.w, size.h) + for (let i = 0; i < dotsArray.length; i++) { + dotsArray[i].update(c, 0) + } + for (var i = 0; i < anim.animatables.length; i++) { + anim.animatables[i].target.update(c, 0) + } + } + + function resize() { + setSize({ w: Window.width, h: Window.height }) + init() + } + + useEffect(() => { + if (!isBrowser) return + resize() + }, [Window]) + + useEffect(() => { + resize() + init() + }, []) + + !hasInitialized && init() + + useEffect(() => { + document.body.classList.add('bg-[#060809]') + + return () => { + if (document.body.classList.contains('bg-[#060809]')) { + document.body.classList.remove('bg-[#060809]') + } + } + }, []) + + return ( + + ) +} + +export default LW11Background diff --git a/apps/www/components/LaunchWeek/11/LW11Day1-v1.tsx b/apps/www/components/LaunchWeek/11/LW11Day1-v1.tsx new file mode 100644 index 00000000000..6726c79b4f0 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LW11Day1-v1.tsx @@ -0,0 +1,79 @@ +import React from 'react' +import Link from 'next/link' +import { ArrowRightIcon } from '@heroicons/react/outline' +import { cn } from 'ui' +import { Edit } from 'lucide-react' +import { WeekDayProps } from './Releases/data' +import { DayLink } from './Releases/components' + +const LW11Day1 = ({ day, className }: { day: WeekDayProps; className?: string }) => { + return ( +
+
+
+ {day.dd} {day.date} +
+ + + Blog post + + +
+
+ {day.description} +
+
+ {day.steps?.map((step, i) => ( +
+
+ + + + {step.title} +
+

{step.description}

+
    + {step.links?.map((link) => ( +
  • + +
  • + ))} +
+
+ ))} +
+
+ ) +} + +export default LW11Day1 diff --git a/apps/www/components/LaunchWeek/11/LW11Day1.tsx b/apps/www/components/LaunchWeek/11/LW11Day1.tsx new file mode 100644 index 00000000000..174823e23fd --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LW11Day1.tsx @@ -0,0 +1,94 @@ +import React from 'react' +import { cn } from 'ui' +import { ExpandableVideo } from 'ui-patterns' +import { WeekDayProps } from './Releases/data' +import { DayLink } from './Releases/components' + +const LW11Day1 = ({ day, className }: { day: WeekDayProps; className?: string }) => { + const cssGroup = 'group/d' + day.d + + return ( +
+ {/* Day title and links */} +
+
+ {day.dd}, {day.date} +
+
+ +

Supabase is ready for production use.

+
+ {!!day.links && ( +
    + {day.links?.map((link) => ( +
  • + +
  • + ))} +
+ )} +
+ {/* Card */} +
+
+ {day.steps?.map((step, i) => ( +
+
+ + + + {step.title} +
+

{step.description}

+
    + {step.links?.map((link) => ( +
  • + +
  • + ))} +
+
+ ))} +
+
+
+ ) +} + +export default LW11Day1 diff --git a/apps/www/components/LaunchWeek/11/LW11Meetups.tsx b/apps/www/components/LaunchWeek/11/LW11Meetups.tsx new file mode 100644 index 00000000000..1499bdc023a --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LW11Meetups.tsx @@ -0,0 +1,138 @@ +import Link from 'next/link' +import React, { useEffect, useState } from 'react' +import { TextLink, cn } from 'ui' +import useConfData from '../hooks/use-conf-data' +import { SupabaseClient } from '@supabase/supabase-js' + +export interface Meetup { + id?: any + title: string + isLive: boolean + link: string + display_info: string + start_at: string +} + +function addHours(date: Date, hours: number) { + const dateCopy = new Date(date) + + dateCopy.setHours(dateCopy.getHours() + hours) + + return dateCopy +} + +const LW11Meetups = ({ meetups }: { meetups?: Meetup[] }) => { + const { supabase } = useConfData() + const now = new Date(Date.now()) + const [meets, setMeets] = useState(meetups ?? []) + const [realtimeChannel, setRealtimeChannel] = useState | null>(null) + const [activeMeetup, setActiveMeetup] = useState(meets[0]) + const [isMounted, setIsMounted] = useState(false) + + useEffect(() => { + setIsMounted(true) + }, []) + + useEffect(() => { + // Listen to realtime changes + if (supabase && !realtimeChannel) { + const channel = supabase + .channel('lw11_meetups') + .on( + 'postgres_changes', + { + event: '*', + schema: 'public', + table: 'lw11_meetups', + filter: undefined, + }, + async () => { + const { data: newMeets } = await supabase + .from('lw11_meetups') + .select('*') + .neq('isPublished', false) + console.log('newMeets', newMeets) + setMeets( + newMeets?.sort((a, b) => (new Date(a.start_at) > new Date(b.start_at) ? 1 : -1))! + ) + } + ) + .subscribe(async (status) => { + if (status !== 'SUBSCRIBED') { + return null + } + }) + setRealtimeChannel(channel) + } + + return () => { + // Cleanup realtime subscription on unmount + realtimeChannel?.unsubscribe() + } + }, []) + + function handleSelectMeetup(meetup: Meetup) { + setActiveMeetup(meetup) + } + + if (!isMounted) return null + + return ( +
+
+

Community meetups

+

+ Celebrate{' '} + + Supabase Special Announcement + {' '} + at our live community-driven meetups. Network with the community, listen to tech talks and + grab some swag. +

+ +
+
+ {meets && + meets + ?.sort((a, b) => (new Date(a.start_at) > new Date(b.start_at) ? 1 : -1)) + .map((meetup: Meetup, i: number) => { + const startAt = new Date(meetup.start_at) + const endAt = addHours(new Date(meetup.start_at), 3) + const after = now > startAt + const before3H = now < endAt + const liveNow = after && before3H + + return ( + handleSelectMeetup(meetup)} + onMouseOver={() => handleSelectMeetup(meetup)} + title={liveNow ? 'Live now' : undefined} + className={cn( + 'h-10 group inline-flex items-center flex-wrap text-4xl', + 'hover:text-[#EDEDED] !leading-none transition-colors', + meetup.id === activeMeetup.id ? 'text-[#EDEDED]' : 'text-[#575E61]', + liveNow && 'text-[#B0B0B0]' + )} + > + {liveNow && ( +
+ )} + {meetup.title} + {i !== meets.length - 1 && ', '} + + ) + })} +
+
+ {activeMeetup?.display_info} +
+
+ ) +} + +export default LW11Meetups diff --git a/apps/www/components/LaunchWeek/11/LW11Summary.tsx b/apps/www/components/LaunchWeek/11/LW11Summary.tsx new file mode 100644 index 00000000000..32c1e6a2428 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LW11Summary.tsx @@ -0,0 +1,93 @@ +import React from 'react' +import Image from 'next/image' +import Link from 'next/link' +import { buildDays, mainDays } from './Releases/data' + +const LWXSummary = () => { + return ( +
+
+ + Launch Week{' '} + Supabase Launch Week X icon + +
11-15 Dec
+
+
+
+ Main Stage +
+
    + {mainDays.map( + (day, i: number) => + day.shipped && ( +
      + + + Day {i + 1} - + + {day.description} + +
    + ) + )} +
+
+
+
+
+ Build Stage +
+
    + {buildDays.map( + (day, i: number) => + day.is_shipped && ( +
      + + + + {i + 1 < 10 ? '0' : ''} + {i + 1} - + + {day.title} + + +
    + ) + )} +
      + + Supabase Launch Week X Hackathon + +
    +
      + + Supabase Launch Week X Community Meetups + +
    +
+
+
+
+ ) +} + +export default LWXSummary diff --git a/apps/www/components/LaunchWeek/11/LabelBadge.tsx b/apps/www/components/LaunchWeek/11/LabelBadge.tsx new file mode 100644 index 00000000000..889646a39c1 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LabelBadge.tsx @@ -0,0 +1,14 @@ +import React from 'react' + +interface Props { + text: string + className?: string +} + +export default function LabelBadge({ text, className }: Props) { + return ( + + {text} + + ) +} diff --git a/apps/www/components/LaunchWeek/11/LaunchWeekLogoHeader.tsx b/apps/www/components/LaunchWeek/11/LaunchWeekLogoHeader.tsx new file mode 100644 index 00000000000..24dac3579cd --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LaunchWeekLogoHeader.tsx @@ -0,0 +1,16 @@ +export function LaunchWeekLogoHeader() { + return ( +
+
+

Launch week 8

+

+ August 7th–11th, 2023 +

+
+

A week of announcing new features has come to an end.

+

Thanks to everyone who participated.

+
+
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/11/LaunchWeekPrizeCard.tsx b/apps/www/components/LaunchWeek/11/LaunchWeekPrizeCard.tsx new file mode 100644 index 00000000000..1b42c15db62 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LaunchWeekPrizeCard.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { cn } from 'ui' +import Panel from '~/components/Panel' + +export default function LaunchWeekPrizeCard({ + content, + className, + contentClassName, +}: { + content: any + className?: string + contentClassName?: string +}) { + return ( + + {content} + + ) +} diff --git a/apps/www/components/LaunchWeek/11/LaunchWeekPrizeSection.tsx b/apps/www/components/LaunchWeek/11/LaunchWeekPrizeSection.tsx new file mode 100644 index 00000000000..4ef9f265595 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/LaunchWeekPrizeSection.tsx @@ -0,0 +1,78 @@ +import React from 'react' +import { cn } from 'ui' +import LabelBadge from './LabelBadge' +import LaunchWeekPrizeCard from './LaunchWeekPrizeCard' +import Image from 'next/image' +import PrizeActions from './Releases/PrizeActions' + +export default function LaunchWeekPrizeSection({ className }: { className?: string }) { + return ( +
+

Awards

+

Generate your ticket for a chance to win.

+
+
+ +
+ Supabase AirPod Max prize +
+
+
+ +

Win AirPods Max

+

+ Secure your ticket to enter our random prize pool, and amplify your odds by + sharing. Or if you don't leave anything up for chance - join our Hackathon and + showcase your creations. With luck or skill, you could snag these top-tier + headphones! +

+
+
+ +
+
+
+ } + /> +
+ + +

Supabase T-shirts

+
+ } + /> + + +

Supabase Pins

+
+ } + /> +
+
+

+
+ ) +} diff --git a/apps/www/components/LaunchWeek/11/Releases/BuildStage.tsx b/apps/www/components/LaunchWeek/11/Releases/BuildStage.tsx new file mode 100644 index 00000000000..48f6e57ca53 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/BuildStage.tsx @@ -0,0 +1,75 @@ +import React, { FC } from 'react' +import { buildDays as days } from '~/components/LaunchWeek/X/Releases/data' + +import SectionContainer from '~/components/Layouts/SectionContainer' +import AdventCard from './components/AdventCard' +import { motion, useInView } from 'framer-motion' +import { cn } from 'ui' + +const BuildStage: FC = () => { + const ref = React.useRef(null) + const isInView = useInView(ref, { margin: '-25%', once: true }) + + const variants = { + reveal: { + transition: { + type: 'spring', + damping: 10, + mass: 0.75, + stiffness: 100, + staggerChildren: 0.08, + }, + }, + } + + return ( + <> + +

+ Build Stage +

+ + {days.map((day, i) => ( +
  • + + {!day.is_shipped && ( +
    + + + + + +
    + )} +
  • + ))} +
    +
    + + ) +} + +export default BuildStage diff --git a/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx b/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx new file mode 100644 index 00000000000..bd95c7db2bb --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/LW11Header.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import { cn } from 'ui' +import GALogo from '../GALogo' +import SectionContainer from '~/components/Layouts/SectionContainer' +import LW11Background from '../LW11Background' + +const LW11Header = ({ className }: { className?: string }) => { + return ( +
    + + +

    + Join us in this major milestone and explore all the features that come with it. +

    +
    + +
    + ) +} + +export default LW11Header diff --git a/apps/www/components/LaunchWeek/11/Releases/LW11StickyNav.tsx b/apps/www/components/LaunchWeek/11/Releases/LW11StickyNav.tsx new file mode 100644 index 00000000000..cc666ba224e --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/LW11StickyNav.tsx @@ -0,0 +1,87 @@ +import React, { FC, useEffect, useRef } from 'react' +import Link from 'next/link' +import { WeekDayProps, mainDays as days } from './data' +import { cn } from 'ui' +import { isBrowser } from 'common' + +import SectionContainer from '~/components/Layouts/SectionContainer' + +const LWXStickyNav: FC = () => { + const OFFSET = 66 + const anchors = useRef | null>(null) + const links = useRef | null>(null) + + const handleScroll = () => { + let newActiveAnchor: string = '' + + anchors.current?.forEach((anchor) => { + const { y: offsetFromTop } = anchor.getBoundingClientRect() + + if (offsetFromTop - OFFSET < 0) { + newActiveAnchor = anchor.id + } + }) + + links.current?.forEach((link) => { + link.classList.remove('!text-foreground') + + const sanitizedHref = decodeURI(link.getAttribute('href') ?? '') + .split('#') + .splice(-1) + .join('') + const isMatch = sanitizedHref === newActiveAnchor + + if (isMatch) { + link.classList.add('!text-foreground') + } + }) + } + + useEffect(() => { + if (!isBrowser) return + anchors.current = document.querySelectorAll('.lwx-nav-anchor') + links.current = document.querySelectorAll('.lwx-sticky-nav li a') + + window.addEventListener('scroll', handleScroll) + return () => { + window.removeEventListener('scroll', handleScroll) + } + }, []) + + return ( +
    + +
    + ) +} + +export default LWXStickyNav diff --git a/apps/www/components/LaunchWeek/11/Releases/MainStage.tsx b/apps/www/components/LaunchWeek/11/Releases/MainStage.tsx new file mode 100644 index 00000000000..e13721e8892 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/MainStage.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { mainDays } from './data' + +import SectionContainer from '~/components/Layouts/SectionContainer' +import DaySection from './components/DaySection' +import LW11Day1 from '../LW11Day1' +import HackathonCallout from '../HackathonCallout' +import { cn } from 'ui' + +const MainStage = ({ className }: { className?: string }) => { + const [day1, ...days] = mainDays + + return ( + + + +
    + {days.map((day) => ( + + ))} +
    +
    + ) +} + +export default MainStage diff --git a/apps/www/components/LaunchWeek/11/Releases/PrizeActions.tsx b/apps/www/components/LaunchWeek/11/Releases/PrizeActions.tsx new file mode 100644 index 00000000000..45947c24262 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/PrizeActions.tsx @@ -0,0 +1,15 @@ +import Link from 'next/link' +import { Button } from 'ui' + +export default function PrizeActions() { + return ( +
    + + +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Releases/components/AdventCard.tsx b/apps/www/components/LaunchWeek/11/Releases/components/AdventCard.tsx new file mode 100644 index 00000000000..0bb656c6daa --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/components/AdventCard.tsx @@ -0,0 +1,119 @@ +import React, { useEffect, useRef, useState } from 'react' +import Link from 'next/link' +import { motion } from 'framer-motion' +import Tilt from 'vanilla-tilt' +import { useWindowSize } from 'react-use' +import { cn } from 'ui' +import { useBreakpoint } from 'common' + +import { AdventDay } from '../data' +import { AdventLink } from '../data/lw11_build_stage' + +const AdventCard = ({ day, index }: { day: AdventDay; index: number }) => { + const isTablet = useBreakpoint(1024) + const tiltRef = useRef(null) + const hiddenRef = useRef(null) + const { width } = useWindowSize() + const [hiddenHeight, setHiddenHeight] = useState(0) + const transition = { type: 'spring', damping: 10, mass: 0.75, stiffness: 100, delay: index / 15 } + const variants = { + initial: { + rotateY: -90, + opacity: 0, + }, + ...(day.is_shipped && { + reveal: { + rotateY: 0, + opacity: 1, + transition, + }, + }), + } + const isClientLibsCard = day.type === 'clientLibs' + + useEffect(() => { + if (tiltRef.current) { + Tilt.init(tiltRef.current, { + glare: false, + max: isClientLibsCard ? 1 : 4, + gyroscope: false, + 'full-page-listening': false, + }) + } + }, [tiltRef]) + + useEffect(() => { + if (hiddenRef?.current) { + const { height } = hiddenRef.current.getBoundingClientRect() + setHiddenHeight(height) + } + }, [hiddenRef, width]) + + return ( +
    + +
    +
    + {day.icon} +
    +
    +

    {day.title}

    +
    +

    {day.description}

    +
    + {day.links?.map((link: AdventLink) => ( + + {link.label} + + ))} +
    +
    +
    +
    + {isClientLibsCard && ( +
    + {day.icons?.map((link: AdventLink) => ( + + {link.icon} + + ))} +
    + )} +
    +
    + ) +} + +export default AdventCard diff --git a/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx b/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx new file mode 100644 index 00000000000..90e22e56584 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/components/DaySection.tsx @@ -0,0 +1,129 @@ +import React from 'react' +import Link from 'next/link' +import Image from 'next/image' +import { ArrowRightIcon } from '@heroicons/react/outline' +import { IconEdit2, cn } from 'ui' +import { useBreakpoint } from 'common' + +import { WeekDayProps } from '../data' +import CountdownComponent from '../../Countdown' +import { DayLink } from '.' + +const DaySection = ({ day }: { day: WeekDayProps }) => { + const isMobile = useBreakpoint(639) + const cssGroup = 'group/d' + day.d + + return ( +
    + {/* Day title and links */} +
    +
    + {day.dd}, {day.date} +
    + {!!day.links && ( +
      + {day.links?.map((link) => ( +
    • + +
    • + ))} +
    + )} +
    + + {/* Day card */} +
    + {day.shipped && day.steps.length > 0 ? ( + +
    +
    + + Blog post + +
    +

    + {day.description} +

    +
    +
    + {day.steps[0]?.bg_layers && + day.steps[0]?.bg_layers?.map( + (layer, i) => + !!layer.img && ( +
    + {day.title} +
    + ) + )} +
    + + ) : ( +
    +
    + + + + + + {day.hasCountdown && } +
    +
    + )} +
    +
    + ) +} + +export default DaySection diff --git a/apps/www/components/LaunchWeek/11/Releases/components/index.tsx b/apps/www/components/LaunchWeek/11/Releases/components/index.tsx new file mode 100644 index 00000000000..d3e59ac0718 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/components/index.tsx @@ -0,0 +1,90 @@ +import { useEffect, useState } from 'react' +import { + IconDocumentation, + IconMicSolid, + IconMusic, + IconProductHunt, + IconYoutubeSolid, + cn, +} from 'ui' + +import Link from 'next/link' +import { StepLink } from '../data/lw11_data' +import { ExpandableVideo } from 'ui-patterns/ExpandableVideo' + +interface DayLink extends StepLink { + className?: string +} + +export const DayLink = ({ type, icon, text, href = '', className }: DayLink) => { + const [isMounted, setIsMounted] = useState(false) + + useEffect(() => { + setIsMounted(true) + }, []) + + if (!isMounted) return null + + const linkTypes = { + blog: { + icon: IconDocumentation, + text: 'Blog Post', + }, + docs: { + icon: IconDocumentation, + text: 'Docs', + }, + productHunt: { + icon: IconProductHunt, + text: 'Product Hunt', + }, + video: { + icon: IconYoutubeSolid, + text: 'Watch video', + }, + podcast: { + icon: IconMusic, + text: 'Podcast', + }, + xSpace: { + icon: IconMicSolid, + text: 'X Space', + }, + } + const isTargetBlank = () => { + switch (type) { + case 'productHunt': + case 'xSpace': + case 'docs': + return true + } + } + const Icon = icon ?? linkTypes[type].icon + const Text = () => <>{text ?? linkTypes[type]?.text} + + const Component = type === 'video' ? 'div' : Link + + const Asd = ({ component: Comp, ...props }: any) => ( + + + + + + + ) + + if (type === 'video') + return } /> + + return +} + +export default { + DayLink, +} diff --git a/apps/www/components/LaunchWeek/11/Releases/data/index.ts b/apps/www/components/LaunchWeek/11/Releases/data/index.ts new file mode 100644 index 00000000000..23d04f18591 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/data/index.ts @@ -0,0 +1,4 @@ +export { default as mainDays } from './lw11_data' +export type { StepProps, StepLink, WeekDayProps } from './lw11_data' +export { days as buildDays } from './lw11_build_stage' +export type { AdventDay } from './lw11_build_stage' diff --git a/apps/www/components/LaunchWeek/11/Releases/data/lw11_build_stage.tsx b/apps/www/components/LaunchWeek/11/Releases/data/lw11_build_stage.tsx new file mode 100644 index 00000000000..edb2274039d --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/data/lw11_build_stage.tsx @@ -0,0 +1,471 @@ +export interface AdventDay { + icon?: any + className?: string + id: string + title: string + description?: string + is_shipped: boolean + links: AdventLink[] + icons?: AdventLink[] + type?: string +} + +export interface AdventLink { + url: string + label?: string + icon?: any + target?: '_blank' +} + +export const days: AdventDay[] = [ + { + title: 'Supabase Album', + description: 'The best soundtrack to build your app in a weekend', + icon: ( + + + + + + ), + id: 'supabase-album', + is_shipped: true, + links: [ + { + url: 'https://supabase.productions/', + label: 'Listen now', + target: '_blank', + }, + ], + }, + { + title: 'Postgres Language Server', + description: 'Implementing the Parser', + icon: ( + + + + + ), + id: 'postgres-language-server-implementing-parser', + is_shipped: true, + links: [ + { + url: '/blog/postgres-language-server-implementing-parser', + label: 'Read blog post', + }, + ], + }, + { + title: 'Design at Supabase', + description: "The transformative journey of Supabase's Design team", + icon: ( + + + + + ), + id: 'design-at-supabase', + is_shipped: true, + links: [ + { + url: '/blog/how-design-works-at-supabase', + label: 'Read blog post', + }, + ], + }, + { + title: 'Supabase Grafana', + description: 'Observability for your Supabase project', + is_shipped: true, + icon: ( + + + + ), + id: 'supabase-grafana', + links: [ + { + url: 'https://github.com/supabase/supabase-grafana', + label: 'View on GitHub', + target: '_blank', + }, + ], + }, + { + title: 'pg_graphql: Postgres functions', + description: 'pg_graphql now supports User Defined Functions (UDFs)', + is_shipped: true, + icon: ( + + + + ), + id: 'pg-graphql-postgres-functions', + links: [ + { + url: '/blog/pg-graphql-postgres-functions', + label: 'Read blog post', + }, + ], + }, + { + title: 'PostgREST 12', + description: 'PostgREST 12 is out: take a look at some of the major new features', + is_shipped: true, + className: 'xl:col-span-2 xl:h-auto xl:aspect-auto', + icon: ( + + + + + ), + id: 'postgrest-12', + links: [ + { + url: '/blog/postgrest-12', + label: 'Read blog post', + }, + ], + }, + { + title: 'Supavisor 1.0', + description: 'A scalable connection pooler for Postgres', + is_shipped: true, + icon: ( + + + + ), + id: 'supavisor', + links: [ + { + url: '/blog/supavisor-postgres-connection-pooler', + label: 'Read blog post', + }, + ], + }, + { + title: 'Supabase Wrappers v0.2', + description: 'Query Pushdown & Remote Subqueries', + is_shipped: true, + icon: ( + + + + ), + id: 'supabase-wrappers-v0.2', + links: [ + { + url: '/blog/supabase-wrappers-v02', + label: 'Read blog post', + }, + ], + }, + { + title: 'Supabase Libraries V2', + description: 'Swift, Kotlin, C#, and Python are now stable and moving to the v2 API', + type: 'clientLibs', + is_shipped: true, + className: 'xl:col-span-2 xl:aspect-auto xl:col-span-3 xl:aspect-auto xl:h-full', + id: 'supabase-libraries-v2', + links: [ + { + url: '/blog/client-libraries-v2', + label: 'Read blog post', + }, + ], + icons: [ + { + url: '/blog/client-libraries-v2#supabase-python-v2', + label: 'Python', + icon: ( + + + + + ), + }, + { + url: '/blog/client-libraries-v2#supabase-swift-v2', + label: 'Swift', + icon: ( + + + + ), + }, + { + url: '/blog/client-libraries-v2#supabase-kotlin-v2', + label: 'Kotlin', + icon: ( + + + + ), + }, + { + url: '/blog/client-libraries-v2#typescript-v2-updates', + label: 'Typescript', + icon: ( + + + + ), + }, + { + url: '/blog/client-libraries-v2#flutter-v2-updates', + label: 'Flutter', + icon: ( + + + + + ), + }, + ], + }, + { + title: 'Supabase x Fly.io', + description: + "We're launching a managed Postgres offering jointly developed by Supabase and Fly.io", + is_shipped: true, + className: 'xl:col-span-2 xl:aspect-auto h-full', + icon: ( + + + + + + + + ), + id: 'fly-postgres', + links: [ + { + url: '/blog/postgres-on-fly-by-supabase', + label: 'Read blog post', + }, + ], + }, + { + title: 'Top 10 Launches of LWX', + description: 'Our CEO takes a look at his favorite ships from LWX', + className: 'sm:aspect-[2/1] sm:col-span-1 sm:col-span-2 sm:aspect-[2/1] h-full', + is_shipped: true, + icon: ( + + + + + ), + id: 'launch-week-x-best-launches', + links: [ + { + url: '/blog/launch-week-x-best-launches', + label: 'Read blog post', + }, + ], + }, +] diff --git a/apps/www/components/LaunchWeek/11/Releases/data/lw11_data.tsx b/apps/www/components/LaunchWeek/11/Releases/data/lw11_data.tsx new file mode 100644 index 00000000000..c5084999db7 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Releases/data/lw11_data.tsx @@ -0,0 +1,305 @@ +import { ReactNode } from 'react' +import { products } from 'shared-data/products' + +type StepLinkType = 'productHunt' | 'video' | 'docs' | 'xSpace' | 'blog' | 'podcast' + +export interface StepLink { + type: StepLinkType + text?: string + icon?: any + href: string +} + +export interface StepProps { + title: string + icon?: string + badge?: string + blog?: string + docs?: string + description?: string + github?: string + hackernews?: string + product_hunt?: string + thumb?: string + url?: string + video?: string + twitter_spaces?: string + className?: string + hideInBlog?: boolean + bg_layers?: { + img?: string + mobileImg?: string + className?: string + }[] + steps?: StepProps[] | [] + links?: StepLink[] +} + +export interface WeekDayProps { + id: string + title: string + shipped: boolean + date: string + published_at: string + isToday?: boolean + hasCountdown?: boolean + description: string | ReactNode + d: number + dd: string + links?: StepLink[] + videoId?: string + videoThumbnail?: string + blog: string + steps: StepProps[] | [] +} + +export const endOfLWXHackathon = '2024-04-17T23:59:59.999-08:00' + +const days: WeekDayProps[] = [ + { + id: 'day-1', + d: 1, + dd: 'Mon', + title: 'Lorem ipsum', + shipped: true, + isToday: false, + blog: '#', + hasCountdown: true, + date: '15 April', + published_at: '2024-04-15T08:00:00.000-08:00', + videoThumbnail: '/images/launchweek/copple-placeholder.jpg', + links: [ + { + type: 'blog', + href: '#', + }, + { + type: 'xSpace', + href: '', + }, + ], + description: ( + <> +

    Our products are now ready for production use.

    {' '} +

    + Confidently leverage the full power of Supabase to build scalable, high-performance + applications with ease. +

    + + ), + steps: [ + { + icon: products.database.icon[16], + title: 'Database', + description: 'Fully portable Postgres Database', + links: [ + // { + // type: 'podcast', + // href: '#', + // }, + { + type: 'docs', + href: 'https://supabase.com/docs/guides/database/overview', + }, + ], + }, + { + icon: products.authentication.icon[16], + title: 'Auth', + description: 'User management out of the box', + links: [ + // { + // type: 'podcast', + // href: '#', + // }, + { + type: 'docs', + href: 'https://supabase.com/docs/guides/auth', + }, + ], + }, + { + icon: products.storage.icon[16], + title: 'Storage', + description: 'Serverless storage for any media', + links: [ + // { + // type: 'podcast', + // href: '#', + // }, + { + type: 'docs', + href: 'https://supabase.com/docs/guides/storage', + }, + ], + }, + { + icon: products.functions.icon[16], + title: 'Edge Functions', + description: 'Deploy code globally on the edge', + links: [ + // { + // type: 'podcast', + // href: '#', + // }, + { + type: 'docs', + href: 'https://supabase.com/docs/guides/functions', + }, + ], + }, + { + icon: products.realtime.icon[16], + title: 'Realtime', + description: 'Syncronize and broadcast events', + links: [ + // { + // type: 'podcast', + // href: '#', + // }, + { + type: 'docs', + href: 'https://supabase.com/docs/guides/realtime', + }, + ], + }, + { + icon: products.vector.icon[16], + title: 'Vector', + description: 'AI toolkit to manage embeddings', + links: [ + // { + // type: 'podcast', + // href: '#', + // }, + { + type: 'docs', + href: 'https://supabase.com/docs/guides/ai', + }, + ], + }, + ], + }, + { + id: 'day-2', + d: 2, + dd: 'Tue', + title: '', + shipped: false, + hasCountdown: false, + blog: '#', + date: '16 April', + published_at: '2024-04-16T08:00:00.000-08:00', + description: <>TBD, + links: [ + // { + // type: 'blog', + // href: '#', + // }, + // { + // type: 'video', + // href: 'eCbiywoDORw', + // }, + // { + // type: 'xSpace', + // href: '#', + // }, + ], + steps: [ + { + title: '', + blog: '#', + bg_layers: [ + { + img: '/images/launchweek/lwx/day2/d2_edge.svg', + mobileImg: '/images/launchweek/lwx/day2/d2_edge_mobile.svg', + }, + ], + steps: [], + }, + ], + }, + { + id: 'day-3', + d: 3, + dd: 'Wed', + title: '', + shipped: false, + hasCountdown: false, + blog: '#', + date: '17 April', + published_at: '2024-04-17T08:00:00.000-08:00', + description: <>TBD, + links: [ + // { + // type: 'blog', + // href: '#', + // }, + // { + // type: 'video', + // href: '', + // }, + // { + // type: 'xSpace', + // href: '#', + // }, + ], + steps: [], + }, + { + id: 'day-4', + d: 4, + dd: 'Thu', + title: '', + shipped: false, + hasCountdown: false, + blog: '#', + date: '18 April', + published_at: '2024-04-18T08:00:00.000-08:00', + description: <>TBD, + links: [ + // { + // type: 'blog', + // href: '#', + // }, + // { + // type: 'video', + // href: '', + // }, + // { + // type: 'xSpace', + // href: '#', + // }, + ], + steps: [], + }, + { + id: 'day-5', + d: 5, + dd: 'Fri', + title: '', + shipped: false, + hasCountdown: false, + blog: '#', + date: '19 April', + published_at: '2024-04-19T08:00:00.000-08:00', + description: <>TBD, + links: [ + // { + // type: 'blog', + // href: '#', + // }, + // { + // type: 'video', + // href: '', + // }, + // { + // type: 'xSpace', + // href: '#', + // }, + ], + steps: [], + }, +] + +export default days diff --git a/apps/www/components/LaunchWeek/11/Ticket/LW11Game.tsx b/apps/www/components/LaunchWeek/11/Ticket/LW11Game.tsx new file mode 100644 index 00000000000..4e5c3950e7c --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/LW11Game.tsx @@ -0,0 +1,240 @@ +import { Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react' +import { Button, cn } from 'ui' +import { SITE_ORIGIN } from '~/lib/constants' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import { VALID_KEYS } from '~/components/LaunchWeek/hooks/useLwGame' +import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'ui' + +const REGEXP_ONLY_CHARS = '^[a-zA-Z]+$' + +const COMPLIMENTS = [ + 'Congratulations!', + 'You won!', + 'Hurray!', + 'Great job!', + 'Nice job!', + "That's right!", +] + +interface Props { + setIsGameMode: Dispatch> +} + +const LWXGame = ({ setIsGameMode }: Props) => { + const { supabase, userData: user } = useConfData() + const phrase = process.env.NEXT_PUBLIC_LWX_GAME_WORD ?? 'its_just_postgres' + const inputRef = useRef(null) + const winningPhrase = phrase?.split('_').map((word) => word.split('')) + const phraseLength = phrase?.replaceAll('_', '').split('').length + const [gameState, setGameState] = useState<'playing' | 'winner' | 'loading'>('playing') + const [hasKeyDown, setHasKeyDown] = useState(false) + const [value, setValue] = useState('') + const hasWon = gameState === 'winner' + const winningCompliment = useMemo( + () => COMPLIMENTS[Math.floor(Math.random() * COMPLIMENTS.length)], + [] + ) + + function onKeyDown(event: KeyboardEvent) { + const newKey = event.key.toLocaleLowerCase() + + if (!(event.metaKey || event.ctrlKey) && VALID_KEYS.includes(newKey)) { + setHasKeyDown(true) + } + + setTimeout(() => { + setHasKeyDown(false) + }, 100) + } + + useEffect(() => { + window.addEventListener('keydown', onKeyDown) + return () => window.removeEventListener('keydown', onKeyDown) + }, [onKeyDown]) + + async function handleGithubSignIn() { + const redirectTo = `${SITE_ORIGIN}/launch-week/${ + user.username ? '?referral=' + user.username : '' + }` + + supabase?.auth.signInWithOAuth({ + provider: 'github', + options: { + redirectTo, + }, + }) + } + const handleClaimTicket = async (e: any) => { + e.preventDefault() + + setGameState('loading') + + if (supabase) { + if (user.id) { + await supabase + .from('lw11_tickets') + .update({ gameWonAt: new Date() }) + .eq('username', user.username) + .then((res) => { + if (res.error) return console.log('error', res.error) + setIsGameMode(false) + }) + } else { + localStorage.setItem('lw11_secret', 'true') + + handleGithubSignIn() + } + } + } + + function handleIndexCount(word_idx: number, char_idx: number) { + let index = -1 + + for (let i = 0; i < word_idx + 1; i++) { + const word = winningPhrase[i] + for (let j = 0; j < word.length; j++) { + if (i < word_idx || j <= char_idx) index++ + } + } + + return index + } + + if (gameState === 'loading') + return ( +
    + + + +
    + ) + + return ( +
    +
    +
    +

    + {winningCompliment} +

    +

    + Claim and share the secret ticket to boost your chances of winning swag. +

    +
    +
    + Guess the word +
    +
    +
    + { + // @ts-ignore + const index = inputRef.current?.dataset.inputOtpMss + const valueAtPhraseIndex = phrase?.replaceAll('_', '').split('')[index] + const currentVal = e.split('')[e.length - 1] + const isMatch = valueAtPhraseIndex === currentVal + + if (isMatch) { + setValue(e) + } + }} + onComplete={() => setGameState('winner')} + > + {winningPhrase.map((word, w_idx) => ( + <> + + {word.map((_, c_idx) => { + // index is sum of every letter of every previous word + index of current wo + const currentIndex = handleIndexCount(w_idx, c_idx) + return ( + + ) + })} + + {w_idx !== winningPhrase.length - 1 && } + + ))} + +
    +
    + +
    +
    +
    + + Play +
    +
    + + Exit +
    +
    +
    + ) +} + +export default LWXGame diff --git a/apps/www/components/LaunchWeek/11/Ticket/Ticket.tsx b/apps/www/components/LaunchWeek/11/Ticket/Ticket.tsx new file mode 100644 index 00000000000..321f9ffd49f --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/Ticket.tsx @@ -0,0 +1,114 @@ +import { useEffect, useRef, useState } from 'react' +import Image from 'next/image' +import { IconEdit2, IconX, cn } from 'ui' +import Tilt from 'vanilla-tilt' +import { useBreakpoint, useParams } from 'common' + +import Panel from '~/components/Panel' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import TicketProfile from './TicketProfile' +import TicketCustomizationForm from './TicketCustomizationForm' +import TicketNumber from './TicketNumber' + +export default function Ticket() { + const ticketRef = useRef(null) + const { userData: user, showCustomizationForm, setShowCustomizationForm } = useConfData() + const isMobile = useBreakpoint() + const { + platinum = false, + bg_image_id: bgImageId = '1', + secret: hasSecretTicket, + ticketNumber, + } = user + const [imageHasLoaded, setImageHasLoaded] = useState(false) + const params = useParams() + const sharePage = !!params.username + const ticketType = hasSecretTicket ? 'secret' : platinum ? 'platinum' : 'regular' + + const fallbackImg = `/images/launchweek/11/tickets/shape/lw11_ticket_${ticketType}.png` + + const ticketBg = { + regular: { + background: `/images/launchweek/11/tickets/shape/lw11_ticket_regular.png`, + }, + platinum: { + background: `/images/launchweek/11/tickets/shape/lw11_ticket_platinum.png`, + }, + secret: { + background: `/images/launchweek/11/tickets/shape/lw11_ticket_purple.png`, + }, + } + + function handleCustomizeTicket() { + setShowCustomizationForm && setShowCustomizationForm(!showCustomizationForm) + } + + useEffect(() => { + if (ticketRef.current && !window.matchMedia('(pointer: coarse)').matches) { + Tilt.init(ticketRef.current, { + glare: true, + max: 3, + gyroscope: true, + 'max-glare': 0.2, + 'full-page-listening': true, + }) + } + }, [ticketRef]) + + return ( +
    + + + + {`Launch setImageHasLoaded(true)} + loading="eager" + fill + className={cn( + 'absolute inset-0 object-cover object-right opacity-0 transition-opacity duration-1000', + imageHasLoaded && 'opacity-100', + isMobile && 'object-left-top' + )} + priority + quality={100} + /> + {/* Edit hover button */} + {!sharePage && ( + <> + + +
    +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketContainer.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketContainer.tsx new file mode 100644 index 00000000000..7c67072a457 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketContainer.tsx @@ -0,0 +1,17 @@ +import { useParams } from 'common' +import Ticket from './Ticket' +import TicketCopy from './TicketCopy' + +export default function TicketContainer() { + const params = useParams() + const sharePage = !!params.username + + return ( +
    + +
    + +
    +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketCopy.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketCopy.tsx new file mode 100644 index 00000000000..b042319c5c1 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketCopy.tsx @@ -0,0 +1,45 @@ +import { useState, useRef } from 'react' +import { SPECIAL_ANNOUNCEMENT_URL } from '~/lib/constants' +import { IconCheck, IconCopy, cn } from 'ui' +import useConfData from '../../hooks/use-conf-data' + +export default function TicketCopy({ sharePage }: { sharePage: boolean }) { + const { userData } = useConfData() + const { username, platinum, secret } = userData + const [copied, setCopied] = useState(false) + const buttonRef = useRef(null) + const hasSecretTicket = secret + const url = `${SPECIAL_ANNOUNCEMENT_URL}/tickets/${username}?lw=11${ + hasSecretTicket ? '&secret=true' : platinum ? `&platinum=true` : '' + }` + + return ( +
    + +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketCustomizationForm.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketCustomizationForm.tsx new file mode 100644 index 00000000000..d2286718b71 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketCustomizationForm.tsx @@ -0,0 +1,162 @@ +import React, { useEffect, useState } from 'react' +import { Button, Checkbox, IconCheck, Input, cn } from 'ui' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import { useBreakpoint, useDebounce } from 'common' +import { useKey } from 'react-use' + +const TicketCustomizationForm = ({ className }: { className?: string }) => { + const isMobile = useBreakpoint() + const { + supabase, + userData: user, + showCustomizationForm, + setShowCustomizationForm, + } = useConfData() + const defaultFormValues = { + role: user.metadata?.role, + company: user.metadata?.company, + hideAvatar: !!user.metadata?.hideAvatar, + } + const [formData, setFormData] = useState(defaultFormValues) + const [formState, setFormState] = useState<'idle' | 'saved' | 'saving' | 'error'>('idle') + const IS_SAVING = formState === 'saving' + const IS_SAVED = formState === 'saved' + const HAS_ERROR = formState === 'error' + + useKey('Escape', () => setShowCustomizationForm && setShowCustomizationForm(false)) + + const handleInputChange = (name: string, value: string | boolean) => { + console.log('name', name, 'value', value) + setFormData((prev) => ({ ...prev, [name]: value })) + + console.log('formData', formData) + } + + const handleFormSubmit = async () => { + setFormState('saving') + const payload = { + metadata: { + ...user.metadata, + ...formData, + }, + } + + if (supabase) { + await supabase + .from('lw11_tickets') + .update(payload) + .eq('username', user.username) + .then((res) => { + if (res.error) return setFormState('error') + setFormState('saved') + setTimeout(() => { + setFormState('idle') + }, 1200) + }) + } + } + + const debouncedChangeHandler = useDebounce(handleFormSubmit, 1200) + + useEffect(() => { + return () => setShowCustomizationForm && setShowCustomizationForm(false) + }, []) + + return ( +
    debouncedChangeHandler()} + onSubmit={(e) => e.preventDefault()} + > + { + handleInputChange('role', event.target.value) + }} + onFocus={() => + !showCustomizationForm && setShowCustomizationForm && setShowCustomizationForm(true) + } + disabled={IS_SAVING} + maxLength={25} + icon={ + + } + /> + { + handleInputChange('company', event.target.value) + }} + onFocus={() => + !showCustomizationForm && setShowCustomizationForm && setShowCustomizationForm(true) + } + disabled={IS_SAVING} + icon={ + + } + /> + { + console.log(event) + handleInputChange('hideAvatar', event.target.checked) + }} + /> +
    +
    + {IS_SAVED && ( + + Saved + + )} + {HAS_ERROR && ( + + Something went wrong + + )} +
    + +
    + + ) +} + +export default TicketCustomizationForm diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketFooter.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketFooter.tsx new file mode 100644 index 00000000000..e2f12672f27 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketFooter.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { LWX_DATE } from '~/lib/constants' +import TicketNumber from './TicketNumber' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import Image from 'next/image' +import { cn } from 'ui' + +export default function TicketFooter() { + const { userData: user } = useConfData() + const { ticketNumber, golden, metadata } = user + const hasLightTicket = golden || metadata?.hasSecretTicket + + const SupabaseLogo = hasLightTicket + ? '/images/launchweek/lwx/logos/supabase_lwx_logo_light.png' + : '/images/launchweek/lwx/logos/supabase_lwx_logo_dark.png' + + return ( +
    + {/* Supabase Logo for Launch Week X */} + + {/* Launch Week X + {LWX_DATE} */} +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketForm.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketForm.tsx new file mode 100644 index 00000000000..4ca2370257a --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketForm.tsx @@ -0,0 +1,147 @@ +import { useState, useEffect } from 'react' +import { SupabaseClient } from '@supabase/supabase-js' +import { useRouter } from 'next/router' +import { SITE_ORIGIN } from '~/lib/constants' +import { Button, IconCheckCircle } from 'ui' + +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' + +type FormState = 'default' | 'loading' | 'error' + +export default function TicketForm() { + const [formState, setFormState] = useState('default') + const [realtimeChannel, setRealtimeChannel] = useState | null>(null) + const [errorMsg] = useState('') + const { supabase, session, setUserData, setTicketState, userData } = useConfData() + const router = useRouter() + + // Triggered on session + async function fetchUser() { + if (supabase && session?.user && !userData.id) { + const username = session.user.user_metadata.user_name + const name = session.user.user_metadata.full_name + const email = session.user.email + + await supabase + .from('lw11_tickets') + .insert({ + email, + name, + username, + referred_by: router.query?.referral ?? null, + }) + .eq('email', email) + .select() + .single() + .then(async ({ error }: any) => { + // If error because of duplicate email, ignore and proceed, otherwise sign out. + if (error && error?.code !== '23505') { + setFormState('error') + return supabase.auth.signOut() + } + const { data } = await supabase + .from('lw11_tickets_platinum') + .select('*') + .eq('username', username) + .single() + if (data) { + setUserData(data) + } + + setFormState('default') + + // Prefetch GitHub avatar + new Image().src = `https://github.com/${username}.png` + + // Prefetch the twitter share URL to eagerly generate the page + fetch(`/launch-week/tickets/${username}`).catch((_) => {}) + + if (!realtimeChannel) { + const channel = supabase + .channel('changes') + .on( + 'postgres_changes', + { + event: 'UPDATE', + schema: 'public', + table: 'lw11_tickets', + filter: `username=eq.${username}`, + }, + (payload: any) => { + const platinum = !!payload.new.sharedOnTwitter && !!payload.new.sharedOnLinkedIn + const secret = !!payload.new.gameWonAt + setUserData({ + ...payload.new, + platinum, + secret, + }) + } + ) + .subscribe() + setRealtimeChannel(channel) + } + }) + } + } + + useEffect(() => { + fetchUser() + + return () => { + // Cleanup realtime subscription on unmount + realtimeChannel?.unsubscribe() + } + }, [session]) + + async function handleGithubSignIn() { + if (formState !== 'default') { + setFormState('default') + return + } + + setFormState('loading') + setTicketState('loading') + + const redirectTo = `${SITE_ORIGIN}/launch-week/${ + userData.username ? '?referral=' + userData.username : '' + }` + + supabase?.auth.signInWithOAuth({ + provider: 'github', + options: { + redirectTo, + }, + }) + } + + return formState === 'error' ? ( +
    +
    {errorMsg}
    + +
    + ) : ( +
    + +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketHeader.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketHeader.tsx new file mode 100644 index 00000000000..1cbf1f371f4 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketHeader.tsx @@ -0,0 +1,13 @@ +import React from 'react' + +interface Props { + golden?: boolean +} + +export default function TicketHeader({ golden = false }: Props) { + return ( +
    + Launch Week X +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketNumber.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketNumber.tsx new file mode 100644 index 00000000000..aae10fed0ef --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketNumber.tsx @@ -0,0 +1,31 @@ +import { cn } from 'ui' + +type Props = { + number: number | undefined + secret?: boolean + platinum?: boolean + className?: string +} + +export default function TicketNumber({ + number, + platinum = false, + secret = false, + className, +}: Props) { + const numDigits = `${number}`.length + const prefix = `0000000`.slice(numDigits) + const ticketNumberText = `NO ${prefix}${number}` + + return ( + + {ticketNumberText} + + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketPresence.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketPresence.tsx new file mode 100644 index 00000000000..1a569113bd2 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketPresence.tsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react' +import { SupabaseClient } from '@supabase/supabase-js' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import solutions from '~/data/Solutions' +import { cn } from 'ui' + +const TicketPresence = (props: { className?: string }) => { + const { supabase, ticketState } = useConfData() + const hasTicket = ticketState === 'ticket' + const [realtimeChannel, setRealtimeChannel] = useState | null>(null) + const [onlineUsers, setOnlineUsers] = useState([]) + const isSingular = onlineUsers.length === 1 + const lowTraffic = onlineUsers.length < 5 + + useEffect(() => { + // Listen to realtime presence + if (!realtimeChannel && supabase) { + const lwxRoom = supabase?.channel('lwx_online', { + config: { broadcast: { self: true, ack: true } }, + }) + + setRealtimeChannel(lwxRoom) + const userStatus = {} + + lwxRoom + ?.on('presence', { event: 'sync' }, () => { + const newState = lwxRoom.presenceState() + const users = [...Object.entries(newState).map(([_, value]) => value[0])] + const uniqueUsrs = users.filter(onlyUnique) + setOnlineUsers(uniqueUsrs) + }) + .subscribe(async (status) => { + if (status !== 'SUBSCRIBED') { + return null + } + await lwxRoom.track(userStatus) + }) + } + + return () => { + realtimeChannel?.unsubscribe() + } + }, []) + + return ( +
    + + {onlineUsers.length} {isSingular ? 'person is' : 'people are'}{' '} + {hasTicket ? 'customizing' : 'generating'} their ticket right now +
    + ) +} + +function onlyUnique(value: any, index: number, array: any[]) { + return array.indexOf(value) === index +} + +export default TicketPresence diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketProfile.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketProfile.tsx new file mode 100644 index 00000000000..4313d9338ad --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketProfile.tsx @@ -0,0 +1,52 @@ +import Image from 'next/image' +import { cn } from 'ui' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' + +export default function TicketProfile({ className }: { className?: string }) { + const { userData: user } = useConfData() + const { username, name, metadata, platinum, secret: hasSecretTicket } = user + const hasLightTicket = platinum + const profileImg = `https://github.com/${username}.png?size=60` + + const hideAvatar = !!metadata?.hideAvatar + const HAS_ROLE = !!metadata?.role + const HAS_COMPANY = !!metadata?.company + const HAS_NO_META = !HAS_ROLE && !HAS_COMPANY + + return ( +
    +
    + {!hideAvatar && profileImg && ( + {`${username} + )} +

    {name || username || 'Your Name'}

    + {HAS_NO_META && username && ( +

    + @{username} +

    + )} +
    + {HAS_ROLE && {metadata?.role}} + {HAS_COMPANY && ( + + {HAS_ROLE && at } + {metadata?.company} + + )} +
    +
    +
    + ) +} diff --git a/apps/www/components/LaunchWeek/11/Ticket/TicketingFlow.tsx b/apps/www/components/LaunchWeek/11/Ticket/TicketingFlow.tsx new file mode 100644 index 00000000000..c7f2eee0b41 --- /dev/null +++ b/apps/www/components/LaunchWeek/11/Ticket/TicketingFlow.tsx @@ -0,0 +1,213 @@ +import React from 'react' +import dynamic from 'next/dynamic' +import Image from 'next/image' +import { AnimatePresence, m, LazyMotion, domAnimation } from 'framer-motion' +import { Badge, cn } from 'ui' +import { DEFAULT_TRANSITION, INITIAL_BOTTOM, getAnimation } from '~/lib/animations' +import { LW11_DATE, LW11_LAUNCH_DATE } from '~/lib/constants' +import useWinningChances from '../../hooks/useWinningChances' +import useLwGame from '../../hooks/useLwGame' + +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import SectionContainer from '~/components/Layouts/SectionContainer' +import TicketContainer from '~/components/LaunchWeek/11/Ticket/TicketContainer' +import LW11Background from '../LW11Background' +import TicketForm from './TicketForm' +import CountdownComponent from '../Countdown' +import TicketPresence from './TicketPresence' +import TicketActions from './TicketActions' + +const LWGame = dynamic(() => import('./LW11Game')) + +const TicketingFlow = () => { + const { ticketState, userData, showCustomizationForm } = useConfData() + const { isGameMode, setIsGameMode } = useLwGame(ticketState !== 'ticket' || showCustomizationForm) + + const isLoading = !isGameMode && ticketState === 'loading' + const isRegistering = !isGameMode && ticketState === 'registration' + const hasTicket = !isGameMode && ticketState === 'ticket' + const hasPlatinumTicket = userData.platinum + const hasSecretTicket = userData.secret + const metadata = userData?.metadata + + const transition = DEFAULT_TRANSITION + const initial = INITIAL_BOTTOM + const animate = getAnimation({ duration: 1 }) + const exit = { opacity: 0, transition: { ...transition, duration: 0.2 } } + + const winningChances = useWinningChances() + + return ( + <> + +
    +

    Supabase Special Announcement | {LW11_DATE}

    +
    + + + {isLoading && ( + +
    + +
    + + + +
    + )} + {isRegistering && ( + +
    +

    + Join us for a Special Announcement +

    +

    + Celebrate a major milestone with us and explore all the features that come + with it. +

    +
    +
    +

    {LW11_DATE}

    + +
    + +
    + )} + {hasTicket && ( + +
    + +
    +
    + {hasSecretTicket && Secret ticket} + {hasPlatinumTicket ? ( +
    + {hasSecretTicket && !metadata?.hasSharedSecret ? ( +

    + Share again to boost your chance of winning! +

    + ) : ( +

    Thanks for sharing!

    + )} +

    + Join on April 15-19 to celebrate a major milestone with us and explore + all the features that come with it. +

    +
    + ) : winningChances !== 2 ? ( +
    + {!hasSecretTicket && ( +

    @{userData.username}, you're in!

    + )} +

    + Now share your ticket to have a chance of winning AirPods Max and other + limited swag. +

    +
    + ) : ( +
    +

    @{userData.username}, almost there!

    +

    + Keep sharing to max out your chances of winning AirPods Max and other + limited swag. +

    +
    + )} +
    + +
    +
    + + Starts in: + + +
    +
    +
    + Supabase AirPod Max prize +
    +
    +

    5 sets

    +

    Win AirPods Max

    +

    + Grow your chances of winning limited edition swag by sharing on{' '} + X and{' '} + Linkedin. +

    +
    +
    + {!hasPlatinumTicket && } +
    +
    + )} + {!showCustomizationForm && isGameMode && ( + + + + )} +
    +
    +
    +
    +
    + + + ) +} + +export default TicketingFlow diff --git a/apps/www/components/LaunchWeek/11/TicketsGrid.tsx b/apps/www/components/LaunchWeek/11/TicketsGrid.tsx new file mode 100644 index 00000000000..78c5df2204f --- /dev/null +++ b/apps/www/components/LaunchWeek/11/TicketsGrid.tsx @@ -0,0 +1,99 @@ +import { motion } from 'framer-motion' +import Image from 'next/image' +import Link from 'next/link' +import { useBreakpoint } from 'common/hooks/useBreakpoint' +import { UserData } from '../hooks/use-conf-data' + +interface Props { + loadedUsers: UserData[] + isLoading: boolean + pageCount: number + offset: number +} + +export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset }: Props) { + const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lwx' + const getTicketImageUrl = (username: string, isGold: boolean) => + `${STORAGE_URL}/tickets/${isGold ? 'platinum' : 'regular'}/${username}.png` + const isMobile = useBreakpoint(1024) + const isTablet = useBreakpoint(1280) + const horizontalOffset = isMobile ? 8 : 20 + + return ( +
    + {loadedUsers?.map((user, i) => { + const rowTickets = isMobile ? 2 : isTablet ? 3 : 4 + const divider = Math.floor(i / rowTickets) + const isOddRow = divider % 2 === 0 + // Delay should be no more than pageCount for new loaded users + const recalculatedDelay = i >= pageCount * 2 ? (i - pageCount * (offset - 1)) / 15 : i / 15 + const imgUrl = + getTicketImageUrl(user.username!, !!user.golden) ?? + '/images/launchweek/lwx/tickets/placeholder.png' + + return ( + window.scrollTo(0, 0)} + legacyBehavior + passHref + > + +
    + {user.username +
    +
    + + ) + })} + {isLoading && + Array.from({ length: pageCount }, (_, i) => ( + +
    +
    +
    + + ))} +
    + ) +} diff --git a/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx b/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx index c9879188f24..0e258a3bf86 100644 --- a/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx +++ b/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx @@ -17,7 +17,7 @@ const ParticlesCanvas = () => { // to stop unnecessary computations const handleScroll = () => { if (canvasRef.current && typeof window !== 'undefined') { - const rect = (canvasRef.current as HTMLDivElement)?.getBoundingClientRect() + const rect = (canvasRef.current as HTMLCanvasElement)?.getBoundingClientRect() const isInView = rect.bottom > 0 setAnimate(isInView) diff --git a/apps/www/components/LaunchWeek/8/LW8CalloutsSection.tsx b/apps/www/components/LaunchWeek/8/LW8CalloutsSection.tsx index 8158f241ce7..a01589348dc 100644 --- a/apps/www/components/LaunchWeek/8/LW8CalloutsSection.tsx +++ b/apps/www/components/LaunchWeek/8/LW8CalloutsSection.tsx @@ -24,8 +24,9 @@ const LW8CalloutsSection = () => { twitter spaces thumbnail
    @@ -47,8 +48,9 @@ const LW8CalloutsSection = () => { hackathon thumbnail diff --git a/apps/www/components/LaunchWeek/8/LW8Meetups.tsx b/apps/www/components/LaunchWeek/8/LW8Meetups.tsx index 7ff6e296e76..a2188ecf8c0 100644 --- a/apps/www/components/LaunchWeek/8/LW8Meetups.tsx +++ b/apps/www/components/LaunchWeek/8/LW8Meetups.tsx @@ -61,6 +61,7 @@ const LW8Meetups = ({ meetups }: { meetups?: Meetup[] }) => { ?.sort((a, b) => (new Date(a.start_at) > new Date(b.start_at) ? 1 : -1)) .map(({ display_info, link, isLive, title }: Meetup) => ( { Launch Week 6 @@ -40,8 +41,9 @@ const LWArchive = () => { Launch Week 7 diff --git a/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx index 98268f1e0c7..1363bc9bc1c 100644 --- a/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx +++ b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx @@ -1,6 +1,7 @@ import Image from 'next/image' import React from 'react' import { motion } from 'framer-motion' +import { cn } from 'ui' type animateFromType = 'left' | 'right' | 'up' | 'down' | 'opacityOnly' @@ -56,10 +57,13 @@ export default function LaunchWeekPrizeCard({ > diff --git a/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx index a2982bbc9e4..2664fbed640 100644 --- a/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx +++ b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx @@ -18,9 +18,9 @@ export default function LaunchWeekPrizeSection({ prizes background @@ -32,8 +32,9 @@ export default function LaunchWeekPrizeSection({ prizes @@ -47,7 +48,6 @@ export default function LaunchWeekPrizeSection({ className="col-span-full" imageUrl="/images/launchweek/8/swag/deskmat-lg.jpg" imageWrapperClassName="w-full aspect-[1.9/1] md:h-[300px]" - imgObjectPosition="center" content={

    Supabase Desk Mat diff --git a/apps/www/components/LaunchWeek/8/Releases/components/index.tsx b/apps/www/components/LaunchWeek/8/Releases/components/index.tsx index e9893fb5e7c..a1c0b967c57 100644 --- a/apps/www/components/LaunchWeek/8/Releases/components/index.tsx +++ b/apps/www/components/LaunchWeek/8/Releases/components/index.tsx @@ -85,8 +85,8 @@ export const HackernewsSvg = () => ( export const TwitterSpacesSvg = () => ( @@ -101,8 +101,8 @@ export const ProductHuntSvg = () => ( @@ -118,8 +118,8 @@ export const ProductHuntSvg = () => ( export const CheckCircleSolidIcon = () => ( @@ -259,7 +259,7 @@ export const VideoPreviewTrigger = ({ /> - + {title && {title}} diff --git a/apps/www/components/LaunchWeek/8/Releases/index.tsx b/apps/www/components/LaunchWeek/8/Releases/index.tsx index e90f9c76c6e..7fa6fe08fb9 100644 --- a/apps/www/components/LaunchWeek/8/Releases/index.tsx +++ b/apps/www/components/LaunchWeek/8/Releases/index.tsx @@ -1,6 +1,6 @@ import Image from 'next/image' import Link from 'next/link' -import { Accordion } from 'ui' +import { Accordion, cn } from 'ui' import { useBreakpoint } from 'common/hooks/useBreakpoint' import { @@ -77,7 +77,10 @@ export default function LW8Releases() { preRelease.steps[0]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -121,7 +123,10 @@ export default function LW8Releases() { preRelease.steps[1]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -163,18 +167,25 @@ export default function LW8Releases() { preRelease.steps[2]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -222,18 +233,23 @@ export default function LW8Releases() { day1.steps[0]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -280,17 +296,20 @@ export default function LW8Releases() { day2.steps[0]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -351,15 +370,21 @@ export default function LW8Releases() { day3.steps[0]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -406,15 +431,21 @@ export default function LW8Releases() { day4.steps[0]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -444,14 +475,17 @@ export default function LW8Releases() { day4.steps[1]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -498,14 +532,17 @@ export default function LW8Releases() { day5.steps[0]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -536,14 +573,17 @@ export default function LW8Releases() { day5.steps[1]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    @@ -574,15 +614,20 @@ export default function LW8Releases() { day5.steps[2]?.bg_layers?.map( (layer, i) => !!layer.img && ( -
    +
    diff --git a/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx b/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx index 589a06da539..e4d97229aef 100644 --- a/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx +++ b/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx @@ -5,6 +5,7 @@ import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import { Swiper, SwiperSlide } from 'swiper/react' import 'swiper/swiper.min.css' +import { cn } from 'ui' interface Props { users: UserData[] @@ -24,10 +25,10 @@ export function TicketBrickWallSlider({ users, reverse, speed = 50000, animate } return (
    diff --git a/apps/www/components/LaunchWeek/X/FaviconImports.tsx b/apps/www/components/LaunchWeek/X/FaviconImports.tsx new file mode 100644 index 00000000000..cb681a9de46 --- /dev/null +++ b/apps/www/components/LaunchWeek/X/FaviconImports.tsx @@ -0,0 +1,93 @@ +import Head from 'next/head' +import { useRouter } from 'next/router' + +function FaviconImports() { + const { basePath } = useRouter() + + return ( + + + + + + + + + + + + + + + + + + + + + ) +} + +export default FaviconImports diff --git a/apps/www/components/LaunchWeek/X/LaunchWeekPrizeSection.tsx b/apps/www/components/LaunchWeek/X/LaunchWeekPrizeSection.tsx index 10011ab5e07..f5c9a1e66c1 100644 --- a/apps/www/components/LaunchWeek/X/LaunchWeekPrizeSection.tsx +++ b/apps/www/components/LaunchWeek/X/LaunchWeekPrizeSection.tsx @@ -14,7 +14,6 @@ export default function LaunchWeekPrizeSection({ className }: { className?: stri )} >

    Awards

    -

    Stay tuned on Launch Week X's final day to find out if you're one of the lucky winners.

    {
    - Launch Week{' '} - Supabase Launch Week X icon + + Launch Week + {' '} + + Supabase Launch Week X icon +
    {/* Nav items */} diff --git a/apps/www/components/LaunchWeek/X/Releases/MainStage.tsx b/apps/www/components/LaunchWeek/X/Releases/MainStage.tsx index 237baf8e915..55a393fd604 100644 --- a/apps/www/components/LaunchWeek/X/Releases/MainStage.tsx +++ b/apps/www/components/LaunchWeek/X/Releases/MainStage.tsx @@ -12,11 +12,11 @@ const MainStage: FC = () => (

    - Hackathon concluded + Hackathon finished
    - - - - - ), - id: 'supabase-album', - is_shipped: true, - links: [ - { - url: 'https://supabase.productions/', - label: 'Listen now', - target: '_blank', - }, - ], - }, - { - title: 'Postgres Language Server', - description: 'Implementing the Parser', + title: 'TBD', + description: '', icon: ( ), - id: 'postgres-language-server-implementing-parser', + id: '', is_shipped: true, links: [ { - url: '/blog/postgres-language-server-implementing-parser', + url: '#', label: 'Read blog post', - }, - ], - }, - { - title: 'Design at Supabase', - description: "The transformative journey of Supabase's Design team", - icon: ( - - - - - ), - id: 'design-at-supabase', - is_shipped: true, - links: [ - { - url: '/blog/how-design-works-at-supabase', - label: 'Read blog post', - }, - ], - }, - { - title: 'Supabase Grafana', - description: 'Observability for your Supabase project', - is_shipped: true, - icon: ( - - - - ), - id: 'supabase-grafana', - links: [ - { - url: 'https://github.com/supabase/supabase-grafana', - label: 'View on GitHub', target: '_blank', }, ], }, { - title: 'pg_graphql: Postgres functions', - description: 'pg_graphql now supports User Defined Functions (UDFs)', - is_shipped: true, + title: 'TBD', + description: '', icon: ( + ), - id: 'pg-graphql-postgres-functions', + id: '', + is_shipped: true, links: [ { - url: '/blog/pg-graphql-postgres-functions', + url: '#', label: 'Read blog post', + target: '_blank', }, ], }, { - title: 'PostgREST 12', - description: 'PostgREST 12 is out: take a look at some of the major new features', - is_shipped: true, - className: 'xl:col-span-2 xl:h-auto xl:aspect-auto', + title: 'TBD', + description: '', icon: ( - - - - ), - id: 'postgrest-12', - links: [ - { - url: '/blog/postgrest-12', - label: 'Read blog post', - }, - ], - }, - { - title: 'Supavisor 1.0', - description: 'A scalable connection pooler for Postgres', - is_shipped: true, - icon: ( - + ), - id: 'supavisor', + id: '', + is_shipped: true, links: [ { - url: '/blog/supavisor-postgres-connection-pooler', + url: '#', label: 'Read blog post', + target: '_blank', }, ], }, { - title: 'Supabase Wrappers v0.2', - description: 'Query Pushdown & Remote Subqueries', - is_shipped: true, + title: 'TBD', + description: '', icon: ( + ), - id: 'supabase-wrappers-v0.2', + id: '', + is_shipped: true, links: [ { - url: '/blog/supabase-wrappers-v02', + url: '#', label: 'Read blog post', + target: '_blank', }, ], }, { - title: 'Supabase Libraries V2', - description: 'Swift, Kotlin, C#, and Python are now stable and moving to the v2 API', - type: 'clientLibs', - is_shipped: true, - className: 'xl:col-span-2 xl:aspect-auto xl:col-span-3 xl:aspect-auto xl:h-full', - id: 'supabase-libraries-v2', - links: [ - { - url: '/blog/client-libraries-v2', - label: 'Read blog post', - }, - ], - icons: [ - { - url: '/blog/client-libraries-v2#supabase-python-v2', - label: 'Python', - icon: ( - - - - - ), - }, - { - url: '/blog/client-libraries-v2#supabase-swift-v2', - label: 'Swift', - icon: ( - - - - ), - }, - { - url: '/blog/client-libraries-v2#supabase-kotlin-v2', - label: 'Kotlin', - icon: ( - - - - ), - }, - { - url: '/blog/client-libraries-v2#typescript-v2-updates', - label: 'Typescript', - icon: ( - - - - ), - }, - { - url: '/blog/client-libraries-v2#flutter-v2-updates', - label: 'Flutter', - icon: ( - - - - - ), - }, - ], - }, - { - title: 'Supabase x Fly.io', - description: - "We're launching a managed Postgres offering jointly developed by Supabase and Fly.io", - is_shipped: true, - className: 'xl:col-span-2 xl:aspect-auto h-full', + title: 'TBD', + description: '', icon: ( - - - - + ), - id: 'fly-postgres', + id: '', + is_shipped: true, links: [ { - url: '/blog/postgres-on-fly-by-supabase', + url: '#', label: 'Read blog post', + target: '_blank', }, ], }, { - title: 'Top 10 Launches of LWX', - description: 'Our CEO takes a look at his favorite ships from LWX', - className: 'sm:aspect-[2/1] sm:col-span-1 sm:col-span-2 sm:aspect-[2/1] h-full', - is_shipped: true, + title: 'TBD', + description: '', icon: ( - + ), - id: 'launch-week-x-best-launches', + id: '', + is_shipped: true, links: [ { - url: '/blog/launch-week-x-best-launches', + url: '#', label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', + }, + ], + }, + { + title: 'TBD', + description: '', + icon: ( + + + + + ), + id: '', + is_shipped: true, + links: [ + { + url: '#', + label: 'Read blog post', + target: '_blank', }, ], }, diff --git a/apps/www/components/LaunchWeek/X/Ticket/LWXGame.tsx b/apps/www/components/LaunchWeek/X/Ticket/LWXGame.tsx index cb63914006e..9e4c6ed6131 100644 --- a/apps/www/components/LaunchWeek/X/Ticket/LWXGame.tsx +++ b/apps/www/components/LaunchWeek/X/Ticket/LWXGame.tsx @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react' import { Button, cn } from 'ui' import { SITE_ORIGIN } from '~/lib/constants' import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' -import { VALID_KEYS } from '~/components/LaunchWeek/hooks/useLwxGame' +import { VALID_KEYS } from '~/components/LaunchWeek/hooks/useLwGame' const COMPLIMENTS = [ 'Congratulations!', diff --git a/apps/www/components/LaunchWeek/X/Ticket/TicketContainer.tsx b/apps/www/components/LaunchWeek/X/Ticket/TicketContainer.tsx index c7491598dc7..20dad57faa3 100644 --- a/apps/www/components/LaunchWeek/X/Ticket/TicketContainer.tsx +++ b/apps/www/components/LaunchWeek/X/Ticket/TicketContainer.tsx @@ -1,7 +1,5 @@ -import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' import { useParams } from 'common' import Ticket from './Ticket' -import TicketActions from './TicketActions' import TicketCustomizationForm from './TicketCustomizationForm' import TicketCopy from './TicketCopy' diff --git a/apps/www/components/LaunchWeek/X/Ticket/TicketCopy.tsx b/apps/www/components/LaunchWeek/X/Ticket/TicketCopy.tsx index 9e53f2050a8..d44b811ce33 100644 --- a/apps/www/components/LaunchWeek/X/Ticket/TicketCopy.tsx +++ b/apps/www/components/LaunchWeek/X/Ticket/TicketCopy.tsx @@ -6,11 +6,11 @@ import { IconCheck, IconCopy, cn } from 'ui' export default function TicketCopy({ sharePage }: { sharePage: boolean }) { const { userData } = useConfData() const { username, golden, metadata } = userData - const [copyEnabled, setCopyEnabled] = useState(false) + const [_copyEnabled, setCopyEnabled] = useState(false) const [copied, setCopied] = useState(false) const buttonRef = useRef(null) const hasSecretTicket = metadata?.hasSecretTicket - const url = `${SITE_URL}/tickets/${username}?lw=x${ + const url = `${SITE_URL}/x/tickets/${username}?lw=x${ hasSecretTicket ? '&secret=true' : golden ? `&platinum=true` : '' }` diff --git a/apps/www/components/LaunchWeek/X/Ticket/TicketingFlow.tsx b/apps/www/components/LaunchWeek/X/Ticket/TicketingFlow.tsx index 47ccfdda752..959a6c7204f 100644 --- a/apps/www/components/LaunchWeek/X/Ticket/TicketingFlow.tsx +++ b/apps/www/components/LaunchWeek/X/Ticket/TicketingFlow.tsx @@ -16,7 +16,7 @@ import CountdownComponent from '../Countdown' import LaunchWeekPrizeSection from '../LaunchWeekPrizeSection' import TicketPresence from './TicketPresence' import TicketActions from './TicketActions' -import useLwxGame from '../../hooks/useLwxGame' +import useLwxGame from '../../hooks/useLwGame' import LWXGame from './LWXGame' const TicketingFlow = () => { diff --git a/apps/www/components/LaunchWeek/X/TicketsGrid.tsx b/apps/www/components/LaunchWeek/X/TicketsGrid.tsx index 614720c7743..ffea3c5bbb0 100644 --- a/apps/www/components/LaunchWeek/X/TicketsGrid.tsx +++ b/apps/www/components/LaunchWeek/X/TicketsGrid.tsx @@ -35,7 +35,7 @@ export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset return ( window.scrollTo(0, 0)} legacyBehavior @@ -63,9 +63,9 @@ export default function TicketsGrid({ loadedUsers, isLoading, pageCount, offset {user.username diff --git a/apps/www/components/LaunchWeek/hooks/use-conf-data.ts b/apps/www/components/LaunchWeek/hooks/use-conf-data.ts index 1f348a0aa3f..397a49262b0 100644 --- a/apps/www/components/LaunchWeek/hooks/use-conf-data.ts +++ b/apps/www/components/LaunchWeek/hooks/use-conf-data.ts @@ -9,6 +9,7 @@ export type UserData = { ticketNumber?: number username?: string name?: string + platinum?: boolean golden?: boolean referrals?: number bg_image_id?: number @@ -17,9 +18,12 @@ export type UserData = { company?: string location?: string hasSecretTicket?: boolean + hasSharedSecret?: boolean + hideAvatar?: boolean } sharedOnTwitter?: string sharedOnLinkedIn?: string + secret?: boolean } type ConfDataContextType = { diff --git a/apps/www/components/LaunchWeek/hooks/useLwxGame.tsx b/apps/www/components/LaunchWeek/hooks/useLwGame.tsx similarity index 74% rename from apps/www/components/LaunchWeek/hooks/useLwxGame.tsx rename to apps/www/components/LaunchWeek/hooks/useLwGame.tsx index bc4c4e63f2a..f5fe1403267 100644 --- a/apps/www/components/LaunchWeek/hooks/useLwxGame.tsx +++ b/apps/www/components/LaunchWeek/hooks/useLwGame.tsx @@ -30,12 +30,11 @@ export const VALID_KEYS = [ 'z', ] -const useLwxGame = (showCustomizationForm?: boolean) => { +const useLwGame = (disabled?: boolean) => { const [isGameMode, setIsGameMode] = useState(false) - const { ticketState } = useConfData() function onKeyDown(event: KeyboardEvent) { - if (showCustomizationForm) return + if (disabled) return if (event.key === 'Escape') { setIsGameMode(false) @@ -49,12 +48,12 @@ const useLwxGame = (showCustomizationForm?: boolean) => { } useEffect(() => { - if (!showCustomizationForm) window.addEventListener('keydown', onKeyDown) + if (!disabled) window.addEventListener('keydown', onKeyDown) return () => window.removeEventListener('keydown', onKeyDown) - }, [showCustomizationForm]) + }, [disabled]) return { isGameMode, setIsGameMode } } -export default useLwxGame +export default useLwGame diff --git a/apps/www/components/Nav/index.tsx b/apps/www/components/Nav/index.tsx index d8d1ca6f1bf..a6253772638 100644 --- a/apps/www/components/Nav/index.tsx +++ b/apps/www/components/Nav/index.tsx @@ -5,7 +5,7 @@ import { useRouter } from 'next/router' import { useTheme } from 'next-themes' import { useWindowSize } from 'react-use' -import { Button, cn } from 'ui' +import { Announcement, Button, cn, LW11CountdownBanner } from 'ui' import { NavigationMenu, NavigationMenuContent, @@ -38,8 +38,10 @@ const Nav = (props: Props) => { const isUserLoading = useIsUserLoading() const isHomePage = router.pathname === '/' - const isLaunchWeekPage = router.pathname.includes('launch-week') - const isLaunchWeekXPage = router.pathname === '/launch-week' + const isSpecialAnnouncementPage = router.pathname.includes('special-announcement') + const isLaunchWeekPage = router.pathname.includes('launch-week') || isSpecialAnnouncementPage + const isLaunchWeekXPage = router.pathname === '/launch-week/x' + const isLaunchWeek11Page = router.pathname === '/special-announcement' const showLaunchWeekNavMode = isLaunchWeekPage && !open React.useEffect(() => { @@ -64,6 +66,9 @@ const Nav = (props: Props) => { return ( <> + + +
    { /> - {isLaunchWeekPage && !isLaunchWeekXPage && ( - - Launch Week - - )} + {!isSpecialAnnouncementPage && + !isLaunchWeek11Page && + isLaunchWeekPage && + !isLaunchWeekXPage && ( + + Launch Week + + )}
    ) => { const outerRef = useRef(null) const innerRef = useRef(null) @@ -85,6 +87,7 @@ const Panel = ({ outerClassName )} {...(hasMotion ? { whileHover: 'hover', animate: 'initial' } : undefined)} + style={style} >
    - +
    Wrap Up @@ -982,7 +977,7 @@ export default function launchweek() { + />

    Super UI

    Previous Best Overall Project Runner Up

    @@ -1071,24 +1066,21 @@ export default function launchweek() { })}
    - + Shipped

    The Supabase Content Storm

    We worked with more than 30 content creators from around the world to drop a mountain of content simultaneously! - -

    + + See all the content +

    {activeCreator !== null && ( diff --git a/apps/www/pages/launch-week/8/index.tsx b/apps/www/pages/launch-week/8/index.tsx index a0c1364e38a..e054c7c8ab0 100644 --- a/apps/www/pages/launch-week/8/index.tsx +++ b/apps/www/pages/launch-week/8/index.tsx @@ -1,3 +1,5 @@ +import 'swiper/swiper.min.css' + import { useState, useEffect } from 'react' import { GetServerSideProps } from 'next' import dynamic from 'next/dynamic' @@ -14,12 +16,8 @@ import SectionContainer from '~/components/Layouts/SectionContainer' import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/8/LaunchWeekLogoHeader' import { Meetup } from '~/components/LaunchWeek/8/LW8Meetups' import LW8CalloutsSection from '~/components/LaunchWeek/8/LW8CalloutsSection' +import AnimatedParticles from '~/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas' -import 'swiper/swiper.min.css' - -const AnimatedParticles = dynamic( - () => import('~/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas') -) const LW8Releases = dynamic(() => import('~/components/LaunchWeek/8/Releases')) const LW8Meetups = dynamic(() => import('~/components/LaunchWeek/8/LW8Meetups')) const LWArchive = dynamic(() => import('~/components/LaunchWeek/8/LWArchive')) @@ -145,7 +143,9 @@ export default function TicketHome({ users, meetups }: Props) { starts background
    diff --git a/apps/www/pages/launch-week/index.tsx b/apps/www/pages/launch-week/x/index.tsx similarity index 95% rename from apps/www/pages/launch-week/index.tsx rename to apps/www/pages/launch-week/x/index.tsx index eb21931d170..9507c9c411d 100644 --- a/apps/www/pages/launch-week/index.tsx +++ b/apps/www/pages/launch-week/x/index.tsx @@ -7,6 +7,7 @@ import { Session } from '@supabase/supabase-js' import { SITE_ORIGIN, SITE_URL } from '~/lib/constants' import supabase from '~/lib/supabaseMisc' +import FaviconImports from '~/components/LaunchWeek/X/FaviconImports' import DefaultLayout from '~/components/Layouts/Default' import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import SectionContainer from '~/components/Layouts/SectionContainer' @@ -98,6 +99,7 @@ export default function LaunchWeekIndex({ meetups }: Props) { ], }} /> + { return { props: { - meetups: meetups?.sort((a, b) => (new Date(a.start_at!) > new Date(b.start_at!) ? 1 : -1)), + // @ts-ignore + meetups: meetups?.sort((a, b) => (new Date(a.start_at) > new Date(b.start_at) ? 1 : -1)), }, } } diff --git a/apps/www/pages/launch-week/tickets/[username].tsx b/apps/www/pages/launch-week/x/tickets/[username].tsx similarity index 91% rename from apps/www/pages/launch-week/tickets/[username].tsx rename to apps/www/pages/launch-week/x/tickets/[username].tsx index 00f291bcbd5..9e6d81d6b55 100644 --- a/apps/www/pages/launch-week/tickets/[username].tsx +++ b/apps/www/pages/launch-week/x/tickets/[username].tsx @@ -10,6 +10,7 @@ import { Button } from 'ui' import { SITE_URL } from '~/lib/constants' import supabase from '~/lib/supabaseMisc' +import FaviconImports from '~/components/LaunchWeek/X/FaviconImports' import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' @@ -32,7 +33,7 @@ export default function UsernamePage({ user, ogImageUrl }: Props) { const TITLE = `${name ? name + '’s' : 'Get your'} #SupaLaunchWeek Ticket` const DESCRIPTION = `Claim and share your Supabase Launch Week X ticket for a chance to win Supabase swag.` - const OG_URL = `${SITE_URL}/tickets/${username}` + const OG_URL = `${SITE_URL}/x/tickets/${username}` const [session, setSession] = useState(null) const { resolvedTheme, setTheme } = useTheme() @@ -71,7 +72,7 @@ export default function UsernamePage({ user, ogImageUrl }: Props) { ], }} /> - + Launch Week X Ticket - - Claim your own ticket and share it for a chance to win. -
    @@ -142,7 +138,8 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { user = data } - const ticketType = (user?.metadata as any).hasSecretTicket + // @ts-ignore + const ticketType = user?.metadata?.hasSecretTicket ? 'secret' : user?.golden ? 'platinum' @@ -159,7 +156,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { ogImageUrl, key: username, }, - revalidate: 5, + // revalidate: 5, } } diff --git a/apps/www/pages/launch-week/tickets/index.tsx b/apps/www/pages/launch-week/x/tickets/index.tsx similarity index 97% rename from apps/www/pages/launch-week/tickets/index.tsx rename to apps/www/pages/launch-week/x/tickets/index.tsx index 93a215c1338..6bb8e7d0a81 100644 --- a/apps/www/pages/launch-week/tickets/index.tsx +++ b/apps/www/pages/launch-week/x/tickets/index.tsx @@ -14,7 +14,7 @@ import SectionContainer from '~/components/Layouts/SectionContainer' import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import CTABanner from '~/components/CTABanner' import TicketsGrid from '~/components/LaunchWeek/X/TicketsGrid' -import supabase from '../../../lib/supabaseMisc' +import supabase from '~/lib/supabaseMisc' interface Props { users: UserData[] @@ -127,7 +127,7 @@ export default function TicketsPage({ users }: Props) {

    diff --git a/apps/www/pages/special-announcement/index.tsx b/apps/www/pages/special-announcement/index.tsx new file mode 100644 index 00000000000..1cf42e3b82a --- /dev/null +++ b/apps/www/pages/special-announcement/index.tsx @@ -0,0 +1,103 @@ +import { useState, useEffect } from 'react' +import { useRouter } from 'next/router' +import { NextSeo } from 'next-seo' +import { Session } from '@supabase/supabase-js' +import { SITE_ORIGIN, SPECIAL_ANNOUNCEMENT_URL } from '~/lib/constants' +import supabase from '~/lib/supabaseMisc' + +import DefaultLayout from '~/components/Layouts/Default' +import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import TicketingFlow from '~/components/LaunchWeek/11/Ticket/TicketingFlow' + +export default function LaunchWeekIndex() { + const { query } = useRouter() + + const TITLE = 'Supabase Special Announcement | 15-19 April' + const DESCRIPTION = 'Join us for a week of announcing new features, every day at 7 AM PT.' + const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/11/lw11-og.png` + + const ticketNumber = query.ticketNumber?.toString() + const bgImageId = query.bgImageId?.toString() + const [session, setSession] = useState(null) + const [showCustomizationForm, setShowCustomizationForm] = useState(false) + + const defaultUserData = { + id: query.id?.toString(), + ticketNumber: ticketNumber ? parseInt(ticketNumber, 10) : undefined, + name: query.name?.toString(), + username: query.username?.toString(), + golden: !!query.golden, + bgImageId: bgImageId ? parseInt(bgImageId, 10) : undefined, + } + + const [userData, setUserData] = useState(defaultUserData) + const [ticketState, setTicketState] = useState('loading') + + useEffect(() => { + if (supabase) { + supabase.auth.getSession().then(({ data: { session } }) => setSession(session)) + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => { + setSession(session) + }) + + return () => subscription.unsubscribe() + } + }, [supabase]) + + useEffect(() => { + document.body.classList.add('bg-[#060809]') + + return () => { + if (document.body.classList.contains('bg-[#060809]')) { + document.body.classList.remove('bg-[#060809]') + } + } + }, []) + + useEffect(() => { + if (session?.user) { + if (userData?.id) { + return setTicketState('ticket') + } + return setTicketState('loading') + } + if (!session) return setTicketState('registration') + }, [session, userData]) + + return ( + <> + + + + + + + + ) +} diff --git a/apps/www/pages/special-announcement/tickets/[username].tsx b/apps/www/pages/special-announcement/tickets/[username].tsx new file mode 100644 index 00000000000..be61b189315 --- /dev/null +++ b/apps/www/pages/special-announcement/tickets/[username].tsx @@ -0,0 +1,158 @@ +import { useEffect, useState } from 'react' +import { NextSeo } from 'next-seo' +import { GetStaticProps, GetStaticPaths } from 'next' +import Link from 'next/link' +import Error from 'next/error' +import dynamic from 'next/dynamic' +import { Session } from '@supabase/supabase-js' +import { Button } from 'ui' +import { SITE_URL, SPECIAL_ANNOUNCEMENT_URL } from '~/lib/constants' +import supabase from '~/lib/supabaseMisc' +import dayjs from 'dayjs' + +import DefaultLayout from '~/components/Layouts/Default' +import SectionContainer from '~/components/Layouts/SectionContainer' +import { TicketState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import LW11Background from '~/components/LaunchWeek/11/LW11Background' + +const LW11TicketContainer = dynamic( + () => import('~/components/LaunchWeek/11/Ticket/TicketContainer') +) +const LaunchWeekPrizeSection = dynamic( + () => import('~/components/LaunchWeek/11/LaunchWeekPrizeSection') +) +const CTABanner = dynamic(() => import('~/components/CTABanner')) + +interface Props { + user: UserData + users: UserData[] + ogImageUrl: string +} + +export default function UsernamePage({ user, ogImageUrl }: Props) { + const { username, ticketNumber, name } = user + + const DISPLAY_NAME = name || username + const TITLE = `${DISPLAY_NAME ? DISPLAY_NAME.split(' ')[0] + '’s' : 'Get your'} ticket` + const DESCRIPTION = `` + const PAGE_URL = `${SPECIAL_ANNOUNCEMENT_URL}/tickets/${username}` + + const [session] = useState(null) + + const [ticketState, setTicketState] = useState('ticket') + + if (!ticketNumber) { + return + } + + useEffect(() => { + document.body.classList.add('bg-[#060809]') + + return () => { + if (document.body.classList.contains('bg-[#060809]')) { + document.body.classList.remove('bg-[#060809]') + } + } + }, []) + + return ( + <> + + null, + ticketState, + setTicketState, + }} + > + +
    + +
    + +
    +
    +

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

    + + Join @{username} for a Special Announcement and create your ticket for a chance to + win swag. + + +
    +
    + +
    + + + + +
    +
    + + ) +} + +export const getStaticProps: GetStaticProps = async ({ params }) => { + const username = params?.username?.toString() || null + let user + + fetch( + `https://obuldanrptloktxcffvn.supabase.co/functions/v1/lw11-og?username=${encodeURIComponent( + username ?? '' + )}` + ) + + // fetch a specific user + if (username) { + const { data } = await supabase! + .from('lw11_tickets_platinum') + .select('name, username, ticketNumber, metadata, platinum, secret') + .eq('username', username) + .single() + + user = data + } + + const ticketType = user?.secret ? 'secret' : user?.platinum ? 'platinum' : 'regular' + const ogImageUrl = `https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw11/og/${ticketType}/${username}.png?t=${dayjs(new Date()).format('DHHmmss')}` + + return { + props: { + user: { + ...user, + username, + }, + ogImageUrl, + key: username, + }, + revalidate: 5, + } +} + +export const getStaticPaths: GetStaticPaths = async () => { + return Promise.resolve({ + paths: [], + fallback: 'blocking', + }) +} diff --git a/apps/www/public/favicon/browserconfig.xml b/apps/www/public/favicon/browserconfig.xml index c5541482230..1a8136def88 100644 --- a/apps/www/public/favicon/browserconfig.xml +++ b/apps/www/public/favicon/browserconfig.xml @@ -1,2 +1,2 @@ -#ffffff \ No newline at end of file +#ffffff \ No newline at end of file diff --git a/apps/www/public/images/launchweek/11/airpods-max-alpha.png b/apps/www/public/images/launchweek/11/airpods-max-alpha.png new file mode 100644 index 00000000000..fef70f2ebb6 Binary files /dev/null and b/apps/www/public/images/launchweek/11/airpods-max-alpha.png differ diff --git a/apps/www/public/images/launchweek/11/lw11-og.png b/apps/www/public/images/launchweek/11/lw11-og.png new file mode 100644 index 00000000000..b73fa580271 Binary files /dev/null and b/apps/www/public/images/launchweek/11/lw11-og.png differ diff --git a/apps/www/public/images/launchweek/11/tickets/lw11_ticket_gold.png b/apps/www/public/images/launchweek/11/tickets/lw11_ticket_gold.png new file mode 100644 index 00000000000..b1ec472ca6b Binary files /dev/null and b/apps/www/public/images/launchweek/11/tickets/lw11_ticket_gold.png differ diff --git a/apps/www/public/images/launchweek/11/tickets/lw11_ticket_platinum.png b/apps/www/public/images/launchweek/11/tickets/lw11_ticket_platinum.png new file mode 100644 index 00000000000..4af219c5726 Binary files /dev/null and b/apps/www/public/images/launchweek/11/tickets/lw11_ticket_platinum.png differ diff --git a/apps/www/public/images/launchweek/11/tickets/lw11_ticket_regular_darker.png b/apps/www/public/images/launchweek/11/tickets/lw11_ticket_regular_darker.png new file mode 100644 index 00000000000..afdd59f527b Binary files /dev/null and b/apps/www/public/images/launchweek/11/tickets/lw11_ticket_regular_darker.png differ diff --git a/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_platinum.png b/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_platinum.png new file mode 100644 index 00000000000..b360c205508 Binary files /dev/null and b/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_platinum.png differ diff --git a/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_purple.png b/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_purple.png new file mode 100644 index 00000000000..9cb2cf17d09 Binary files /dev/null and b/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_purple.png differ diff --git a/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_regular.png b/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_regular.png new file mode 100644 index 00000000000..80be4cb01c0 Binary files /dev/null and b/apps/www/public/images/launchweek/11/tickets/shape/lw11_ticket_regular.png differ diff --git a/apps/www/public/images/launchweek/2x-gold-ticket-background.jpg b/apps/www/public/images/launchweek/2x-gold-ticket-background.jpg deleted file mode 100644 index 2458ba6e641..00000000000 Binary files a/apps/www/public/images/launchweek/2x-gold-ticket-background.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/2x-regular-ticket-background.jpg b/apps/www/public/images/launchweek/2x-regular-ticket-background.jpg deleted file mode 100644 index e2bcd23470e..00000000000 Binary files a/apps/www/public/images/launchweek/2x-regular-ticket-background.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/PITR-visual-hover-light.svg b/apps/www/public/images/launchweek/PITR-visual-hover-light.svg deleted file mode 100644 index b1c339242dd..00000000000 --- a/apps/www/public/images/launchweek/PITR-visual-hover-light.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/PITR-visual-light.svg b/apps/www/public/images/launchweek/PITR-visual-light.svg deleted file mode 100644 index 4c9747c6d5f..00000000000 --- a/apps/www/public/images/launchweek/PITR-visual-light.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/PgGraphql-visual-hover-light.svg b/apps/www/public/images/launchweek/PgGraphql-visual-hover-light.svg deleted file mode 100644 index a1d5e9c1f43..00000000000 --- a/apps/www/public/images/launchweek/PgGraphql-visual-hover-light.svg +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/PgGraphql-visual-light.svg b/apps/www/public/images/launchweek/PgGraphql-visual-light.svg deleted file mode 100644 index 5f74d7bb177..00000000000 --- a/apps/www/public/images/launchweek/PgGraphql-visual-light.svg +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/PostgREST11-visual-hover-light.svg b/apps/www/public/images/launchweek/PostgREST11-visual-hover-light.svg deleted file mode 100644 index d194dc6df07..00000000000 --- a/apps/www/public/images/launchweek/PostgREST11-visual-hover-light.svg +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/PostgREST11-visual-light.svg b/apps/www/public/images/launchweek/PostgREST11-visual-light.svg deleted file mode 100644 index b4f142b54ee..00000000000 --- a/apps/www/public/images/launchweek/PostgREST11-visual-light.svg +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/TCE-visual-hover-light.svg b/apps/www/public/images/launchweek/TCE-visual-hover-light.svg deleted file mode 100644 index 0059b38395e..00000000000 --- a/apps/www/public/images/launchweek/TCE-visual-hover-light.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/TCE-visual-light.svg b/apps/www/public/images/launchweek/TCE-visual-light.svg deleted file mode 100644 index c8730cb8927..00000000000 --- a/apps/www/public/images/launchweek/TCE-visual-light.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/antcopple.png b/apps/www/public/images/launchweek/antcopple.png deleted file mode 100644 index 769cd8b3b4f..00000000000 Binary files a/apps/www/public/images/launchweek/antcopple.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/blog post placeholder.jpg b/apps/www/public/images/launchweek/blog post placeholder.jpg deleted file mode 100644 index bcdced1a6f5..00000000000 Binary files a/apps/www/public/images/launchweek/blog post placeholder.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/blog-icon.svg b/apps/www/public/images/launchweek/blog-icon.svg deleted file mode 100644 index 7540a84898d..00000000000 --- a/apps/www/public/images/launchweek/blog-icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/apps/www/public/images/launchweek/blogpost.svg b/apps/www/public/images/launchweek/blogpost.svg deleted file mode 100644 index f3cd890cfeb..00000000000 --- a/apps/www/public/images/launchweek/blogpost.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/apps/www/public/images/launchweek/cdn-caching-bg-light.png b/apps/www/public/images/launchweek/cdn-caching-bg-light.png deleted file mode 100644 index 6fad05721a2..00000000000 Binary files a/apps/www/public/images/launchweek/cdn-caching-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/community-visual-light-hover.svg b/apps/www/public/images/launchweek/community-visual-light-hover.svg deleted file mode 100644 index 020fd56d4bd..00000000000 --- a/apps/www/public/images/launchweek/community-visual-light-hover.svg +++ /dev/null @@ -1,1046 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/community-visual-light.svg b/apps/www/public/images/launchweek/community-visual-light.svg deleted file mode 100644 index 2cd8d4b224e..00000000000 --- a/apps/www/public/images/launchweek/community-visual-light.svg +++ /dev/null @@ -1,1010 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/copple-placeholder.jpg b/apps/www/public/images/launchweek/copple-placeholder.jpg new file mode 100644 index 00000000000..f8939639d6d Binary files /dev/null and b/apps/www/public/images/launchweek/copple-placeholder.jpg differ diff --git a/apps/www/public/images/launchweek/custom-domains-visual-hover-light.svg b/apps/www/public/images/launchweek/custom-domains-visual-hover-light.svg deleted file mode 100644 index 78291133ced..00000000000 --- a/apps/www/public/images/launchweek/custom-domains-visual-hover-light.svg +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/custom-domains-visual-light.svg b/apps/www/public/images/launchweek/custom-domains-visual-light.svg deleted file mode 100644 index 2c1ad7bd8c4..00000000000 --- a/apps/www/public/images/launchweek/custom-domains-visual-light.svg +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/day1/thumb.jpg b/apps/www/public/images/launchweek/day1/thumb.jpg deleted file mode 100644 index 783f8bde69f..00000000000 Binary files a/apps/www/public/images/launchweek/day1/thumb.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/day2/thumb.jpg b/apps/www/public/images/launchweek/day2/thumb.jpg deleted file mode 100644 index fb6b788688e..00000000000 Binary files a/apps/www/public/images/launchweek/day2/thumb.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/day3/thumb.jpg b/apps/www/public/images/launchweek/day3/thumb.jpg deleted file mode 100644 index 9fe98a7d90e..00000000000 Binary files a/apps/www/public/images/launchweek/day3/thumb.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/day4/thumb.jpg b/apps/www/public/images/launchweek/day4/thumb.jpg deleted file mode 100644 index 4fbafc5d1dc..00000000000 Binary files a/apps/www/public/images/launchweek/day4/thumb.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/day5/thumb-old.jpg b/apps/www/public/images/launchweek/day5/thumb-old.jpg deleted file mode 100644 index 79c944bc84f..00000000000 Binary files a/apps/www/public/images/launchweek/day5/thumb-old.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/day5/thumb.jpg b/apps/www/public/images/launchweek/day5/thumb.jpg deleted file mode 100644 index ed7fe17f52a..00000000000 Binary files a/apps/www/public/images/launchweek/day5/thumb.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/docs-update-bg-light.png b/apps/www/public/images/launchweek/docs-update-bg-light.png deleted file mode 100644 index c08fbd148a6..00000000000 Binary files a/apps/www/public/images/launchweek/docs-update-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/docs-update-bg__.png b/apps/www/public/images/launchweek/docs-update-bg__.png deleted file mode 100644 index 12de95b1b48..00000000000 Binary files a/apps/www/public/images/launchweek/docs-update-bg__.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/docs.svg b/apps/www/public/images/launchweek/docs.svg deleted file mode 100644 index d682b98a8a8..00000000000 --- a/apps/www/public/images/launchweek/docs.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/apps/www/public/images/launchweek/encryption-bg-light.png b/apps/www/public/images/launchweek/encryption-bg-light.png deleted file mode 100644 index 9de76cf9127..00000000000 Binary files a/apps/www/public/images/launchweek/encryption-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/encryption-bg.png b/apps/www/public/images/launchweek/encryption-bg.png deleted file mode 100644 index 01dff450301..00000000000 Binary files a/apps/www/public/images/launchweek/encryption-bg.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/encryption-bg.svg b/apps/www/public/images/launchweek/encryption-bg.svg deleted file mode 100644 index 906921b7c9d..00000000000 --- a/apps/www/public/images/launchweek/encryption-bg.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/gold-ticket-background.png b/apps/www/public/images/launchweek/gold-ticket-background.png deleted file mode 100644 index 5cdf8d40a9c..00000000000 Binary files a/apps/www/public/images/launchweek/gold-ticket-background.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/golden-ticket.svg b/apps/www/public/images/launchweek/golden-ticket.svg deleted file mode 100644 index 201bffffbf6..00000000000 --- a/apps/www/public/images/launchweek/golden-ticket.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/green-ticket.svg b/apps/www/public/images/launchweek/green-ticket.svg deleted file mode 100644 index dda47904664..00000000000 --- a/apps/www/public/images/launchweek/green-ticket.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/icons-docs_.svg b/apps/www/public/images/launchweek/icons-docs_.svg deleted file mode 100644 index 274b20b8e4f..00000000000 --- a/apps/www/public/images/launchweek/icons-docs_.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/apps/www/public/images/launchweek/image-processing-bg-light-alt.png b/apps/www/public/images/launchweek/image-processing-bg-light-alt.png deleted file mode 100644 index de01c7bf28c..00000000000 Binary files a/apps/www/public/images/launchweek/image-processing-bg-light-alt.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/image-processing-bg-light.png b/apps/www/public/images/launchweek/image-processing-bg-light.png deleted file mode 100644 index 72eed0b732a..00000000000 Binary files a/apps/www/public/images/launchweek/image-processing-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/launchweek-logo--dark_old.svg b/apps/www/public/images/launchweek/launchweek-logo--dark_old.svg deleted file mode 100644 index 7153efa952a..00000000000 --- a/apps/www/public/images/launchweek/launchweek-logo--dark_old.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/launchweek-logo--light__old.svg b/apps/www/public/images/launchweek/launchweek-logo--light__old.svg deleted file mode 100644 index 4f5785bab9d..00000000000 --- a/apps/www/public/images/launchweek/launchweek-logo--light__old.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/launchweek-logo--light_old0.svg b/apps/www/public/images/launchweek/launchweek-logo--light_old0.svg deleted file mode 100644 index a2359311df5..00000000000 --- a/apps/www/public/images/launchweek/launchweek-logo--light_old0.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/mfa-bg-light.png b/apps/www/public/images/launchweek/mfa-bg-light.png deleted file mode 100644 index 6ee535ba251..00000000000 Binary files a/apps/www/public/images/launchweek/mfa-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/mfa-bg.png b/apps/www/public/images/launchweek/mfa-bg.png deleted file mode 100644 index 7ab2738a2e5..00000000000 Binary files a/apps/www/public/images/launchweek/mfa-bg.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/mfa-light-hover.png b/apps/www/public/images/launchweek/mfa-light-hover.png deleted file mode 100644 index 7abd2a8c38b..00000000000 Binary files a/apps/www/public/images/launchweek/mfa-light-hover.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/mfa-light.png b/apps/www/public/images/launchweek/mfa-light.png deleted file mode 100644 index d4038672ce3..00000000000 Binary files a/apps/www/public/images/launchweek/mfa-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/og-image.jpg b/apps/www/public/images/launchweek/og-image.jpg deleted file mode 100644 index 21c58be7988..00000000000 Binary files a/apps/www/public/images/launchweek/og-image.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/outro-light.svg b/apps/www/public/images/launchweek/outro-light.svg deleted file mode 100644 index f24f0760446..00000000000 --- a/apps/www/public/images/launchweek/outro-light.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/www/public/images/launchweek/pg_crdt-visual-hover-light.svg b/apps/www/public/images/launchweek/pg_crdt-visual-hover-light.svg deleted file mode 100644 index 79e1c80a97c..00000000000 --- a/apps/www/public/images/launchweek/pg_crdt-visual-hover-light.svg +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/pg_crdt-visual-light.svg b/apps/www/public/images/launchweek/pg_crdt-visual-light.svg deleted file mode 100644 index 7c3f559b59a..00000000000 --- a/apps/www/public/images/launchweek/pg_crdt-visual-light.svg +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/pitr-bg-light.png b/apps/www/public/images/launchweek/pitr-bg-light.png deleted file mode 100644 index 5960c9314bd..00000000000 Binary files a/apps/www/public/images/launchweek/pitr-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/pitr-bg.png b/apps/www/public/images/launchweek/pitr-bg.png deleted file mode 100644 index efc950051d4..00000000000 Binary files a/apps/www/public/images/launchweek/pitr-bg.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/postgres-visual-hover-light.svg b/apps/www/public/images/launchweek/postgres-visual-hover-light.svg deleted file mode 100644 index a3116dbd48d..00000000000 --- a/apps/www/public/images/launchweek/postgres-visual-hover-light.svg +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/postgres-visual-light.svg b/apps/www/public/images/launchweek/postgres-visual-light.svg deleted file mode 100644 index aa5c513319b..00000000000 --- a/apps/www/public/images/launchweek/postgres-visual-light.svg +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/regular-ticket-background.png b/apps/www/public/images/launchweek/regular-ticket-background.png deleted file mode 100644 index 044d67a0de7..00000000000 Binary files a/apps/www/public/images/launchweek/regular-ticket-background.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/supabadge-connected.svg b/apps/www/public/images/launchweek/supabadge-connected.svg deleted file mode 100644 index 2a697b09bc9..00000000000 --- a/apps/www/public/images/launchweek/supabadge-connected.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/supabadge-disconnected.svg b/apps/www/public/images/launchweek/supabadge-disconnected.svg deleted file mode 100644 index 7c5fcd76a89..00000000000 --- a/apps/www/public/images/launchweek/supabadge-disconnected.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/supabadge-gold.svg b/apps/www/public/images/launchweek/supabadge-gold.svg deleted file mode 100644 index 17cbf3b680f..00000000000 --- a/apps/www/public/images/launchweek/supabadge-gold.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/supabase-launch-week-5-bg--light.jpg b/apps/www/public/images/launchweek/supabase-launch-week-5-bg--light.jpg deleted file mode 100644 index c7b26f1edfc..00000000000 Binary files a/apps/www/public/images/launchweek/supabase-launch-week-5-bg--light.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/supabase-launch-week-5-bg.jpg b/apps/www/public/images/launchweek/supabase-launch-week-5-bg.jpg deleted file mode 100644 index 98c373f9cff..00000000000 Binary files a/apps/www/public/images/launchweek/supabase-launch-week-5-bg.jpg and /dev/null differ diff --git a/apps/www/public/images/launchweek/ticket-header-logo.svg b/apps/www/public/images/launchweek/ticket-header-logo.svg deleted file mode 100644 index 96613bac403..00000000000 --- a/apps/www/public/images/launchweek/ticket-header-logo.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/www/public/images/launchweek/vault-bg-light.png b/apps/www/public/images/launchweek/vault-bg-light.png deleted file mode 100644 index 50ae3c9462f..00000000000 Binary files a/apps/www/public/images/launchweek/vault-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/vault-bg.png b/apps/www/public/images/launchweek/vault-bg.png deleted file mode 100644 index 2b927dcbab8..00000000000 Binary files a/apps/www/public/images/launchweek/vault-bg.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/vault-visual-hover-light.svg b/apps/www/public/images/launchweek/vault-visual-hover-light.svg deleted file mode 100644 index fef124cca82..00000000000 --- a/apps/www/public/images/launchweek/vault-visual-hover-light.svg +++ /dev/null @@ -1,548 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/vault-visual-light.svg b/apps/www/public/images/launchweek/vault-visual-light.svg deleted file mode 100644 index 768b527e4a5..00000000000 --- a/apps/www/public/images/launchweek/vault-visual-light.svg +++ /dev/null @@ -1,547 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/wrappers-bg-light.png b/apps/www/public/images/launchweek/wrappers-bg-light.png deleted file mode 100644 index e70745d217c..00000000000 Binary files a/apps/www/public/images/launchweek/wrappers-bg-light.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/wrappers-bg.png b/apps/www/public/images/launchweek/wrappers-bg.png deleted file mode 100644 index cef57946f8b..00000000000 Binary files a/apps/www/public/images/launchweek/wrappers-bg.png and /dev/null differ diff --git a/apps/www/public/images/launchweek/wrappers-bg.svg b/apps/www/public/images/launchweek/wrappers-bg.svg deleted file mode 100644 index 7e447c9648d..00000000000 --- a/apps/www/public/images/launchweek/wrappers-bg.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/wrappers-visual-hover-light.svg b/apps/www/public/images/launchweek/wrappers-visual-hover-light.svg deleted file mode 100644 index 3e54e814ec0..00000000000 --- a/apps/www/public/images/launchweek/wrappers-visual-hover-light.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/wrappers-visual-light.svg b/apps/www/public/images/launchweek/wrappers-visual-light.svg deleted file mode 100644 index 105ea201435..00000000000 --- a/apps/www/public/images/launchweek/wrappers-visual-light.svg +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/launchweek/wrapup.svg b/apps/www/public/images/launchweek/wrapup.svg deleted file mode 100644 index 098cdc6b7b9..00000000000 --- a/apps/www/public/images/launchweek/wrapup.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml index 5bd3a23686a..4524ba5e84b 100644 --- a/apps/www/public/rss.xml +++ b/apps/www/public/rss.xml @@ -5,14 +5,28 @@ https://supabase.com Latest news from Supabase en - Wed, 28 Feb 2024 06:00:00 GMT + Wed, 28 Feb 2024 23:00:00 GMT - + https://supabase.com/blog/postgrest-aggregate-functions PostgREST Aggregate Functions https://supabase.com/blog/postgrest-aggregate-functions - Usage of Aggregate functions on PostgREST 12 - Wed, 28 Feb 2024 06:00:00 GMT + Summarize your data by performing calculations across groups of rows in PostgREST + Wed, 28 Feb 2024 23:00:00 GMT + + + https://supabase.com/blog/content-recommendation-with-flutter + Build a content recommendation app with Flutter and OpenAI + https://supabase.com/blog/content-recommendation-with-flutter + Build a movie-viewing app that recommends another movie based on what the user is viewing using OpenAI, Flutter and Supabase. + Sun, 25 Feb 2024 23:00:00 GMT + + + https://supabase.com/blog/automating-performance-tests + Automating performance tests + https://supabase.com/blog/automating-performance-tests + Learn about our story to get to the automated performance testing. + Tue, 20 Feb 2024 23:00:00 GMT https://supabase.com/blog/matryoshka-embeddings diff --git a/apps/www/supabase/functions/launchweek-ticket-og/README.md b/apps/www/supabase/functions/launchweek-ticket-og/README.md deleted file mode 100644 index 50104fcea85..00000000000 --- a/apps/www/supabase/functions/launchweek-ticket-og/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Open Graph (OG) Image Generation with Supabase Storage CDN Caching - -Generate Open Graph images with Deno and Supabase Edge Functions and cache the generated image with Supabase Storage CDN. - -- Docs: https://deno.land/x/og_edge@0.0.2 -- Examples: https://vercel.com/docs/concepts/functions/edge-functions/og-image-examples -- Demo: https://obuldanrptloktxcffvn.supabase.co/functions/v1/launchweek-ticket-og?username=thorwebdev - -## Run locally - -```bash -supabase functions serve launchweek-ticket-og --no-verify-jwt -``` - -Navigate to http://localhost:54321/functions/v1/launchweek-ticket-og?username=thorwebdev - -## Deploy - -```bash -supabase functions deploy launchweek-ticket-og --no-verify-jwt -``` diff --git a/apps/www/supabase/functions/launchweek-ticket-og/handler.tsx b/apps/www/supabase/functions/launchweek-ticket-og/handler.tsx deleted file mode 100644 index c35b8b8cd3b..00000000000 --- a/apps/www/supabase/functions/launchweek-ticket-og/handler.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' -import { corsHeaders } from '../_shared/cors.ts' - -const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw6' -const BACKGROUND_IMAGE_STD = `${STORAGE_URL}/lw6_ticket_regular.png?prod=true&v=2` -const BACKGROUND_IMAGE_GOLDEN = `${STORAGE_URL}/lw6_ticket_gold.png?prod=true&v=2` -const SUPA_CHECKMARK = `${STORAGE_URL}/supaverified.png?prod=true&v=2` -const SUPA_CHECKMARK_GOLD = `${STORAGE_URL}/supaverified_gold.png?prod=true&v=2` - -// Load custom font -const FONT_URL = `${STORAGE_URL}/CircularStd-Book.otf?prod=true&v=2` -const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) - -export async function handler(req: Request) { - const url = new URL(req.url) - const username = url.searchParams.get('username') ?? url.searchParams.get('amp;username') - const userAgent = req.headers.get('user-agent') - - try { - if (!username) throw new Error('missing username param') - // Track social shares - const supabaseAdminClient = createClient( - // Supabase API URL - env var exported by default when deployed. - Deno.env.get('SUPABASE_URL') ?? '', - // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' - ) - if (userAgent?.toLocaleLowerCase().includes('twitter')) { - const { error } = await supabaseAdminClient - .from('lw6_tickets') - .update({ sharedOnTwitter: true }) - .eq('username', username) - if (error) console.log(error.message) - } else if (userAgent?.toLocaleLowerCase().includes('linkedin')) { - const { error } = await supabaseAdminClient - .from('lw6_tickets') - .update({ sharedOnLinkedIn: true }) - .eq('username', username) - if (error) console.log(error.message) - } - - // Try to get image from Supabase Storage CDN. - const storageResponse = await fetch(`${STORAGE_URL}/tickets/${username}.png?prod=true&v=5`) - if (storageResponse.ok) return storageResponse - - // Get ticket data - const { data, error } = await supabaseAdminClient - .from('lw6_tickets_golden') - .select('name, ticketNumber, golden') - .eq('username', username) - .maybeSingle() - if (error) throw error - - if (!data?.ticketNumber) throw new Error('missing data') - const { name, ticketNumber } = data - const golden = data?.golden ?? false - - // Else, generate image ad upload to storage. - const fontData = await font - const numDigits = `${Number(ticketNumber)}`.length - const prefix = `00000000`.slice(numDigits) - - const generatedImage = new ImageResponse( - ( - <> -
    - {/* Background image */} - - {/* GitHub Avatar image */} - - {/* Name & username */} -
    -
    -

    - {name ?? username} -

    -
    - {/* Username and supaverified checkmark */} -
    - {`@${username}`} - - - -
    -
    - {/* Date */} -

    - December 12th 2022 -

    - {/* URL */} -

    - supabase.com/launch-week -

    -
    - {/* Ticket No */} -
    -

    - {`No ${prefix}${ticketNumber}`} -

    -
    - - ), - { - width: 1200, - height: 630, - fonts: [ - { - name: 'Circular', - data: fontData, - style: 'normal', - }, - ], - headers: { - 'content-type': 'image/png', - 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', - 'cdn-cache-control': 'max-age=31536000', - }, - } - ) - - // Upload image to storage. - const { error: storageError } = await supabaseAdminClient.storage - .from('images') - .upload(`lw6/tickets/${username}.png`, generatedImage.body!, { - contentType: 'image/png', - cacheControl: '31536000', - upsert: false, - }) - if (storageError) throw storageError - - return generatedImage - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 400, - }) - } -} diff --git a/apps/www/supabase/functions/launchweek-ticket-og/index.ts b/apps/www/supabase/functions/launchweek-ticket-og/index.ts deleted file mode 100644 index 7da9ffc001b..00000000000 --- a/apps/www/supabase/functions/launchweek-ticket-og/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -import { serve } from 'https://deno.land/std@0.140.0/http/server.ts' - -import { handler } from './handler.tsx' - -console.log(`Function "launchweek-ticket-og" up and running!`) - -serve(handler) diff --git a/apps/www/supabase/functions/lw7-ticket-og/README.md b/apps/www/supabase/functions/lw11-og/README.md similarity index 63% rename from apps/www/supabase/functions/lw7-ticket-og/README.md rename to apps/www/supabase/functions/lw11-og/README.md index 08ac5c8bfc9..1fe3e27a5d7 100644 --- a/apps/www/supabase/functions/lw7-ticket-og/README.md +++ b/apps/www/supabase/functions/lw11-og/README.md @@ -4,18 +4,19 @@ Generate Open Graph images with Deno and Supabase Edge Functions and cache the g - Docs: https://deno.land/x/og_edge@0.0.2 - Examples: https://vercel.com/docs/concepts/functions/edge-functions/og-image-examples -- Demo: https://obuldanrptloktxcffvn.supabase.co/functions/v1/lw7-ticket-og?username=thorwebdev +- Demo: https://obuldanrptloktxcffvn.supabase.co/functions/v1/lw11-og?username= ## Run locally ```bash -supabase functions serve --no-verify-jwt +supabase start +supabase functions serve lw11-og --no-verify-jwt --env-file ./supabase/.env.local ``` -Navigate to http://localhost:54321/functions/v1/lw7-ticket-og?username=thorwebdev +Navigate to http://localhost:54321/functions/v1/lw11-og?username= ## Deploy ```bash -supabase functions deploy lw7-ticket-og --no-verify-jwt +supabase functions deploy lw11-og --no-verify-jwt ``` diff --git a/apps/www/supabase/functions/lw11-og/handler.tsx b/apps/www/supabase/functions/lw11-og/handler.tsx new file mode 100644 index 00000000000..4ec3ae7f0de --- /dev/null +++ b/apps/www/supabase/functions/lw11-og/handler.tsx @@ -0,0 +1,419 @@ +import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' +import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +} + +const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw11' + +// Load custom font +const FONT_URL = `${STORAGE_URL}/assets/font/CircularStd-Book.otf` +const MONO_FONT_URL = `${STORAGE_URL}/assets/font/SourceCodePro-Regular.ttf` +const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) +const mono_font = fetch(new URL(MONO_FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) +// const BUCKET_FOLDER_VERSION = 'v1' + +const LW_TABLE = 'lw11_tickets' +const LW_MATERIALIZED_VIEW = 'lw11_tickets_platinum' + +const STYLING_CONGIF = { + regular: { + BACKGROUND: '#f1f1f1', + FOREGROUND: '#11181C', + FOREGROUND_LIGHT: '#6c7277', + TICKET_FOREGROUND: '#F8F9FA', + TICKET_FOREGROUND_LIGHT: '#8B9092', + BORDER: '#4e4e4e', + }, + platinum: { + BACKGROUND: '#060809', + FOREGROUND: '#F8F9FA', + FOREGROUND_LIGHT: '#8B9092', + TICKET_FOREGROUND: '#11181C', + TICKET_FOREGROUND_LIGHT: '#6c7277', + BORDER: '#adadad', + }, + secret: { + BACKGROUND: '#060809', + FOREGROUND: '#F8F9FA', + FOREGROUND_LIGHT: '#8B9092', + TICKET_FOREGROUND: '#F8F9FA', + TICKET_FOREGROUND_LIGHT: '#F8F9FA', + BORDER: '#959595', + }, +} + +export async function handler(req: Request) { + const url = new URL(req.url) + const username = url.searchParams.get('username') ?? url.searchParams.get('amp;username') + const assumePlatinum = url.searchParams.get('platinum') ?? url.searchParams.get('amp;platinum') + const userAgent = req.headers.get('user-agent') + + try { + if (!username) throw new Error('missing username param') + + const supabaseAdminClient = createClient( + // Supabase API URL - env var exported by default when deployed. + Deno.env.get('MISC_USE_URL') ?? '', + // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. + Deno.env.get('MISC_USE_ANON_KEY') ?? '' + ) + + // Track social shares + if (userAgent?.toLocaleLowerCase().includes('twitter')) { + await supabaseAdminClient + .from(LW_TABLE) + .update({ sharedOnTwitter: 'now' }) + .eq('username', username) + .is('sharedOnTwitter', null) + } else if (userAgent?.toLocaleLowerCase().includes('linkedin')) { + await supabaseAdminClient + .from(LW_TABLE) + .update({ sharedOnLinkedIn: 'now' }) + .eq('username', username) + .is('sharedOnLinkedIn', null) + } + + // Get ticket data + const { data, error } = await supabaseAdminClient + .from(LW_MATERIALIZED_VIEW) + .select('name, ticketNumber, sharedOnTwitter, sharedOnLinkedIn, metadata, secret') + .eq('username', username) + .maybeSingle() + + if (error) console.log('fetch error', error.message) + if (!data) throw new Error(error?.message ?? 'user not found') + const { name, ticketNumber, metadata, secret } = data + + const platinum = (!!data?.sharedOnTwitter && !!data?.sharedOnLinkedIn) ?? false + if (assumePlatinum && !platinum) + return await fetch(`${STORAGE_URL}/assets/platinum_no_meme.jpg`) + + // Generate image and upload to storage. + const ticketType = secret ? 'secret' : platinum ? 'platinum' : 'regular' + + const fontData = await font + const monoFontData = await mono_font + const numDigits = `${Number(ticketNumber)}`.length + const prefix = `0000000`.slice(numDigits) + const HAS_ROLE = !!metadata?.role + const HAS_COMPANY = !!metadata?.company + const HAS_LOCATION = !!metadata?.location + const HAS_AVATAR = !metadata?.hideAvatar + const HAS_NO_META = !HAS_ROLE && !HAS_COMPANY && !HAS_LOCATION + const OG_WIDTH = 1200 + const OG_HEIGHT = 628 + const OG_PADDING_X = 60 + const OG_PADDING_Y = 60 + const TICKET_WIDTH = 600 + const TICKET_RATIO = 396 / 613 + const TICKET_HEIGHT = TICKET_WIDTH / TICKET_RATIO + const TICKET_POS_TOP = OG_PADDING_Y + const TICKET_POS_LEFT = 540 + const TICKET_PADDING_X = 40 + const TICKET_PADDING_Y = 40 + const LOGO_WIDTH = 40 + // const LOGO_RATIO = 581 / 113 + const LOGO_RATIO = 436 / 449 + // Select one of 30 pre generated bg images base on ticket number + const BG_NUMBER = `000${(ticketNumber % 30) + 1}`.slice(-3) + const profileImg = `https://github.com/${username}.png?size=100` + const DISPLAY_NAME = name ?? username + + const BACKGROUND = { + regular: { + OG: `${STORAGE_URL}/assets/backgrounds/platinum/${BG_NUMBER}.png`, + BG: `${STORAGE_URL}/assets/shape/v2/lw11_ticket_regular.png?t=1`, + LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png`, + }, + platinum: { + OG: `${STORAGE_URL}/assets/backgrounds/regular/${BG_NUMBER}.png`, + BG: `${STORAGE_URL}/assets/shape/v2/lw11_ticket_platinum.png?t=1`, + LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png`, + }, + secret: { + OG: `${STORAGE_URL}/assets/backgrounds/secret/${BG_NUMBER}.png`, + BG: `${STORAGE_URL}/assets/shape/v2/lw11_ticket_purple.png?t=1`, + LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png`, + }, + } + + const generatedTicketImage = new ImageResponse( + ( + <> +
    + {/* Background */} + + {/* Ticket */} +
    + + + {/* Ticket No */} +

    + {`NO ${prefix}${ticketNumber}`} +

    + + {/* Name & username */} +
    + {HAS_AVATAR && ( + + )} +

    + {DISPLAY_NAME} +

    + +
    + {HAS_NO_META && username && `@${username}`} + {HAS_ROLE && `${metadata.role}`} + {HAS_COMPANY && `${HAS_ROLE ? ' at ' : ''}${metadata.company} `} +
    +
    +
    + +
    +
    + +
    + +

    + + Join us for a + + + Special Announcement + +

    +

    + April 15-19 / 7AM PT +

    +
    +
    + + ), + { + width: OG_WIDTH, + height: OG_HEIGHT, + fonts: [ + { + name: 'Circular', + data: fontData, + style: 'normal', + }, + { + name: 'SourceCodePro', + data: monoFontData, + style: 'normal', + }, + ], + headers: { + 'content-type': 'image/png', + 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', + 'cdn-cache-control': 'max-age=31536000', + }, + } + ) + + // Upload image to storage. + const { error: storageError } = await supabaseAdminClient.storage + .from('images') + .upload(`lw11/og/${ticketType}/${username}.png`, generatedTicketImage.body!, { + contentType: 'image/png', + // cacheControl: `${60 * 60 * 24 * 7}`, + cacheControl: `0`, + // Update cached og image, people might need to update info + upsert: true, + }) + + if (storageError) throw new Error(`storageError: ${storageError.message}`) + + const NEW_TIMESTAMP = new Date() + + return await fetch(`${STORAGE_URL}/og/${ticketType}/${username}.png?t=${NEW_TIMESTAMP}`) + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 400, + }) + } +} diff --git a/apps/www/supabase/functions/lw8-ticket/index.ts b/apps/www/supabase/functions/lw11-og/index.ts similarity index 86% rename from apps/www/supabase/functions/lw8-ticket/index.ts rename to apps/www/supabase/functions/lw11-og/index.ts index 84d78bdf295..53f5af7e4bc 100644 --- a/apps/www/supabase/functions/lw8-ticket/index.ts +++ b/apps/www/supabase/functions/lw11-og/index.ts @@ -6,6 +6,6 @@ import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' import { handler } from './handler.tsx' -console.log(`Function "lw8-ticket" up and running`) +console.log(`Function "lw11-og" up and running`) serve(handler) diff --git a/apps/www/supabase/functions/lw7-ticket-gallery/handler.tsx b/apps/www/supabase/functions/lw7-ticket-gallery/handler.tsx deleted file mode 100644 index 4947b1c5c19..00000000000 --- a/apps/www/supabase/functions/lw7-ticket-gallery/handler.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' -import { corsHeaders } from '../_shared/cors.ts' - -const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw7' - -// Load custom font -const FONT_URL = `${STORAGE_URL}/CircularStd-Book.otf` -const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) -const BUCKET_FOLDER_VERSION = 'v3' - -export async function handler(req: Request) { - try { - if (req.method !== 'POST') throw new Error('method not supported') - const { - username = 'thorwebdev', - name = 'Thoggen Schaeppy', - ticketNumber = 1234, - golden = true, - bg_image_id = 80, - }: { - username?: string - name?: string - ticketNumber?: number - golden?: boolean - bg_image_id?: number - } = await req.json() - - // Else, generate image ad upload to storage. - const BACKGROUND = { - REG: { - AI: `${STORAGE_URL}/tickets_bg/blurred/regular/png/reg_bg_${bg_image_id}.png`, - TICKET: `${STORAGE_URL}/reg_ticket_overlay.png`, - }, - GOLD: { - AI: `${STORAGE_URL}/tickets_bg/blurred/golden/png/gold_bg_${bg_image_id}.png`, - TICKET: `${STORAGE_URL}/gold_ticket_overlay.png`, - }, - } - - const fontData = await font - const numDigits = `${Number(ticketNumber)}`.length - const prefix = `00000000`.slice(numDigits) - - const generatedImage = new ImageResponse( - ( - <> -
    - {/* Background ai */} - - {/* Background ticket */} - - {/* GitHub Avatar image */} - - {/* Name & username */} -
    -
    -

    - {name ?? username} -

    {' '} -
    - - {/* Username */} -
    - {`@${username}`} -
    -
    -
    - {/* Ticket No */} -
    -

    - {`No ${prefix}${ticketNumber}`} -

    -
    - - ), - { - width: 1027, - height: 520, - fonts: [ - { - name: 'Circular', - data: fontData, - style: 'normal', - }, - ], - headers: { - 'content-type': 'image/png', - 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', - 'cdn-cache-control': 'max-age=31536000', - }, - } - ) - - // Upload image to storage. - const supabaseAdminClient = createClient( - // Supabase API URL - env var exported by default when deployed. - Deno.env.get('SUPABASE_URL') ?? '', - // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' - ) - const { error: storageError } = await supabaseAdminClient.storage - .from('images') - .upload( - `lw7/tickets/gallery/${ - golden ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png`, - generatedImage.body!, - { - contentType: 'image/png', - cacheControl: '31536000', - upsert: true, - } - ) - if (storageError) throw new Error(`storageError: ${storageError.message}`) - - return new Response(JSON.stringify({ success: true }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 200, - }) - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 400, - }) - } -} diff --git a/apps/www/supabase/functions/lw7-ticket-gallery/index.ts b/apps/www/supabase/functions/lw7-ticket-gallery/index.ts deleted file mode 100644 index c24f7c7b2c8..00000000000 --- a/apps/www/supabase/functions/lw7-ticket-gallery/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' - -import { handler } from './handler.tsx' - -console.log(`Function "lw7-ticket-gallery" up and running!`) - -serve(handler) diff --git a/apps/www/supabase/functions/lw7-ticket-og/handler.tsx b/apps/www/supabase/functions/lw7-ticket-og/handler.tsx deleted file mode 100644 index b1a7c95d3e5..00000000000 --- a/apps/www/supabase/functions/lw7-ticket-og/handler.tsx +++ /dev/null @@ -1,286 +0,0 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' -import { corsHeaders } from '../_shared/cors.ts' - -const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw7' - -// Load custom font -const FONT_URL = `${STORAGE_URL}/CircularStd-Book.otf` -const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) -const BUCKET_FOLDER_VERSION = 'v3' - -export async function handler(req: Request) { - const url = new URL(req.url) - const username = url.searchParams.get('username') ?? url.searchParams.get('amp;username') - const assumeGolden = url.searchParams.get('golden') ?? url.searchParams.get('amp;golden') - const userAgent = req.headers.get('user-agent') - - try { - if (!username) throw new Error('missing username param') - // Track social shares - const supabaseAdminClient = createClient( - // Supabase API URL - env var exported by default when deployed. - Deno.env.get('SUPABASE_URL') ?? '', - // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' - ) - if (userAgent?.toLocaleLowerCase().includes('twitter')) { - await supabaseAdminClient - .from('lw7_tickets') - .update({ sharedOnTwitter: 'now' }) - .eq('username', username) - .is('sharedOnTwitter', null) - } else if (userAgent?.toLocaleLowerCase().includes('linkedin')) { - await supabaseAdminClient - .from('lw7_tickets') - .update({ sharedOnLinkedIn: 'now' }) - .eq('username', username) - .is('sharedOnLinkedIn', null) - } - - // Try to get image from Supabase Storage CDN. - let storageResponse: Response - storageResponse = await fetch( - `${STORAGE_URL}/tickets/golden/${BUCKET_FOLDER_VERSION}/${username}.png` - ) - if (storageResponse.ok) return storageResponse - storageResponse = await fetch( - `${STORAGE_URL}/tickets/regular/${BUCKET_FOLDER_VERSION}/${username}.png` - ) - if (!assumeGolden && storageResponse.ok) return storageResponse - - // Get ticket data - const { data, error } = await supabaseAdminClient - .from('lw7_tickets_golden') - .select('name, ticketNumber, golden, bg_image_id') - .eq('username', username) - .maybeSingle() - if (error) console.log(error.message) - if (!data) throw new Error('user not found') - const { name, ticketNumber, bg_image_id } = data - const golden = data?.golden ?? false - - if (assumeGolden && !golden) return await fetch(`${STORAGE_URL}/golden_no_meme.png`) - - // Else, generate image ad upload to storage. - const BACKGROUND = { - REG: { - BG: `${STORAGE_URL}/reg_bg.png`, - AI: `${STORAGE_URL}/tickets_bg/blurred/regular/png/reg_bg_${bg_image_id ?? '1'}.png`, - TICKET: `${STORAGE_URL}/reg_ticket.png`, - }, - GOLD: { - BG: `${STORAGE_URL}/gold_bg.png`, - AI: `${STORAGE_URL}/tickets_bg/blurred/golden/png/gold_bg_${bg_image_id ?? '1'}.png`, - TICKET: `${STORAGE_URL}/gold_ticket.png`, - }, - } - - const fontData = await font - const numDigits = `${Number(ticketNumber)}`.length - const prefix = `00000000`.slice(numDigits) - - const generatedImage = new ImageResponse( - ( - <> -
    - {/* Background gradient */} - - {/* Background ai */} - - {/* Background ticket */} - - {/* GitHub Avatar image */} - - {/* Name & username */} -
    -
    -

    - {name ?? username} -

    {' '} -
    - - {/* Username */} -
    - {`@${username}`} -
    -
    -
    - {/* Ticket No */} -
    -

    - {`No ${prefix}${ticketNumber}`} -

    -
    - - ), - { - width: 1200, - height: 630, - fonts: [ - { - name: 'Circular', - data: fontData, - style: 'normal', - }, - ], - headers: { - 'content-type': 'image/png', - 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', - 'cdn-cache-control': 'max-age=31536000', - }, - } - ) - - // Upload image to storage. - const { error: storageError } = await supabaseAdminClient.storage - .from('images') - .upload( - `lw7/tickets/${golden ? 'golden' : 'regular'}/${BUCKET_FOLDER_VERSION}/${username}.png`, - generatedImage.body!, - { - contentType: 'image/png', - cacheControl: '31536000', - upsert: false, - } - ) - if (storageError) throw new Error(`storageError: ${storageError.message}`) - // Generate image for gallery - fetch('https://obuldanrptloktxcffvn.supabase.co/functions/v1/lw7-ticket-gallery', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: - 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTIyNjkzMjYsImV4cCI6MjAwNzg0NTMyNn0.1S6qpBbHtEmGuMsIx5UOhRiFd4YbVv-yLTrLk6tVGmM', - }, - body: JSON.stringify({ - username, - name, - ticketNumber, - bg_image_id, - golden, - }), - }) - - return await fetch( - `${STORAGE_URL}/tickets/${ - golden ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png` - ) - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 400, - }) - } -} diff --git a/apps/www/supabase/functions/lw7-ticket-og/index.ts b/apps/www/supabase/functions/lw7-ticket-og/index.ts deleted file mode 100644 index 97fbf105d96..00000000000 --- a/apps/www/supabase/functions/lw7-ticket-og/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' - -import { handler } from './handler.tsx' - -console.log(`Function "lw7-ticket-og" up and running!`) - -serve(handler) diff --git a/apps/www/supabase/functions/lw8-ticket-og/handler.tsx b/apps/www/supabase/functions/lw8-ticket-og/handler.tsx deleted file mode 100644 index 4794d65b5d7..00000000000 --- a/apps/www/supabase/functions/lw8-ticket-og/handler.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' -import { corsHeaders } from '../_shared/cors.ts' - -const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw8' - -const BUCKET_FOLDER_VERSION = 'v1' - -export async function handler(req: Request) { - try { - if (req.method !== 'POST') throw new Error('method not supported') - const { - username = 'thorwebdev', - golden = false, - }: { - username?: string - golden?: boolean - } = await req.json() - - // Else, generate image and upload to storage. - const timestamp = encodeURI(new Date().toISOString()) - const ticketImg = `${STORAGE_URL}/tickets/${ - golden ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png?t=${timestamp}` - - const bgImg = `${STORAGE_URL}/assets/backgrounds/og-${golden ? 'golden' : 'regular'}.png` - const ticketWidth = 1100 - const ticketHeight = ticketWidth / 2 - const boxOffset = 24 - const boxWidth = `${ticketWidth + boxOffset}px` - const boxHeight = `${ticketHeight + boxOffset}px` - const leftOffset = `${(1200 - ticketWidth) / 2 - boxOffset / 2}px` - const topOffset = `${(628 - ticketHeight) / 2 - boxOffset / 2}px` - - const geneartedOGImage = new ImageResponse( - ( -
    - {/* Background */} - - {/* Ticket Box */} -
    - {/* Ticket */} - -
    - ), - { - width: 1200, - height: 628, - headers: { - 'content-type': 'image/png', - 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', - 'cdn-cache-control': 'max-age=31536000', - }, - } - ) - - // Upload image to storage. - const supabaseAdminClient = createClient( - // Supabase API URL - env var exported by default when deployed. - Deno.env.get('SUPABASE_URL') ?? '', - // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' - ) - const { error: storageError } = await supabaseAdminClient.storage - .from('images') - .upload( - `lw8/og/${golden ? 'golden' : 'regular'}/${BUCKET_FOLDER_VERSION}/${username}.png`, - geneartedOGImage.body!, - { - contentType: 'image/png', - cacheControl: '0', - upsert: true, - } - ) - if (storageError) throw new Error(`storageError: ${storageError.message}`) - // return geneartedOGImage - - return new Response(JSON.stringify({ success: true }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 200, - }) - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 400, - }) - } -} diff --git a/apps/www/supabase/functions/lw8-ticket-og/index.ts b/apps/www/supabase/functions/lw8-ticket-og/index.ts deleted file mode 100644 index 26986700825..00000000000 --- a/apps/www/supabase/functions/lw8-ticket-og/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' - -import { handler } from './handler.tsx' - -console.log(`Function "lw8-ticket-og" up and running`) - -serve(handler) diff --git a/apps/www/supabase/functions/lw8-ticket/README.md b/apps/www/supabase/functions/lw8-ticket/README.md deleted file mode 100644 index b7f60e0f9ec..00000000000 --- a/apps/www/supabase/functions/lw8-ticket/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Open Graph (OG) Image Generation with Supabase Storage CDN Caching - -Generate Open Graph images with Deno and Supabase Edge Functions and cache the generated image with Supabase Storage CDN. - -- Docs: https://deno.land/x/og_edge@0.0.2 -- Examples: https://vercel.com/docs/concepts/functions/edge-functions/og-image-examples -- Demo: https://obuldanrptloktxcffvn.supabase.co/functions/v1/lw8-ticket?username=thorwebdev - -## Run locally - -```bash -supabase functions serve lw8-ticket --no-verify-jwt -``` - -Navigate to http://localhost:54321/functions/v1/lw8-ticket?username=thorwebdev - -## Deploy - -```bash -supabase functions deploy lw8-ticket --no-verify-jwt -``` diff --git a/apps/www/supabase/functions/lw8-ticket/handler.tsx b/apps/www/supabase/functions/lw8-ticket/handler.tsx deleted file mode 100644 index 57c8d597fe4..00000000000 --- a/apps/www/supabase/functions/lw8-ticket/handler.tsx +++ /dev/null @@ -1,279 +0,0 @@ -import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' -import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' -import { corsHeaders } from '../_shared/cors.ts' - -const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw8' - -// Load custom font -const FONT_URL = `${STORAGE_URL}/font/CircularStd-Book.otf` -const MONO_FONT_URL = `${STORAGE_URL}/font/SourceCodePro-Regular.ttf?t=2023-07-18T13%3A03%3A29.474Z` -const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) -const mono_font = fetch(new URL(MONO_FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) -const BUCKET_FOLDER_VERSION = 'v1' - -const LW_TABLE = 'lw8_tickets_golden' - -export async function handler(req: Request) { - const url = new URL(req.url) - const username = url.searchParams.get('username') ?? url.searchParams.get('amp;username') - const assumeGolden = url.searchParams.get('golden') ?? url.searchParams.get('amp;golden') - const userAgent = req.headers.get('user-agent') - - try { - if (!username) throw new Error('missing username param') - - const supabaseAdminClient = createClient( - // Supabase API URL - env var exported by default when deployed. - Deno.env.get('SUPABASE_URL') ?? '', - // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' - ) - - // Track social shares - if (userAgent?.toLocaleLowerCase().includes('twitter')) { - await supabaseAdminClient - .from(LW_TABLE) - .update({ sharedOnTwitter: 'now' }) - .eq('username', username) - .is('sharedOnTwitter', null) - } else if (userAgent?.toLocaleLowerCase().includes('linkedin')) { - await supabaseAdminClient - .from(LW_TABLE) - .update({ sharedOnLinkedIn: 'now' }) - .eq('username', username) - .is('sharedOnLinkedIn', null) - } - - // Get ticket data - const { data, error } = await supabaseAdminClient - .from(LW_TABLE) - .select('name, ticketNumber, sharedOnTwitter, sharedOnLinkedIn, metadata') - .eq('username', username) - .maybeSingle() - - if (error) console.log(error.message) - if (!data) throw new Error(error?.message ?? 'user not found') - const { name, ticketNumber, metadata } = data - - const golden = (!!data?.sharedOnTwitter && !!data?.sharedOnLinkedIn) ?? false - if (assumeGolden && !golden) return await fetch(`${STORAGE_URL}/golden_no_meme.png`) - - // Else, generate image and upload to storage. - const BACKGROUND = { - REG: { - BG: `${STORAGE_URL}/assets/backgrounds/regular.png`, - LOGO: `${STORAGE_URL}/assets/lw8-logo-regular.png`, - }, - GOLD: { - BG: `${STORAGE_URL}/assets/backgrounds/golden.png`, - LOGO: `${STORAGE_URL}/assets/lw8-logo-gold.png`, - }, - } - - const fontData = await font - const monoFontData = await mono_font - const numDigits = `${Number(ticketNumber)}`.length - const prefix = `00000000`.slice(numDigits) - const HAS_ROLE = !!metadata?.role - const HAS_COMPANY = !!metadata?.company - const HAS_LOCATION = !!metadata?.location - const HAS_NO_META = !HAS_ROLE && !HAS_COMPANY && !HAS_LOCATION - - const generatedTicketImage = new ImageResponse( - ( - <> -
    - {/* Background */} - -
    - -
    - - {/* Name & username */} -
    -
    -

    - {name ?? username} -

    -
    - - {/* Username */} -
    - {HAS_NO_META && username && `@${username}`} - {HAS_ROLE && `${metadata.role}`} - {HAS_COMPANY && `${HAS_ROLE ? ' at ' : ''}${metadata.company} `} - {HAS_LOCATION && `${(HAS_ROLE || HAS_COMPANY) && ' —'} ${metadata.location}`} -
    -
    -

    - August 7-11 - supabase.com/launch-week -

    -
    - {/* Ticket No */} -

    - {`NO ${prefix}${ticketNumber}`} -

    - - ), - { - width: 1200, - height: 628, - fonts: [ - { - name: 'Circular', - data: fontData, - style: 'normal', - }, - { - name: 'SourceCodePro', - data: monoFontData, - style: 'normal', - }, - ], - headers: { - 'content-type': 'image/png', - 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', - 'cdn-cache-control': 'max-age=31536000', - }, - } - ) - - // Upload image to storage. - const { error: storageError } = await supabaseAdminClient.storage - .from('images') - .upload( - `lw8/tickets/${golden ? 'golden' : 'regular'}/${BUCKET_FOLDER_VERSION}/${username}.png`, - generatedTicketImage.body!, - { - contentType: 'image/png', - // cacheControl: `${60 * 60 * 24 * 7}`, - cacheControl: `0`, - // Update cached og image, people might need to update info - upsert: true, - } - ) - if (storageError) throw new Error(`storageError: ${storageError.message}`) - - // Generate og image - fetch('https://obuldanrptloktxcffvn.supabase.co/functions/v1/lw8-ticket-og', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: - 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTIyNjkzMjYsImV4cCI6MjAwNzg0NTMyNn0.1S6qpBbHtEmGuMsIx5UOhRiFd4YbVv-yLTrLk6tVGmM', - }, - body: JSON.stringify({ - username, - golden, - }), - }) - - return await fetch( - `${STORAGE_URL}/tickets/${ - golden ? 'golden' : 'regular' - }/${BUCKET_FOLDER_VERSION}/${username}.png` - ) - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 400, - }) - } -} diff --git a/package-lock.json b/package-lock.json index b13151a21e8..43e2001d8b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3395,6 +3395,7 @@ "@supabase/auth-helpers-react": "^0.4.2", "@supabase/auth-ui-react": "^0.4.5", "@supabase/supabase-js": "^2.41.1", + "animejs": "^3.2.2", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", @@ -3438,6 +3439,7 @@ "yup": "^1.4.0" }, "devDependencies": { + "@types/animejs": "^3.1.12", "@types/classnames": "^2.3.1", "@types/dat.gui": "^0.7.10", "@types/mdx-js__react": "^1.5.6", @@ -16449,6 +16451,12 @@ "@types/estree": "*" } }, + "node_modules/@types/animejs": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@types/animejs/-/animejs-3.1.12.tgz", + "integrity": "sha512-fpdH+ZtlO0kqjTOqRaBdsEmvpRNOayI8k4EVkEtitL5l6wducDOXk0rgQgfZqWf/ZX9DzXrHf257S5i9xTcISQ==", + "dev": true + }, "node_modules/@types/aria-query": { "version": "5.0.2", "dev": true, @@ -18368,6 +18376,11 @@ "properties": "^1.2.1" } }, + "node_modules/animejs": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.2.tgz", + "integrity": "sha512-Ao95qWLpDPXXM+WrmwcKbl6uNlC5tjnowlaRYtuVDHHoygjtIPfDUoK9NthrlZsQSKjZXlmji2TrBUAVbiH0LQ==" + }, "node_modules/ansi-colors": { "version": "4.1.3", "dev": true, @@ -27123,6 +27136,15 @@ "fast-loops": "^1.1.3" } }, + "node_modules/input-otp": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.2.3.tgz", + "integrity": "sha512-pxYvgnihL9KAdpcShX2+iKctdMRbDs36bIqd8uIsN3e5vv9VjMv2bhO3S5Bl1PjcDPsA/OXZe5R71n8oVtucfQ==", + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, "node_modules/internal-slot": { "version": "1.0.5", "license": "MIT", @@ -47699,6 +47721,7 @@ "common-tags": "^1.8.2", "date-fns": "^2.30.0", "formik": "^2.2.9", + "input-otp": "^1.2.3", "lodash": "^4.17.21", "lucide-react": "^0.338.0", "next-themes": "^0.3.0", diff --git a/packages/ui-patterns/CountdownWidget/CountdownStep.tsx b/packages/ui-patterns/CountdownWidget/CountdownStep.tsx index 10121fe8d5b..85927e3d805 100644 --- a/packages/ui-patterns/CountdownWidget/CountdownStep.tsx +++ b/packages/ui-patterns/CountdownWidget/CountdownStep.tsx @@ -5,9 +5,11 @@ interface CountdownStepProps { value: string | number unit: string showCard?: boolean + size?: 'small' | 'large' } -function CountdownStep({ value, unit, showCard = true }: CountdownStepProps) { +function CountdownStep({ value, unit, showCard = true, size = 'small' }: CountdownStepProps) { + const isLarge = size === 'large' const [isMounted, setIsMounted] = useState(false) const valueWithZero = (value as number) > 9 ? value : '0' + value @@ -20,17 +22,18 @@ function CountdownStep({ value, unit, showCard = true }: CountdownStepProps) { return (
    {valueWithZero} diff --git a/packages/ui-patterns/CountdownWidget/index.tsx b/packages/ui-patterns/CountdownWidget/index.tsx index 572475f8629..09b1073b208 100644 --- a/packages/ui-patterns/CountdownWidget/index.tsx +++ b/packages/ui-patterns/CountdownWidget/index.tsx @@ -1,3 +1,4 @@ +import { cn } from 'ui/src/lib/utils' import CountdownStep from './CountdownStep' interface CountdownWidgetProps { @@ -6,6 +7,8 @@ interface CountdownWidgetProps { minutes?: string seconds?: string showCard?: boolean + className?: string + size?: 'small' | 'large' } export function CountdownWidget({ @@ -14,33 +17,36 @@ export function CountdownWidget({ minutes, seconds, showCard = true, + className, + size, }: CountdownWidgetProps) { - const Colon = () => : + const isLarge = size === 'large' + const Colon = () => : const showItem = (item: string | undefined) => item !== undefined && item !== '0' return ( -
    +
    {days !== undefined && days !== '0' ? ( <> - + ) : null} {hours !== undefined ? ( <> - + ) : null} {minutes !== undefined ? ( <> - + {seconds !== undefined && } ) : null} {seconds !== undefined ? ( - + ) : null}
    ) diff --git a/packages/ui/index.tsx b/packages/ui/index.tsx index 26e0d26ef05..cb4c4f0b0f8 100644 --- a/packages/ui/index.tsx +++ b/packages/ui/index.tsx @@ -84,6 +84,7 @@ export * from './src/components/shadcn/ui/toaster' export * from './src/components/shadcn/ui/separator' export * from './src/components/shadcn/ui/sheet' export * from './src/components/shadcn/ui/badge' +export * from './src/components/shadcn/ui/input-otp' export { Command as Command_Shadcn_, diff --git a/packages/ui/package.json b/packages/ui/package.json index d26eb39e465..d7c83cdaddf 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -58,6 +58,7 @@ "common-tags": "^1.8.2", "date-fns": "^2.30.0", "formik": "^2.2.9", + "input-otp": "^1.2.3", "lodash": "^4.17.21", "lucide-react": "^0.338.0", "next-themes": "^0.3.0", diff --git a/packages/ui/src/components/PromoToast/PromoToast.tsx b/packages/ui/src/components/PromoToast/PromoToast.tsx index 05fdd9263aa..314c7e052aa 100644 --- a/packages/ui/src/components/PromoToast/PromoToast.tsx +++ b/packages/ui/src/components/PromoToast/PromoToast.tsx @@ -3,13 +3,19 @@ import Link from 'next/link' import { useEffect, useState } from 'react' import { Button, cn } from 'ui' import { LOCAL_STORAGE_KEYS } from 'common' -import PromoBg from './PromoBg' +import CountdownComponent from '../../layout/banners/LW11CountdownBanner/Countdown' +import announcement from '../../layout/banners/data/Announcement.json' +import { useTheme } from 'next-themes' -const LWXLogo = - 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lwx/assets/lwx_logo.svg?t=2023-11-22T17%3A45%3A52.077Z' +const LW11BGDark = + 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw11/assets/backgrounds/regular/001.png' +const LW11BGLight = + 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw11/assets/backgrounds/platinum/001.png' const PromoToast = () => { const [visible, setVisible] = useState(false) + const { resolvedTheme } = useTheme() + const bgImage = resolvedTheme?.includes('dark') ? LW11BGDark : LW11BGLight useEffect(() => { const shouldHide = @@ -26,36 +32,42 @@ const PromoToast = () => { localStorage?.setItem(LOCAL_STORAGE_KEYS.HIDE_PROMO_TOAST, 'true') } - if (!visible) { - return null - } + if (!visible) return null return (
    -
    -
    -

    Launch Week

    - Supabase Launch Week X Logo -
    - 11-15 Dec +

    + Join us for a + Special Announcement +

    +
    + APRIL 15-19 / 7AM PT + {/* */}
    - +
    ) } diff --git a/packages/ui/src/components/shadcn/stories/Input.stories.tsx b/packages/ui/src/components/shadcn/stories/Input.stories.tsx index e4bc207548a..69dce93ce2b 100644 --- a/packages/ui/src/components/shadcn/stories/Input.stories.tsx +++ b/packages/ui/src/components/shadcn/stories/Input.stories.tsx @@ -4,13 +4,13 @@ import { useForm } from 'react-hook-form' import * as z from 'zod' import { Button } from '../ui/button' import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, } from '../ui/form' import { Input } from '../ui/input' import { Label } from '../ui/label' diff --git a/packages/ui/src/components/shadcn/stories/InputOTP.stories.tsx b/packages/ui/src/components/shadcn/stories/InputOTP.stories.tsx new file mode 100644 index 00000000000..75c14a130b6 --- /dev/null +++ b/packages/ui/src/components/shadcn/stories/InputOTP.stories.tsx @@ -0,0 +1,27 @@ +import { Meta } from '@storybook/react' +import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'ui' + +const meta: Meta = { + title: 'shadcn/InputOTP', + component: InputOTP, +} + +export function Default() { + return ( + + + + + + + + + + + + + + ) +} + +export default meta diff --git a/packages/ui/src/components/shadcn/ui/input-otp.tsx b/packages/ui/src/components/shadcn/ui/input-otp.tsx new file mode 100644 index 00000000000..d3c75c21ac6 --- /dev/null +++ b/packages/ui/src/components/shadcn/ui/input-otp.tsx @@ -0,0 +1,71 @@ +'use client' + +import * as React from 'react' +import { OTPInput, OTPInputContext } from 'input-otp' +import { Dot } from 'lucide-react' + +import { cn } from '../../../lib/utils/cn' + +const InputOTP = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, containerClassName, ...props }, ref) => ( + +)) +InputOTP.displayName = 'InputOTP' + +const InputOTPGroup = React.forwardRef< + React.ElementRef<'div'>, + React.ComponentPropsWithoutRef<'div'> +>(({ className, ...props }, ref) => ( +
    +)) +InputOTPGroup.displayName = 'InputOTPGroup' + +const InputOTPSlot = React.forwardRef< + React.ElementRef<'div'>, + React.ComponentPropsWithoutRef<'div'> & { index: number } +>(({ index, className, ...props }, ref) => { + const inputOTPContext = React.useContext(OTPInputContext) + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] + + return ( +
    + {char} + {hasFakeCaret && ( +
    +
    +
    + )} +
    + ) +}) +InputOTPSlot.displayName = 'InputOTPSlot' + +const InputOTPSeparator = React.forwardRef< + React.ElementRef<'div'>, + React.ComponentPropsWithoutRef<'div'> +>(({ ...props }, ref) => ( +
    + +
    +)) +InputOTPSeparator.displayName = 'InputOTPSeparator' + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } diff --git a/packages/ui/src/layout/banners/LW11CountdownBanner/Countdown.tsx b/packages/ui/src/layout/banners/LW11CountdownBanner/Countdown.tsx new file mode 100644 index 00000000000..dcc0b717529 --- /dev/null +++ b/packages/ui/src/layout/banners/LW11CountdownBanner/Countdown.tsx @@ -0,0 +1,34 @@ +import Countdown from 'react-countdown' +import { CountdownWidget } from 'ui-patterns' + +const CountdownComponent = ({ + date, + showCard = true, +}: { + date: string | number | Date + showCard?: boolean +}) => { + if (!date) return null + + const renderer = ({ days, hours, minutes, seconds, completed }: any) => { + if (completed) { + // Render a completed state + return null + } else { + // Render countdown + return ( + + ) + } + } + + return +} + +export default CountdownComponent diff --git a/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx b/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx new file mode 100644 index 00000000000..016c753ca2d --- /dev/null +++ b/packages/ui/src/layout/banners/LW11CountdownBanner/LW11CountdownBanner.tsx @@ -0,0 +1,40 @@ +import { useRouter } from 'next/router' +import { Button, cn } from 'ui' +import Countdown from './Countdown' +import announcement from '../data/Announcement.json' +import Link from 'next/link' +import { isBrowser } from 'common' + +export function LW11CountdownBanner() { + const router = useRouter() + const isHomePage = router.pathname === '/' + const isLaunchWeekPage = router.pathname === '/special-announcement' + const isLaunchWeekSection = + router.pathname.includes('launch-week') || router.pathname.includes('special-announcement') + const siteUrl = isBrowser && window.location.origin + + if (isLaunchWeekPage || isHomePage) return null + + return ( +
    +
    +
    +

    {announcement.text}

    +
    + +
    + +
    +
    +
    + ) +} + +export default LW11CountdownBanner diff --git a/packages/ui/src/layout/banners/LW8CountdownBanner/VideoPreview.tsx b/packages/ui/src/layout/banners/LW11CountdownBanner/VideoPreview.tsx similarity index 100% rename from packages/ui/src/layout/banners/LW8CountdownBanner/VideoPreview.tsx rename to packages/ui/src/layout/banners/LW11CountdownBanner/VideoPreview.tsx diff --git a/packages/ui/src/layout/banners/LW11CountdownBanner/index.ts b/packages/ui/src/layout/banners/LW11CountdownBanner/index.ts new file mode 100644 index 00000000000..d1f607a3717 --- /dev/null +++ b/packages/ui/src/layout/banners/LW11CountdownBanner/index.ts @@ -0,0 +1 @@ +export { LW11CountdownBanner } from './LW11CountdownBanner' diff --git a/packages/ui/src/layout/banners/LW8CountdownBanner/LW8CountdownBanner.tsx b/packages/ui/src/layout/banners/LW8CountdownBanner/LW8CountdownBanner.tsx deleted file mode 100644 index e12413c5e65..00000000000 --- a/packages/ui/src/layout/banners/LW8CountdownBanner/LW8CountdownBanner.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useRouter } from 'next/router' -import Lw8BannerBg from './Lw8BannerBg' -// import VideoPreview from './VideoPreview' - -export function LW8CountdownBanner() { - const { pathname } = useRouter() - const isHomePage = pathname === '/' - const isLaunchWeekPage = pathname === '/launch-week' - const isLaunchWeekSection = pathname.includes('launch-week') - - if (isLaunchWeekPage || isHomePage) return null - - return ( -
    -
    -
    -

    - Supabase Launch Week 8 -

    - {/* */} -
    -
    - -
    - ) -} - -export default LW8CountdownBanner diff --git a/packages/ui/src/layout/banners/LW8CountdownBanner/Lw8BannerBg.tsx b/packages/ui/src/layout/banners/LW8CountdownBanner/Lw8BannerBg.tsx deleted file mode 100644 index 72fc431e99c..00000000000 --- a/packages/ui/src/layout/banners/LW8CountdownBanner/Lw8BannerBg.tsx +++ /dev/null @@ -1,224 +0,0 @@ -export default function Lw8BannerBg({ className }: { className?: string }) { - return ( -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - ) -} diff --git a/packages/ui/src/layout/banners/LW8CountdownBanner/index.ts b/packages/ui/src/layout/banners/LW8CountdownBanner/index.ts deleted file mode 100644 index f87f7ca1cb9..00000000000 --- a/packages/ui/src/layout/banners/LW8CountdownBanner/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LW8CountdownBanner } from './LW8CountdownBanner' diff --git a/packages/ui/src/layout/banners/data/Announcement.json b/packages/ui/src/layout/banners/data/Announcement.json index 7dc4b6baf6d..7a7802d2131 100644 --- a/packages/ui/src/layout/banners/data/Announcement.json +++ b/packages/ui/src/layout/banners/data/Announcement.json @@ -1,7 +1,7 @@ { "show": true, - "text": "Supabase Launch Week X", - "launchDate": "2023-12-11T08:00:00.000-08:00", - "link": "https://supabase.com/launch-week", - "badge": "View announcements" + "text": "Join us for a Special Announcement", + "launchDate": "2024-04-15T07:00:00.000-08:00", + "link": "https://supabase.com/special-announcement", + "badge": "Claim your ticket" } diff --git a/packages/ui/src/layout/banners/data/constants.ts b/packages/ui/src/layout/banners/data/constants.ts index 75cd31eb613..cee33925a39 100644 --- a/packages/ui/src/layout/banners/data/constants.ts +++ b/packages/ui/src/layout/banners/data/constants.ts @@ -1,8 +1,8 @@ export const LW_SITE_URL = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production' - ? 'https://supabase.com/launch-week' + ? 'https://supabase.com/special-announcement' : process.env.NEXT_PUBLIC_VERCEL_URL - ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}/launch-week` - : 'http://localhost:3000/launch-week' + ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}/special-announcement` + : 'http://localhost:3000/special-announcement' export const LW8_END_DATE = '2023-08-11T23:59:59.999-07:00' diff --git a/packages/ui/src/layout/banners/index.tsx b/packages/ui/src/layout/banners/index.tsx index 49a7d6ec582..b8cb19c8d8f 100644 --- a/packages/ui/src/layout/banners/index.tsx +++ b/packages/ui/src/layout/banners/index.tsx @@ -1,2 +1,2 @@ export { default as Announcement } from './Announcement' -export { default as LW8CountdownBanner } from './LW8CountdownBanner/LW8CountdownBanner' +export { default as LW11CountdownBanner } from './LW11CountdownBanner/LW11CountdownBanner' diff --git a/packages/ui/tailwind.config.js b/packages/ui/tailwind.config.js index ceed2515361..cc5546ac08d 100644 --- a/packages/ui/tailwind.config.js +++ b/packages/ui/tailwind.config.js @@ -17,10 +17,15 @@ module.exports = config({ '0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' }, '100%': { backgroundColor: 'transparent' }, }, + 'caret-blink': { + '0%,70%,100%': { opacity: '1' }, + '20%,50%': { opacity: '0' }, + }, }, animation: { 'flash-code': 'flash-code 1s forwards', 'flash-code-slow': 'flash-code 2s forwards', + 'caret-blink': 'caret-blink 1.25s ease-out infinite', }, colors: { border: 'hsl(var(--border-overlay))',