From 131a8425029f397b7637b09173d8b11b677d7e3a Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 7 Feb 2023 16:08:53 +0800 Subject: [PATCH 001/351] feat: add ticket referral mechanism --- .../LaunchWeek/Ticket/TicketContainer.tsx | 1 + .../www/components/LaunchWeek/Ticket/form.tsx | 40 +---------- .../LaunchWeek/Ticket/hooks/use-conf-data.ts | 1 + .../LaunchWeek/Ticket/ticket-form.tsx | 63 +++++++++++++++--- .../components/LaunchWeek/Ticket/ticket.tsx | 43 +++++++----- apps/www/package.json | 2 +- .../pages/launch-week/tickets/[username].tsx | 11 +++- apps/www/pages/launch-week/tickets/index.tsx | 6 +- package-lock.json | 66 ++++++++++++++++++- 9 files changed, 158 insertions(+), 75 deletions(-) diff --git a/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx b/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx index f658037f136..678662425f2 100644 --- a/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx +++ b/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx @@ -42,6 +42,7 @@ export default function Conf({ ticketNumber={userData.ticketNumber} sharePage={sharePage} golden={userData.golden} + referrals={userData.referrals ?? 0} /> )} diff --git a/apps/www/components/LaunchWeek/Ticket/form.tsx b/apps/www/components/LaunchWeek/Ticket/form.tsx index 59dbcb902ac..2ac809410a9 100644 --- a/apps/www/components/LaunchWeek/Ticket/form.tsx +++ b/apps/www/components/LaunchWeek/Ticket/form.tsx @@ -37,45 +37,7 @@ export default function Form({ sharePage, align = 'Center' }: Props) { const router = useRouter() const isCaptchaEnabled = false - useEffect(() => { - if (session?.user) { - document.body.classList.add('ticket-generated') - const username = session.user.user_metadata.user_name - const name = session.user.user_metadata.full_name - const email = session.user.email - supabase - .from('lw6_tickets') - .upsert({ email, name, username }, { onConflict: 'email', ignoreDuplicates: false }) - .eq('email', email) - .select() - .single() - .then(async ({ error }) => { - if (error) return supabase.auth.signOut() - const { data } = await supabase - .from('lw6_tickets_golden') - .select('*') - .eq('username', username) - .single() - 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((_) => {}) - // Prefetch ticket og image. - fetch( - `https://obuldanrptloktxcffvn.functions.supabase.co/launchweek-ticket-og?username=${encodeURIComponent( - username ?? '' - )}` - ).catch((_) => {}) - - setPageState('ticket') - }) - } - }, [session]) - + // NOTE: this is not in use anymore as we're only allowing GitHub signups since LW7! async function register(email: string, token?: string): Promise { const { error } = await supabase!.from('lw6_tickets').insert({ email }) if (error) { diff --git a/apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts b/apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts index 4855f4f9a56..574e23fca2e 100644 --- a/apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts +++ b/apps/www/components/LaunchWeek/Ticket/hooks/use-conf-data.ts @@ -9,6 +9,7 @@ export type UserData = { username?: string name?: string golden?: boolean + referrals?: number } type ConfDataContextType = { diff --git a/apps/www/components/LaunchWeek/Ticket/ticket-form.tsx b/apps/www/components/LaunchWeek/Ticket/ticket-form.tsx index 8ded0d8cdf1..44ad832b3d8 100644 --- a/apps/www/components/LaunchWeek/Ticket/ticket-form.tsx +++ b/apps/www/components/LaunchWeek/Ticket/ticket-form.tsx @@ -1,4 +1,5 @@ -import { useState, useRef } from 'react' +import { useState, useRef, useEffect } from 'react' +import { useRouter } from 'next/router' // import { scrollTo } from '@lib/smooth-scroll'; import cn from 'classnames' // import GithubIcon from '~/components/LaunchWeek/Ticket/icons/icon-github' @@ -18,11 +19,53 @@ type Props = { } export default function Form({ defaultUsername = '', setTicketGenerationState }: Props) { - const [username] = useState(defaultUsername) + const [username, setUsername] = useState(defaultUsername) const [formState, setFormState] = useState('default') const [errorMsg] = useState('') - const { supabase } = useConfData() + const { supabase, session, setUserData, setPageState, userData } = useConfData() const formRef = useRef(null) + const router = useRouter() + + useEffect(() => { + if (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('lw7_tickets') + .insert({ email, name, username, referred_by: router.query?.referral ?? null }) + .eq('email', email) + .select() + .single() + .then(async ({ error }) => { + // 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('lw7_tickets_golden') + .select('*') + .eq('username', username) + .single() + 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((_) => {}) + // Prefetch ticket og image. + fetch( + `https://obuldanrptloktxcffvn.functions.supabase.co/launchweek-ticket-og?username=${encodeURIComponent( + username ?? '' + )}` + ).catch((_) => {}) + + setPageState('ticket') + }) + } + }, [session]) return formState === 'error' ? (
@@ -59,7 +102,11 @@ export default function Form({ defaultUsername = '', setTicketGenerationState }: await supabase.auth.signInWithOAuth({ provider: 'github', - options: { redirectTo: `${SITE_ORIGIN}/launch-week/tickets` }, + options: { + redirectTo: `${SITE_ORIGIN}/launch-week/tickets${ + userData.username ? '?referral=' + userData.username : '' + }`, + }, }) }} className="flex flex-col items-center xl:block" @@ -69,15 +116,15 @@ export default function Form({ defaultUsername = '', setTicketGenerationState }:
- {!username &&

Only public info will be used.

} + {!session &&

Only public info will be used.

} ) diff --git a/apps/www/components/LaunchWeek/Ticket/ticket.tsx b/apps/www/components/LaunchWeek/Ticket/ticket.tsx index 4cc479bd591..c494a9652ed 100644 --- a/apps/www/components/LaunchWeek/Ticket/ticket.tsx +++ b/apps/www/components/LaunchWeek/Ticket/ticket.tsx @@ -20,10 +20,18 @@ type Props = { ticketNumber: UserData['ticketNumber'] name: UserData['name'] golden: UserData['golden'] + referrals: number sharePage?: boolean } -export default function Ticket({ username, name, ticketNumber, sharePage, golden }: Props) { +export default function Ticket({ + username, + name, + ticketNumber, + sharePage, + golden, + referrals, +}: Props) { const ticketRef = useRef(null) const [ticketGenerationState, setTicketGenerationState] = useState('default') @@ -70,7 +78,9 @@ export default function Ticket({ username, name, ticketNumber, sharePage, golden ) : golden ? (

Congratulations!

) : ( -

You have a ticket!

+

+ Claim your unique Ticket! +

)}

@@ -80,31 +90,28 @@ export default function Ticket({ username, name, ticketNumber, sharePage, golden

) : golden ? ( <> - You got a Golden ticket. This means you’re in, and you also won a Supabase goodie - bag. Tweet it to redeem your swag pack! + You got a Golden ticket. This means you’re in, and you also won a Supabase sticker + pack! ) : username ? ( - <>Here is your unique ticket image for bragging on socials! + <> +

Here is your unique ticket image for bragging on socials!

+

Referrals: {referrals}

+ ) : ( <> - This means you're in. Generate a unique ticket image with your GitHub profile, cause - a few of the lucky attendees will get a limited edition Supabase goodie bag. Make - sure you don't skip your chance. + Generate a unique ticket image with your GitHub profile, cause a few of the lucky + attendees will get a limited edition Supabase goodie bag. Make sure you don't skip + your chance. )}

- {!sharePage ? ( - <> - - - ) : ( -
- )} +
diff --git a/apps/www/package.json b/apps/www/package.json index 69108a1066b..5415fb31d4f 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -19,7 +19,7 @@ "@next/bundle-analyzer": "^13.0.0", "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", - "@supabase/supabase-js": "^2.0.0", + "@supabase/supabase-js": "^2.5.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", diff --git a/apps/www/pages/launch-week/tickets/[username].tsx b/apps/www/pages/launch-week/tickets/[username].tsx index 7bd706c81bd..45b3b027df3 100644 --- a/apps/www/pages/launch-week/tickets/[username].tsx +++ b/apps/www/pages/launch-week/tickets/[username].tsx @@ -15,6 +15,7 @@ type Props = { name: string | null ticketNumber: number | null golden: boolean + referrals: number } const supabaseAdmin = createClient( @@ -23,7 +24,7 @@ const supabaseAdmin = createClient( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9idWxkYW5ycHRsb2t0eGNmZnZuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2Njk3MjcwMTIsImV4cCI6MTk4NTMwMzAxMn0.SZLqryz_-stF8dgzeVXmzZWPOqdOrBwqJROlFES8v3I' ) -export default function TicketShare({ username, ticketNumber, name, golden }: Props) { +export default function TicketShare({ username, ticketNumber, name, golden, referrals }: Props) { const { isDarkMode } = useTheme() const [supabase] = useState(() => @@ -76,6 +77,7 @@ export default function TicketShare({ username, ticketNumber, name, golden }: Pr name: name || '', ticketNumber, golden, + referrals, }} sharePage /> @@ -90,16 +92,18 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { let name: string | null | undefined let ticketNumber: number | null | undefined let golden = false + let referrals = 0 if (username) { const { data: user } = await supabaseAdmin! - .from('lw6_tickets_golden') - .select('name, ticketNumber, golden') + .from('lw7_tickets_golden') + .select('name, ticketNumber, golden, referrals') .eq('username', username) .single() name = user?.name ticketNumber = user?.ticketNumber golden = user?.golden ?? false + referrals = user?.referrals ?? 0 } return { props: { @@ -108,6 +112,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { name: ticketNumber ? name || username || null : null, ticketNumber: ticketNumber || SAMPLE_TICKET_NUMBER, golden, + referrals, }, revalidate: 5, } diff --git a/apps/www/pages/launch-week/tickets/index.tsx b/apps/www/pages/launch-week/tickets/index.tsx index fba61ea897e..def343e0796 100644 --- a/apps/www/pages/launch-week/tickets/index.tsx +++ b/apps/www/pages/launch-week/tickets/index.tsx @@ -26,8 +26,8 @@ export default function TicketHome() { } useEffect(() => { - supabase.auth.onAuthStateChange((event, session) => { - setSession(session) + supabase.auth.onAuthStateChange((event, supaSession) => { + setSession(supaSession) }) }, []) @@ -68,7 +68,7 @@ export default function TicketHome() { supabase={supabase} session={session} defaultUserData={defaultUserData} - defaultPageState={query.ticketNumber ? 'ticket' : 'registration'} + defaultPageState="ticket" /> diff --git a/package-lock.json b/package-lock.json index 5bb23e0dd7c..4f831603087 100644 --- a/package-lock.json +++ b/package-lock.json @@ -883,7 +883,7 @@ "@next/bundle-analyzer": "^13.0.0", "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", - "@supabase/supabase-js": "^2.0.0", + "@supabase/supabase-js": "^2.5.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", @@ -962,6 +962,36 @@ "node": ">= 8.3" } }, + "apps/www/node_modules/@supabase/gotrue-js": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.11.0.tgz", + "integrity": "sha512-yo3yExoTqpJH4YpdI5PDjZ1YLOj3wURppimfsV0ep5uxDY/lE1uOhiMCPnhy59DbFAKG7bjnhJ1L7sk+B2os3w==", + "dependencies": { + "cross-fetch": "^3.1.5" + } + }, + "apps/www/node_modules/@supabase/realtime-js": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.6.0.tgz", + "integrity": "sha512-tOVulMobhpxyDuu8VIImpL8FXmZOKsGNOSyS5ihJdj2xYmPPvYG+D2J51Ewfl+MFF65tweiB6p9N9bNIW1cDNA==", + "dependencies": { + "@types/phoenix": "^1.5.4", + "websocket": "^1.0.34" + } + }, + "apps/www/node_modules/@supabase/supabase-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.1.tgz", + "integrity": "sha512-Q/e+JAluZEvy7D4ul3aAs3aOiKkGvHlZULy6wjchWQyU9YlJKZLr6VPYcwUeitcnRKZi4al5iTS55LgdJFfqIA==", + "dependencies": { + "@supabase/functions-js": "^2.0.0", + "@supabase/gotrue-js": "^2.10.2", + "@supabase/postgrest-js": "^1.1.1", + "@supabase/realtime-js": "^2.4.0", + "@supabase/storage-js": "^2.1.0", + "cross-fetch": "^3.1.5" + } + }, "apps/www/node_modules/@tailwindcss/typography": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", @@ -46190,7 +46220,7 @@ "@sinclair/typebox": "^0.25.1", "pg": "^8.7.1", "pg-format": "^1.0.4", - "pgsql-parser": "^13.3.0", + "pgsql-parser": "13.4.0", "postgres-array": "^3.0.1", "prettier": "^2.6.0", "prettier-plugin-sql": "^0.12.1" @@ -68989,7 +69019,7 @@ "@next/bundle-analyzer": "^13.0.0", "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", - "@supabase/supabase-js": "^2.0.0", + "@supabase/supabase-js": "^2.5.0", "@tailwindcss/typography": "^0.5.7", "@types/classnames": "^2.3.1", "@types/jest": "^25.2.2", @@ -69062,6 +69092,36 @@ "chalk": "^3.0.0" } }, + "@supabase/gotrue-js": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.11.0.tgz", + "integrity": "sha512-yo3yExoTqpJH4YpdI5PDjZ1YLOj3wURppimfsV0ep5uxDY/lE1uOhiMCPnhy59DbFAKG7bjnhJ1L7sk+B2os3w==", + "requires": { + "cross-fetch": "^3.1.5" + } + }, + "@supabase/realtime-js": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.6.0.tgz", + "integrity": "sha512-tOVulMobhpxyDuu8VIImpL8FXmZOKsGNOSyS5ihJdj2xYmPPvYG+D2J51Ewfl+MFF65tweiB6p9N9bNIW1cDNA==", + "requires": { + "@types/phoenix": "^1.5.4", + "websocket": "^1.0.34" + } + }, + "@supabase/supabase-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.1.tgz", + "integrity": "sha512-Q/e+JAluZEvy7D4ul3aAs3aOiKkGvHlZULy6wjchWQyU9YlJKZLr6VPYcwUeitcnRKZi4al5iTS55LgdJFfqIA==", + "requires": { + "@supabase/functions-js": "^2.0.0", + "@supabase/gotrue-js": "^2.10.2", + "@supabase/postgrest-js": "^1.1.1", + "@supabase/realtime-js": "^2.4.0", + "@supabase/storage-js": "^2.1.0", + "cross-fetch": "^3.1.5" + } + }, "@tailwindcss/typography": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", From 6aadcdb2118e9335a4a62e5df7458429d6fdac35 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 7 Feb 2023 16:25:47 +0800 Subject: [PATCH 002/351] chore: roll back supabase-js update. --- apps/www/package.json | 2 +- package-lock.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/www/package.json b/apps/www/package.json index 5415fb31d4f..69108a1066b 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -19,7 +19,7 @@ "@next/bundle-analyzer": "^13.0.0", "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", - "@supabase/supabase-js": "^2.5.0", + "@supabase/supabase-js": "^2.0.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", diff --git a/package-lock.json b/package-lock.json index 4f831603087..9f8b0c07c27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -883,7 +883,7 @@ "@next/bundle-analyzer": "^13.0.0", "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", - "@supabase/supabase-js": "^2.5.0", + "@supabase/supabase-js": "^2.0.0", "classnames": "^2.3.1", "cobe": "^0.6.2", "common": "*", @@ -69019,7 +69019,7 @@ "@next/bundle-analyzer": "^13.0.0", "@next/mdx": "^12.3.2", "@supabase/auth-ui-react": "^0.1.6", - "@supabase/supabase-js": "^2.5.0", + "@supabase/supabase-js": "^2.0.0", "@tailwindcss/typography": "^0.5.7", "@types/classnames": "^2.3.1", "@types/jest": "^25.2.2", From 6c51c52ff6e3dfa299281bb438b5e970a2e73d3e Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 7 Feb 2023 17:44:07 +0800 Subject: [PATCH 003/351] chore: roll back package-lock --- package-lock.json | 62 +---------------------------------------------- 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ae802026c7..78869d57003 100644 --- a/package-lock.json +++ b/package-lock.json @@ -962,36 +962,6 @@ "node": ">= 8.3" } }, - "apps/www/node_modules/@supabase/gotrue-js": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.11.0.tgz", - "integrity": "sha512-yo3yExoTqpJH4YpdI5PDjZ1YLOj3wURppimfsV0ep5uxDY/lE1uOhiMCPnhy59DbFAKG7bjnhJ1L7sk+B2os3w==", - "dependencies": { - "cross-fetch": "^3.1.5" - } - }, - "apps/www/node_modules/@supabase/realtime-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.6.0.tgz", - "integrity": "sha512-tOVulMobhpxyDuu8VIImpL8FXmZOKsGNOSyS5ihJdj2xYmPPvYG+D2J51Ewfl+MFF65tweiB6p9N9bNIW1cDNA==", - "dependencies": { - "@types/phoenix": "^1.5.4", - "websocket": "^1.0.34" - } - }, - "apps/www/node_modules/@supabase/supabase-js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.1.tgz", - "integrity": "sha512-Q/e+JAluZEvy7D4ul3aAs3aOiKkGvHlZULy6wjchWQyU9YlJKZLr6VPYcwUeitcnRKZi4al5iTS55LgdJFfqIA==", - "dependencies": { - "@supabase/functions-js": "^2.0.0", - "@supabase/gotrue-js": "^2.10.2", - "@supabase/postgrest-js": "^1.1.1", - "@supabase/realtime-js": "^2.4.0", - "@supabase/storage-js": "^2.1.0", - "cross-fetch": "^3.1.5" - } - }, "apps/www/node_modules/@tailwindcss/typography": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", @@ -46211,7 +46181,7 @@ "@sinclair/typebox": "^0.25.1", "pg": "^8.7.1", "pg-format": "^1.0.4", - "pgsql-parser": "13.4.0", + "pgsql-parser": "^13.3.0", "postgres-array": "^3.0.1", "prettier": "^2.6.0", "prettier-plugin-sql": "^0.12.1" @@ -69076,36 +69046,6 @@ "chalk": "^3.0.0" } }, - "@supabase/gotrue-js": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.11.0.tgz", - "integrity": "sha512-yo3yExoTqpJH4YpdI5PDjZ1YLOj3wURppimfsV0ep5uxDY/lE1uOhiMCPnhy59DbFAKG7bjnhJ1L7sk+B2os3w==", - "requires": { - "cross-fetch": "^3.1.5" - } - }, - "@supabase/realtime-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.6.0.tgz", - "integrity": "sha512-tOVulMobhpxyDuu8VIImpL8FXmZOKsGNOSyS5ihJdj2xYmPPvYG+D2J51Ewfl+MFF65tweiB6p9N9bNIW1cDNA==", - "requires": { - "@types/phoenix": "^1.5.4", - "websocket": "^1.0.34" - } - }, - "@supabase/supabase-js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.7.1.tgz", - "integrity": "sha512-Q/e+JAluZEvy7D4ul3aAs3aOiKkGvHlZULy6wjchWQyU9YlJKZLr6VPYcwUeitcnRKZi4al5iTS55LgdJFfqIA==", - "requires": { - "@supabase/functions-js": "^2.0.0", - "@supabase/gotrue-js": "^2.10.2", - "@supabase/postgrest-js": "^1.1.1", - "@supabase/realtime-js": "^2.4.0", - "@supabase/storage-js": "^2.1.0", - "cross-fetch": "^3.1.5" - } - }, "@tailwindcss/typography": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.8.tgz", From 9dab245f523ff018ced279631caf61d283a0f91e Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 8 Feb 2023 20:30:23 +0800 Subject: [PATCH 004/351] Revert 94d9a3a2cc43752797e676c52b41c1e9044fcc90 --- .../interfaces/Auth/Users/AddUserDropdown.tsx | 108 ++++++++++++ .../interfaces/Auth/Users/CreateUserModal.tsx | 164 ++++++++++++++++++ .../interfaces/Auth/Users/InviteUserModal.tsx | 47 ++--- .../interfaces/Auth/Users/Users.tsx | 21 +-- studio/data/auth/user-create-mutation.ts | 47 +++++ 5 files changed, 339 insertions(+), 48 deletions(-) create mode 100644 studio/components/interfaces/Auth/Users/AddUserDropdown.tsx create mode 100644 studio/components/interfaces/Auth/Users/CreateUserModal.tsx create mode 100644 studio/data/auth/user-create-mutation.ts diff --git a/studio/components/interfaces/Auth/Users/AddUserDropdown.tsx b/studio/components/interfaces/Auth/Users/AddUserDropdown.tsx new file mode 100644 index 00000000000..29505af9dab --- /dev/null +++ b/studio/components/interfaces/Auth/Users/AddUserDropdown.tsx @@ -0,0 +1,108 @@ +import * as Tooltip from '@radix-ui/react-tooltip' +import { PermissionAction } from '@supabase/shared-types/out/constants' +import { checkPermissions } from 'hooks' +import { IS_PLATFORM } from 'lib/constants' +import { useState } from 'react' +import semver from 'semver' +import { Button, Dropdown, IconMail, IconPlus, IconUserPlus } from 'ui' +import CreateUserModal from './CreateUserModal' +import InviteUserModal from './InviteUserModal' + +export type AddUserDropdownProps = { + projectKpsVersion?: string +} + +const AddUserDropdown = ({ projectKpsVersion }: AddUserDropdownProps) => { + const inviteEnabled = IS_PLATFORM + ? semver.gte( + // @ts-ignore + semver.coerce(projectKpsVersion ?? 'kps-v2.5.4'), + semver.coerce('kps-v2.5.3') + ) + : true + + const canInviteUsers = checkPermissions(PermissionAction.AUTH_EXECUTE, 'invite_user') + const canCreateUsers = checkPermissions(PermissionAction.AUTH_EXECUTE, 'create_user') + + const [inviteVisible, setInviteVisible] = useState(false) + const [createVisible, setCreateVisible] = useState(false) + + return ( + <> + + {inviteEnabled && ( + + + } + disabled={!canInviteUsers} + onClick={() => setInviteVisible(true)} + > + Send Invitation + + + {!canInviteUsers && ( + + +
+ + You need additional permissions to invite users + +
+
+ )} +
+ )} + + + + } + disabled={!canCreateUsers} + onClick={() => setCreateVisible(true)} + > + Create New User + + + + {!canCreateUsers && ( + + +
+ + You need additional permissions to create users + +
+
+ )} +
+ + } + > + +
+ + {inviteEnabled && } + + + ) +} + +export default AddUserDropdown diff --git a/studio/components/interfaces/Auth/Users/CreateUserModal.tsx b/studio/components/interfaces/Auth/Users/CreateUserModal.tsx new file mode 100644 index 00000000000..94698aca740 --- /dev/null +++ b/studio/components/interfaces/Auth/Users/CreateUserModal.tsx @@ -0,0 +1,164 @@ +import { PermissionAction } from '@supabase/shared-types/out/constants' +import { observer } from 'mobx-react-lite' +import { useContext } from 'react' +import { Button, Checkbox, Form, IconLock, IconMail, Input, Loading, Modal } from 'ui' + +import { useUserCreateMutation } from 'data/auth/user-create-mutation' +import { useProjectApiQuery } from 'data/config/project-api-query' +import { checkPermissions, useStore } from 'hooks' +import { PageContext } from 'pages/project/[ref]/auth/users' + +export type CreateUserModalProps = { + visible: boolean + setVisible: (visible: boolean) => void +} + +const CreateUserModal = ({ visible, setVisible }: CreateUserModalProps) => { + const { ui } = useStore() + const PageState: any = useContext(PageContext) + const projectRef = PageState.projectRef + + const { data, isLoading, isSuccess } = useProjectApiQuery({ projectRef }, { enabled: visible }) + + const handleToggle = () => setVisible(!visible) + const canCreateUsers = checkPermissions(PermissionAction.AUTH_EXECUTE, 'create_user') + + const validate = (values: any) => { + const errors: any = {} + const emailValidateRegex = + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ + + if (values.email.length === 0) { + errors.email = 'Please enter a valid email' + } else if (!emailValidateRegex.test(values.email)) { + errors.email = `${values.email} is an invalid email` + } + + if (!values.password?.trim()) { + errors.password = 'Please enter a password' + } + + return errors + } + + const { mutateAsync: createUser } = useUserCreateMutation({ + async onSuccess() { + await PageState.fetchData(1) + }, + }) + + const onCreateUser = async (values: any, { setSubmitting }: any) => { + if (!isSuccess) { + ui.setNotification({ + category: 'error', + message: `Failed to create user: Error loading project config`, + }) + + return + } + + const { protocol, endpoint, serviceApiKey } = data.autoApiService + + setSubmitting(true) + + try { + await createUser({ + endpoint, + protocol, + serviceApiKey, + user: values, + }) + + ui.setNotification({ + category: 'success', + message: `Created user: ${values.email}`, + }) + + setVisible(false) + } catch (error: any) { + ui.setNotification({ + category: 'error', + message: `Failed to create user: ${error.message}`, + }) + } + + setSubmitting(false) + } + + return ( + + + {({ isSubmitting }: { isSubmitting: boolean }) => ( + +
+ +
+ } + type="email" + name="email" + placeholder="user@example.com" + disabled={isSubmitting || isLoading} + /> + + } + disabled={isSubmitting || isLoading} + autoComplete="new-password" + /> + + +
+
+ + + + +
+
+ )} + +
+ ) +} + +export default observer(CreateUserModal) diff --git a/studio/components/interfaces/Auth/Users/InviteUserModal.tsx b/studio/components/interfaces/Auth/Users/InviteUserModal.tsx index 5f5898714cf..2ec628eb448 100644 --- a/studio/components/interfaces/Auth/Users/InviteUserModal.tsx +++ b/studio/components/interfaces/Auth/Users/InviteUserModal.tsx @@ -1,18 +1,21 @@ -import { useContext, useState } from 'react' -import { observer } from 'mobx-react-lite' -import * as Tooltip from '@radix-ui/react-tooltip' -import { Button, Modal, Input, IconPlus, IconMail, Form } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' +import { observer } from 'mobx-react-lite' +import { useContext } from 'react' +import { Button, Form, IconMail, Input, Modal } from 'ui' -import { useStore, checkPermissions } from 'hooks' +import { checkPermissions, useStore } from 'hooks' import { post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { PageContext } from 'pages/project/[ref]/auth/users' -const InviteUserModal = () => { +export type InviteUserModalProps = { + visible: boolean + setVisible: (visible: boolean) => void +} + +const InviteUserModal = ({ visible, setVisible }: InviteUserModalProps) => { const { ui } = useStore() const PageState: any = useContext(PageContext) - const [visible, setVisible] = useState(false) const handleToggle = () => setVisible(!visible) const canInviteUsers = checkPermissions(PermissionAction.AUTH_EXECUTE, 'invite_user') @@ -31,7 +34,7 @@ const InviteUserModal = () => { return errors } - const onInviteuser = async (values: any, { setSubmitting }: any) => { + const onInviteUser = async (values: any, { setSubmitting }: any) => { setSubmitting(true) const response = await post(`${API_URL}/auth/${PageState.projectRef}/invite`, { @@ -56,28 +59,6 @@ const InviteUserModal = () => { return (
- - - - - {!canInviteUsers && ( - - -
- - You need additional permissions to invite users - -
-
- )} -
{ onCancel={handleToggle} >
{({ isSubmitting }: { isSubmitting: boolean }) => (
{ placeholder="User email" /> +
-
+
- {inviteEnabled && } + +
diff --git a/studio/data/auth/user-create-mutation.ts b/studio/data/auth/user-create-mutation.ts new file mode 100644 index 00000000000..8b41d9612a3 --- /dev/null +++ b/studio/data/auth/user-create-mutation.ts @@ -0,0 +1,47 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query' +import { post } from 'lib/common/fetch' + +export type UserCreateVariables = { + protocol: string + endpoint: string + serviceApiKey: string + user: { + email: string + password: string + autoConfirmUser: string + } +} + +export async function createUser({ protocol, endpoint, serviceApiKey, user }: UserCreateVariables) { + const response = await post( + `${protocol}://${endpoint}/auth/v1/admin/users`, + { + email: user.email, + password: user.password, + email_confirm: user.autoConfirmUser, + }, + { + headers: { + apikey: serviceApiKey, + Authorization: `Bearer ${serviceApiKey}`, + }, + credentials: 'omit', + } + ) + if (response.error) { + throw response.error + } + + return response +} + +type UserCreateData = Awaited> + +export const useUserCreateMutation = ( + options: Omit, 'mutationFn'> = {} +) => { + return useMutation( + (vars) => createUser(vars), + options + ) +} From c0fd3974796d07f1c23d28550313e990ade413ef Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Wed, 15 Feb 2023 15:33:43 +0800 Subject: [PATCH 005/351] feat: add social tracking. --- apps/www/supabase/functions/_shared/cors.ts | 4 + .../functions/launchweek-ticket-og/README.md | 21 ++ .../launchweek-ticket-og/handler.tsx | 242 ++++++++++++++++++ .../functions/launchweek-ticket-og/index.ts | 11 + apps/www/www.code-workspace | 18 ++ 5 files changed, 296 insertions(+) create mode 100644 apps/www/supabase/functions/_shared/cors.ts create mode 100644 apps/www/supabase/functions/launchweek-ticket-og/README.md create mode 100644 apps/www/supabase/functions/launchweek-ticket-og/handler.tsx create mode 100644 apps/www/supabase/functions/launchweek-ticket-og/index.ts create mode 100644 apps/www/www.code-workspace diff --git a/apps/www/supabase/functions/_shared/cors.ts b/apps/www/supabase/functions/_shared/cors.ts new file mode 100644 index 00000000000..2ac4d89b14a --- /dev/null +++ b/apps/www/supabase/functions/_shared/cors.ts @@ -0,0 +1,4 @@ +export const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +} diff --git a/apps/www/supabase/functions/launchweek-ticket-og/README.md b/apps/www/supabase/functions/launchweek-ticket-og/README.md new file mode 100644 index 00000000000..260f2a6457d --- /dev/null +++ b/apps/www/supabase/functions/launchweek-ticket-og/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/launchweek-ticket-og?username=thorwebdev + +## Run locally + +```bash +supabase functions serve launchweek-ticket-og --no-verify-jwt +``` + +Navigate to http://localhost:54321/functions/v1/launchweek-ticket-og?username=thorwebdev + +## Deploy + +```bash +supabase functions deploy launchweek-ticket-og --no-verify-jwt +``` diff --git a/apps/www/supabase/functions/launchweek-ticket-og/handler.tsx b/apps/www/supabase/functions/launchweek-ticket-og/handler.tsx new file mode 100644 index 00000000000..c35b8b8cd3b --- /dev/null +++ b/apps/www/supabase/functions/launchweek-ticket-og/handler.tsx @@ -0,0 +1,242 @@ +import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0' +import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1' +import { corsHeaders } from '../_shared/cors.ts' + +const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw6' +const BACKGROUND_IMAGE_STD = `${STORAGE_URL}/lw6_ticket_regular.png?prod=true&v=2` +const BACKGROUND_IMAGE_GOLDEN = `${STORAGE_URL}/lw6_ticket_gold.png?prod=true&v=2` +const SUPA_CHECKMARK = `${STORAGE_URL}/supaverified.png?prod=true&v=2` +const SUPA_CHECKMARK_GOLD = `${STORAGE_URL}/supaverified_gold.png?prod=true&v=2` + +// Load custom font +const FONT_URL = `${STORAGE_URL}/CircularStd-Book.otf?prod=true&v=2` +const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer()) + +export async function handler(req: Request) { + const url = new URL(req.url) + const username = url.searchParams.get('username') ?? url.searchParams.get('amp;username') + const userAgent = req.headers.get('user-agent') + + try { + if (!username) throw new Error('missing username param') + // Track social shares + const supabaseAdminClient = createClient( + // Supabase API URL - env var exported by default when deployed. + Deno.env.get('SUPABASE_URL') ?? '', + // Supabase API SERVICE ROLE KEY - env var exported by default when deployed. + Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' + ) + if (userAgent?.toLocaleLowerCase().includes('twitter')) { + const { error } = await supabaseAdminClient + .from('lw6_tickets') + .update({ sharedOnTwitter: true }) + .eq('username', username) + if (error) console.log(error.message) + } else if (userAgent?.toLocaleLowerCase().includes('linkedin')) { + const { error } = await supabaseAdminClient + .from('lw6_tickets') + .update({ sharedOnLinkedIn: true }) + .eq('username', username) + if (error) console.log(error.message) + } + + // Try to get image from Supabase Storage CDN. + const storageResponse = await fetch(`${STORAGE_URL}/tickets/${username}.png?prod=true&v=5`) + if (storageResponse.ok) return storageResponse + + // Get ticket data + const { data, error } = await supabaseAdminClient + .from('lw6_tickets_golden') + .select('name, ticketNumber, golden') + .eq('username', username) + .maybeSingle() + if (error) throw error + + if (!data?.ticketNumber) throw new Error('missing data') + const { name, ticketNumber } = data + const golden = data?.golden ?? false + + // Else, generate image ad upload to storage. + const fontData = await font + const numDigits = `${Number(ticketNumber)}`.length + const prefix = `00000000`.slice(numDigits) + + const generatedImage = new ImageResponse( + ( + <> +
+ {/* Background image */} + + {/* GitHub Avatar image */} + + {/* Name & username */} +
+
+

+ {name ?? username} +

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

+ December 12th 2022 +

+ {/* URL */} +

+ supabase.com/launch-week +

+
+ {/* Ticket No */} +
+

+ {`No ${prefix}${ticketNumber}`} +

+
+ + ), + { + width: 1200, + height: 630, + fonts: [ + { + name: 'Circular', + data: fontData, + style: 'normal', + }, + ], + headers: { + 'content-type': 'image/png', + 'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable', + 'cdn-cache-control': 'max-age=31536000', + }, + } + ) + + // Upload image to storage. + const { error: storageError } = await supabaseAdminClient.storage + .from('images') + .upload(`lw6/tickets/${username}.png`, generatedImage.body!, { + contentType: 'image/png', + cacheControl: '31536000', + upsert: false, + }) + if (storageError) throw storageError + + return generatedImage + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 400, + }) + } +} diff --git a/apps/www/supabase/functions/launchweek-ticket-og/index.ts b/apps/www/supabase/functions/launchweek-ticket-og/index.ts new file mode 100644 index 00000000000..7da9ffc001b --- /dev/null +++ b/apps/www/supabase/functions/launchweek-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.140.0/http/server.ts' + +import { handler } from './handler.tsx' + +console.log(`Function "launchweek-ticket-og" up and running!`) + +serve(handler) diff --git a/apps/www/www.code-workspace b/apps/www/www.code-workspace new file mode 100644 index 00000000000..1b5f7b7d4a1 --- /dev/null +++ b/apps/www/www.code-workspace @@ -0,0 +1,18 @@ +{ + "folders": [ + { + "name": "www", + "path": "./" + }, + { + "name": "supabase-functions", + "path": "supabase/functions" + } + ], + "settings": { + "files.exclude": { + "node_modules/": true, + "supabase/functions/": true + } + } +} From 0d5a5b750195a28e0052edc282c0e35f03e0499c Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Thu, 16 Feb 2023 13:27:40 +0800 Subject: [PATCH 006/351] fix: issue with detecting session on page load. --- .../components/LaunchWeek/Ticket/ticket.tsx | 7 ++- apps/www/pages/launch-week/index.tsx | 48 ++++++++++++------- apps/www/pages/launch-week/tickets/index.tsx | 44 ++++++++++++----- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/apps/www/components/LaunchWeek/Ticket/ticket.tsx b/apps/www/components/LaunchWeek/Ticket/ticket.tsx index c494a9652ed..86f76f97a8b 100644 --- a/apps/www/components/LaunchWeek/Ticket/ticket.tsx +++ b/apps/www/components/LaunchWeek/Ticket/ticket.tsx @@ -90,8 +90,11 @@ export default function Ticket({

) : golden ? ( <> - You got a Golden ticket. This means you’re in, and you also won a Supabase sticker - pack! +

+ You got a Golden ticket. This means you’re in, and you also won a Supabase sticker + pack! +

+

Referrals: {referrals}

) : username ? ( <> diff --git a/apps/www/pages/launch-week/index.tsx b/apps/www/pages/launch-week/index.tsx index 10cddcf4113..b6b041b51c9 100644 --- a/apps/www/pages/launch-week/index.tsx +++ b/apps/www/pages/launch-week/index.tsx @@ -6,7 +6,7 @@ import _days from '~/components/LaunchWeek/lw6_days.json' import DefaultLayout from '~/components/Layouts/Default' import SectionContainer from '~/components/Layouts/SectionContainer' -import { createClient, Session } from '@supabase/supabase-js' +import { createClient, Session, SupabaseClient } from '@supabase/supabase-js' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import TicketContainer from '~/components/LaunchWeek/Ticket/TicketContainer' @@ -40,10 +40,7 @@ export default function launchweek() { const description = 'Supabase Launch Week 6 | 12-18 Dec 2022' const liveDay = null - 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 [creators, setCreators] = useState([]) const [activeCreator, setActiveCreator] = useState(null) @@ -57,13 +54,30 @@ export default function launchweek() { } useEffect(() => { - supabase.auth.onAuthStateChange((event, session) => { - setSession(session) - }) - - getCreators() + if (!supabase) { + setSupabase( + createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + ) + ) + } }, []) + useEffect(() => { + if (supabase) { + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((event, session) => { + setSession(session) + }) + + getCreators() + + return () => subscription.unsubscribe() + } + }, [supabase]) + useEffect(() => { document.body.className = isDarkMode ? 'dark bg-[#121212]' : 'light bg-[#fff]' }, [isDarkMode]) @@ -1259,12 +1273,14 @@ export default function launchweek() { {process.env.NEXT_PUBLIC_LW_STARTED && (
- + {supabase && ( + + )}
)}
diff --git a/apps/www/pages/launch-week/tickets/index.tsx b/apps/www/pages/launch-week/tickets/index.tsx index def343e0796..ced16c72c87 100644 --- a/apps/www/pages/launch-week/tickets/index.tsx +++ b/apps/www/pages/launch-week/tickets/index.tsx @@ -4,16 +4,14 @@ import SectionContainer from '~/components/Layouts/SectionContainer' import TicketContainer from '~/components/LaunchWeek/Ticket/TicketContainer' import { SITE_URL } from '~/lib/constants' import { useRouter } from 'next/router' -import { createClient, Session } from '@supabase/supabase-js' +import { createClient, Session, SupabaseClient } from '@supabase/supabase-js' import { useState, useEffect } from 'react' import { useTheme } from '~/components/Providers' export default function TicketHome() { const { isDarkMode } = useTheme() - 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 description = 'Supabase Launch Week 6 | 12-16 Dec 2022' const { query } = useRouter() @@ -26,11 +24,29 @@ export default function TicketHome() { } useEffect(() => { - supabase.auth.onAuthStateChange((event, supaSession) => { - setSession(supaSession) - }) + if (!supabase) { + setSupabase( + createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + ) + ) + } }, []) + useEffect(() => { + if (supabase) { + supabase.auth.getSession().then(({ data: { session } }) => setSession(session)) + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((event, session) => { + setSession(session) + }) + + return () => subscription.unsubscribe() + } + }, [supabase]) + useEffect(() => { document.body.className = isDarkMode ? 'dark bg-[#121212]' : 'light bg-[#fff]' }, [isDarkMode]) @@ -64,12 +80,14 @@ export default function TicketHome() {

Dec 12 – 16 at 8 AM PT | 11 AM ET

- + {supabase && ( + + )} From cd8cd9b975ac16d53ff4b0155eb5c3eef5c06c4a Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 21 Feb 2023 16:41:47 -0330 Subject: [PATCH 007/351] Move lw6 to subdir --- apps/www/components/Nav/index.tsx | 38 +- apps/www/pages/launch-week/6/index.tsx | 1243 ++++++++++++++++ .../6/styles/launchWeek6.module.css | 25 + .../launch-week/6/styles/utils6.module.css | 76 + apps/www/pages/launch-week/6/types6.d.ts | 43 + apps/www/pages/launch-week/index.tsx | 1253 +---------------- .../pages/launch-week/launchWeek.module.css | 26 +- apps/www/public/rss.xml | 310 ++-- 8 files changed, 1607 insertions(+), 1407 deletions(-) create mode 100644 apps/www/pages/launch-week/6/index.tsx create mode 100644 apps/www/pages/launch-week/6/styles/launchWeek6.module.css create mode 100644 apps/www/pages/launch-week/6/styles/utils6.module.css create mode 100644 apps/www/pages/launch-week/6/types6.d.ts diff --git a/apps/www/components/Nav/index.tsx b/apps/www/components/Nav/index.tsx index 387aefacc5d..edbfd03169f 100644 --- a/apps/www/components/Nav/index.tsx +++ b/apps/www/components/Nav/index.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react' import Link from 'next/link' +import { useRouter } from 'next/router' import { Button, Badge, IconStar, IconChevronDown } from 'ui' import FlyOut from '~/components/UI/FlyOut' @@ -19,6 +20,8 @@ import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-w const Nav = () => { const { isDarkMode } = useTheme() + const { pathname } = useRouter() + const isLauchWeekPage = pathname.includes('launch-week') const [open, setOpen] = useState(false) const [openProduct, setOpenProduct] = useState(false) @@ -160,6 +163,7 @@ const Nav = () => { px-1 text-sm font-medium transition-colors + ${isLauchWeekPage ? '!text-white' : ''} ` + props.active } onClick={props.onClick} @@ -172,7 +176,11 @@ const Nav = () => { (props.active && ' rotate-180 transform transition-all duration-100') } > - +
@@ -182,8 +190,16 @@ const Nav = () => { <> {/* */}
-
-