diff --git a/apps/docs/layouts/SiteLayout.tsx b/apps/docs/layouts/SiteLayout.tsx index b24019de139..59bead2a64d 100644 --- a/apps/docs/layouts/SiteLayout.tsx +++ b/apps/docs/layouts/SiteLayout.tsx @@ -8,6 +8,7 @@ import Head from 'next/head' import { PropsWithChildren, memo } from 'react' import Footer from '~/components/Navigation/Footer' import { menuState, useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState' +import { Announcement, LW8CountdownBanner } from 'ui' const levelsData = { home: { @@ -326,6 +327,9 @@ const SiteLayout = ({ children }: PropsWithChildren<{}>) => { Supabase Docs
+ + +
diff --git a/apps/www/_blog/2023-04-07-designing-with-ai-midjourney.mdx b/apps/www/_blog/2023-04-07-designing-with-ai-midjourney.mdx index 07f29e5190f..d15b4143e63 100644 --- a/apps/www/_blog/2023-04-07-designing-with-ai-midjourney.mdx +++ b/apps/www/_blog/2023-04-07-designing-with-ai-midjourney.mdx @@ -57,7 +57,7 @@ And voila, we had a baseline for generating variations: The final visual that you see everywhere won us over because of its vibrant swirls. While each swirl looks slightly different, a consistent style is still maintained throughout. -Fortunately, we didn't require full-width images for each ticket. Upscaled MidJourney images sufficed. Nevertheless, we had to recreate the main visual on the landing page in vectors to use it effectively. You can view it in full glory at [https://supabase.com/launch-week](https://supabase.com/launch-week). +Fortunately, we didn't require full-width images for each ticket. Upscaled MidJourney images sufficed. Nevertheless, we had to recreate the main visual on the landing page in vectors to use it effectively. You can view it in full glory at [https://supabase.com/launch-week/7](https://supabase.com/launch-week/7). ![Screenshot 2023-04-06 at 09.47.47.png](/images/blog/launch-week-7/designing-with-ai-midjourney/launch-week-7-ticket-home.png) @@ -258,13 +258,13 @@ const channel = supabase .subscribe() ``` -Interested to know how this fits within your Next.js application? Find the code [here](https://github.com/supabase/supabase/blob/master/apps/www/components/LaunchWeek/Ticket/TicketForm.tsx#L70-L102). +Interested to know how this fits within your Next.js application? Find the code [here](https://github.com/supabase/supabase/blob/master/apps/www/components/LaunchWeek/7/Ticket/TicketForm.tsx#L70-L102). ## Displaying the images -We have some images, so how can we now display them somewhere? [@Francesco Sansalvadore](https://twitter.com/frnk_snslvdr) saw one of our team members trying to “swipe” on the tickets slider component on the [launch week page](https://supabase.com/launch-week/tickets) and he thought, why not feature all the fantastic people who generated tickets on a single page? +We have some images, so how can we now display them somewhere? [@Francesco Sansalvadore](https://twitter.com/frnk_snslvdr) saw one of our team members trying to “swipe” on the tickets slider component on the [launch week page](https://supabase.com/launch-week/7/tickets) and he thought, why not feature all the fantastic people who generated tickets on a single page? -We built a [ticket wall](https://supabase.com/launch-week/tickets) that you can scroll endlessly. We approached it with an infinite scroll technique, lazy-loading a few tickets at a time. +We built a [ticket wall](https://supabase.com/launch-week/7/tickets) that you can scroll endlessly. We approached it with an infinite scroll technique, lazy-loading a few tickets at a time. ![launch week 7 ticket wall](/images/blog/launch-week-7/designing-with-ai-midjourney/launch-week-7-ticket-wall.png) diff --git a/apps/www/components/CTABanner/index.tsx b/apps/www/components/CTABanner/index.tsx index fb08eb5630e..ff1f2ddec1a 100644 --- a/apps/www/components/CTABanner/index.tsx +++ b/apps/www/components/CTABanner/index.tsx @@ -1,18 +1,26 @@ import { Button } from 'ui' -const CTABanner = (props: any) => { - const { darkerBg } = props +interface Props { + className?: string + darkerBg?: boolean +} + +const CTABanner = ({ darkerBg, className }: Props) => { return (

Build in a weekend, - scale to millions + + {' '} + scale to millions +

diff --git a/apps/www/components/DarkModeToggle.tsx b/apps/www/components/DarkModeToggle.tsx index 4b3b03cc7aa..f8c9ad1abff 100644 --- a/apps/www/components/DarkModeToggle.tsx +++ b/apps/www/components/DarkModeToggle.tsx @@ -1,20 +1,21 @@ import { useTheme } from 'common/Providers' import { IconSun, IconMoon } from 'ui' -function DarkModeToggle() { +function DarkModeToggle({ disabled = false }: { disabled?: boolean }) { const { isDarkMode, toggleTheme } = useTheme() return ( -
+
© Supabase Inc - +
diff --git a/apps/www/components/Hero/Hero.tsx b/apps/www/components/Hero/Hero.tsx index a309e4087f1..3b50464f47d 100644 --- a/apps/www/components/Hero/Hero.tsx +++ b/apps/www/components/Hero/Hero.tsx @@ -3,31 +3,59 @@ import { useRouter } from 'next/router' import Telemetry, { TelemetryEvent } from '~/lib/telemetry' import { useTelemetryProps } from 'common/hooks/useTelemetryProps' import gaEvents from '~/lib/gaEvents' -import { Button, IconBookOpen } from 'ui' +import { Badge, Button, IconArrowRight, IconBookOpen } from 'ui' import SectionContainer from '~/components/Layouts/SectionContainer' import HeroFrameworks from './HeroFrameworks' import styles from './hero.module.css' +import Image from 'next/image' +import { range } from 'lodash' +import ParticlesCanvas from '../LaunchWeek/8/AnimatedParticles/ParticlesCanvas' +import { useBreakpoint } from 'common' +import { useEffect, useState } from 'react' const Hero = () => { const router = useRouter() + const [showAnnouncement, setShowAnnouncement] = useState(false) const telemetryProps = useTelemetryProps() + const isMobile = useBreakpoint() const sendTelemetryEvent = async (event: TelemetryEvent) => { await Telemetry.sendEvent(event, telemetryProps, router) } + useEffect(() => { + if (typeof window !== undefined) { + const lw8AnnouncementKey = window.localStorage.getItem('announcement_SupabaseLaunchWeek8') + setShowAnnouncement(!!lw8AnnouncementKey) + } + }, [showAnnouncement]) + return ( -
+
-
+
+ {showAnnouncement && ( + + +
+ Get you ticket +
+ Launch Week 8 - August 7th + +
+ + )}

Build in a weekend @@ -63,12 +91,35 @@ const Hero = () => {

+
+
+ + starts background +
+
+ +
+
) diff --git a/apps/www/components/LaunchWeek/ScheduleInfo.tsx b/apps/www/components/LaunchWeek/5/ScheduleInfo.tsx similarity index 100% rename from apps/www/components/LaunchWeek/ScheduleInfo.tsx rename to apps/www/components/LaunchWeek/5/ScheduleInfo.tsx diff --git a/apps/www/components/LaunchWeek/days.json b/apps/www/components/LaunchWeek/5/days.json similarity index 100% rename from apps/www/components/LaunchWeek/days.json rename to apps/www/components/LaunchWeek/5/days.json diff --git a/apps/www/components/LaunchWeek/HackathonSection.tsx b/apps/www/components/LaunchWeek/6/HackathonSection.tsx similarity index 100% rename from apps/www/components/LaunchWeek/HackathonSection.tsx rename to apps/www/components/LaunchWeek/6/HackathonSection.tsx diff --git a/apps/www/components/LaunchWeek/LaunchHero.tsx b/apps/www/components/LaunchWeek/6/LaunchHero.tsx similarity index 100% rename from apps/www/components/LaunchWeek/LaunchHero.tsx rename to apps/www/components/LaunchWeek/6/LaunchHero.tsx diff --git a/apps/www/components/LaunchWeek/PreLaunchTeaser.tsx b/apps/www/components/LaunchWeek/6/PreLaunchTeaser.tsx similarity index 100% rename from apps/www/components/LaunchWeek/PreLaunchTeaser.tsx rename to apps/www/components/LaunchWeek/6/PreLaunchTeaser.tsx diff --git a/apps/www/components/LaunchWeek/launch-controller.json b/apps/www/components/LaunchWeek/6/launch-controller.json similarity index 100% rename from apps/www/components/LaunchWeek/launch-controller.json rename to apps/www/components/LaunchWeek/6/launch-controller.json diff --git a/apps/www/components/LaunchWeek/lw6_days.json b/apps/www/components/LaunchWeek/6/lw6_days.json similarity index 100% rename from apps/www/components/LaunchWeek/lw6_days.json rename to apps/www/components/LaunchWeek/6/lw6_days.json diff --git a/apps/www/components/LaunchWeek/7/BlogLinks.tsx b/apps/www/components/LaunchWeek/7/BlogLinks.tsx index 41168f03550..6ccf0de19ca 100644 --- a/apps/www/components/LaunchWeek/7/BlogLinks.tsx +++ b/apps/www/components/LaunchWeek/7/BlogLinks.tsx @@ -2,7 +2,7 @@ import { motion } from 'framer-motion' import Image from 'next/image' import Link from 'next/link' import React from 'react' -import days from '~/components/LaunchWeek/lw7_days' +import days from '~/components/LaunchWeek/7/lw7_days' const BlogLinks = () => { const activeDays = days.filter((day) => Date.parse(day.publishedAt) <= Date.now()) diff --git a/apps/www/components/LaunchWeek/LW7BgGraphic.tsx b/apps/www/components/LaunchWeek/7/LW7BgGraphic.tsx similarity index 100% rename from apps/www/components/LaunchWeek/LW7BgGraphic.tsx rename to apps/www/components/LaunchWeek/7/LW7BgGraphic.tsx diff --git a/apps/www/components/LaunchWeek/LabelBadge.tsx b/apps/www/components/LaunchWeek/7/LabelBadge.tsx similarity index 100% rename from apps/www/components/LaunchWeek/LabelBadge.tsx rename to apps/www/components/LaunchWeek/7/LabelBadge.tsx diff --git a/apps/www/components/LaunchWeek/LaunchSection/Announcement.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/Announcement.tsx similarity index 94% rename from apps/www/components/LaunchWeek/LaunchSection/Announcement.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/Announcement.tsx index 73805f6de09..5f8f4db18a9 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/Announcement.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/Announcement.tsx @@ -1,4 +1,4 @@ -import { Announcement as AnnouncementType } from '../types' +import { Announcement as AnnouncementType } from '../../types' export const Announcement = (props: AnnouncementType) => { const containerClasses = [] diff --git a/apps/www/components/LaunchWeek/LaunchSection/ArticleButton.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/ArticleButton.tsx similarity index 95% rename from apps/www/components/LaunchWeek/LaunchSection/ArticleButton.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/ArticleButton.tsx index 10826a772f0..364e9ccd5eb 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/ArticleButton.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/ArticleButton.tsx @@ -1,5 +1,5 @@ import { NewspaperIcon } from '@heroicons/react/outline' -import { Article } from '../types' +import { Article } from '../../types' const ArticleButton = (props: Article) => { return ( diff --git a/apps/www/components/LaunchWeek/LaunchSection/ArticleButtonListItem.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/ArticleButtonListItem.tsx similarity index 94% rename from apps/www/components/LaunchWeek/LaunchSection/ArticleButtonListItem.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/ArticleButtonListItem.tsx index be927a23cd2..e374bd023fc 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/ArticleButtonListItem.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/ArticleButtonListItem.tsx @@ -1,6 +1,6 @@ import { NewspaperIcon } from '@heroicons/react/outline' import Link from 'next/link' -import { Article } from '../types' +import { Article } from '../../types' const ArticleButtonListItem = (props: Article) => { return ( diff --git a/apps/www/components/LaunchWeek/LaunchSection/LaunchWeekLogoHeader.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/LaunchWeekLogoHeader.tsx similarity index 100% rename from apps/www/components/LaunchWeek/LaunchSection/LaunchWeekLogoHeader.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/LaunchWeekLogoHeader.tsx diff --git a/apps/www/components/LaunchWeek/LaunchSection/ProductButton.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/ProductButton.tsx similarity index 95% rename from apps/www/components/LaunchWeek/LaunchSection/ProductButton.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/ProductButton.tsx index c1b89239015..550d0e28815 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/ProductButton.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/ProductButton.tsx @@ -1,5 +1,5 @@ import { SparklesIcon } from '@heroicons/react/outline' -import { Article } from '../types' +import { Article } from '../../types' const ProductButton = (props: Article) => { return ( diff --git a/apps/www/components/LaunchWeek/LaunchSection/ProductButtonListItem.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/ProductButtonListItem.tsx similarity index 95% rename from apps/www/components/LaunchWeek/LaunchSection/ProductButtonListItem.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/ProductButtonListItem.tsx index bb8edfb1af4..07ed656ae0c 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/ProductButtonListItem.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/ProductButtonListItem.tsx @@ -1,6 +1,6 @@ import { SparklesIcon } from '@heroicons/react/outline' import Link from 'next/link' -import { Article } from '../types' +import { Article } from '../../types' export const ProductButtonListItem = (props: Article) => { return ( diff --git a/apps/www/components/LaunchWeek/LaunchSection/TicketBrick.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrick.tsx similarity index 96% rename from apps/www/components/LaunchWeek/LaunchSection/TicketBrick.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/TicketBrick.tsx index 50e0438868d..5f393f57650 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/TicketBrick.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrick.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import TicketHeader from '../Ticket/TicketHeader' import TicketNumber from '../Ticket/TicketNumber' -import { UserData } from '../Ticket/hooks/use-conf-data' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import Image from 'next/image' interface Props { diff --git a/apps/www/components/LaunchWeek/LaunchSection/TicketBrickWall.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWall.tsx similarity index 96% rename from apps/www/components/LaunchWeek/LaunchSection/TicketBrickWall.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWall.tsx index b757f4cdeaf..ea7922d61f4 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/TicketBrickWall.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWall.tsx @@ -2,7 +2,7 @@ import Link from 'next/link' import React from 'react' import { Button } from 'ui' import { useBreakpoint } from 'common/hooks/useBreakpoint' -import { UserData } from '../Ticket/hooks/use-conf-data' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import { TicketBrickWallSlider } from './TicketBrickWallSlider' type user = UserData diff --git a/apps/www/components/LaunchWeek/LaunchSection/TicketBrickWallSlider.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWallSlider.tsx similarity index 92% rename from apps/www/components/LaunchWeek/LaunchSection/TicketBrickWallSlider.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWallSlider.tsx index 8ff947547f9..ebc7a12ffb9 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/TicketBrickWallSlider.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/TicketBrickWallSlider.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react' import Image from 'next/image' import Link from 'next/link' -import { UserData } from '../Ticket/hooks/use-conf-data' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import { useWindowSize } from 'react-use' import { Swiper, SwiperSlide } from 'swiper/react' @@ -34,7 +34,7 @@ export function TicketBrickWallSlider({ users, reverse, speed = 10000 }: Props) return (
-
+
setSwiperInstance(swiper)} centeredSlides={true} @@ -65,7 +65,7 @@ export function TicketBrickWallSlider({ users, reverse, speed = 10000 }: Props) > {users.map((user, i) => ( - +
diff --git a/apps/www/components/LaunchWeek/LaunchSection/brick-wall.module.css b/apps/www/components/LaunchWeek/7/LaunchSection/brick-wall.module.css similarity index 100% rename from apps/www/components/LaunchWeek/LaunchSection/brick-wall.module.css rename to apps/www/components/LaunchWeek/7/LaunchSection/brick-wall.module.css diff --git a/apps/www/components/LaunchWeek/LaunchSection/index.tsx b/apps/www/components/LaunchWeek/7/LaunchSection/index.tsx similarity index 99% rename from apps/www/components/LaunchWeek/LaunchSection/index.tsx rename to apps/www/components/LaunchWeek/7/LaunchSection/index.tsx index f48efa21534..3faf36f20d0 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/index.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchSection/index.tsx @@ -3,7 +3,7 @@ import { useState } from 'react' import { PlayIcon, TruckIcon, XIcon } from '@heroicons/react/outline' import { Badge, Modal } from 'ui' -import { Article, Product, WeekDayProps } from '../types' +import { Article, Product, WeekDayProps } from '../../types' import _days from './../days.json' import ArticleButtonListItem from './ArticleButtonListItem' import ProductButtonListItem from './ProductButtonListItem' diff --git a/apps/www/components/LaunchWeek/LaunchSection/ticket-brick-wall-slider.module.css b/apps/www/components/LaunchWeek/7/LaunchSection/ticket-brick-wall-slider.module.css similarity index 100% rename from apps/www/components/LaunchWeek/LaunchSection/ticket-brick-wall-slider.module.css rename to apps/www/components/LaunchWeek/7/LaunchSection/ticket-brick-wall-slider.module.css diff --git a/apps/www/components/LaunchWeek/LaunchSection/LaunchWeekPrizeCard.tsx b/apps/www/components/LaunchWeek/7/LaunchWeekPrizeCard.tsx similarity index 100% rename from apps/www/components/LaunchWeek/LaunchSection/LaunchWeekPrizeCard.tsx rename to apps/www/components/LaunchWeek/7/LaunchWeekPrizeCard.tsx diff --git a/apps/www/components/LaunchWeek/LaunchSection/LaunchWeekPrizeSection.tsx b/apps/www/components/LaunchWeek/7/LaunchWeekPrizeSection.tsx similarity index 99% rename from apps/www/components/LaunchWeek/LaunchSection/LaunchWeekPrizeSection.tsx rename to apps/www/components/LaunchWeek/7/LaunchWeekPrizeSection.tsx index 052d6c44479..4f8d365192b 100644 --- a/apps/www/components/LaunchWeek/LaunchSection/LaunchWeekPrizeSection.tsx +++ b/apps/www/components/LaunchWeek/7/LaunchWeekPrizeSection.tsx @@ -1,5 +1,5 @@ import React from 'react' -import LabelBadge from '../LabelBadge' +import LabelBadge from './LabelBadge' import LaunchWeekPrizeCard from './LaunchWeekPrizeCard' import { motion } from 'framer-motion' diff --git a/apps/www/components/LaunchWeek/ReferralIndicator.tsx b/apps/www/components/LaunchWeek/7/ReferralIndicator.tsx similarity index 82% rename from apps/www/components/LaunchWeek/ReferralIndicator.tsx rename to apps/www/components/LaunchWeek/7/ReferralIndicator.tsx index fd3a7f4c3fd..c4ad96a0871 100644 --- a/apps/www/components/LaunchWeek/ReferralIndicator.tsx +++ b/apps/www/components/LaunchWeek/7/ReferralIndicator.tsx @@ -1,6 +1,6 @@ import React from 'react' -import useConfData from '~/components/LaunchWeek/Ticket//hooks/use-conf-data' -import useWinningChances from './Ticket/hooks/useWinningChances' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import useWinningChances from '../hooks/useWinningChances' export default function ReferralIndicator() { const winningChances = useWinningChances() diff --git a/apps/www/components/LaunchWeek/Releases/LW7/Day5.tsx b/apps/www/components/LaunchWeek/7/Releases/Day5.tsx similarity index 99% rename from apps/www/components/LaunchWeek/Releases/LW7/Day5.tsx rename to apps/www/components/LaunchWeek/7/Releases/Day5.tsx index b36d06f1c32..01371b11a04 100644 --- a/apps/www/components/LaunchWeek/Releases/LW7/Day5.tsx +++ b/apps/www/components/LaunchWeek/7/Releases/Day5.tsx @@ -11,8 +11,8 @@ import { StyledArticleBadge, } from './components' -import { WeekDayProps } from '~/components/LaunchWeek/lw7_days' -import { opacityVariant4, scaleOpacityVariant2 } from './LW7Releases' +import { WeekDayProps } from '~/components/LaunchWeek/7/lw7_days' +import { opacityVariant4, scaleOpacityVariant2 } from '.' import styles from './day5.module.css' import Link from 'next/link' diff --git a/apps/www/components/LaunchWeek/Releases/LW7/components/index.tsx b/apps/www/components/LaunchWeek/7/Releases/components/index.tsx similarity index 100% rename from apps/www/components/LaunchWeek/Releases/LW7/components/index.tsx rename to apps/www/components/LaunchWeek/7/Releases/components/index.tsx diff --git a/apps/www/components/LaunchWeek/Releases/LW7/day5.module.css b/apps/www/components/LaunchWeek/7/Releases/day5.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Releases/LW7/day5.module.css rename to apps/www/components/LaunchWeek/7/Releases/day5.module.css diff --git a/apps/www/components/LaunchWeek/Releases/LW7/LW7Releases.tsx b/apps/www/components/LaunchWeek/7/Releases/index.tsx similarity index 99% rename from apps/www/components/LaunchWeek/Releases/LW7/LW7Releases.tsx rename to apps/www/components/LaunchWeek/7/Releases/index.tsx index bf6cf2c54be..6445cb5f8a2 100644 --- a/apps/www/components/LaunchWeek/Releases/LW7/LW7Releases.tsx +++ b/apps/www/components/LaunchWeek/7/Releases/index.tsx @@ -3,7 +3,7 @@ import { useEffect } from 'react' import { Accordion } from 'ui' import Lottie from 'lottie-react' -import days, { WeekDayProps, endOfLW7 } from '~/components/LaunchWeek/lw7_days' +import days, { WeekDayProps, endOfLW7 } from '~/components/LaunchWeek/7/lw7_days' import SectionContainer from '~/components/Layouts/SectionContainer' import { AccordionHeader, diff --git a/apps/www/components/LaunchWeek/Releases/LW7/styles/launchWeek7.module.css b/apps/www/components/LaunchWeek/7/Releases/styles/launchWeek7.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Releases/LW7/styles/launchWeek7.module.css rename to apps/www/components/LaunchWeek/7/Releases/styles/launchWeek7.module.css diff --git a/apps/www/components/LaunchWeek/Releases/LW7/styles/utils7.module.css b/apps/www/components/LaunchWeek/7/Releases/styles/utils7.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Releases/LW7/styles/utils7.module.css rename to apps/www/components/LaunchWeek/7/Releases/styles/utils7.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/ActualTicket.tsx b/apps/www/components/LaunchWeek/7/Ticket/ActualTicket.tsx similarity index 97% rename from apps/www/components/LaunchWeek/Ticket/ActualTicket.tsx rename to apps/www/components/LaunchWeek/7/Ticket/ActualTicket.tsx index bb59bd906de..72afd641dfb 100644 --- a/apps/www/components/LaunchWeek/Ticket/ActualTicket.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/ActualTicket.tsx @@ -1,14 +1,14 @@ import cn from 'classnames' import { useRef, useState } from 'react' import styles from './ticket.module.css' -import styleUtils from './utils.module.css' +import styleUtils from '../../utils.module.css' import TicketVisual from './TicketVisual' import TicketActions from './TicketActions' import TicketCopy from './ticket-copy' -import { UserData } from './hooks/use-conf-data' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import ReferralIndicator from '../ReferralIndicator' -import useWinningChances from './hooks/useWinningChances' +import useWinningChances from '../../hooks/useWinningChances' import { SITE_URL } from '~/lib/constants' import { useBreakpoint } from 'common/hooks/useBreakpoint' diff --git a/apps/www/components/LaunchWeek/Ticket/TicketActions.tsx b/apps/www/components/LaunchWeek/7/Ticket/TicketActions.tsx similarity index 97% rename from apps/www/components/LaunchWeek/Ticket/TicketActions.tsx rename to apps/www/components/LaunchWeek/7/Ticket/TicketActions.tsx index ee351e58f04..ad775d8a483 100644 --- a/apps/www/components/LaunchWeek/Ticket/TicketActions.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/TicketActions.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react' import { SITE_URL, TWEET_TEXT, TWEET_TEXT_GOLDEN } from '~/lib/constants' import { IconCheckCircle } from 'ui' -import useConfData from '~/components/LaunchWeek/Ticket//hooks/use-conf-data' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' import { useParams } from '~/hooks/useParams' import TicketForm from './TicketForm' @@ -62,7 +62,7 @@ export default function TicketActions({
- Connect with Github + Connect with GitHub
{ + .then(async ({ error }: any) => { // If error because of duplicate email, ignore and proceed, otherwise sign out. if (error && error?.code !== '23505') return supabase.auth.signOut() const { data } = await supabase @@ -79,7 +79,7 @@ export default function TicketForm({ defaultUsername = '', setTicketGenerationSt table: 'lw7_tickets', filter: `username=eq.${username}`, }, - (payload) => { + (payload: any) => { const golden = !!payload.new.sharedOnTwitter && !!payload.new.sharedOnLinkedIn setUserData({ ...payload.new, @@ -157,12 +157,12 @@ export default function TicketForm({ defaultUsername = '', setTicketGenerationSt {session ? ( <> - Connect with Github + Connect with GitHub ) : ( {formState === 'loading' && } - Connect with Github + Connect with GitHub )} diff --git a/apps/www/components/LaunchWeek/Ticket/TicketHeader.tsx b/apps/www/components/LaunchWeek/7/Ticket/TicketHeader.tsx similarity index 81% rename from apps/www/components/LaunchWeek/Ticket/TicketHeader.tsx rename to apps/www/components/LaunchWeek/7/Ticket/TicketHeader.tsx index da3d1e79401..2b082bb954a 100644 --- a/apps/www/components/LaunchWeek/Ticket/TicketHeader.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/TicketHeader.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { DATE } from '~/lib/constants' +import { LW7_DATE } from '~/lib/constants' interface Props { size?: 'default' | 'small' @@ -12,7 +12,7 @@ export default function TicketHeader({ size }: Props) { className={size === 'small' ? 'w-[165px]' : 'w-[220px] md:w-[230px]'} src={`/images/launchweek/ticket-header-logo.png`} /> - {DATE} + {LW7_DATE}
) diff --git a/apps/www/components/LaunchWeek/Ticket/TicketNumber.tsx b/apps/www/components/LaunchWeek/7/Ticket/TicketNumber.tsx similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/TicketNumber.tsx rename to apps/www/components/LaunchWeek/7/Ticket/TicketNumber.tsx diff --git a/apps/www/components/LaunchWeek/Ticket/TicketProfile.tsx b/apps/www/components/LaunchWeek/7/Ticket/TicketProfile.tsx similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/TicketProfile.tsx rename to apps/www/components/LaunchWeek/7/Ticket/TicketProfile.tsx diff --git a/apps/www/components/LaunchWeek/Ticket/TicketVisual.tsx b/apps/www/components/LaunchWeek/7/Ticket/TicketVisual.tsx similarity index 98% rename from apps/www/components/LaunchWeek/Ticket/TicketVisual.tsx rename to apps/www/components/LaunchWeek/7/Ticket/TicketVisual.tsx index be31e8c231c..7dffd9a3439 100644 --- a/apps/www/components/LaunchWeek/Ticket/TicketVisual.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/TicketVisual.tsx @@ -2,7 +2,7 @@ import styles from './ticket-visual.module.css' import TicketProfile from './TicketProfile' import TicketNumber from './TicketNumber' import Tilt from 'vanilla-tilt' -import useConfData from '~/components/LaunchWeek/Ticket/hooks/use-conf-data' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' import TicketHeader from './TicketHeader' import { useEffect, useRef, useState } from 'react' import Image from 'next/image' diff --git a/apps/www/components/LaunchWeek/Ticket/conf-container.module.css b/apps/www/components/LaunchWeek/7/Ticket/conf-container.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/conf-container.module.css rename to apps/www/components/LaunchWeek/7/Ticket/conf-container.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/conf-container.tsx b/apps/www/components/LaunchWeek/7/Ticket/conf-container.tsx similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/conf-container.tsx rename to apps/www/components/LaunchWeek/7/Ticket/conf-container.tsx diff --git a/apps/www/components/LaunchWeek/Ticket/form.module.css b/apps/www/components/LaunchWeek/7/Ticket/form.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/form.module.css rename to apps/www/components/LaunchWeek/7/Ticket/form.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/form.tsx b/apps/www/components/LaunchWeek/7/Ticket/form.tsx similarity index 96% rename from apps/www/components/LaunchWeek/Ticket/form.tsx rename to apps/www/components/LaunchWeek/7/Ticket/form.tsx index d8a9c9dbc3d..15b20e2edb8 100644 --- a/apps/www/components/LaunchWeek/Ticket/form.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/form.tsx @@ -1,10 +1,10 @@ import { useState, useCallback } from 'react' import cn from 'classnames' -import useConfData from '~/components/LaunchWeek/Ticket/hooks/use-conf-data' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' import { useRouter } from 'next/router' -import styleUtils from './utils.module.css' +import styleUtils from '../../utils.module.css' import styles from './form.module.css' -import useEmailQueryParam from '~/components/LaunchWeek/Ticket/hooks/use-email-query-param' +import useEmailQueryParam from '~/components/LaunchWeek/hooks/use-email-query-param' import { IconLoader } from '~/../../packages/ui' type FormState = 'default' | 'loading' | 'error' diff --git a/apps/www/components/LaunchWeek/Ticket/loading-dots.module.css b/apps/www/components/LaunchWeek/7/Ticket/loading-dots.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/loading-dots.module.css rename to apps/www/components/LaunchWeek/7/Ticket/loading-dots.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/loading-dots.tsx b/apps/www/components/LaunchWeek/7/Ticket/loading-dots.tsx similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/loading-dots.tsx rename to apps/www/components/LaunchWeek/7/Ticket/loading-dots.tsx diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-actions.module.css b/apps/www/components/LaunchWeek/7/Ticket/ticket-actions.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/ticket-actions.module.css rename to apps/www/components/LaunchWeek/7/Ticket/ticket-actions.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-copy.module.css b/apps/www/components/LaunchWeek/7/Ticket/ticket-copy.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/ticket-copy.module.css rename to apps/www/components/LaunchWeek/7/Ticket/ticket-copy.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-copy.tsx b/apps/www/components/LaunchWeek/7/Ticket/ticket-copy.tsx similarity index 98% rename from apps/www/components/LaunchWeek/Ticket/ticket-copy.tsx rename to apps/www/components/LaunchWeek/7/Ticket/ticket-copy.tsx index c228e815a67..2286aae4293 100644 --- a/apps/www/components/LaunchWeek/Ticket/ticket-copy.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/ticket-copy.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, useRef } from 'react' import cn from 'classnames' import { SITE_URL } from '~/lib/constants' -import styleUtils from './utils.module.css' +import styleUtils from '../../utils.module.css' import { IconCopy, IconCheck } from 'ui' type Props = { diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-form.module.css b/apps/www/components/LaunchWeek/7/Ticket/ticket-form.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/ticket-form.module.css rename to apps/www/components/LaunchWeek/7/Ticket/ticket-form.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-image.module.css b/apps/www/components/LaunchWeek/7/Ticket/ticket-image.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/ticket-image.module.css rename to apps/www/components/LaunchWeek/7/Ticket/ticket-image.module.css diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-image.tsx b/apps/www/components/LaunchWeek/7/Ticket/ticket-image.tsx similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/ticket-image.tsx rename to apps/www/components/LaunchWeek/7/Ticket/ticket-image.tsx diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-info-footer.tsx b/apps/www/components/LaunchWeek/7/Ticket/ticket-info-footer.tsx similarity index 90% rename from apps/www/components/LaunchWeek/Ticket/ticket-info-footer.tsx rename to apps/www/components/LaunchWeek/7/Ticket/ticket-info-footer.tsx index 7dd3b821630..375223f161c 100644 --- a/apps/www/components/LaunchWeek/Ticket/ticket-info-footer.tsx +++ b/apps/www/components/LaunchWeek/7/Ticket/ticket-info-footer.tsx @@ -1,6 +1,6 @@ import styles from './ticket-info.module.css' import cn from 'classnames' -import { DATE, SITE_URL } from '~/lib/constants' +import { LW7_DATE, SITE_URL } from '~/lib/constants' const siteUrl = new URL(SITE_URL) const siteUrlForTicket = `${siteUrl.host}${siteUrl.pathname}`.replace(/\/$/, '') @@ -16,7 +16,7 @@ export default function TicketInfoFooter({ golden && '!text-white' }`} > -
{DATE}
+
{LW7_DATE}
window.scrollTo(0, 0)} > diff --git a/apps/www/components/LaunchWeek/lw7-bg-graphic.module.css b/apps/www/components/LaunchWeek/7/lw7-bg-graphic.module.css similarity index 100% rename from apps/www/components/LaunchWeek/lw7-bg-graphic.module.css rename to apps/www/components/LaunchWeek/7/lw7-bg-graphic.module.css diff --git a/apps/www/components/LaunchWeek/lw7_days.ts b/apps/www/components/LaunchWeek/7/lw7_days.ts similarity index 100% rename from apps/www/components/LaunchWeek/lw7_days.ts rename to apps/www/components/LaunchWeek/7/lw7_days.ts diff --git a/apps/www/components/LaunchWeek/8/AnimatedParticles/Particle.tsx b/apps/www/components/LaunchWeek/8/AnimatedParticles/Particle.tsx new file mode 100644 index 00000000000..29908f79016 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/AnimatedParticles/Particle.tsx @@ -0,0 +1,49 @@ +import React, { useRef } from 'react' +import { useFrame } from '@react-three/fiber' + +interface Props { + animate?: boolean + children: any + config: any + user?: any +} + +const Particle = ({ animate = true, children, config }: Props) => { + const particle = useRef(null) + + const pathOffsetShape = + Math.pow( + Math.random() * config.xRandomnessShape, + config.xRandomness - 1 + 1 - config.xRandomness / 2 + ) * config.xThickness + + const pathOffset = pathOffsetShape * (pathOffsetShape < 1 || Math.random() < 0.5 ? 1 : -1) + const verticalRandomness = Math.random() * (config.yThickness - 1) + 1 - config.yThickness / 2 + + const speed = Math.random() * (config.min_speed - config.max_speed) + config.max_speed + + const circumference = (config.widthRadius * Math.PI * 2) / 100 + const delayOffsetFactor = 100 + const delayOffset = Math.random() * delayOffsetFactor + + useFrame(({ clock }) => { + if (animate) { + const timer = clock.getElapsedTime() * speed + delayOffset + // When the loop count is even, draw bottom 8 shape + // if odd, draw top 8 shape + const isEven = Math.floor(timer / circumference) % 2 == 0 + particle.current.position.x = isEven + ? Math.sin(timer) * config.widthRadius * config.widthRatio + pathOffset + : Math.sin(timer) * config.widthRadius + pathOffset + particle.current.position.y = isEven + ? Math.cos(timer) * config.bottomHeightRadius - + config.bottomHeightRadius + + verticalRandomness + : -Math.cos(timer) * config.topHeightRadius + config.topHeightRadius + verticalRandomness + } + }) + + return {children} +} + +export default Particle diff --git a/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticleHoverable.tsx b/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticleHoverable.tsx new file mode 100644 index 00000000000..27811c3f880 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticleHoverable.tsx @@ -0,0 +1,94 @@ +import React, { useRef, useEffect, useState } from 'react' +import { useFrame } from '@react-three/fiber' +import { Html } from '@react-three/drei' + +interface Props { + animate?: boolean + children: any + config: any + user: any +} + +const Particle = ({ animate = true, user, children, config }: Props) => { + const particle = useRef(null) + const isDebugMode = !user?.username + const [isHovered, setIsHovered] = useState(false) + + useEffect(() => { + if (isHovered) { + setTimeout(() => { + setIsHovered(false) + }, 1000) + } + }, [isHovered]) + + const timestamp = new Date(user.createdAt).getTime() + const unixTimestamp = Math.floor(timestamp / 1000) + const lastDigit = parseInt(timestamp.toString().split('')?.pop() as string) + + const pathOffsetShape = isDebugMode + ? Math.pow( + Math.random() * config.xRandomnessShape, + config.xRandomness - 1 + 1 - config.xRandomness / 2 + ) * config.xThickness + : Math.pow( + Math.random() * config.xRandomnessShape, + config.xRandomness - 1 + 1 - config.xRandomness / 2 + ) * config.xThickness + + const pathOffset = pathOffsetShape * (pathOffsetShape < 1 || Math.random() < 0.5 ? 1 : -1) + const verticalRandomness = isDebugMode + ? Math.random() * (config.yThickness - 1) + 1 - config.yThickness / 2 + : Math.random() * (config.yThickness - 1) + 1 - config.yThickness / 2 + + const smol = (unixTimestamp / 10000000000) * -1 + const sub1 = 1 / smol + + console.log(smol, sub1) + const speed = isDebugMode + ? Math.random() * (config.min_speed - config.max_speed) + config.max_speed + : sub1 * (config.min_speed - config.max_speed) + config.max_speed + // Math.random() * (config.min_speed - config.max_speed) + config.max_speed + + const circumference = (config.widthRadius * Math.PI * 2) / 100 + const delayOffsetFactor = 100 + const delayOffset = isDebugMode ? Math.random() : unixTimestamp * delayOffsetFactor + + useFrame(({ clock }) => { + if (animate) { + const timer = clock.getElapsedTime() * speed + delayOffset + // When the loop count is even, draw bottom 8 shape + // if odd, draw top 8 shape + const isEven = Math.floor(timer / circumference) % 2 == 0 + particle.current.position.x = isEven + ? Math.sin(timer) * config.widthRadius * config.widthRatio + pathOffset + : Math.sin(timer) * config.widthRadius + pathOffset + particle.current.position.y = isEven + ? Math.cos(timer) * config.bottomHeightRadius - + config.bottomHeightRadius + + verticalRandomness + : -Math.cos(timer) * config.topHeightRadius + config.topHeightRadius + verticalRandomness + } + }) + + return ( + !isDebugMode && setIsHovered(true)}> + {children} + + {!isDebugMode && isHovered && user.username && ( + div]:!relative`, + isHovered && '!visible !flex', + ].join(' ')} + > + @{user.username} + + )} + + ) +} + +export default Particle diff --git a/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx b/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx new file mode 100644 index 00000000000..904710ded6a --- /dev/null +++ b/apps/www/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas.tsx @@ -0,0 +1,140 @@ +import React, { useMemo, useEffect, useState } from 'react' +import { Canvas } from '@react-three/fiber' +import { AdditiveBlending } from 'three' +import Particle from './Particle' +import useParticlesConfig from './hooks/useParticlesConfig' +import { SupabaseClient } from '@supabase/supabase-js' + +const ParticlesCanvas = ({ supabase, users }: { supabase?: SupabaseClient; users: any }) => { + const isWindowUndefined = typeof window === 'undefined' + if (isWindowUndefined) return null + + const canvasRef = React.useRef(null) + + const [animate, setAnimate] = useState(true) + const { config, handleSetConfig, particles, setParticles, isDebugMode } = + useParticlesConfig(users) + const [realtimeChannel, setRealtimeChannel] = useState | null>(null) + + const loadUsers = async () => { + return await supabase!.from('lw8_tickets_golden').select('id, golden', { count: 'exact' }) + } + + // Update particles live when new tickets are generated + useEffect(() => { + if (!!supabase) { + const channel = supabase + .channel('lw8_tickets_changes') + .on( + 'postgres_changes', + { + event: 'INSERT', + schema: 'public', + table: 'lw8_tickets_golden', + }, + async () => { + const { data: users } = await loadUsers() + + handleSetConfig('particles', users?.length) + setParticles((prev: any) => users! ?? prev) + } + ) + .subscribe() + setRealtimeChannel(channel) + } + + return () => { + // Cleanup realtime subscription on unmount + realtimeChannel?.unsubscribe() + } + }, []) + + // stop animation if canvas if is not in viewport + // to avoid unnecessary computations + const handleScroll = () => { + if (canvasRef.current && typeof window !== 'undefined') { + const rect = (canvasRef.current as HTMLDivElement)?.getBoundingClientRect() + const isInView = rect.bottom > 0 + + setAnimate(isInView) + } + } + + useEffect(() => { + window.addEventListener('scroll', handleScroll) + + return () => { + window.removeEventListener('scroll', handleScroll) + } + }, []) + + /* ThreeJs setup */ + + const Geometry = useMemo( + () => () => , + [] + ) + const GoldGeometry = useMemo( + () => () => , + [] + ) + const Material = () => + useMemo( + () => ( + + ), + [] + ) + const GoldMaterial = () => + useMemo( + () => ( + + ), + [] + ) + + return ( + + + + {/* Animated 8 shape particles */} + {particles?.map((user: any, index: number) => ( + + {(!!user.id && user.golden) || (config.showGold && Math.random() <= 0.5) ? ( + <> + + + + ) : ( + <> + + + + )} + + ))} + + + ) +} + +export default ParticlesCanvas diff --git a/apps/www/components/LaunchWeek/8/AnimatedParticles/hooks/useParticlesConfig.tsx b/apps/www/components/LaunchWeek/8/AnimatedParticles/hooks/useParticlesConfig.tsx new file mode 100644 index 00000000000..6eb0dadb087 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/AnimatedParticles/hooks/useParticlesConfig.tsx @@ -0,0 +1,182 @@ +import { useEffect, useState } from 'react' +import { range } from 'lodash' + +let defaultConfig = { + particles: 20, + particlesSize: 1.2, + goldParticlesSize: 1.5, + particlesSides: 5, + particlesBlending: true, + lightIntensity: 0.3, + widthRadius: 100, + widthRatio: 1.2, + topHeightRadius: 80, + bottomHeightRadius: 100, + trails: 0, + trailWidth: 40, + trailLength: 100, + trailDecay: 450, + color: 'white', + colorGold: '#b89d18', + xThickness: 7, + xRandomnessFactor: 2.2, + xRandomnessShape: 2.2, + xRandomness: 5, + yThickness: 20, + max_speed: 0.1, + min_speed: -0.1, + showGold: true, +} + +const useParticlesConfig = (users: any): any => { + const isWindowUndefined = typeof window === 'undefined' + if (isWindowUndefined) return null + const hash = window.location.hash + const isDebugMode = hash.includes('#debug') + const [particles, setParticles] = useState(users) + + const [config, setConfig] = useState(defaultConfig) + + const handleSetConfig = (name: string, value: any) => { + setConfig((prevConfig) => ({ ...prevConfig, [name]: value })) + } + + const init = async () => { + if (!isDebugMode) return + const dat = await import('dat.gui') + const gui = new dat.GUI() + const particlesFolder = gui.addFolder('Particles') + const shapeFolder = gui.addFolder('Shape') + const speedFolder = gui.addFolder('Speed') + // const trailFolder = gui.addFolder('Trail') + gui.width = 500 + particlesFolder + .add(config, 'particles') + .min(1) + .max(5000) + .step(1) + .name('Count') + .onChange((value) => { + handleSetConfig('particles', value) + setParticles(range(0, value)) + }) + particlesFolder + .add(config, 'particlesSize') + .min(1) + .max(10) + .step(0.05) + .name('Size') + .onChange((value) => handleSetConfig('particlesSize', value)) + particlesFolder + .add(config, 'goldParticlesSize') + .min(1) + .max(10) + .step(0.05) + .name('Gold Particles Size') + .onChange((value) => handleSetConfig('goldParticlesSize', value)) + particlesFolder + .add(config, 'particlesSides') + .min(3) + .max(20) + .step(1) + .name('Sides') + .onChange((value) => handleSetConfig('particlesSides', value)) + particlesFolder + .add(config, 'lightIntensity') + .min(0) + .max(10) + .step(0.05) + .name('Light intensity') + .onChange((value) => handleSetConfig('lightIntensity', value)) + particlesFolder + .add(config, 'showGold') + .name('Show Gold Particles') + .onChange((value) => handleSetConfig('showGold', value)) + particlesFolder + .add(config, 'particlesBlending') + .name('Blending') + .onChange((value) => handleSetConfig('particlesBlending', value)) + shapeFolder + .add(config, 'widthRadius') + .min(1) + .max(200) + .step(1) + .name('Width Radius') + .onChange((value) => handleSetConfig('widthRadius', value)) + shapeFolder + .add(config, 'widthRatio') + .min(0.5) + .max(3) + .step(0.01) + .name('Top/Bottom Ratio') + .onChange((value) => handleSetConfig('widthRatio', value)) + shapeFolder + .add(config, 'topHeightRadius') + .min(1) + .max(200) + .step(1) + .name('Height Radius - Top') + .onChange((value) => handleSetConfig('topHeightRadius', value)) + shapeFolder + .add(config, 'bottomHeightRadius') + .min(1) + .max(200) + .step(1) + .name('Height Radius - Bottom') + .onChange((value) => handleSetConfig('bottomHeightRadius', value)) + shapeFolder + .add(config, 'xThickness') + .min(1) + .max(100) + .step(0.1) + .name('Stroke Width') + .onChange((value) => handleSetConfig('xThickness', value)) + shapeFolder + .add(config, 'xRandomnessShape') + .min(0) + .max(5) + .step(0.001) + .name('Randomness shape') + .onChange((value) => handleSetConfig('xRandomnessShape', value)) + shapeFolder + .add(config, 'xRandomness') + .min(0) + .max(50) + .step(0.01) + .name('Randomness') + .onChange((value) => handleSetConfig('xRandomness', value)) + shapeFolder + .add(config, 'yThickness') + .min(1) + .max(50) + .step(0.1) + .name('y thickness') + .onChange((value) => handleSetConfig('yThickness', value)) + speedFolder + .add(config, 'min_speed') + .min(-6) + .max(6) + .step(0.01) + .name('Min speed') + .onChange((value) => handleSetConfig('min_speed', value)) + speedFolder + .add(config, 'max_speed') + .min(-6) + .max(6) + .step(0.01) + .name('Max speed') + .onChange((value) => handleSetConfig('max_speed', value)) + + particlesFolder.open() + shapeFolder.open() + speedFolder.open() + } + + useEffect(() => { + init() + }, []) + + return { config, handleSetConfig, particles, setParticles, isDebugMode } +} + +export default useParticlesConfig diff --git a/apps/www/components/LaunchWeek/8/Countdown.tsx b/apps/www/components/LaunchWeek/8/Countdown.tsx new file mode 100644 index 00000000000..322b7a944db --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Countdown.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import Countdown from 'react-countdown' +import { CountdownWidget } from 'ui' +import { LW8_LAUNCH_DATE } from '~/lib/constants' + +const CountdownComponent = () => { + 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/8/LabelBadge.tsx b/apps/www/components/LaunchWeek/8/LabelBadge.tsx new file mode 100644 index 00000000000..521970158fa --- /dev/null +++ b/apps/www/components/LaunchWeek/8/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/8/LaunchWeekLogoHeader.tsx b/apps/www/components/LaunchWeek/8/LaunchWeekLogoHeader.tsx new file mode 100644 index 00000000000..4c0786c62c3 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/LaunchWeekLogoHeader.tsx @@ -0,0 +1,31 @@ +import Link from 'next/link' +import CountdownComponent from './Countdown' + +export function LaunchWeekLogoHeader() { + return ( + + ) +} diff --git a/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx new file mode 100644 index 00000000000..353fb85499e --- /dev/null +++ b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeCard.tsx @@ -0,0 +1,69 @@ +import Image from 'next/image' +import React from 'react' +import { motion } from 'framer-motion' + +type animateFromType = 'left' | 'right' | 'up' | 'down' | 'opacityOnly' + +export default function LaunchWeekPrizeCard({ + imageUrl = '', + imageWrapperClassName, + content, + className, + animateFrom, + imgObjectPosition = 'center', +}: { + imageUrl: string + imageWrapperClassName?: string + content: any + className?: string + animateFrom?: animateFromType + imgObjectPosition?: string +}) { + const finalState = { x: 0, y: 0, opacity: 1 } + + function getAnimationFrom(animationDirection: animateFromType) { + switch (animationDirection) { + case 'left': + return { x: -20, opacity: 0 } + case 'up': + return { y: -20, opacity: 0 } + case 'right': + return { x: 20, opacity: 0 } + case 'down': + return { y: 20, opacity: 0 } + case 'opacityOnly': + return { opacity: 0 } + } + } + + return ( + +
+
+ +
+
{content && content}
+
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx new file mode 100644 index 00000000000..b59b35da825 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/LaunchWeekPrizeSection.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import LabelBadge from './LabelBadge' +import LaunchWeekPrizeCard from './LaunchWeekPrizeCard' +import Image from 'next/image' + +export default function LaunchWeekPrizeSection({ + className, + ticket, +}: { + className?: string + ticket?: any +}) { + const Ticket = ticket + + return ( +
+
+ prizes background +
+
+
+
+
+
+ prizes +
+

Get your winning ticket

+

+ Join us on August 11th for Launch Week 8's final day +
and find out if you are one of the lucky + winners. Get sharing! +

+
+
+ {ticket && } +
+
+ + Supabase Desk Mat + + } + animateFrom="down" + /> + + Supabase T-Shirt + + } + animateFrom="down" + /> + + Supabase Cap + + } + /> + + Supabase Sticker Pack + + } + /> +
+
+
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/Ticket.tsx b/apps/www/components/LaunchWeek/8/Ticket/Ticket.tsx new file mode 100644 index 00000000000..71a0b3113ed --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/Ticket.tsx @@ -0,0 +1,79 @@ +import styles from './ticket-visual.module.css' +import TicketProfile from './TicketProfile' +import TicketNumber from './TicketNumber' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import TicketHeader from './TicketHeader' +import Image from 'next/image' +import TicketForm from './TicketForm' +import TicketFooter from './TicketFooter' + +type TicketGenerationState = 'default' | 'loading' +type Props = { + user: UserData + ticketGenerationState?: TicketGenerationState + setTicketGenerationState?: any +} + +export default function TicketVisual({ + user, + ticketGenerationState = 'default', + setTicketGenerationState, +}: Props) { + const { username, golden = false, bg_image_id: bgImageId, ticketNumber } = user + + const ticketBg = { + regular: { + background: `/images/launchweek/8/ticket-bg/regular.png`, + }, + golden: { + background: `/images/launchweek/8/ticket-bg/golden.png`, + }, + } + + const CURRENT_TICKET = golden ? 'golden' : 'regular' + const CURRENT_TICKET_BG = ticketBg[CURRENT_TICKET].background + + return ( +
+
+ {username ? ( +
+ +
+ +
+ + +
+ ticket background +
+
+ ) : ( + + )} +
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketActions.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketActions.tsx new file mode 100644 index 00000000000..54d417d7188 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketActions.tsx @@ -0,0 +1,133 @@ +import { useEffect, useRef, useState } from 'react' +import { SITE_URL, TWEET_TEXT, TWEET_TEXT_GOLDEN } from '~/lib/constants' +import { IconCheckCircle } from 'ui' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import { useParams } from '~/hooks/useParams' +import TicketForm from './TicketForm' + +type TicketGenerationState = 'default' | 'loading' + +type Props = { + username: string + golden?: boolean + ticketGenerationState?: TicketGenerationState + setTicketGenerationState: (ticketGenerationState: TicketGenerationState) => void +} + +export default function TicketActions({ + username, + golden = false, + ticketGenerationState, + setTicketGenerationState, +}: Props) { + const [imgReady, setImgReady] = useState(false) + const [loading, setLoading] = useState(false) + const downloadLink = useRef() + const link = `${SITE_URL}/tickets/${username}?lw=8${golden ? `&golden=true` : ''}` + const permalink = encodeURIComponent(link) + const text = golden ? TWEET_TEXT_GOLDEN : TWEET_TEXT + const encodedText = encodeURIComponent(text) + const { userData, supabase } = useConfData() + const tweetUrl = `https://twitter.com/intent/tweet?url=${permalink}&via=supabase&text=${encodedText}` + const linkedInUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${permalink}` + const downloadUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw8-ticket?username=${encodeURIComponent( + username + )}` + const params = useParams() + const sharePage = params.username + const LW_TABLE = 'lw8_tickets' + + useEffect(() => { + setImgReady(false) + + const img = new Image() + + img.src = downloadUrl + img.onload = () => { + setImgReady(true) + setLoading(false) + if (downloadLink.current) { + downloadLink.current.click() + downloadLink.current = undefined + } + } + }, [downloadUrl]) + + const handleShare = async (social: 'twitter' | 'linkedin') => { + if (!supabase) return + if (social === 'twitter') { + await supabase + .from(LW_TABLE) + .update({ sharedOnTwitter: 'now' }) + .eq('username', username) + .is('sharedOnTwitter', null) + } else if (social === 'linkedin') { + const res = await supabase + .from(LW_TABLE) + .update({ sharedOnLinkedIn: 'now' }) + .eq('username', username) + .is('sharedOnLinkedIn', null) + + console.log('shareing on LinkedIn', res) + } + } + + return ( +
+ {!sharePage ? ( + <> +
+
+
+ +
+ Connect with GitHub +
+
+ handleShare('twitter')} + href={tweetUrl} + rel="noopener noreferrer prefetch" + target="_blank" + className={[ + `flex items-center justify-center gap-2 rounded text-scale-500 py-1 px-3 border border-[#3e3e3e] text-xs mb-1 transition-all ease-out hover:text-scale-100 hover:bg-[#dfe1e3]`, + userData.sharedOnTwitter ? 'bg-[#E6E8EB] text-scale-500' : 'text-white', + ].join(' ')} + > + {userData.sharedOnTwitter && ( +
+ +
+ )} + Share on Twitter +
+ handleShare('linkedin')} + href={linkedInUrl} + rel="noopener noreferrer prefetch" + target="_blank" + className={[ + `flex items-center justify-center gap-2 rounded text-scale-500 py-1 px-3 border border-[#3e3e3e] text-xs mb-1 transition-all ease-out hover:text-scale-100 hover:bg-[#dfe1e3]`, + userData.sharedOnLinkedIn ? 'bg-[#E6E8EB] text-scale-500' : 'text-white', + ].join(' ')} + > + {userData.sharedOnLinkedIn && ( +
+ +
+ )} + Share on Linkedin +
+ + ) : ( + !username && ( + + ) + )} +
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketContainer.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketContainer.tsx new file mode 100644 index 00000000000..10d2e68b269 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketContainer.tsx @@ -0,0 +1,181 @@ +import { useState } from 'react' +import { SupabaseClient } from '@supabase/supabase-js' +import Link from 'next/link' +import cn from 'classnames' +import { Button } from 'ui' + +import styles from './ticket.module.css' +import styleUtils from '../../utils.module.css' +import Ticket from './Ticket' +import TicketActions from '~/components/LaunchWeek/8/Ticket/TicketActions' +import TicketCopy from '~/components/LaunchWeek/8/Ticket/ticket-copy' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import useWinningChances from '~/components/LaunchWeek/hooks/useWinningChances' +import { SITE_URL } from '~/lib/constants' +import { useBreakpoint } from 'common/hooks/useBreakpoint' +import TicketCustomizationForm from './TicketCustomizationForm' +import TicketDisclaimer from './TicketDisclaimer' + +type TicketGenerationState = 'default' | 'loading' + +type Props = { + user: UserData + supabase: SupabaseClient + referrals: number + sharePage?: boolean +} + +export default function TicketContainer({ user, sharePage, referrals, supabase }: Props) { + const { username, name, golden } = user + const isMobile = useBreakpoint(1023) + const [ticketGenerationState, setTicketGenerationState] = + useState('default') + const winningChances = useWinningChances() + + console.log(supabase) + + if (!user.username) + return ( +
+
+ + +
+
+ ) + + return ( +
+
+
+

+ {!sharePage ? ( + name ? ( + <> + {winningChances === 1 && ( + <> + You're in! + Now make it unique and share. + + )} + {winningChances === 2 && ( + <> + That's x2! +
Share again to get a golden ticket. + + )} + {winningChances === 3 && ( + <> + You have a golden + chance of winning! + + )} + + ) : ( + <> + Generate your ticket.
+ Win the SupaKeyboard. + + ) + ) : ( + <> + {name ? name : username}'s
+ unique ticket + + )} +

+ +
+ {!sharePage ? ( + golden ? ( +

+ Join us on August 11th for Launch Week 8's final day and find out if you are one + of the lucky winners. +

+ ) : ( +

+ Customize your ticket and boost your chances of winning{' '} + + limited edition awards + {' '} + by sharing it with the community. +

+ ) + ) : ( + <> +

+ Generate and share your own custom ticket for a chance to win{' '} + + awesome swag + + . +

+ + + + )} +
+ + {!sharePage && user.username && ( + + )} +
+
+
+
+ + {username && ( +
+ + +
+ )} +
+
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketCustomizationForm.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketCustomizationForm.tsx new file mode 100644 index 00000000000..dd9aa9e243c --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketCustomizationForm.tsx @@ -0,0 +1,147 @@ +import React, { useState } from 'react' +import { Badge, IconCheck, Input } from 'ui' +import { UserData } from '../../hooks/use-conf-data' +import { SupabaseClient } from '@supabase/supabase-js' +import { useDebounce } from 'common' + +interface Props { + supabase: SupabaseClient + user: UserData +} + +const TicketCustomizationForm = ({ supabase, user }: Props) => { + const defaultFormValues = { + role: user.metadata?.role, + company: user.metadata?.company, + location: user.metadata?.location, + } + 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' + + const handleInputChange = (name: string, value: string) => { + setFormData((prev) => ({ ...prev, [name]: value })) + } + + const handleFormSubmit = async () => { + setFormState('saving') + const payload = { metadata: formData } + + if (supabase) { + await supabase + .from('lw8_tickets') + .update(payload) + .eq('username', user.username) + .then((res) => { + if (res.error) return setFormState('error') + setFormState('saved') + setTimeout(() => { + setFormState('idle') + }, 1800) + }) + } + } + + const debouncedChangeHandler = useDebounce(handleFormSubmit, 1200) + + return ( +
debouncedChangeHandler()}> +
+ {!IS_SAVED && !HAS_ERROR && ( + + Connected account + + )} + {IS_SAVED && ( + Saved + )} + {HAS_ERROR && ( + + Something went wrong + + )} + @{user.username} +
+ } + /> + { + handleInputChange('role', event.target.value) + }} + disabled={IS_SAVING} + maxLength={30} + icon={ + + } + /> + { + handleInputChange('company', event.target.value) + }} + disabled={IS_SAVING} + icon={ + + } + /> + { + handleInputChange('location', event.target.value) + }} + disabled={IS_SAVING} + maxLength={20} + icon={ + + } + /> + + ) +} + +export default TicketCustomizationForm diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketDisclaimer.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketDisclaimer.tsx new file mode 100644 index 00000000000..e23e77804e2 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketDisclaimer.tsx @@ -0,0 +1,7 @@ +const TicketDisclaimer = ({ className, children }: { className?: string; children?: any }) => ( +

+ {children || 'By registering you accept to receive email updates on Supabase Launch Week.'} +

+) + +export default TicketDisclaimer diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketFooter.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketFooter.tsx new file mode 100644 index 00000000000..ebffc62ecf2 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketFooter.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { LW8_DATE } from '~/lib/constants' + +interface Props {} + +export default function TicketFooter({}: Props) { + return ( +
+ {LW8_DATE} + supabase.com/launch-week +
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketForm.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketForm.tsx new file mode 100644 index 00000000000..51a1b7bcb41 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketForm.tsx @@ -0,0 +1,169 @@ +import { useState, useRef, useEffect } from 'react' +import { useRouter } from 'next/router' +import cn from 'classnames' +import { SITE_ORIGIN } from '~/lib/constants' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' +import formStyles from './form.module.css' +import ticketFormStyles from './ticket-form.module.css' +import { Button, IconCheckCircle, IconLoader } from 'ui' + +type FormState = 'default' | 'loading' | 'error' +type TicketGenerationState = 'default' | 'loading' + +type Props = { + defaultUsername?: string + ticketGenerationState?: TicketGenerationState + setTicketGenerationState: any +} + +export default function TicketForm({ defaultUsername = '', setTicketGenerationState }: Props) { + const [username, setUsername] = useState(defaultUsername) + const [formState, setFormState] = useState('default') + const [errorMsg] = useState('') + const { supabase, session, setUserData, setPageState, userData } = useConfData() + const [realtimeChannel, setRealtimeChannel] = useState | null>(null) + const formRef = useRef(null) + const router = useRouter() + + useEffect(() => { + if (supabase && session?.user && !userData.id) { + document.body.classList.add('ticket-generated') + const username = session.user.user_metadata.user_name + setUsername(username) + const name = session.user.user_metadata.full_name + const email = session.user.email + supabase + .from('lw8_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') return supabase.auth.signOut() + const { data } = await supabase + .from('lw8_tickets_golden') + .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((_) => {}) + + setPageState('ticket') + + // Listen to realtime changes + if (!realtimeChannel && !data?.golden) { + const channel = supabase + .channel('changes') + .on( + 'postgres_changes', + { + event: 'UPDATE', + schema: 'public', + table: 'lw8_tickets', + filter: `username=eq.${username}`, + }, + (payload: any) => { + const golden = !!payload.new.sharedOnTwitter && !!payload.new.sharedOnLinkedIn + setUserData({ + ...payload.new, + golden, + }) + if (golden) { + channel.unsubscribe() + } + } + ) + .subscribe() + setRealtimeChannel(channel) + } + }) + } + return () => { + // Cleanup realtime subscription on unmount + realtimeChannel?.unsubscribe() + } + }, [session]) + + return formState === 'error' ? ( +
+
+
+
{errorMsg}
+ +
+
+
+ ) : ( +
{ + e.preventDefault() + + if (formState !== 'default') { + setTicketGenerationState('default') + setFormState('default') + return + } + + setFormState('loading') + setTicketGenerationState('loading') + + await supabase.auth.signInWithOAuth({ + provider: 'github', + options: { + redirectTo: `${SITE_ORIGIN}/launch-week/${ + userData.username ? '?referral=' + userData.username : '' + }`, + }, + }) + }} + className="flex flex-col h-full items-center justify-center relative z-20" + > +
+
+ +
+ {/* {!session &&

Only public info will be used.

} */} +
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketHeader.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketHeader.tsx new file mode 100644 index 00000000000..217cf6a38bb --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketHeader.tsx @@ -0,0 +1,20 @@ +import Image from 'next/image' +import React from 'react' + +interface Props { + golden?: boolean +} + +export default function TicketHeader({ golden = false }: Props) { + return ( +
+ Launch Week 8 logo +
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketNumber.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketNumber.tsx new file mode 100644 index 00000000000..d7348a6b545 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketNumber.tsx @@ -0,0 +1,31 @@ +import styles from './ticket.module.css' + +type Props = { + number: number | undefined + golden?: boolean +} + +export default function TicketNumber({ number, golden = false }: Props) { + const numDigits = `${number}`.length + const prefix = `00000000`.slice(numDigits) + const ticketNumberText = `NO ${prefix}${number}` + + return ( + <> +
+ + {ticketNumberText} + +
+ + ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/TicketProfile.tsx b/apps/www/components/LaunchWeek/8/Ticket/TicketProfile.tsx new file mode 100644 index 00000000000..e1cacdff171 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/TicketProfile.tsx @@ -0,0 +1,42 @@ +import { UserData } from '../../hooks/use-conf-data' + +type TicketGenerationState = 'default' | 'loading' +type Props = { + user: UserData + ticketGenerationState?: TicketGenerationState + setTicketGenerationState?: (ticketGenerationState: TicketGenerationState) => void + golden?: boolean +} + +export default function TicketProfile({ user, ticketGenerationState, golden = false }: Props) { + const { username, name, metadata } = user + + 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 + + return ( +
+
+

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

+ {HAS_NO_META && username &&

@{username}

} +
+ {HAS_ROLE && {metadata?.role}} + {HAS_COMPANY && ( + + {HAS_ROLE && ' '} + at {metadata?.company} + + )} + {HAS_LOCATION && ( + + {' '} + {(HAS_ROLE || HAS_COMPANY) && '—'} {metadata?.location} + + )} +
+
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/form.module.css b/apps/www/components/LaunchWeek/8/Ticket/form.module.css new file mode 100644 index 00000000000..5b6a078561b --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/form.module.css @@ -0,0 +1,244 @@ +/* .form { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-bottom: 12px; + gap: 12px; +} */ + +/* .formLeft { + align-items: center; +} +.formCenter { + align-items: center; +} */ + +/* @media (min-width: 1200px) { + .formLeft { + align-items: start; + } + .formCenter { + align-items: center; + } +} */ +/* +.formInfo { + max-width: 520px; +} */ + +.formInfo h3 { + font-size: 1.25em; + font-weight: 400; +} + +.formInfo p { + font-size: 1.1em; + line-height: 1.4; + margin-bottom: 32px; + color: var(--lw-secondary-color); +} + +@media (min-width: 1200px) { + /* .form.share-page { + justify-content: flex-start; + } */ +} + +/* .formInfoLeft { + text-align: center; +} +.formInfoCenter { + text-align: center; + margin: 0 auto; +} */ + +@media (min-width: 1200px) { + .formInfoLeft { + text-align: left; + } + .formInfoCenter { + text-align: center; + margin: 0 auto; + } +} + +.input { + border: none; + width: 100%; + background: transparent; + outline: none; + height: 56px; + padding-left: 15px; + padding-right: 15px; + font-size: var(--text-md); + color: #fff; + font-family: inherit; +} + +@media (min-width: 768px) { + .input { + width: calc(100% - var(--space-32x)); + padding-right: 0; + } +} + +.input::placeholder { + color: var(--lw-secondary-color); +} + +.input-label { + background-color: var(--gray); + border-radius: var(--space-2x); + border: 1px solid transparent; + transition: border-color 0.2s ease, background-color 0.2s ease; + display: block; +} + +.input-label.focused { + background-color: #33373c; +} + +.input-label.error { + background: red; +} + +.input-label.success { + background: #0070f3; +} + +.input-text { + display: flex; + align-items: center; + width: 100%; +} + +.form-row { + position: relative; + max-width: 400px; + width: 100%; +} + +@media (min-width: 768px) { + .form-row { + max-width: 480px; + } +} + +.submit { + width: 100%; + height: 56px; + margin-top: var(--space-4x); + border-radius: var(--space-2x); + border: 1px solid var(--brand); + background: var(--brand); + cursor: pointer; + font-family: inherit; + font-size: var(--text-md); + letter-spacing: -0.02em; + outline: none; + font-weight: 500; + color: white; + transition: background-color 0.2s ease, color 0.2s ease; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.submit.generate-with-github { + display: flex; + margin-bottom: 12px; + position: relative; +} + +@media (min-width: 768px) { + .submit.register { + width: 120px; + height: 40px; + margin-top: 0; + position: absolute; + border-radius: 5px; + right: var(--space-2x); + top: var(--space-2x); + } +} + +.submit.loading { + cursor: default; +} + +.submit.default:hover, +.submit.default:focus { + background: black; + color: var(--brand); +} + +.submit.default.generate-with-github:hover path, +.submit.default.generate-with-github:focus path { + fill: var(--brand); +} + +.submit.error:hover, +.submit.error:focus { + background: #000; + color: #fff; +} + +.submit.default:disabled, +.submit.default:disabled:hover, +.submit.default:disabled:focus { + cursor: default; + background: var(--gray); + border-color: var(--gray); + color: #fff; + justify-content: flex-start; + overflow: hidden; +} + +.submit.default.generate-with-github:disabled path, +.submit.default.generate-with-github:disabled:hover path, +.submit.default.generate-with-github:disabled:focus path { + fill: #fff; +} + +.submit.default.generate-with-github.not-allowed:disabled { + cursor: not-allowed; +} + +@media (min-width: 1200px) { + .form-row { + margin: 0; + } + + .submit.generate-with-github { + width: 240px; + } +} + +.stage-btn { + background: #323332; + border: 2px solid #34b27b; +} + +.github-wrapper { + display: flex; + align-items: center; + flex-direction: column; +} + +.or-divider { + width: 100%; + text-align: center; + color: var(--lw-secondary-color); + margin: var(--space-4x) 0; +} + +@media (min-width: 1200px) { + .github-wrapper { + flex-direction: row; + } + .or-divider { + width: 240px; + margin: 0; + } +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket-actions.module.css b/apps/www/components/LaunchWeek/8/Ticket/ticket-actions.module.css new file mode 100644 index 00000000000..d13c42418d7 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket-actions.module.css @@ -0,0 +1,70 @@ +.button { + border-radius: var(--space-2x); + border: 1px solid #fff; + color: #000; + background: #fff; + cursor: pointer; + font-family: inherit; + font-size: var(--text-md); + letter-spacing: -0.02em; + font-weight: 500; + outline: none; + transition: background-color 0.2s ease; + display: grid; + grid-template-columns: 24px 1fr; + text-align: center; + align-items: center; + padding: 0 var(--space-4x); + height: 55px; + width: 100%; + max-width: 400px; + margin: var(--space-2x) auto; +} + +.loading { + grid-template-columns: 1fr; + justify-items: center; +} + +@media (min-width: 768px) { + .button { + width: 200px; + margin: 0 6px; + } +} + +.button:hover, +.button:focus { + background: black; + color: #fff; +} + +.first { + animation-delay: 2s; +} +.second { + animation-delay: 2.2s; +} +.third { + animation-delay: 2.4s; +} + +:global(.ticket-generated) .first { + animation-delay: 0.6s; +} +:global(.ticket-generated) .second { + animation-delay: 0.8s; +} +:global(.ticket-generated) .third { + animation-delay: 1s; +} + +.linkedin-button { + display: none !important; +} + +@media (min-width: 768px) { + .linkedin-button { + display: grid !important; + } +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket-copy.module.css b/apps/www/components/LaunchWeek/8/Ticket/ticket-copy.module.css new file mode 100644 index 00000000000..65007c67559 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket-copy.module.css @@ -0,0 +1,190 @@ +.wrapper { + color: var(--lw-secondary-color); + display: flex; + align-items: center; + justify-content: center; + animation-delay: 2.6s; + flex-direction: column; +} + +:global(.ticket-generated) .wrapper { + animation-delay: 1.2s; +} + +.label { + width: 100%; + margin-bottom: var(--space-2x); +} + +.label-wrapper { + display: flex; + align-items: center; + max-width: 400px; + width: 100%; + justify-content: space-between; +} + +@media (min-width: 768px) { + .wrapper { + flex-direction: row; + } + + .label { + width: auto; + margin-right: 10px; + margin-bottom: 0px; + } + + .label-wrapper { + width: auto; + } +} + +.field { + position: relative; + border-radius: var(--space-2x); + overflow: hidden; + background: var(--gray); +} + +@media (min-width: 768px) { + .field { + padding-right: 50px; + } + .field.desktop-copy-disabled { + padding-right: 0px; + } +} + +.mobile-copy { + margin-bottom: 10px; + display: flex; +} + +.mobile-copy-disabled.mobile-copy-disabled { + display: none; +} + +.mobile-copy * { + color: var(--lw-secondary-color); + transition: color 0.2s ease; +} + +.mobile-copy:hover * { + color: #fff; + transition: color 0.2s ease; +} + +.field * { + color: var(--lw-secondary-color); + transition: color 0.2s ease; +} + +.field:hover * { + color: #fff; + transition: color 0.2s ease; +} + +.url { + width: calc(100vw - 36px); + user-select: all; + overflow-x: scroll; + white-space: nowrap; + display: block; + + padding: var(--space-2x) 14px; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +@media (min-width: 436px) { + .url { + width: 400px; + } +} + +@media (min-width: 768px) { + .url { + width: 400px; + } + .mobile-copy { + margin-bottom: 0; + display: none; + } +} + +.url::-webkit-scrollbar { + display: none; +} + +/* .fade { + position: absolute; + background: linear-gradient(90deg, rgba(37, 39, 41, 0%) 0%, #252729 100%); + top: 0; + bottom: 0; + right: 0px; + width: 50px; + z-index: 1; + display: block; + border-radius: var(--space-2x); + pointer-events: none; +} */ + +.desktop-copy.desktop-copy-disabled { + display: none; +} + +@media (min-width: 768px) { + .fade { + right: 50px; + } + .fade.desktop-copy-disabled { + right: 0px; + } +} + +.copied { + display: flex; + z-index: 2; + align-items: center; + justify-content: flex-end; + opacity: 0; + transition: opacity 0.2s ease !important; +} + +.copied.visible { + opacity: 1; +} + +.copy-button { + background: none; + outline: none; + border: none; + cursor: pointer; + z-index: 2; + border-radius: var(--space-2x); + width: 40px; + display: flex; + align-items: center; + justify-content: center; + margin-right: -10px; +} + +@media (min-width: 768px) { + .copied { + position: absolute; + right: 50px; + top: 0px; + bottom: 0px; + background: linear-gradient(90deg, rgba(37, 39, 41, 0%) 0px, #252729 40px); + padding-left: 50px; + } + .copy-button { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + margin-right: 0px; + width: 50px; + } +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket-copy.tsx b/apps/www/components/LaunchWeek/8/Ticket/ticket-copy.tsx new file mode 100644 index 00000000000..65e700c6730 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket-copy.tsx @@ -0,0 +1,85 @@ +import { useEffect, useState, useRef } from 'react' +import cn from 'classnames' +import { SITE_URL } from '~/lib/constants' +import styleUtils from '../../utils.module.css' +import { IconCopy, IconCheck } from 'ui' + +type Props = { + username: string + isGolden?: boolean +} + +export default function TicketCopy({ username, isGolden }: Props) { + const [fadeOpacity, setFadeOpacity] = useState(1) + const [scrolling, setScrolling] = useState(false) + const [copyEnabled, setCopyEnabled] = useState(false) + const [copied, setCopied] = useState(false) + const scrollRef = useRef(null) + const buttonRef = useRef(null) + const url = `${SITE_URL}/tickets/${username}?lw=8${isGolden ? `&golden=true` : ''}` + useEffect(() => { + if (navigator.clipboard) { + setCopyEnabled(true) + } + }, []) + + return ( +
+ +
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket-form.module.css b/apps/www/components/LaunchWeek/8/Ticket/ticket-form.module.css new file mode 100644 index 00000000000..d10b43a0284 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket-form.module.css @@ -0,0 +1,65 @@ +.githubIcon { + margin-left: 12px; + margin-right: var(--space-4x); + display: inline-flex; +} + +.checkIcon { + position: absolute; + right: var(--space-4x); + display: inline-flex; +} + +.generateWithGithub { + display: flex; + align-items: center; + font-weight: 500; +} + +.stageIcon { + position: absolute; + left: var(--space-4x); + display: inline-flex; +} + +.form-row { + margin-left: auto; + margin-right: auto; + max-width: 600px; +} + +.description { + color: var(--secondary-color); + margin: 0; + text-align: center; + margin-left: var(--space-4x); +} + +@media (min-width: 768px) { + .form-row { + margin-bottom: 20px; + } +} + +@media (min-width: 1200px) { + .form-row { + margin: 0; + } + + .description { + text-align: left; + } + + .generateWithGithub { + width: 100%; + } +} + +.learn-more { + color: #fff; +} + +.learn-more:hover { + text-decoration: underline; + color: #fff; +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket-profile.module.css b/apps/www/components/LaunchWeek/8/Ticket/ticket-profile.module.css new file mode 100644 index 00000000000..ac08447a22a --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket-profile.module.css @@ -0,0 +1,149 @@ +.image { + width: 50px; + height: 50px; + border-radius: 50%; + border: 1px solid #999; +} + +@media (min-width: 768px) { + .image { + width: calc(100px * var(--size)); + height: calc(100px * var(--size)); + } +} + +.name { + font-size: 24px; + display: inline-block; + line-height: 1.15; + letter-spacing: -0.02em; + margin: 0; +} + +.name-golden { + color: var(--gold-primary); +} + +@media (min-width: 768px) { + .name { + font-size: calc(var(--space-8x) * var(--size)); + margin-bottom: 5px; + } +} + +.username { + display: inline-block; + font-size: calc(1em * var(--size)); + color: var(--lw-secondary-color); + display: flex; + align-items: center; + margin: 0; +} + +.username-golden { + color: var(--gold-secondary); +} + +.githubIcon { + margin-right: calc(5px * var(--size)); + display: inline-flex; +} + +.githubIcon-golden svg { + color: var(--gold-secondary); + opacity: 0.5; +} + +.empty-icon { + display: block; + background: linear-gradient(320deg, #121212, #191919); + width: 60px; +} + +.empty-icon--golden { + display: block; + background: linear-gradient(90deg, #fff9eb, #e2ba52); + width: 60px; + border: 1px solid #ecc154ad; +} + +@media (min-width: 768px) { + .empty-icon { + width: calc(80px * var(--size)); + } + + .empty-icon--golden { + width: calc(80px * var(--size)); + } +} + +@keyframes loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} +/* +.skeleton { + display: flex; + align-items: center; + border-radius: 25px; +} */ + +.skeleton.loaded { + width: unset !important; +} + +.skeleton:not(.wrapper):not(.show) { + display: none; +} + +.wrapper:not(.show)::before { + content: none; +} + +.skeleton:not(.wrapper):not(.loaded) { + border-radius: var(--space); + background-image: linear-gradient(270deg, #252729, #34383d, #34383d, #252729); + background-size: 200% 100%; + animation: loading 2s ease-in-out infinite; +} + +.wrapper { + position: relative; +} + +.wrapper::before { + content: ''; + position: absolute; + top: -1px; + left: -1px; + right: 0; + bottom: 0; + border-radius: var(--space); + z-index: 100; + background-image: linear-gradient(270deg, #111111, #333333, #333333, #111111); + background-size: 200% 100%; + animation: loading 2s ease-in-out infinite; +} + +.inline { + display: inline-block !important; +} + +.rounded, +.rounded.wrapper::before { + width: calc(80px * var(--size)); + height: calc(80px * var(--size)); + border-radius: 50% !important; +} + +@media (min-width: 768px) { + .rounded, + .rounded.wrapper::before { + width: calc(110px * var(--size)); + height: calc(110px * var(--size)); + } +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket-visual.module.css b/apps/www/components/LaunchWeek/8/Ticket/ticket-visual.module.css new file mode 100644 index 00000000000..f399e967aa1 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket-visual.module.css @@ -0,0 +1,70 @@ +.visual { + /* transform: translateZ(0); */ + background: linear-gradient( + 30deg, + hsla(274, 100%, 77%, 0.05) 0%, + hsla(200, 43%, 1%, 0.2) 30%, + hsla(200, 43%, 1%, 0.2) 70%, + hsla(151, 50%, 53%, 0.1) 100% + ), + hsla(200, 43%, 1%, 0.8); + /* background: hsla(200, 43%, 1%, 0.2); */ + box-shadow: 0px 1px 27px rgba(158, 68, 239, 0.06), + /* 0 0 0 1px rgba(121, 89, 134, 0.25), */ 0 10px 20px 0px rgba(20, 17, 21, 0.8), + inset 0 0.2px 1px 0.2px rgba(214, 210, 210, 0.4), + inset -0.4px -0.4px 1px -0.3px rgba(74, 252, 175, 0.4); + /* border: 1px solid rgba(0, 0, 0, 0.05); */ + /* border-radius: 16px; */ + + -webkit-backdrop-filter: blur(2px); + backdrop-filter: blur(2px); + + /* border: 1px solid #1c1c1c; */ + /* animation: 2s ease-in-out infinite alternate; */ + + background-repeat: no-repeat; + background-size: contain; +} + +.visual--gold { + background: radial-gradient(#fcdf9d50, #dbb33340); +} + +.visual--gold::before { + content: ' '; + position: absolute; + inset: 0; + filter: drop-shadow(0 0 60px #fcdf9d); + background: radial-gradient(#fcdf9d50, #dbb33320); + /* border: 1px solid #dbb33320; */ +} + +@keyframes opacity-pulse { + from { + opacity: 0.5; + } + + to { + opacity: 1; + } +} + +@keyframes svg-shadow { + from { + filter: drop-shadow(0 0 2px #fff) drop-shadow(0 0 4px #d99f0c) drop-shadow(0 0 6px #d99f0c); + } + + to { + filter: drop-shadow(0 0 10px #fff) drop-shadow(0 0 6px #d99f0c) drop-shadow(0 0 9px #d99f0c); + } +} + +@keyframes blank-svg-shadow { + from { + filter: drop-shadow(0 0 2px #bada55) drop-shadow(0 0 2px #bada55) drop-shadow(0 0 2px #bada55); + } + + to { + filter: drop-shadow(0 0 3px #bada55) drop-shadow(0 0 2px #bada55) drop-shadow(0 0 2px #bada55); + } +} diff --git a/apps/www/components/LaunchWeek/8/Ticket/ticket.module.css b/apps/www/components/LaunchWeek/8/Ticket/ticket.module.css new file mode 100644 index 00000000000..51d423c3b22 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/Ticket/ticket.module.css @@ -0,0 +1,150 @@ +.ticket-hero { + background: linear-gradient(137.46deg, rgba(125, 196, 176, 0.04) 0%, rgba(255, 255, 255, 0) 100%), + linear-gradient(137.46deg, rgba(255, 255, 255, 0) 6.19%, rgba(255, 255, 255, 0) 103.29%); + z-index: 10; + box-shadow: 0 1px 1px rgba(131, 6, 184, 0.1), 0 1px 1px rgba(128, 6, 184, 0.1), + 0 20px 30px -30px rgba(76, 195, 138, 0.05), 0 80px 40px -40px rgba(128, 6, 184, 0.04), + inset 0.25px 0.3px 0px 0.3px rgba(209, 243, 227, 0.1), + inset 0.25px 0.3px 0px 0.3px rgba(76, 195, 138, 0.02), + inset 0 -3px 0 -2px rgba(255, 255, 255, 0.08), 0px 4px 20px rgba(118, 7, 170, 0.1); +} + +.hero { + line-height: 1.15; + letter-spacing: -0.05em; + font-weight: 400; + margin: 0; + text-align: center; +} + +@media (min-width: 768px) { + .hero { + line-height: 1; + } +} + +.description { + font-size: 20px; + line-height: 1.4; + color: var(--lw-secondary-color); + text-align: center; + margin: 30px auto; + max-width: 350px; +} + +@media (min-width: 768px) { + .description { + font-size: 24px; + max-width: 415px; + } +} + +.ticket-text { + margin-bottom: 30px; +} + +.ticket-layout { + display: grid; + grid-template-columns: 1fr; + gap: 45px; + margin: auto; + padding: var(--space-4x); + padding-top: 64px; + padding-bottom: 64px; +} + +@media (min-width: 1200px) { + .ticket-layout { + padding-top: 0; + padding-bottom: 0; + } +} + +.ticket-share-layout { + align-items: center; + margin-bottom: auto; +} + +.ticket-visual:before { + left: 50%; + top: -7.5%; + transform: translateZ(0) translateX(-50%) rotate(90deg); +} + +.ticket-visual:after { + left: 50%; + bottom: -7.5%; + transform: translateZ(0) translateX(-50%) rotate(90deg); +} + +@media (min-width: 768px) { + .ticket-visual:before { + top: 50%; + left: -7%; + transform: translateZ(0) translateY(-50%); + } + + .ticket-visual:after { + top: 50%; + left: 96%; + transform: translateZ(0) translateY(-50%); + } +} + +.ticket-actions { + margin-top: 70px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.ticket-copy { + margin-top: 30px; +} + +.ticket-actions-placeholder { + height: 280px; +} + +@media (min-width: 768px) { + .ticket-actions { + flex-direction: row; + } + .ticket-actions-placeholder { + height: 157px; + } +} + +@media (min-width: 1200px) { + .ticket-layout { + grid-template-columns: 1fr 1fr; + } + .hero { + text-align: left; + } + .description { + text-align: left; + margin: 30px 0; + } +} + +.gold-text { + color: #f4cf72; + text-shadow: 0 0 1px rgba(0, 0, 0, 0.5), 0 0 4px rgba(0, 0, 0, 0.1); +} + +@media (min-width: 768px) { + .gold-text { + color: #e4b641; + } +} + +.ticket-number { + text-shadow: 0 0px 2px rgba(22, 22, 22, 0.9), 0 0px 6px rgba(131, 6, 184, 0.9), + 0 0px 12px rgba(217, 195, 226, 0.5); +} +.ticket-number-gold { + text-shadow: 0 0px 2px rgba(22, 22, 22, 0.9), 0 0px 6px rgba(255, 195, 125, 0.9), + 0 0px 12px rgba(226, 224, 195, 0.5); +} diff --git a/apps/www/components/LaunchWeek/8/TicketBrickWall.tsx b/apps/www/components/LaunchWeek/8/TicketBrickWall.tsx new file mode 100644 index 00000000000..11f32de267c --- /dev/null +++ b/apps/www/components/LaunchWeek/8/TicketBrickWall.tsx @@ -0,0 +1,53 @@ +import Link from 'next/link' +import React, { useEffect, useRef, useState } from 'react' +import { Button } from 'ui' +import { useBreakpoint } from 'common/hooks/useBreakpoint' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import { TicketBrickWallSlider } from './TicketBrickWallSlider' + +type user = UserData + +interface Props { + users: user[] +} + +export default function TicketBrickWall({ users }: Props) { + const isMobile = useBreakpoint(768) + + return ( +
+
+
+

Tickets from the community

+
+
+ + + {isMobile && } +
+ +
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx b/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx new file mode 100644 index 00000000000..c1484e5f961 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/TicketBrickWallSlider.tsx @@ -0,0 +1,78 @@ +import Image from 'next/image' +import Link from 'next/link' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' + +import { Swiper, SwiperSlide } from 'swiper/react' + +import 'swiper/swiper.min.css' + +interface Props { + users: UserData[] + reverse?: boolean + speed?: number + animate?: boolean +} + +export function TicketBrickWallSlider({ users, reverse, speed = 50000, animate }: Props) { + const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw8' + const BUCKET_FOLDER_VERSION = 'v1' + const getOgUrl = (username: string, isGold: boolean) => + `${STORAGE_URL}/tickets/${ + isGold ? 'golden' : 'regular' + }/${BUCKET_FOLDER_VERSION}/${username}.png` + + return ( +
+
+ + {users.map((user, i) => ( + + + +
+
+ {user.username} +
+
+
+ +
+ ))} +
+
+
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/TicketsGrid.tsx b/apps/www/components/LaunchWeek/8/TicketsGrid.tsx new file mode 100644 index 00000000000..ba88e77d1ee --- /dev/null +++ b/apps/www/components/LaunchWeek/8/TicketsGrid.tsx @@ -0,0 +1,98 @@ +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/lw8' + const BUCKET_FOLDER_VERSION = 'v1' + const getTicketImageUrl = (username: string, isGold: boolean) => + `${STORAGE_URL}/tickets/${ + isGold ? 'golden' : 'regular' + }/${BUCKET_FOLDER_VERSION}/${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) + + return ( + window.scrollTo(0, 0)} + > + +
+ {user.username} +
+
+ + ) + })} + {isLoading && + Array.from({ length: pageCount }, (_, i) => ( + +
+
+
+ + ))} +
+ ) +} diff --git a/apps/www/components/LaunchWeek/8/ticket-brick-wall-slider.module.css b/apps/www/components/LaunchWeek/8/ticket-brick-wall-slider.module.css new file mode 100644 index 00000000000..164e683b3a2 --- /dev/null +++ b/apps/www/components/LaunchWeek/8/ticket-brick-wall-slider.module.css @@ -0,0 +1,3 @@ +.swiper-wrapper .swiper-slide { + width: 300px !important; +} diff --git a/apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts b/apps/www/components/LaunchWeek/hooks/use-conf-data.ts similarity index 86% rename from apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts rename to apps/www/components/LaunchWeek/hooks/use-conf-data.ts index fc8d1df8fbb..e8ba6d233c8 100644 --- a/apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts +++ b/apps/www/components/LaunchWeek/hooks/use-conf-data.ts @@ -5,18 +5,24 @@ export type PageState = 'registration' | 'ticket' export type UserData = { id?: string + email?: string ticketNumber?: number username?: string name?: string golden?: boolean referrals?: number bg_image_id?: number + metadata?: { + role?: string + company?: string + location?: string + } sharedOnTwitter?: string sharedOnLinkedIn?: string } type ConfDataContextType = { - supabase: SupabaseClient + supabase: SupabaseClient | any session: Session | null userData: UserData setUserData: React.Dispatch> diff --git a/apps/www/components/LaunchWeek/Ticket/hooks/use-email-query-param.ts b/apps/www/components/LaunchWeek/hooks/use-email-query-param.ts similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/hooks/use-email-query-param.ts rename to apps/www/components/LaunchWeek/hooks/use-email-query-param.ts diff --git a/apps/www/components/LaunchWeek/Ticket/hooks/useWinningChances.ts b/apps/www/components/LaunchWeek/hooks/useWinningChances.ts similarity index 88% rename from apps/www/components/LaunchWeek/Ticket/hooks/useWinningChances.ts rename to apps/www/components/LaunchWeek/hooks/useWinningChances.ts index b843fd0f5fe..46783c103d3 100644 --- a/apps/www/components/LaunchWeek/Ticket/hooks/useWinningChances.ts +++ b/apps/www/components/LaunchWeek/hooks/useWinningChances.ts @@ -1,5 +1,5 @@ import { UserData } from './use-conf-data' -import useConfData from '~/components/LaunchWeek/Ticket//hooks/use-conf-data' +import useConfData from '~/components/LaunchWeek/hooks/use-conf-data' function calculateWinChances(userData: UserData): number { // Just signing up gives you 1 diff --git a/apps/www/components/LaunchWeek/Ticket/utils.module.css b/apps/www/components/LaunchWeek/utils.module.css similarity index 100% rename from apps/www/components/LaunchWeek/Ticket/utils.module.css rename to apps/www/components/LaunchWeek/utils.module.css diff --git a/apps/www/components/Nav/index.tsx b/apps/www/components/Nav/index.tsx index ec9a2c5d075..1869751d9f2 100644 --- a/apps/www/components/Nav/index.tsx +++ b/apps/www/components/Nav/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -import { Button, Badge, IconStar, IconChevronDown } from 'ui' +import { Button, Badge, IconStar, IconChevronDown, Announcement, LW8CountdownBanner } from 'ui' import FlyOut from '~/components/UI/FlyOut' import Transition from 'lib/Transition' @@ -23,14 +23,16 @@ import * as supabaseLogoWordmarkWhite from 'common/assets/images/supabase-logo-w const Nav = () => { const { isDarkMode } = useTheme() - const { pathname } = useRouter() + const router = useRouter() const [open, setOpen] = useState(false) const [openProduct, setOpenProduct] = useState(false) const [openDevelopers, setOpenDevelopers] = useState(false) const isLoggedIn = useIsLoggedIn() - const isLaunchWeekPage = pathname.includes('launch-week') - const showLaunchWeekNavMode = isLaunchWeekPage && !open && !openProduct && !openDevelopers + const isHomePage = router.pathname === '/' + const isLaunchWeekPage = router.pathname.includes('launch-week') + const showLaunchWeekNavMode = + (isLaunchWeekPage || isHomePage) && !open && !openProduct && !openDevelopers React.useEffect(() => { if (open) { @@ -197,8 +199,9 @@ const Nav = () => { return ( <> - {/* - */} + + +
{ className={[ `border-scale-300 border-b backdrop-blur-sm transition-opacity`, showLaunchWeekNavMode && '!opacity-100 !border-[#e0d2f430]', + isLaunchWeekPage && showLaunchWeekNavMode && '!border-b-0', ].join(' ')} > {/*
*/} @@ -225,19 +229,21 @@ const Nav = () => { Supabase Logo + + {isLaunchWeekPage && ( + + + Launch Week + + + )}
import('~/components/TwitterSocialSec type Props = { customerStories: PostTypes[] } const Index = ({ customerStories }: Props) => { + const { isDarkMode, toggleTheme } = useTheme() + + useEffect(() => { + toggleTheme(true) + document.body.className = 'dark' + return () => { + document.body.className = '' + isDarkMode ? toggleTheme(true) : toggleTheme(false) + } + }, []) + return ( diff --git a/apps/www/pages/launch-week/6/index.tsx b/apps/www/pages/launch-week/6/index.tsx index 2c14b90cd98..7f8f8426cc1 100644 --- a/apps/www/pages/launch-week/6/index.tsx +++ b/apps/www/pages/launch-week/6/index.tsx @@ -3,7 +3,7 @@ import { NextSeo } from 'next-seo' import Image from 'next/image' -import _days from '~/components/LaunchWeek/lw6_days.json' +import _days from '~/components/LaunchWeek/6/lw6_days.json' import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' @@ -34,7 +34,7 @@ const constellation = [ ] export default function launchweek() { - const { isDarkMode } = useTheme() + const { isDarkMode, toggleTheme } = useTheme() const title = 'Launch Week 6' const description = 'Supabase Launch Week 6 | 12-18 Dec 2022' const liveDay = null @@ -64,8 +64,13 @@ export default function launchweek() { }, [supabase]) useEffect(() => { - document.body.className = isDarkMode ? 'dark bg-[#121212]' : 'light bg-[#fff]' - }, [isDarkMode]) + toggleTheme(true) + document.body.className = 'bg-[#121212]' + return () => { + document.body.className = '' + isDarkMode ? toggleTheme(true) : toggleTheme(false) + } + }, []) async function getCreators() { try { @@ -94,20 +99,20 @@ export default function launchweek() {
{shipped ? 'Shipped' : 'Coming Soon'} - + {day} ・ {date}
- {title} + {title}
) } @@ -116,18 +121,18 @@ export default function launchweek() { return (
-
+
Blog post -
+
{docs && ( -
+
Docs -
+
@@ -135,9 +140,9 @@ export default function launchweek() { )} {video && (
-
+
Video -
+
@@ -173,13 +178,13 @@ export default function launchweek() { )} >
- + /> */}

Dec 12 – 16 at 6 AM PT | 9 AM ET

@@ -214,7 +219,7 @@ export default function launchweek() {
-
+
- Who we hire at Supabase + Who we hire at Supabase Fireside chat with founders
-
+
Watch video -
+
-
+
Read blogpost -
+
@@ -250,25 +255,26 @@ export default function launchweek() {
-
+
- Wrap Up + Wrap Up Everything we shipped
-
+
Read blogpost -
+
@@ -281,7 +287,7 @@ export default function launchweek() { type="default" openBehaviour="multiple" size="large" - className="text-scale-900 dark:text-white" + className="text-white" justified={false} bordered={false} chevronAlign="right" @@ -293,7 +299,7 @@ export default function launchweek() { day5.d?.toString(), ]} > -
+
-
+
{day1.description} - + Redesigned
@@ -336,7 +342,7 @@ export default function launchweek() { )}
-
+
0 && (
-
+
{day2.steps[0].title}
- + New
- + New - {day2.steps[1].title} + {day2.steps[1].title}

{day2.steps[1].description}

@@ -395,7 +401,7 @@ export default function launchweek() { )}
-
+
0 && (
-
+
{day3.steps[0].title} - + Updated
@@ -452,7 +458,7 @@ export default function launchweek() { )}
-
+
0 && (
-
+
{day4.steps[0].title} - + New
@@ -518,7 +524,7 @@ export default function launchweek() { )}
-
+
- {day5.steps[0].title} - + {day5.steps[0].title} + New
- + New - - {day5.steps[1].title} - + {day5.steps[1].title}
-

Community

+

Community

- Community Day + Community Day
@@ -666,9 +670,9 @@ export default function launchweek() { target="_blank" rel="noopener" > -
+
Blog post -
+
-
+
Flutterflow -
+
-
+
OneSignal -
+
-
+
NextAuth -
+
-

One more thing

+

One more thing

- + Updated - - pg_graphql v1.0 - + pg_graphql v1.0
- + New - - Custom Domains - + Custom Domains
- + New - - Point-in-time recovery - + Point-in-time recovery
- + Experimental - pg_crdt + pg_crdt
- + Upgrade - - Postgres 15 - + Postgres 15
- + Upgrade - - PostgREST 11 - + PostgREST 11
Submissions Closed -

Launch Week Hackathon

+

Launch Week Hackathon

The traditional parallel Hackathon is back! Build a new open source project with Supabase and you can win $1500 in GitHub sponsorships and a coveted Supabase Darkmode @@ -1034,14 +1028,14 @@ export default function launchweek() {

-

Prizes

+

Prizes

There are 5 categories to win, with prizes for winners and runner-ups of each category. Each team member gets a prize.

-

Judges

+

Judges

The Supabase team will judge all the categories except the Best Edge Functions Project, which will be judged by our friends at Deno. @@ -1049,7 +1043,7 @@ export default function launchweek() {

-

Community

+

Community

If you need help or advice when building, find other people to join your team, or if you just want to chill and watch people build, come and join us! @@ -1066,7 +1060,7 @@ export default function launchweek() {

-

Submission

+

Submission

Submit your project through{' '}

@@ -1093,12 +1088,12 @@ export default function launchweek() { rel="noopener noreferrer" className="self-end" > -
+
-

Supabase Cache Helpers

+

Supabase Cache Helpers

Previous Best Overall Project Winner

@@ -1107,12 +1102,12 @@ export default function launchweek() { target="_blank" rel="noopener noreferrer" > -
+
-

Super UI

+

Super UI

Previous Best Overall Project Runner Up

@@ -1122,22 +1117,22 @@ export default function launchweek() { rel="noopener noreferrer" className="self-end" > -
+
-

Quid Faciam Hodie?

+

Quid Faciam Hodie?

Winner Best Flutter Project

-
+
-

That Hot Dog Game

+

That Hot Dog Game

Previous Winner: Most Fun/Interesting

@@ -1147,12 +1142,12 @@ export default function launchweek() { rel="noopener noreferrer" className="self-end" > -
Shipped -

The Supabase Content Storm

+

The Supabase Content Storm

We worked with more than 30 content creators from around the world to drop a mountain of content simultaneously! @@ -1217,7 +1212,7 @@ export default function launchweek() {

{activeCreator !== null && (
-

+

{activeCreator !== null ? `${creators[activeCreator].first_name} ${creators[activeCreator].last_name}` : 'Title'} diff --git a/apps/www/pages/launch-week/7/index.tsx b/apps/www/pages/launch-week/7/index.tsx new file mode 100644 index 00000000000..55c3b4348aa --- /dev/null +++ b/apps/www/pages/launch-week/7/index.tsx @@ -0,0 +1,104 @@ +import { GetServerSideProps } from 'next' +import { NextSeo } from 'next-seo' +import { useEffect } from 'react' +import dynamic from 'next/dynamic' +import { createClient } from '@supabase/supabase-js' + +import { SITE_ORIGIN, SITE_URL } from '~/lib/constants' + +import DefaultLayout from '~/components/Layouts/Default' +import SectionContainer from '~/components/Layouts/SectionContainer' +import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/7/LaunchSection/LaunchWeekLogoHeader' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import LW7BgGraphic from '~/components/LaunchWeek/7/LW7BgGraphic' +import { useTheme } from 'common/Providers' + +const LW7Releases = dynamic(() => import('~/components/LaunchWeek/7/Releases')) +const LaunchWeekPrizeSection = dynamic( + () => import('~/components/LaunchWeek/7/LaunchWeekPrizeSection') +) +const TicketBrickWall = dynamic( + () => import('~/components/LaunchWeek/7/LaunchSection/TicketBrickWall') +) +const CTABanner = dynamic(() => import('~/components/CTABanner')) + +interface Props { + users: UserData[] +} + +const supabaseAdmin = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL ?? 'http://localhost:54321', + process.env.SUPABASE_SERVICE_ROLE_SECRET ?? + process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_SECRET ?? + '' +) + +export default function TicketHome({ users }: Props) { + const { isDarkMode, toggleTheme } = useTheme() + + const TITLE = 'Supabase LaunchWeek 7' + const DESCRIPTION = 'Supabase Launch Week 7 | 10–14 April 2023' + const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/seven/launch-week-7-teaser.jpg` + + useEffect(() => { + toggleTheme(true) + document.body.className = 'bg-[#1C1C1C]' + return () => { + document.body.className = '' + isDarkMode ? toggleTheme(true) : toggleTheme(false) + } + }, []) + + return ( + <> + + +
+
+
+ + + + +
+
+ +
+ + +
+ {users && } +
+ +
+ + ) +} + +export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { + // fetch users for the TicketBrickWall + const { data: users } = await supabaseAdmin! + .from('lw7_tickets_golden') + .select('*') + .order('createdAt', { ascending: false }) + .limit(17) + + return { + props: { + users, + }, + } +} diff --git a/apps/www/pages/launch-week/7/tickets/[username].tsx b/apps/www/pages/launch-week/7/tickets/[username].tsx new file mode 100644 index 00000000000..7f74869f8b9 --- /dev/null +++ b/apps/www/pages/launch-week/7/tickets/[username].tsx @@ -0,0 +1,166 @@ +import { NextSeo } from 'next-seo' +import { GetStaticProps, GetStaticPaths } from 'next' +import Error from 'next/error' +import DefaultLayout from '~/components/Layouts/Default' +import SectionContainer from '~/components/Layouts/SectionContainer' +import TicketContainer from '~/components/LaunchWeek/7/Ticket/TicketContainer' +import { SITE_URL, SAMPLE_TICKET_NUMBER } from '~/lib/constants' +import { createClient } from '@supabase/supabase-js' +import { useEffect, useState } from 'react' +import { IconArrowDown } from 'ui' +import LaunchWeekPrizeSection from '~/components/LaunchWeek/7/LaunchWeekPrizeSection' +import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/7/LaunchSection/LaunchWeekLogoHeader' +import TicketBrickWall from '~/components/LaunchWeek/7/LaunchSection/TicketBrickWall' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import LW7BgGraphic from '~/components/LaunchWeek/7/LW7BgGraphic' +import CTABanner from '~/components/CTABanner' +import { useTheme } from 'common/Providers' + +interface Props { + user: UserData + users: UserData[] + ogImageUrl: string +} + +const supabaseAdmin = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL ?? 'http://localhost:54321', + process.env.SUPABASE_SERVICE_ROLE_SECRET ?? + process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_SECRET ?? + '' +) + +export default function UsernamePage({ user, users, ogImageUrl }: Props) { + const { isDarkMode } = useTheme() + const { username, ticketNumber, name, golden, referrals, bg_image_id } = user + const TITLE = `${name ? name + '’s' : 'Get your'} #SupaLaunchWeek Ticket` + const DESCRIPTION = 'Supabase Launch Week 7 | 10–14 April 2023 | Generate your ticket. Win swag.' + const OG_URL = `${SITE_URL}/tickets/${username}` + + const [supabase] = useState(() => + createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!) + ) + + if (!ticketNumber) { + return + } + + useEffect(() => { + document.body.className = '!dark bg-[#1C1C1C]' + + return () => { + document.body.className = isDarkMode ? 'dark' : 'light' + } + }, []) + + return ( + <> + + +
+ + + + + ) +} + +export const getStaticProps: GetStaticProps = async ({ params }) => { + const username = params?.username?.toString() || null + let name: string | null | undefined + let ticketNumber: number | null | undefined + let golden = false + let referrals = 0 + let bg_image_id + let ogImageUrl + + // fetch users for the TicketBrickWall + const { data: users } = await supabaseAdmin!.from('lw7_tickets_golden').select().limit(17) + + // fetch a specific user + if (username) { + const { data: user } = await supabaseAdmin! + .from('lw7_tickets_golden') + .select('name, ticketNumber, golden, referrals, bg_image_id') + .eq('username', username) + .single() + name = user?.name + ticketNumber = user?.ticketNumber + golden = user?.golden ?? false + bg_image_id = user?.bg_image_id ?? 1 + referrals = user?.referrals ?? 0 + ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent( + username ?? '' + )}${golden ? '&golden=true' : ''}` + } + + return { + props: { + user: { + username: ticketNumber ? username : null, + usernameFromParams: username || null, + name: ticketNumber ? name || username || null : null, + ticketNumber: ticketNumber || SAMPLE_TICKET_NUMBER, + golden, + referrals, + bg_image_id, + }, + ogImageUrl, + users, + key: username, + }, + revalidate: 5, + } +} + +export const getStaticPaths: GetStaticPaths = async () => { + return Promise.resolve({ + paths: [], + fallback: 'blocking', + }) +} diff --git a/apps/www/pages/launch-week/7/tickets/index.tsx b/apps/www/pages/launch-week/7/tickets/index.tsx new file mode 100644 index 00000000000..61b1b0cd3a6 --- /dev/null +++ b/apps/www/pages/launch-week/7/tickets/index.tsx @@ -0,0 +1,181 @@ +import { NextSeo } from 'next-seo' +import { GetServerSideProps } from 'next' +import DefaultLayout from '~/components/Layouts/Default' +import SectionContainer from '~/components/Layouts/SectionContainer' +import { SITE_ORIGIN, SITE_URL } from '~/lib/constants' +import { createClient } from '@supabase/supabase-js' +import { useEffect, useRef, useState } from 'react' +import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/7/LaunchSection/LaunchWeekLogoHeader' +import { motion } from 'framer-motion' +import { UserData } from '~/components/LaunchWeek/hooks/use-conf-data' +import LW7BgGraphic from '~/components/LaunchWeek/7/LW7BgGraphic' +import CTABanner from '~/components/CTABanner' +import { debounce } from 'lodash' +import TicketsGrid from '~/components/LaunchWeek/7/TicketsGrid' +import { Button } from 'ui' +import Link from 'next/link' +import { useTheme } from 'common/Providers' + +interface Props { + users: UserData[] +} + +const supabaseAdmin = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL ?? 'http://localhost:54321', + process.env.SUPABASE_SERVICE_ROLE_SECRET ?? + process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_SECRET ?? + '' +) + +const generateOgs = async (users: UserData[]) => { + users?.map(async (user) => { + const ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent( + user.username ?? '' + )}${!!user.golden ? '&golden=true' : ''}` + return await fetch(ogImageUrl) + }) +} + +export default function TicketsPage({ users }: Props) { + const ref = useRef(null) + const PAGE_COUNT = 20 + const TITLE = '#SupaLaunchWeek Tickets' + const DESCRIPTION = 'Supabase Launch Week 7 | 10–14 April 2023' + const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/seven/launch-week-7-teaser.jpg` + + const { isDarkMode } = useTheme() + const [isLoading, setIsLoading] = useState(false) + const [offset, setOffset] = useState(1) + const [isLast, setIsLast] = useState(false) + const [hasLoaded, setHasLoaded] = useState(false) + const [loadedUsers, setLoadedUsers] = useState(users) + + useEffect(() => { + loadMoreUsers(offset) + }, [hasLoaded]) + + const loadUsers = async (offset: number) => { + const from = offset * PAGE_COUNT + return await supabaseAdmin! + .from('lw7_tickets_golden') + .select('*') + .range(from, from + PAGE_COUNT - 1) + .order('createdAt', { ascending: false }) + } + + const loadMoreUsers = async (offset: number) => { + if (isLast) return + setIsLoading(true) + setOffset((prev) => prev + 1) + const { data: users } = await loadUsers(offset) + setLoadedUsers((prevUsers) => [...prevUsers, ...(users as any[])]) + if ((users as any[]).length < PAGE_COUNT) setIsLast(true) + setIsLoading(false) + } + + const handleScroll = () => { + if (ref.current && typeof window !== 'undefined') { + const rect = (ref.current as HTMLDivElement)?.getBoundingClientRect() + const isInView = rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + setHasLoaded((prev) => !prev && isInView) + } + } + + useEffect(() => { + document.body.className = '!dark bg-[#1C1C1C]' + + const handleDebouncedScroll = debounce(() => !isLast && handleScroll(), 200) + window.addEventListener('scroll', handleDebouncedScroll) + + return () => { + document.body.className = isDarkMode ? 'dark' : 'light' + window.removeEventListener('scroll', handleDebouncedScroll) + } + }, []) + + return ( + <> + + +
+
+
+ + + + +
+
+
+ +
+ +

+ Check out all the tickets +

+

+ Join us on April 16th for Launch Week 7's final day{' '} +
and find out if you are one of the lucky + winners. Get sharing! +

+ +
+
+
+ +
+
+
+ + + + ) +} + +export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { + const { data: users } = await supabaseAdmin! + .from('lw7_tickets_golden') + .select('*') + .order('createdAt', { ascending: false }) + .limit(20) + + // Generate og images of not present + generateOgs(users as any[]) + + return { + props: { + users, + }, + } +} diff --git a/apps/www/pages/launch-week/index.tsx b/apps/www/pages/launch-week/index.tsx index 8e12bacdf8e..da8706c61d2 100644 --- a/apps/www/pages/launch-week/index.tsx +++ b/apps/www/pages/launch-week/index.tsx @@ -1,35 +1,39 @@ import { GetServerSideProps } from 'next' import { NextSeo } from 'next-seo' import { useState, useEffect } from 'react' +import Image from 'next/image' import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import { createClient, Session, SupabaseClient } from '@supabase/supabase-js' - import { SITE_ORIGIN, SITE_URL } from '~/lib/constants' +import { PageState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' -import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/LaunchSection/LaunchWeekLogoHeader' -import { UserData } from '~/components/LaunchWeek/Ticket/hooks/use-conf-data' -import LW7BgGraphic from '~/components/LaunchWeek/LW7BgGraphic' +import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/8/LaunchWeekLogoHeader' + import { useTheme } from 'common/Providers' -const TicketContainer = dynamic(() => import('~/components/LaunchWeek/Ticket/TicketContainer')) -const LW7Releases = dynamic(() => import('~/components/LaunchWeek/Releases/LW7/LW7Releases')) +import 'swiper/swiper.min.css' +import Head from 'next/head' + +const AnimatedParticles = dynamic( + () => import('~/components/LaunchWeek/8/AnimatedParticles/ParticlesCanvas') +) +const TicketContainer = dynamic(() => import('~/components/LaunchWeek/8/Ticket/TicketContainer')) const LaunchWeekPrizeSection = dynamic( - () => import('~/components/LaunchWeek/LaunchSection/LaunchWeekPrizeSection') -) -const TicketBrickWall = dynamic( - () => import('~/components/LaunchWeek/LaunchSection/TicketBrickWall') + () => import('~/components/LaunchWeek/8/LaunchWeekPrizeSection') ) +const TicketBrickWall = dynamic(() => import('~/components/LaunchWeek/8/TicketBrickWall')) const CTABanner = dynamic(() => import('~/components/CTABanner')) interface Props { - users: UserData[] + users?: UserData[] } const supabaseAdmin = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL ?? 'http://localhost:54321', + // ANON KEY process.env.SUPABASE_SERVICE_ROLE_SECRET ?? process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_SECRET ?? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2Njk3MjcwMTIsImV4cCI6MTk4NTMwMzAxMn0.SZLqryz_-stF8dgzeVXmzZWPOqdOrBwqJROlFES8v3I' @@ -37,15 +41,16 @@ const supabaseAdmin = createClient( export default function TicketHome({ users }: Props) { const { query } = useRouter() - const [supabase, setSupabase] = useState(null) - const [session, setSession] = useState(null) - const { isDarkMode } = useTheme() - const TITLE = 'Supabase LaunchWeek 7' - const DESCRIPTION = 'Supabase Launch Week 7 | 10–14 April 2023' - const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/seven/launch-week-7-teaser.jpg` + const TITLE = 'Supabase LaunchWeek 8' + const DESCRIPTION = 'Supabase Launch Week 8 | 7–11 August 2023' + const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/8/lw8-og.jpg` + const ticketNumber = query.ticketNumber?.toString() const bgImageId = query.bgImageId?.toString() + const [supabase, setSupabase] = useState(null) + const [session, setSession] = useState(null) + const { isDarkMode, toggleTheme } = useTheme() const defaultUserData = { id: query.id?.toString(), @@ -56,6 +61,9 @@ export default function TicketHome({ users }: Props) { bgImageId: bgImageId ? parseInt(bgImageId, 10) : undefined, } + const [userData, setUserData] = useState(defaultUserData) + const [pageState, setPageState] = useState('ticket') + useEffect(() => { if (!supabase) { setSupabase( @@ -81,24 +89,14 @@ export default function TicketHome({ users }: Props) { }, [supabase]) useEffect(() => { - document.body.className = '!dark bg-[#1C1C1C]' + toggleTheme(true) + document.body.className = 'dark bg-[#020405]' return () => { - document.body.className = isDarkMode ? 'dark' : 'light' + document.body.className = '' + isDarkMode ? toggleTheme(true) : toggleTheme(false) } }, []) - const Ticket = () => - supabase && ( -
- -
- ) - return ( <> - -
-
-
- - - - + + + + + +
+
+
+ +
+ +
+
+ {supabase && } + starts background +
+
+
+ +
+
-
-
- - +
+ {supabase && ( +
+ +
+ )} +
+ + + + {users && }
- {users && } -
- - + + + ) } @@ -141,10 +187,8 @@ export default function TicketHome({ users }: Props) { export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { // fetch users for the TicketBrickWall const { data: users } = await supabaseAdmin! - .from('lw7_tickets_golden') - .select('*') - .order('createdAt', { ascending: false }) - .limit(17) + .from('lw8_tickets_golden') + .select('username, golden', { count: 'exact' }) return { props: { diff --git a/apps/www/pages/launch-week/launchWeek.css b/apps/www/pages/launch-week/launchWeek.css index bfd8b1b9187..d07b25683f7 100644 --- a/apps/www/pages/launch-week/launchWeek.css +++ b/apps/www/pages/launch-week/launchWeek.css @@ -1,5 +1,4 @@ .bg-lw7 { - /* background: radial-gradient(72.03% 66.03% at 50% 69.72%, #3fcf8e 0%, #9e44ef 100%); */ background: #9e44ef; } @@ -8,23 +7,13 @@ } .bg-lw7-gradient { - /* background: radial-gradient(72.03% 66.03% at 50% 69.72%, #3fcf8e 0%, transparent 100%); */ background: radial-gradient(80% 65% at 50% 95%, #dbb8bf 0%, transparent 100%); } -/* .bg-lw7-gradient.gold { - background: radial-gradient(80% 65% at 50% 95%, #ffd132 0%, transparent 100%); -} */ - @media (min-width: 768px) { .bg-lw7-gradient { - /* background: radial-gradient(72.03% 66.03% at 50% 69.72%, #3fcf8e 0%, transparent 100%); */ background: radial-gradient(72.03% 66.03% at 50% 69.72%, #dbb8bf 0%, transparent 100%); } - - /* .bg-lw7-gradient.gold { - background: radial-gradient(72.03% 70% at 50% 75%, #ffd132 0%, transparent 100%); - } */ } .opacity-pulse { diff --git a/apps/www/pages/launch-week/tickets/[username].tsx b/apps/www/pages/launch-week/tickets/[username].tsx index 07061ff48fc..c246ce203c9 100644 --- a/apps/www/pages/launch-week/tickets/[username].tsx +++ b/apps/www/pages/launch-week/tickets/[username].tsx @@ -1,20 +1,23 @@ +import { useEffect, useState } from 'react' import { NextSeo } from 'next-seo' import { GetStaticProps, GetStaticPaths } from 'next' +import Image from 'next/image' import Error from 'next/error' +import dynamic from 'next/dynamic' +import { Session, SupabaseClient, createClient } from '@supabase/supabase-js' +import { useTheme } from 'common/Providers' + import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' -import TicketContainer from '~/components/LaunchWeek/Ticket/TicketContainer' -import { SITE_URL, SAMPLE_TICKET_NUMBER } from '~/lib/constants' -import { createClient } from '@supabase/supabase-js' -import { useEffect, useState } from 'react' -import { IconArrowDown } from 'ui' -import LaunchWeekPrizeSection from '~/components/LaunchWeek/LaunchSection/LaunchWeekPrizeSection' -import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/LaunchSection/LaunchWeekLogoHeader' -import TicketBrickWall from '~/components/LaunchWeek/LaunchSection/TicketBrickWall' -import { UserData } from '~/components/LaunchWeek/Ticket/hooks/use-conf-data' -import LW7BgGraphic from '../../../components/LaunchWeek/LW7BgGraphic' -import CTABanner from '../../../components/CTABanner' -import { useTheme } from 'common/Providers' +import TicketContainer from '~/components/LaunchWeek/8/Ticket/TicketContainer' +import { SITE_URL } from '~/lib/constants' +import { PageState, ConfDataContext, UserData } from '~/components/LaunchWeek/hooks/use-conf-data' + +const LaunchWeekPrizeSection = dynamic( + () => import('~/components/LaunchWeek/8/LaunchWeekPrizeSection') +) +const TicketBrickWall = dynamic(() => import('~/components/LaunchWeek/8/TicketBrickWall')) +const CTABanner = dynamic(() => import('~/components/CTABanner')) interface Props { user: UserData @@ -24,31 +27,47 @@ interface Props { const supabaseAdmin = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL ?? 'http://localhost:54321', + // ANON KEY process.env.SUPABASE_SERVICE_ROLE_SECRET ?? process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_SECRET ?? 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2Njk3MjcwMTIsImV4cCI6MTk4NTMwMzAxMn0.SZLqryz_-stF8dgzeVXmzZWPOqdOrBwqJROlFES8v3I' ) export default function UsernamePage({ user, users, ogImageUrl }: Props) { - const { isDarkMode } = useTheme() - const { username, ticketNumber, name, golden, referrals, bg_image_id } = user + const { username, ticketNumber, name } = user + const TITLE = `${name ? name + '’s' : 'Get your'} #SupaLaunchWeek Ticket` - const DESCRIPTION = 'Supabase Launch Week 7 | 10–14 April 2023 | Generate your ticket. Win swag.' + const DESCRIPTION = + 'Supabase Launch Week 8 | 7–11 August 2023 | Generate your ticket & win awesome swag.' const OG_URL = `${SITE_URL}/tickets/${username}` - const [supabase] = useState(() => - createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!) - ) + const [supabase, setSupabase] = useState(null) + const [session, setSession] = useState(null) + const { isDarkMode, toggleTheme } = useTheme() + + const [pageState, setPageState] = useState('ticket') if (!ticketNumber) { return } useEffect(() => { - document.body.className = '!dark bg-[#1C1C1C]' + if (!supabase) { + setSupabase( + createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + ) + ) + } + }, []) + useEffect(() => { + toggleTheme(true) + document.body.className = 'dark bg-[#020405]' return () => { - document.body.className = isDarkMode ? 'dark' : 'light' + document.body.className = '' + isDarkMode ? toggleTheme(true) : toggleTheme(false) } }, []) @@ -69,86 +88,90 @@ export default function UsernamePage({ user, users, ogImageUrl }: Props) { ], }} /> - -
-
-
- - - -
- - See the prizes{' '} - - - - + null, + setPageState, + }} + > + +
+
+
+ +
+ {supabase && ( + + )} +
+
+
+
- - +
-
+ + + + {users && }
- - -
- -
+ + +
) } export const getStaticProps: GetStaticProps = async ({ params }) => { const username = params?.username?.toString() || null - let name: string | null | undefined - let ticketNumber: number | null | undefined - let golden = false - let referrals = 0 - let bg_image_id - let ogImageUrl + let user // fetch users for the TicketBrickWall - const { data: users } = await supabaseAdmin!.from('lw7_tickets_golden').select().limit(17) + const { data: users } = await supabaseAdmin!.from('lw8_tickets_golden').select().limit(17) + + fetch( + `https://obuldanrptloktxcffvn.functions.supabase.co/lw8-ticket?username=${encodeURIComponent( + username ?? '' + )}` + ).catch((_) => {}) // fetch a specific user if (username) { - const { data: user } = await supabaseAdmin! - .from('lw7_tickets_golden') - .select('name, ticketNumber, golden, referrals, bg_image_id') + const { data } = await supabaseAdmin! + .from('lw8_tickets_golden') + .select('name, username, ticketNumber, metadata, golden') .eq('username', username) .single() - name = user?.name - ticketNumber = user?.ticketNumber - golden = user?.golden ?? false - bg_image_id = user?.bg_image_id ?? 1 - referrals = user?.referrals ?? 0 - ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent( - username ?? '' - )}${golden ? '&golden=true' : ''}` + + user = data } + const BUCKET_FOLDER_VERSION = 'v1' + + const ogImageUrl = `https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw8/og/${ + user?.golden ? 'golden' : 'regular' + }/${BUCKET_FOLDER_VERSION}/${username}.png` + return { props: { user: { - username: ticketNumber ? username : null, - usernameFromParams: username || null, - name: ticketNumber ? name || username || null : null, - ticketNumber: ticketNumber || SAMPLE_TICKET_NUMBER, - golden, - referrals, - bg_image_id, + ...user, + username, }, ogImageUrl, users, diff --git a/apps/www/pages/launch-week/tickets/index.tsx b/apps/www/pages/launch-week/tickets/index.tsx index 84e7d8234d0..bd8b573b75b 100644 --- a/apps/www/pages/launch-week/tickets/index.tsx +++ b/apps/www/pages/launch-week/tickets/index.tsx @@ -1,21 +1,21 @@ -import { NextSeo } from 'next-seo' -import { GetServerSideProps } from 'next' -import DefaultLayout from '~/components/Layouts/Default' -import SectionContainer from '~/components/Layouts/SectionContainer' -import { SITE_ORIGIN, SITE_URL } from '~/lib/constants' -import { createClient } from '@supabase/supabase-js' import { useEffect, useRef, useState } from 'react' -import { LaunchWeekLogoHeader } from '~/components/LaunchWeek/LaunchSection/LaunchWeekLogoHeader' -import { motion } from 'framer-motion' -import { UserData } from '~/components/LaunchWeek/Ticket/hooks/use-conf-data' -import LW7BgGraphic from '../../../components/LaunchWeek/LW7BgGraphic' -import CTABanner from '../../../components/CTABanner' -import { debounce } from 'lodash' -import TicketsGrid from '../../../components/LaunchWeek/Ticket/TicketsGrid' +import { GetServerSideProps } from 'next' +import { NextSeo } from 'next-seo' +import Image from 'next/image' import { Button } from 'ui' import Link from 'next/link' +import { motion } from 'framer-motion' +import { createClient } from '@supabase/supabase-js' + +import { SITE_ORIGIN, SITE_URL } from '~/lib/constants' import { useTheme } from 'common/Providers' +import DefaultLayout from '~/components/Layouts/Default' +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/8/TicketsGrid' + interface Props { users: UserData[] } @@ -29,7 +29,7 @@ const supabaseAdmin = createClient( const generateOgs = async (users: UserData[]) => { users?.map(async (user) => { - const ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent( + const ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw8-ticket-og?username=${encodeURIComponent( user.username ?? '' )}${!!user.golden ? '&golden=true' : ''}` return await fetch(ogImageUrl) @@ -40,10 +40,10 @@ export default function TicketsPage({ users }: Props) { const ref = useRef(null) const PAGE_COUNT = 20 const TITLE = '#SupaLaunchWeek Tickets' - const DESCRIPTION = 'Supabase Launch Week 7 | 10–14 April 2023' - const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/seven/launch-week-7-teaser.jpg` + const DESCRIPTION = 'Supabase Launch Week 8 | 7–11 August 2023' + const OG_IMAGE = `${SITE_ORIGIN}/images/launchweek/8/lw8-og.jpg` - const { isDarkMode } = useTheme() + const { isDarkMode, toggleTheme } = useTheme() const [isLoading, setIsLoading] = useState(false) const [offset, setOffset] = useState(1) const [isLast, setIsLast] = useState(false) @@ -57,7 +57,7 @@ export default function TicketsPage({ users }: Props) { const loadUsers = async (offset: number) => { const from = offset * PAGE_COUNT return await supabaseAdmin! - .from('lw7_tickets_golden') + .from('lw8_tickets_golden') .select('*') .range(from, from + PAGE_COUNT - 1) .order('createdAt', { ascending: false }) @@ -82,14 +82,11 @@ export default function TicketsPage({ users }: Props) { } useEffect(() => { - document.body.className = '!dark bg-[#1C1C1C]' - - const handleDebouncedScroll = debounce(() => !isLast && handleScroll(), 200) - window.addEventListener('scroll', handleDebouncedScroll) - + toggleTheme(true) + document.body.className = 'dark bg-[#020405]' return () => { - document.body.className = isDarkMode ? 'dark' : 'light' - window.removeEventListener('scroll', handleDebouncedScroll) + document.body.className = '' + isDarkMode ? toggleTheme(true) : toggleTheme(false) } }, []) @@ -109,17 +106,8 @@ export default function TicketsPage({ users }: Props) { }} /> -
-
-
- - - - -
-
-
- +
+
-

- Check out all the tickets -

-

- Join us on April 16th for Launch Week 7's final day{' '} +

Launch Week 8 tickets

+

+ Join us on August 11th for Launch Week 8's final day{' '}
and find out if you are one of the lucky - winners. Get sharing! + winners.

@@ -156,8 +142,18 @@ export default function TicketsPage({ users }: Props) { />
+
+ +
- + ) @@ -165,7 +161,7 @@ export default function TicketsPage({ users }: Props) { export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const { data: users } = await supabaseAdmin! - .from('lw7_tickets_golden') + .from('lw8_tickets_golden') .select('*') .order('createdAt', { ascending: false }) .limit(20) diff --git a/apps/www/public/images/launchweek/8/LW8-gradient.png b/apps/www/public/images/launchweek/8/LW8-gradient.png new file mode 100644 index 00000000000..5ac9a61d922 Binary files /dev/null and b/apps/www/public/images/launchweek/8/LW8-gradient.png differ diff --git a/apps/www/public/images/launchweek/8/lw8-logo-gold.png b/apps/www/public/images/launchweek/8/lw8-logo-gold.png new file mode 100644 index 00000000000..51f85e84265 Binary files /dev/null and b/apps/www/public/images/launchweek/8/lw8-logo-gold.png differ diff --git a/apps/www/public/images/launchweek/8/lw8-logo.png b/apps/www/public/images/launchweek/8/lw8-logo.png new file mode 100644 index 00000000000..8a6722e759f Binary files /dev/null and b/apps/www/public/images/launchweek/8/lw8-logo.png differ diff --git a/apps/www/public/images/launchweek/8/lw8-og.jpg b/apps/www/public/images/launchweek/8/lw8-og.jpg new file mode 100644 index 00000000000..49cbefc13be Binary files /dev/null and b/apps/www/public/images/launchweek/8/lw8-og.jpg differ diff --git a/apps/www/public/images/launchweek/8/prize-icon.svg b/apps/www/public/images/launchweek/8/prize-icon.svg new file mode 100644 index 00000000000..238b8c2c317 --- /dev/null +++ b/apps/www/public/images/launchweek/8/prize-icon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/www/public/images/launchweek/8/stars.svg b/apps/www/public/images/launchweek/8/stars.svg new file mode 100644 index 00000000000..0f077672b56 --- /dev/null +++ b/apps/www/public/images/launchweek/8/stars.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/www/public/images/launchweek/8/swag-bg.png b/apps/www/public/images/launchweek/8/swag-bg.png new file mode 100644 index 00000000000..667a6b4479c Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag-bg.png differ diff --git a/apps/www/public/images/launchweek/8/swag-illustration.png b/apps/www/public/images/launchweek/8/swag-illustration.png new file mode 100644 index 00000000000..ad00d5c2ada Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag-illustration.png differ diff --git a/apps/www/public/images/launchweek/8/swag/cap.jpg b/apps/www/public/images/launchweek/8/swag/cap.jpg new file mode 100644 index 00000000000..81016d0a64b Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag/cap.jpg differ diff --git a/apps/www/public/images/launchweek/8/swag/deskmat-lg.jpg b/apps/www/public/images/launchweek/8/swag/deskmat-lg.jpg new file mode 100644 index 00000000000..d6e7819d404 Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag/deskmat-lg.jpg differ diff --git a/apps/www/public/images/launchweek/8/swag/deskmat.jpg b/apps/www/public/images/launchweek/8/swag/deskmat.jpg new file mode 100644 index 00000000000..e0666bb9c57 Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag/deskmat.jpg differ diff --git a/apps/www/public/images/launchweek/8/swag/shirt.jpg b/apps/www/public/images/launchweek/8/swag/shirt.jpg new file mode 100644 index 00000000000..e6c1cbba65a Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag/shirt.jpg differ diff --git a/apps/www/public/images/launchweek/8/swag/stickers.jpg b/apps/www/public/images/launchweek/8/swag/stickers.jpg new file mode 100644 index 00000000000..74882b4c2e2 Binary files /dev/null and b/apps/www/public/images/launchweek/8/swag/stickers.jpg differ diff --git a/apps/www/public/images/launchweek/8/ticket-bg/golden.png b/apps/www/public/images/launchweek/8/ticket-bg/golden.png new file mode 100644 index 00000000000..cfd151ca8b8 Binary files /dev/null and b/apps/www/public/images/launchweek/8/ticket-bg/golden.png differ diff --git a/apps/www/public/images/launchweek/8/ticket-bg/regular.png b/apps/www/public/images/launchweek/8/ticket-bg/regular.png new file mode 100644 index 00000000000..45bc0cac006 Binary files /dev/null and b/apps/www/public/images/launchweek/8/ticket-bg/regular.png differ diff --git a/apps/www/styles/index.css b/apps/www/styles/index.css index 2b755d95c61..e0843c3b687 100644 --- a/apps/www/styles/index.css +++ b/apps/www/styles/index.css @@ -177,9 +177,6 @@ h6:not(.overwrite) { .section-container { position: relative; - /* z-index: 1; */ - /* display: flex; - justify-content: center; */ } /* Database page */ @@ -188,10 +185,6 @@ h6:not(.overwrite) { @apply border border-gray-50 dark:border-gray-700; } -/* a { - @apply hover:text-black dark:hover:text-white; -} */ - .sbui-tabs--alt div[role='tablist'] .sbui-btn-primary { @apply dark:bg-scale-300 bg-gray-800 text-white dark:text-white; text-shadow: none; @@ -213,20 +206,6 @@ h6:not(.overwrite) { /* Auth page */ -/* .auth-container > div:nth-child(2) { - width: 420px; -} */ - -/* .auth-container { - @apply rounded; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23edebf0' fill-opacity='0.4'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); -} - -.dark .auth-container { - background-color: #181818; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23313131' fill-opacity='0.4'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); -} */ - .dark .header--light { display: none; } @@ -303,6 +282,47 @@ table { -webkit-box-orient: vertical; } +/** +animations +*/ + +.fade-in-up-2ms { + opacity: 0; + transform: translate3d(0, 10px, 0); + animation: fadeInUp 0.2s cubic-bezier(0.25, 0.25, 0, 1) both; +} + +.fade-in-up-4ms { + opacity: 0; + transform: translate3d(0, 10px, 0); + animation: fadeInUp 0.4s cubic-bezier(0.25, 0.25, 0, 1) both; +} + +.fade-in-2ms { + opacity: 0; + transform: translate3d(0, 10px, 0); + animation: fadeIn 0.2s cubic-bezier(0.25, 0.25, 0, 1) both; +} + +.fade-in-4ms { + opacity: 0; + transform: translate3d(0, 10px, 0); + animation: fadeIn 0.4s cubic-bezier(0.25, 0.25, 0, 1) both; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes fadeIn { + to { + opacity: 1; + } +} + /** launch week */ @@ -491,13 +511,6 @@ launch week left: 0; right: 0; overflow: hidden; - /* background: radial-gradient( - circle at 50% -150%, - #39617d 0%, - #00ded1 30%, - #121212 80%, - #121212 100% - ); */ } @media screen and (max-width: 769px) { .gradient-container { @@ -524,32 +537,10 @@ launch week ); } -.light .gradient-mask { - background: radial-gradient( - circle at 50% -35%, - rgba(57, 97, 125, 0.32) 0%, - rgba(57, 97, 125, 0.32) 5%, - rgb(113 191 187 / 75%) 8%, - #ffffff 65%, - #ffffff 100% - ); -} - @media screen and (max-width: 769px) { .gradient-mask { width: 100%; } - - .light .gradient-mask { - background: radial-gradient( - circle at 50% -35%, - rgba(57, 97, 125, 0.32) 0%, - rgba(57, 97, 125, 0.32) 5%, - rgb(155 246 241 / 75%) 8%, - #90cebd 65%, - #67eae8 100% - ); - } } /* The bottom part below the stroke */ @@ -564,18 +555,10 @@ launch week background: radial-gradient(circle at 50% -60%, rgba(255, 255, 255, 0) 0%, #121212 65%); } -.light .gradient-mask--masked { - background: radial-gradient(circle at 50% -60%, rgba(255, 255, 255, 0) 0%, #fff 65%); -} - @media screen and (max-width: 769px) { .gradient-mask--masked { background: radial-gradient(circle at 50% -40%, rgba(255, 255, 255, 0) 0%, #121212 65%); } - - .light .gradient-mask--masked { - background: radial-gradient(circle at 50% -40%, rgba(255, 255, 255, 0) 0%, #fff 65%); - } } /* The stroke on the circle */ @@ -590,10 +573,6 @@ launch week clip-path: ellipse(52% 213px at 50% -75px); } -.light .flair-mask-a { - background-color: #d7d7d7; -} - /* The inside of the circle */ .flair-mask-b { position: absolute; @@ -607,10 +586,6 @@ launch week clip-path: ellipse(52% 213px at 50% -75px); } -.light .flair-mask-b { - background-color: #fff; -} - @media only screen and (min-width: 600px) { .flair-mask-a { top: 1px; @@ -645,15 +620,6 @@ launch week } } -/* @media only screen and (min-width: 1024px) { - .flair-mask-a { - width: 69.5%; - } - .flair-mask-b { - width: 70%; - } -} */ - .orbit { width: 192px; height: 192px; @@ -698,23 +664,6 @@ launch week } .radial-mask { - /* -webkit-mask-image: conic-gradient( - from 0deg at 45% 47%, - #ffffffff 0%, - #000000ff 25%, - #000000ff 50%, - #000000ff 75%, - #ffffffff 100% - ); - mask-image: conic-gradient( - from 0deg at 45% 47%, - #ffffffff 0%, - #000000ff 25%, - #000000ff 50%, - #000000ff 75%, - #ffffffff 100% - ); */ - -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 1), transparent); } @@ -789,3 +738,14 @@ launch week .swiper-transition-linear .swiper-wrapper { transition-timing-function: linear; } + +/* .lw8-particle-handle { + @apply text-xs bg-black flex items-center justify-center rounded-full py-1 px-2 [&>div]:!relative; +} */ + +/* dat.GUI */ + +.dg.ac { + z-index: 9999 !important; + margin-top: 65px !important; +} diff --git a/apps/www/supabase/functions/lw8-ticket-og/handler.tsx b/apps/www/supabase/functions/lw8-ticket-og/handler.tsx new file mode 100644 index 00000000000..4794d65b5d7 --- /dev/null +++ b/apps/www/supabase/functions/lw8-ticket-og/handler.tsx @@ -0,0 +1,139 @@ +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 new file mode 100644 index 00000000000..26986700825 --- /dev/null +++ b/apps/www/supabase/functions/lw8-ticket-og/index.ts @@ -0,0 +1,11 @@ +// 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 new file mode 100644 index 00000000000..9c5b3d4e49e --- /dev/null +++ b/apps/www/supabase/functions/lw8-ticket/README.md @@ -0,0 +1,21 @@ +# 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.functions.supabase.co/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 new file mode 100644 index 00000000000..cda66443332 --- /dev/null +++ b/apps/www/supabase/functions/lw8-ticket/handler.tsx @@ -0,0 +1,279 @@ +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.functions.supabase.co/lw8-ticket-og', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: + 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2Njk3MjcwMTIsImV4cCI6MTk4NTMwMzAxMn0.SZLqryz_-stF8dgzeVXmzZWPOqdOrBwqJROlFES8v3I', + }, + 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/apps/www/supabase/functions/lw8-ticket/index.ts b/apps/www/supabase/functions/lw8-ticket/index.ts new file mode 100644 index 00000000000..84d78bdf295 --- /dev/null +++ b/apps/www/supabase/functions/lw8-ticket/index.ts @@ -0,0 +1,11 @@ +// 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.168.0/http/server.ts' + +import { handler } from './handler.tsx' + +console.log(`Function "lw8-ticket" up and running`) + +serve(handler) diff --git a/apps/www/tailwind.config.js b/apps/www/tailwind.config.js index 66c96328173..3d8cfeba688 100644 --- a/apps/www/tailwind.config.js +++ b/apps/www/tailwind.config.js @@ -17,10 +17,20 @@ module.exports = config({ '0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' }, '100%': { backgroundColor: 'transparent' }, }, + slideIn: { + '0%, 100%': { transform: 'translate3d(0,0,0)' }, + }, + slideIn: { + '0%, 100%': { transform: 'translate3d(0,0,0)' }, + }, + spin: { + '0%, 100%': { transform: 'rotate(360deg)' }, + }, }, animation: { 'flash-code': 'flash-code 1s forwards', 'flash-code-slow': 'flash-code 2s forwards', + spin: 'spin 1s both infinite', }, }, }, diff --git a/package-lock.json b/package-lock.json index 084b2d064df..a44f1c24ac3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -311,12 +311,15 @@ "@mdx-js/react": "^2.1.5", "@next/bundle-analyzer": "^13.4.0", "@next/mdx": "^12.3.2", + "@react-three/drei": "^8.20.2", + "@react-three/fiber": "^7.0.29", "@supabase/auth-ui-react": "^0.1.6", "@supabase/supabase-js": "^2.23.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", "config": "*", + "dat.gui": "^0.7.9", "date-fns": "2.10.0", "dayjs": "^1.11.7", "framer-motion": "^6.5.1", @@ -334,6 +337,7 @@ "parse-numeric-range": "^1.3.0", "react": "^17.0.2", "react-copy-to-clipboard": "^5.1.0", + "react-countdown": "^2.3.5", "react-dom": "^17.0.2", "react-ga": "^3.3.0", "react-markdown": "^8.0.3", @@ -349,6 +353,7 @@ "shared-data": "*", "swiper": "^6.5.0", "swr": "^0.5.4", + "three": "^0.154.0", "typescript": "^5.0.4", "ui": "*", "use-debounce": "^7.0.1", @@ -359,6 +364,7 @@ "devDependencies": { "@tailwindcss/typography": "^0.5.9", "@types/classnames": "^2.3.1", + "@types/dat.gui": "^0.7.10", "@types/jest": "^27.4.0", "@types/mdx-js__react": "^1.5.5", "@types/node": "^17.0.24", @@ -368,6 +374,7 @@ "@types/react-copy-to-clipboard": "^5.0.4", "@types/react-dom": "^17.0.11", "@types/react-syntax-highlighter": "^15.5.6", + "@types/three": "^0.154.0", "autoprefixer": "^10.4.14", "config": "*", "file-loader": "^6.2.0", @@ -2674,6 +2681,35 @@ "version": "6.0.2", "license": "MIT" }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==" + }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", "dev": true, @@ -6646,6 +6682,130 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/@react-spring/animated": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", + "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", + "dependencies": { + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", + "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/shared": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", + "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", + "dependencies": { + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/three": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.7.3.tgz", + "integrity": "sha512-Q1p512CqUlmMK8UMBF/Rj79qndhOWq4XUTayxMP9S892jiXzWQuj+xC3Xvm59DP/D4JXusXpxxqfgoH+hmOktA==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/core": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "@react-three/fiber": ">=6.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "three": ">=0.126" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", + "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" + }, + "node_modules/@react-three/drei": { + "version": "8.20.2", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-8.20.2.tgz", + "integrity": "sha512-V2P/YHYg2+N58eWtIqmOJf0t5nWGTcmpT+ci4tB5gt3CsWU2vuoeQFhvBmqEF+cXwZD5ZYKPfQDf/06NzTx/lw==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@react-spring/three": "^9.3.1", + "@use-gesture/react": "^10.2.0", + "detect-gpu": "^4.0.14", + "glsl-noise": "^0.0.0", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "meshline": "^2.0.4", + "react-composer": "^5.0.2", + "react-merge-refs": "^1.1.0", + "stats.js": "^0.17.0", + "suspend-react": "^0.0.8", + "three-mesh-bvh": "^0.5.7", + "three-stdlib": "^2.8.9", + "troika-three-text": "^0.46.3", + "utility-types": "^3.10.0", + "zustand": "^3.5.13" + }, + "peerDependencies": { + "@react-three/fiber": ">=6.0", + "react": ">=17.0", + "react-dom": ">=17.0", + "three": ">=0.137" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber": { + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-7.0.29.tgz", + "integrity": "sha512-TE+/iaGrjluGsPwN2RkLXxKD4ak+unsh0YKIFKGcuAOW9jYcAyMGKAtgFO8MS7/HNCrZZQQSg23sJ8IPgtdkDg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@types/react-reconciler": "^0.26.2", + "react-merge-refs": "^1.1.0", + "react-reconciler": "^0.26.2", + "react-three-fiber": "0.0.0-deprecated", + "react-use-measure": "^2.1.1", + "resize-observer-polyfill": "^1.5.1", + "scheduler": "^0.20.2", + "use-asset": "^1.0.4", + "utility-types": "^3.10.0", + "zustand": "^3.5.1" + }, + "peerDependencies": { + "react": ">=17.0", + "react-dom": ">=17.0", + "three": ">=0.133" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.3.0", "license": "MIT" @@ -13941,6 +14101,12 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@tweenjs/tween.js": { + "version": "18.6.4", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz", + "integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==", + "dev": true + }, "node_modules/@types/acorn": { "version": "4.0.6", "license": "MIT", @@ -14084,6 +14250,12 @@ "version": "3.0.0", "license": "MIT" }, + "node_modules/@types/dat.gui": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@types/dat.gui/-/dat.gui-0.7.10.tgz", + "integrity": "sha512-sLifoNrvs4lgJkAUZcTB28dbWy0AM05Yc0BzTqHheK0tRYRVsJHoFLocB5heDbb5amaFLEXna41GOM6IG06oqA==", + "dev": true + }, "node_modules/@types/debounce-promise": { "version": "3.1.6", "license": "MIT" @@ -14105,6 +14277,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/draco3d": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.2.tgz", + "integrity": "sha512-goh23EGr6CLV6aKPwN1p8kBD/7tT5V/bLpToSbarKrwVejqNrspVrv8DhliteYkkhZYrlq/fwKZRRUzH4XN88w==" + }, "node_modules/@types/ejs": { "version": "3.1.2", "dev": true, @@ -14358,6 +14535,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz", + "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==" + }, "node_modules/@types/papaparse": { "version": "5.3.7", "dev": true, @@ -14468,6 +14650,14 @@ "@types/react": "*" } }, + "node_modules/@types/react-reconciler": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz", + "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-redux": { "version": "7.1.25", "license": "MIT", @@ -14564,6 +14754,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==", + "dev": true + }, "node_modules/@types/strip-ansi": { "version": "5.2.1", "dev": true, @@ -14587,6 +14783,20 @@ "@types/jest": "*" } }, + "node_modules/@types/three": { + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.154.0.tgz", + "integrity": "sha512-IioqpGhch6FdLDh4zazRn3rXHj6Vn2nVOziJdXVbJFi9CaI65LtP9qqUtpzbsHK2Ezlox8NtsLNHSw3AQzucjA==", + "dev": true, + "dependencies": { + "@tweenjs/tween.js": "~18.6.4", + "@types/stats.js": "*", + "@types/webxr": "*", + "fflate": "~0.6.9", + "lil-gui": "~0.17.0", + "meshoptimizer": "~0.18.1" + } + }, "node_modules/@types/uglify-js": { "version": "3.17.1", "dev": true, @@ -14665,6 +14875,11 @@ "@types/node": "*" } }, + "node_modules/@types/webxr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.2.tgz", + "integrity": "sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw==" + }, "node_modules/@types/yargs": { "version": "15.0.15", "dev": true, @@ -14883,6 +15098,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@use-gesture/core": { + "version": "10.2.27", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.27.tgz", + "integrity": "sha512-V4XV7hn9GAD2MYu8yBBVi5iuWBsAMfjPRMsEVzoTNGYH72tf0kFP+OKqGKc8YJFQIJx6yj+AOqxmEHOmx2/MEA==" + }, + "node_modules/@use-gesture/react": { + "version": "10.2.27", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.27.tgz", + "integrity": "sha512-7E5vnWCxeslWlxwZ8uKIcnUZVMTRMZ8cvSnLLKF1NkyNb3PnNiAzoXM4G1vTKJKRhgOTeI6wK1YsEpwo9ABV5w==", + "dependencies": { + "@use-gesture/core": "10.2.27" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.9.0", "license": "MIT", @@ -16418,6 +16649,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bidi-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.2.tgz", + "integrity": "sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw==", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/big-integer": { "version": "1.6.51", "license": "Unlicense", @@ -17236,6 +17475,19 @@ "node": "*" } }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, "node_modules/chokidar": { "version": "3.5.3", "funding": [ @@ -19032,6 +19284,11 @@ "version": "1.0.8", "license": "BSD-2-Clause" }, + "node_modules/dat.gui": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz", + "integrity": "sha512-sCNc1OHobc+Erc1HqiswYgHdVNpSJUlk/Hz8vzOCsER7rl+oF/4+v8GXFUyCgtXpoCX6+bnmg07DedLvBLwYKQ==" + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "dev": true, @@ -19075,6 +19332,11 @@ "node": ">=0.11.0" } }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + }, "node_modules/debounce-promise": { "version": "3.1.2", "license": "MIT" @@ -19485,6 +19747,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/detect-gpu": { + "version": "4.0.50", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-4.0.50.tgz", + "integrity": "sha512-T67HE5+ONONN8rPXCBJPupyCg2QT8+l2NUUMuPxAppsMJBDPG/Jg0URLs6GyDzLm2niUE+oncIHSuy3VinoPeQ==", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "dev": true, @@ -19759,6 +20029,11 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/draco3d": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz", + "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ==" + }, "node_modules/duplexer": { "version": "0.1.2", "license": "MIT" @@ -21368,6 +21643,11 @@ "dev": true, "license": "MIT" }, + "node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==" + }, "node_modules/figgy-pudding": { "version": "3.5.2", "license": "ISC" @@ -22224,6 +22504,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" + }, "node_modules/goober": { "version": "2.1.13", "license": "MIT", @@ -26077,6 +26362,11 @@ "node": ">= 8" } }, + "node_modules/ktx-parse": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.4.5.tgz", + "integrity": "sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg==" + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "license": "CC0-1.0" @@ -26218,6 +26508,12 @@ "immediate": "~3.0.5" } }, + "node_modules/lil-gui": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz", + "integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==", + "dev": true + }, "node_modules/lilconfig": { "version": "2.1.0", "license": "MIT", @@ -26372,6 +26668,16 @@ "version": "4.6.2", "license": "MIT" }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, "node_modules/lodash.template": { "version": "4.5.0", "license": "MIT", @@ -27284,6 +27590,20 @@ } } }, + "node_modules/meshline": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/meshline/-/meshline-2.0.4.tgz", + "integrity": "sha512-Jh6DJl/zLqA4xsKvGv5950jr2ukyXQE1wgxs8u94cImHrvL6soVIggqjP+2hVHZXGYaKnWszhtjuCbKNeQyYiw==", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true + }, "node_modules/methods": { "version": "1.1.2", "dev": true, @@ -28259,6 +28579,11 @@ "dev": true, "license": "MIT" }, + "node_modules/mmd-parser": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz", + "integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==" + }, "node_modules/mobx": { "version": "6.9.0", "license": "MIT", @@ -30295,6 +30620,21 @@ "opener": "bin/opener-bin.js" } }, + "node_modules/opentype.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz", + "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==", + "dependencies": { + "string.prototype.codepointat": "^0.2.1", + "tiny-inflate": "^1.0.3" + }, + "bin": { + "ot": "bin/ot" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/optionator": { "version": "0.9.1", "license": "MIT", @@ -32382,6 +32722,11 @@ "node": ">=0.10.0" } }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" + }, "node_modules/preact": { "version": "10.15.1", "license": "MIT", @@ -32890,6 +33235,17 @@ "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-composer": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz", + "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", + "dependencies": { + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-contexify": { "version": "5.0.0", "license": "MIT", @@ -33278,7 +33634,6 @@ }, "node_modules/react-merge-refs": { "version": "1.1.0", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -33314,6 +33669,22 @@ "version": "3.2.2", "license": "MIT" }, + "node_modules/react-reconciler": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.26.2.tgz", + "integrity": "sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^17.0.2" + } + }, "node_modules/react-redux": { "version": "7.2.9", "license": "MIT", @@ -33490,6 +33861,11 @@ "react": ">= 0.14.0" } }, + "node_modules/react-three-fiber": { + "version": "0.0.0-deprecated", + "resolved": "https://registry.npmjs.org/react-three-fiber/-/react-three-fiber-0.0.0-deprecated.tgz", + "integrity": "sha512-EblIqTAsIpkYeM8bZtC4lcpTE0A2zCEGipFB52RgcQq/q+0oryrk7Sxt+sqhIjUu6xMNEVywV8dr74lz5yWO6A==" + }, "node_modules/react-tooltip": { "version": "4.5.1", "license": "MIT", @@ -33597,6 +33973,18 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-use-measure": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", + "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", + "dependencies": { + "debounce": "^1.2.1" + }, + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + } + }, "node_modules/react-virtualized-auto-sizer": { "version": "1.0.18", "license": "MIT", @@ -34002,6 +34390,11 @@ "dev": true, "license": "MIT" }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", "license": "MIT", @@ -35979,6 +36372,11 @@ "node": ">=0.10.0" } }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==" + }, "node_modules/statuses": { "version": "2.0.1", "dev": true, @@ -36351,6 +36749,11 @@ "version": "8.0.0", "license": "MIT" }, + "node_modules/string.prototype.codepointat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" + }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "license": "MIT", @@ -36722,6 +37125,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/suspend-react": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz", + "integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==", + "peerDependencies": { + "react": ">=17.0" + } + }, "node_modules/swiper": { "version": "6.8.4", "funding": [ @@ -37097,6 +37508,40 @@ "node": ">=0.8" } }, + "node_modules/three": { + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.154.0.tgz", + "integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==" + }, + "node_modules/three-mesh-bvh": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.24.tgz", + "integrity": "sha512-VTIgfjz8aFoPKTQoMIQQv9jJD4ybFRZuKKE1/kqy78FQcuHQ0+iIWv7C5cSb2inlvs7bNMVY3yRx3RXGZfrvzQ==", + "peerDependencies": { + "three": ">= 0.123.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.23.13", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.23.13.tgz", + "integrity": "sha512-WU4XHs4E6szAyoREzTs5bUAc1WFadiz5jRjNlA7aIIqf+raT4jfA320P0XmlyZz/wJwmpZnOuki4kAFsmadkTQ==", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "chevrotain": "^10.1.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "ktx-parse": "^0.4.5", + "mmd-parser": "^1.0.4", + "opentype.js": "^1.3.3", + "potpack": "^1.0.1", + "zstddec": "^0.0.2" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, "node_modules/throat": { "version": "6.0.2", "dev": true, @@ -37131,6 +37576,11 @@ "version": "2.1.0", "license": "MIT" }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/tiny-invariant": { "version": "1.3.1", "license": "MIT" @@ -37330,6 +37780,33 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/troika-three-text": { + "version": "0.46.4", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.46.4.tgz", + "integrity": "sha512-Qsv0HhUKTZgSmAJs5wvO7YlBoJSP9TGPLmrg+K9pbQq4lseQdcevbno/WI38bwJBZ/qS56hvfqEzY0zUEFzDIw==", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.46.0", + "troika-worker-utils": "^0.46.0", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.103.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.46.0.tgz", + "integrity": "sha512-llHyrXAcwzr0bpg80GxsIp73N7FuImm4WCrKDJkAqcAsWmE5pfP9+Qzw+oMWK1P/AdHQ79eOrOl9NjyW4aOw0w==", + "peerDependencies": { + "three": ">=0.103.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.46.0.tgz", + "integrity": "sha512-bzOx5f2ZBxkFhXtIvDJlLn2AI3bzCkGVbCndl/2dL5QZrwHEKl45OEIilCxYQQWJG1rEbOD9O80tMjoYjw19OA==" + }, "node_modules/trough": { "version": "1.0.5", "license": "MIT", @@ -38370,6 +38847,17 @@ "node": ">=0.10.0" } }, + "node_modules/use-asset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/use-asset/-/use-asset-1.0.4.tgz", + "integrity": "sha512-7/hqDrWa0iMnCoET9W1T07EmD4Eg/Wmoj/X8TGBc++ECRK4m5yTsjP4O6s0yagbxfqIOuUkIxe2/sA+VR2GxZA==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "react": ">=17.0" + } + }, "node_modules/use-callback-ref": { "version": "1.3.0", "license": "MIT", @@ -38477,6 +38965,14 @@ "dev": true, "license": "MIT" }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/utils-merge": { "version": "1.0.1", "dev": true, @@ -39109,6 +39605,16 @@ "version": "1.2.0", "license": "Apache-2.0" }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==" + }, "node_modules/webidl-conversions": { "version": "6.1.0", "dev": true, @@ -39895,6 +40401,27 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zstddec": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz", + "integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==" + }, + "node_modules/zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "license": "MIT", diff --git a/packages/common/hooks/index.ts b/packages/common/hooks/index.ts index 65b5a76ec98..12b0a79170f 100644 --- a/packages/common/hooks/index.ts +++ b/packages/common/hooks/index.ts @@ -1,3 +1,4 @@ export * from './useBreakpoint' +export * from './useDebounce' export * from './useParams' export * from './useTelemetryProps' diff --git a/packages/common/hooks/useDebounce.tsx b/packages/common/hooks/useDebounce.tsx new file mode 100644 index 00000000000..9f70e50df7f --- /dev/null +++ b/packages/common/hooks/useDebounce.tsx @@ -0,0 +1,20 @@ +import { useEffect, useMemo, useRef } from 'react' +import { debounce } from 'lodash' + +export const useDebounce = (callback: () => void, delay: number = 1000) => { + const ref = useRef() + + useEffect(() => { + ref.current = callback + }, [callback]) + + const debouncedCallback = useMemo(() => { + const func = () => { + ref.current?.() + } + + return debounce(func, delay) + }, []) + + return debouncedCallback +} diff --git a/packages/config/ui.config.js b/packages/config/ui.config.js index 1683f2178a5..be34a9cf667 100644 --- a/packages/config/ui.config.js +++ b/packages/config/ui.config.js @@ -257,8 +257,8 @@ const uiConfig = { }, }, animation: { - 'fade-in': 'fadeIn 300ms', - 'fade-out': 'fadeOut 300ms', + 'fade-in': 'fadeIn 300ms both', + 'fade-out': 'fadeOut 300ms both', 'dropdown-content-show': 'overlayContentShow 100ms cubic-bezier(0.16, 1, 0.3, 1)', 'dropdown-content-hide': 'overlayContentHide 100ms cubic-bezier(0.16, 1, 0.3, 1)', diff --git a/packages/ui/index.tsx b/packages/ui/index.tsx index ae5581d03ea..88f5d9b0ef5 100644 --- a/packages/ui/index.tsx +++ b/packages/ui/index.tsx @@ -62,6 +62,9 @@ export * from './src/components/Form' // CMD+K export * from './src/components/Command' +// Widgets +export * from './src/components/CountdownWidget' + // layout export * from './src/components/LoadingLine' diff --git a/packages/ui/src/components/CountdownWidget/CountdownStep.tsx b/packages/ui/src/components/CountdownWidget/CountdownStep.tsx new file mode 100644 index 00000000000..b1521b5ff24 --- /dev/null +++ b/packages/ui/src/components/CountdownWidget/CountdownStep.tsx @@ -0,0 +1,17 @@ +interface CountdownStepProps { + value: string | number + unit: string +} + +function CountdownStep({ value, unit }: CountdownStepProps) { + return ( +
+
+ {value} + {unit} +
+
+ ) +} + +export default CountdownStep diff --git a/packages/ui/src/components/CountdownWidget/CountdownWidget.tsx b/packages/ui/src/components/CountdownWidget/CountdownWidget.tsx new file mode 100644 index 00000000000..ed61492064b --- /dev/null +++ b/packages/ui/src/components/CountdownWidget/CountdownWidget.tsx @@ -0,0 +1,34 @@ +import CountdownStep from './CountdownStep' + +interface CountdownWidgetProps { + days?: string + hours?: string + minutes?: string + seconds?: string +} + +function CountdownWidget({ days, hours, minutes, seconds }: CountdownWidgetProps) { + return ( +
+ {days !== undefined && ( + <> + : + + )} + {hours !== undefined && ( + <> + : + + )} + {minutes !== undefined && ( + <> + + {seconds !== undefined && ' :'} + + )} + {seconds !== undefined && } +
+ ) +} + +export default CountdownWidget diff --git a/packages/ui/src/components/CountdownWidget/index.ts b/packages/ui/src/components/CountdownWidget/index.ts new file mode 100644 index 00000000000..0c677d46517 --- /dev/null +++ b/packages/ui/src/components/CountdownWidget/index.ts @@ -0,0 +1 @@ +export { default as CountdownWidget } from './CountdownWidget' diff --git a/packages/ui/src/layout/banners/Announcement.tsx b/packages/ui/src/layout/banners/Announcement.tsx index 505c20b36bc..6613c5f4aee 100644 --- a/packages/ui/src/layout/banners/Announcement.tsx +++ b/packages/ui/src/layout/banners/Announcement.tsx @@ -58,11 +58,11 @@ const Announcement = ({ return (
window.location.assign(announcement.link)} - className={['relative w-full cursor-pointer', className].join(' ')} + className={['relative z-40 w-full cursor-pointer', className].join(' ')} > {!isLaunchWeekSection && (
diff --git a/packages/ui/src/layout/banners/Announcement.Countdown.tsx b/packages/ui/src/layout/banners/LW8CountdownBanner/LW8CountdownBanner.tsx similarity index 52% rename from packages/ui/src/layout/banners/Announcement.Countdown.tsx rename to packages/ui/src/layout/banners/LW8CountdownBanner/LW8CountdownBanner.tsx index 8031b9ab36d..e1b9a590ff1 100644 --- a/packages/ui/src/layout/banners/Announcement.Countdown.tsx +++ b/packages/ui/src/layout/banners/LW8CountdownBanner/LW8CountdownBanner.tsx @@ -1,13 +1,10 @@ import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' import Countdown from 'react-countdown' -import { AnnouncementProps } from './Announcement' -import _announcement from './data/Announcement.json' - -interface CountdownStepProps { - value: string | number - unit: string -} +import { AnnouncementProps } from '../Announcement' +import _announcement from '../data/Announcement.json' +import CountdownWidget from '../../../components/CountdownWidget/CountdownWidget' +import Lw8BannerBg from './Lw8BannerBg' interface CountdownButtonProps { dest: string @@ -15,41 +12,32 @@ interface CountdownButtonProps { const announcement = _announcement as AnnouncementProps -function CountdownStep({ value, unit }: CountdownStepProps) { - return ( -
-
- {value} - {unit} -
-
- ) -} - function CountdownButton({ children, dest }: PropsWithChildren) { return ( {children} ) } -function CountdownBanner() { +export function LW8CountdownBanner() { const { pathname } = useRouter() const isLaunchWeekPage = pathname === '/launch-week' const isLaunchWeekSection = pathname.includes('launch-week') + if (isLaunchWeekPage) return null + const renderer = ({ days, hours, minutes, seconds, completed }: any) => { if (completed) { // Render a completed state return (
-

Supabase Launch Week 7

+

Supabase Launch Week 8

- Explore + Live now
) @@ -63,17 +51,16 @@ function CountdownBanner() { ].join(' ')} >

- Supabase Launch Week 7 + Supabase Launch Week 8

- : - : - : - +
{!isLaunchWeekPage && (
- Get your ticket + + Get your ticket +
)}
@@ -82,10 +69,19 @@ function CountdownBanner() { } return ( -
- +
+
+ +
+
) } -export default CountdownBanner +export default LW8CountdownBanner diff --git a/packages/ui/src/layout/banners/LW8CountdownBanner/Lw8BannerBg.tsx b/packages/ui/src/layout/banners/LW8CountdownBanner/Lw8BannerBg.tsx new file mode 100644 index 00000000000..72fc431e99c --- /dev/null +++ b/packages/ui/src/layout/banners/LW8CountdownBanner/Lw8BannerBg.tsx @@ -0,0 +1,224 @@ +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 new file mode 100644 index 00000000000..f87f7ca1cb9 --- /dev/null +++ b/packages/ui/src/layout/banners/LW8CountdownBanner/index.ts @@ -0,0 +1 @@ +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 f8a5db4f13e..abb5cec8362 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 7", - "launchDate": "2023-04-10T07:00:00.000-07:00", + "text": "Supabase Launch Week 8", + "launchDate": "2023-08-07T09:00:00.000-07:00", "link": "https://supabase.com/launch-week", "badge": "Get your ticket" } diff --git a/packages/ui/src/layout/banners/index.tsx b/packages/ui/src/layout/banners/index.tsx index 88c3f4190ac..49a7d6ec582 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 AnnouncementCountdown } from './Announcement.Countdown' +export { default as LW8CountdownBanner } from './LW8CountdownBanner/LW8CountdownBanner'