damus.io

damus.io website
git clone git://jb55.com/damus.io
Log | Files | Refs | README | LICENSE

commit ac86a41c0786c8cea6f930faf2875380a1f6f91c
parent ae285a22f40600aa8642fd9c5e9acd69576dcdcc
Author: Daniel D’Aquino <daniel@daquino.me>
Date:   Sat, 30 Dec 2023 00:01:52 -0800

Damus Purple landing page first draft

Diffstat:
Apublic/logo-icon-dark.png | 0
Apublic/stars-bg.png | 0
Apublic/stars-bg.webp | 0
Asrc/components/pages/purple.tsx | 35+++++++++++++++++++++++++++++++++++
Asrc/components/sections/PurpleBenefits.tsx | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/sections/PurpleHero.tsx | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/ui/RoundedContainerWithGradientBorder.tsx | 16++++++++++++++--
Asrc/pages/purple/index.tsx | 41+++++++++++++++++++++++++++++++++++++++++
Mtailwind.config.ts | 3+++
9 files changed, 217 insertions(+), 2 deletions(-)

diff --git a/public/logo-icon-dark.png b/public/logo-icon-dark.png Binary files differ. diff --git a/public/stars-bg.png b/public/stars-bg.png Binary files differ. diff --git a/public/stars-bg.webp b/public/stars-bg.webp Binary files differ. diff --git a/src/components/pages/purple.tsx b/src/components/pages/purple.tsx @@ -0,0 +1,35 @@ +import Image from 'next/image' +import { Inter } from 'next/font/google' +import { PurpleHero } from '@/components/sections/PurpleHero' +import Head from "next/head"; +import { useIntl } from 'react-intl' +import { BannedInChina } from '@/components/sections/BannedInChina' +import { DamusOnMedia } from '@/components/sections/DamusOnMedia'; +import { MeetTheTeam } from '@/components/sections/MeetTheTeam'; +import { DamusAroundTheWorld } from '@/components/sections/DamusAroundTheWorld'; +import { Footer } from '@/components/sections/Footer'; +import { DamusLiveEvents } from '@/components/sections/DamusLiveEvents'; +import { Contribute } from '@/components/sections/Contribute'; +import { FinalCTA } from '@/components/sections/FinalCTA'; +import { Benefits } from '@/components/sections/Benefits'; +import { PurpleBenefits } from '../sections/PurpleBenefits'; + +const inter = Inter({ subsets: ['latin'] }) + +export function Purple() { + const intl = useIntl() + + return (<> + <Head> + <title>Damus</title> + <meta name="description" content={intl.formatMessage({ id: "home.meta_description", defaultMessage: "Damus is a new social network that you control. Available now on iOS, iPad and macOS (M1/M2)" })} /> + <meta name="apple-itunes-app" content="app-id=1628663131" /> + </Head> + <main style={{ scrollBehavior: "smooth" }}> + <PurpleHero /> + <PurpleBenefits/> + <FinalCTA /> + <Footer /> + </main> + </>) +} diff --git a/src/components/sections/PurpleBenefits.tsx b/src/components/sections/PurpleBenefits.tsx @@ -0,0 +1,68 @@ +import { MeshGradient2 } from "../effects/MeshGradient.2"; +import { Button } from "../ui/Button"; +import { FormattedMessage, useIntl } from "react-intl"; +import Link from "next/link"; +import { motion } from "framer-motion"; +import { RoundedContainerWithGradientBorder } from "../ui/RoundedContainerWithGradientBorder"; +import { cn } from "@/lib/utils"; +import Image from "next/image"; +import { Award, Badge, Heart, Joystick, KeyRound, Scale, Stars, Wallet } from "lucide-react"; +import { MeshGradient4 } from "../effects/MeshGradient.4"; +import { MeshGradient3 } from "../effects/MeshGradient.3"; + +export function PurpleBenefits({ className }: { className?: string }) { + const intl = useIntl() + + const benefits = [ + { + icon: <Heart className="h-12 w-12 text-white opacity-80"/>, + headline: intl.formatMessage({ id: "purple.benefits.benefit1.name", defaultMessage: "Help Build The Future" }), + description: intl.formatMessage({ id: "purple.benefits.benefit1.description", defaultMessage: "Support Damus development to help build the future of decentralized communication on the web." }), + }, + { + icon: <Stars className="text-white h-12 w-12 opacity-80"/>, + headline: intl.formatMessage({ id: "purple.benefits.benefit2.name", defaultMessage: "Exclusive features" }), + description: intl.formatMessage({ id: "purple.benefits.benefit2.description", defaultMessage: "Be the first to access upcoming premium features: Automatic translations, longer note storage, and more" }), + }, + { + icon: <Award className="h-12 w-12 text-white opacity-80"/>, + headline: intl.formatMessage({ id: "purple.benefits.benefit3.name", defaultMessage: "Supporter Badge" }), + description: intl.formatMessage({ id: "purple.benefits.benefit3.description", defaultMessage: "Get a special badge on your profile to show everyone your contribution to Freedom tech" }), + }, + ] + + return (<> + <div className={cn("bg-black overflow-hidden relative", className)}> + <MeshGradient3 className="absolute top-0 left-0 pointer-events-none translate-y-3/4 overflow-visible scale-150"/> + <div className="container mx-auto px-6 pb-32 pt-20"> + <div className="flex flex-col items-center justify-center mt-32 lg:mt-16"> + <div className="relative mb-32 flex flex-col items-center"> + <motion.h2 className="text-4xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold pb-8 break-keep"> + { intl.formatMessage({ id: "purple.benefits.headline", defaultMessage: "Get more from Damus." }) } + </motion.h2> + <motion.div className="text-white/60 text-xl text-center max-w-2xl mb-6 mt-6 break-keep"> + { intl.formatMessage({ id: "purple.benefits.description", defaultMessage: "Help us stay independent in our mission for Freedom tech with our Purple subscription, and look cool doing it!" }) } + </motion.div> + </div> + {(intl.locale != "ja" || process.env.FORCE_LOAD_ALL_JA_SECTIONS) && ( + <div className="flex flex-wrap gap-x-8 gap-y-16 items-stretch justify-center"> + {benefits.map((item, index) => ( + <div key={index} className="max-w-xs flex flex-col items-center justify-between"> + <RoundedContainerWithGradientBorder className=""> + {item.icon} + </RoundedContainerWithGradientBorder> + <h3 className="text-xl font-semibold text-transparent bg-clip-text bg-gradient-to-br from-white from-30% to-cyan-200 to-100% text-center text-normal mt-6"> + {item.headline} + </h3> + <p className="text-white/80 text-center text-normal mt-4"> + {item.description} + </p> + </div> + ))} + </div> + )} + </div> + </div> + </div> + </>) +} diff --git a/src/components/sections/PurpleHero.tsx b/src/components/sections/PurpleHero.tsx @@ -0,0 +1,56 @@ +import { ArrowUpRight, ChevronRight, Globe2 } from "lucide-react"; +import { MeshGradient1 } from "../effects/MeshGradient.1"; +import { TopMenu } from "./TopMenu"; +import { Button } from "../ui/Button"; +import { FormattedMessage, useIntl } from "react-intl"; +import Link from "next/link"; +import { DAMUS_APP_STORE_URL, DAMUS_TESTFLIGHT_URL } from "@/lib/constants"; +import { motion } from "framer-motion"; +import Image from "next/image"; +import { RoundedContainerWithColorGradientBorder } from "../ui/RoundedContainerWithGradientBorder"; + +export function PurpleHero() { + const intl = useIntl() + + return (<> + <div + className="bg-black overflow-hidden relative min-h-screen" + > + <div className="absolute z-0 w-full h-full pointer-events-none"> + <Image src="/stars-bg.webp" fill className="absolute top-0 left-0 object-contain object-center w-full h-full" alt="" aria-hidden="true" /> + <MeshGradient1 className="-translate-x-1/3" /> + </div> + <div className="container z-10 mx-auto px-6 pb-32 pt-12"> + <TopMenu className="w-full" /> + <div className="relative mb-32 flex flex-col items-center justify-center min-h-screen"> + <div className="flex gap-x-4 items-center mb-6"> + <RoundedContainerWithColorGradientBorder className="w-24 h-24 p-[1px]"> + <Image src="logo-icon-dark.png" fill className="overflow-hidden w-full h-full object-fill shadow-xl rounded-2xl" alt="Damus Purple logo" /> + </RoundedContainerWithColorGradientBorder> + <motion.h2 className="text-4xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold break-keep tracking-tight"> + Purple + </motion.h2> + </div> + <motion.div + className="mt-10 md:mt-6 flex flex-col md:flex-row items-center md:items-center gap-y-4 gap-x-6" + style={{ opacity: 0 }} + animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }} + > + <Link href={DAMUS_APP_STORE_URL} target="_blank"> + <Button variant="default" className="w-full md:w-auto"> + {intl.formatMessage({ id: "purple.hero.subscribe", defaultMessage: "Subscribe" })} + <ArrowUpRight className="ml-2" /> + </Button> + </Link> + <Link href={DAMUS_TESTFLIGHT_URL} target="_blank"> + <Button variant="link" className="w-full md:w-auto"> + {intl.formatMessage({ id: "purple.hero.learn-more", defaultMessage: "Learn more" })} + <ArrowUpRight className="text-damuspink-600 ml-2" /> + </Button> + </Link> + </motion.div> + </div> + </div> + </div> + </>) +} diff --git a/src/components/ui/RoundedContainerWithGradientBorder.tsx b/src/components/ui/RoundedContainerWithGradientBorder.tsx @@ -11,4 +11,17 @@ export function RoundedContainerWithGradientBorder({ className, allItemsClassNam </div> </div> ) -}- \ No newline at end of file +} + +export function RoundedContainerWithColorGradientBorder({ className, allItemsClassName, children }: { className?: string, allItemsClassName?: string, children: React.ReactNode }) { + return ( + <div className={cn("relative w-24 h-24 flex justify-center items-center backdrop-blur-sm", allItemsClassName, className)}> + <div className="z-10 relative p-6 w-full h-full"> + {children} + </div> + <div className={cn("absolute z-0 w-full h-full p-[1px] rounded-2xl bg-gradient-to-br from-damuspink-500 via-damuspink-500/20 to-deeppurple-700 opacity-40 shadow-lg", allItemsClassName)}> + <div className={cn("w-full h-full flex justify-center items-center rounded-2xl bg-gray-900", allItemsClassName)}/> + </div> + </div> + ) +} diff --git a/src/pages/purple/index.tsx b/src/pages/purple/index.tsx @@ -0,0 +1,41 @@ +import { Inter } from 'next/font/google' +import { IntlProvider, useIntl } from 'react-intl' +import English from "@/../content/compiled-locales/en.json"; +import Japanese from "@/../content/compiled-locales/ja.json"; +import { useEffect } from 'react'; +import { useState } from 'react'; +import { Purple } from '@/components/pages/purple'; + +export default function HomePage() { + // Automatically detect the user's locale based on their browser settings + const [language, setLanguage] = useState("en"); + const [messages, setMessages] = useState(English); + + useEffect(() => { + setLanguage(navigator.language); + }, []); + + useEffect(() => { + switch (language) { + case "en": + setMessages(English); + break; + case "ja": + setMessages(Japanese); + break; + default: + setMessages(English); + break; + } + }, [language]); + + return (<> + <IntlProvider + locale={language} + messages={messages} + onError={() => null}> + <Purple /> + </IntlProvider> + </>) +} + diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -21,6 +21,9 @@ const config: Config = { 600: "#D34CD9", 500: "#F869B6", }, + "deeppurple": { + 700: "#BF25ED", + } }, }, plugins: [],