Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Landing page's SeasonCountdownSection component #368

Merged
merged 22 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ea11fbb
Implement `SeasonCountdownSection` background
kpyszkowski Apr 16, 2024
322644a
Implement `SeasonCountdownSection`'s typography
kpyszkowski Apr 16, 2024
ff59bb6
Implement `CountdownTimer` component
kpyszkowski Apr 16, 2024
61a9503
Update export directive
kpyszkowski Apr 16, 2024
f60dfb8
Adjust `CountdownTimer` animation`
kpyszkowski Apr 16, 2024
076d2ce
Adjust background parallax offset boundaries
kpyszkowski Apr 16, 2024
e962925
Remove `Tag` shadow
kpyszkowski Apr 17, 2024
a4ba563
Recompose component structure
kpyszkowski Apr 17, 2024
e958c8e
Merge branch 'landing-page' into landing-season-countdown-section
kpyszkowski Apr 18, 2024
102a2d8
Merge branch 'landing-page' into landing-season-countdown-section
kpyszkowski Apr 18, 2024
2df35a3
Adjust spacing and height
kpyszkowski Apr 18, 2024
d87bc51
Merge branch 'landing-page' into landing-season-countdown-section
kpyszkowski Apr 19, 2024
7f39cce
Disable text selection on timer component
kpyszkowski Apr 19, 2024
b876889
Fix timer `NaN` value when timestamp expires
kpyszkowski Apr 22, 2024
32db87b
Resolve resize glitch
kpyszkowski Apr 23, 2024
bff469a
Merge branch 'landing-page' into landing-season-countdown-section
kpyszkowski Apr 23, 2024
1087104
Resolve Eslint error
kpyszkowski Apr 23, 2024
789bcac
Adjust width and horizontal content flow
kpyszkowski Apr 23, 2024
eb3cb02
Resolve Eslint error
kpyszkowski Apr 23, 2024
a26e6ea
Restructure `LandingPage`'s components
kpyszkowski Apr 25, 2024
5796c8b
Resolve ESLint error
kpyszkowski Apr 26, 2024
99ba8fb
Resolve `CountdownTimer` component bug
kpyszkowski Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions dapp/src/components/shared/SeasonCountdownSection/CountdownTimer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useMemo } from "react"
import { useCountdown } from "#/hooks"
import {
BoxProps,
HStack,
Grid,
Box,
Text,
TextProps,
StackProps,
} from "@chakra-ui/react"
import { AnimatePresence, motion } from "framer-motion"

const MotionBox = motion(Box)

type CountdownTimerDigitProps = Omit<BoxProps, "children"> & {
children: number
}
function CountdownTimerDigit(props: CountdownTimerDigitProps) {
const { children, ...restProps } = props
return (
<MotionBox
px={5}
w={14}
py={4}
rounded="xl"
color="gold.200"
bg="grey.700"
overflow="hidden"
initial={{ opacity: 0 }}
animate={{ opacity: 1, transition: { staggerChildren: 0.1 } }}
{...restProps}
>
<AnimatePresence mode="popLayout">
<motion.div
key={children}
initial={{ y: -32, rotateX: -90, opacity: 1 }}
animate={{ y: 0, rotateX: 0, opacity: 1 }}
exit={{ y: 32, rotateX: 90, opacity: 0 }}
transition={{ type: "spring", mass: 1.05 }}
>
{children}
</motion.div>
</AnimatePresence>
</MotionBox>
)
}

function CountdownTimerSegmentLabel(props: TextProps) {
return (
<Text
color="white"
fontSize="md"
fontWeight="medium"
lineHeight={5}
gridColumn="1/3"
textTransform="capitalize"
{...props}
/>
)
}

type CountdownTimerSegmentProps = Omit<BoxProps, "children"> & {
label: string
value: [number, number]
}
function CountdownTimerSegment(props: CountdownTimerSegmentProps) {
const { label, value, ...restProps } = props
return (
<HStack
spacing={2}
alignItems="baseline"
color="gold.200"
fontSize="2xl"
fontWeight="bold"
_notLast={{
_after: {
content: '":"',
},
}}
>
<Grid templateColumns="repeat(2, 1fr)" gap={2} {...restProps}>
<CountdownTimerDigit>{value[0]}</CountdownTimerDigit>
<CountdownTimerDigit>{value[1]}</CountdownTimerDigit>
<CountdownTimerSegmentLabel>{label}</CountdownTimerSegmentLabel>
</Grid>
</HStack>
)
}

type CountdownTimerProps = Omit<StackProps, "children"> & {
timestamp: number
}
export function CountdownTimer(props: CountdownTimerProps) {
const { timestamp, ...restProps } = props
const countdown = useCountdown(timestamp, true)

const parsedCountdown = useMemo<[string, [number, number]][]>(
() =>
Object.entries(countdown)
.filter(([key]) => key !== "seconds")
.map(([key, value]) => [
key,
[...value].map((x) => +x || 0) as [number, number],
]),
[countdown],
)
return (
<HStack spacing={2} userSelect="none" {...restProps}>
{parsedCountdown.map(([label, value]) => (
<CountdownTimerSegment key={label} label={label} value={value} />
))}
</HStack>
)
}
45 changes: 45 additions & 0 deletions dapp/src/components/shared/SeasonCountdownSection/LiveTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react"
import { TagProps, Tag, TagLabel, Box } from "@chakra-ui/react"
import { motion } from "framer-motion"

const MotionBox = motion(Box)

export function LiveTag(props: TagProps) {
return (
<Tag
px={4}
py={2}
rounded="3xl"
bg="grey.700"
variant="solid"
pos="relative"
{...props}
>
<Box rounded="full" w={2} h={2} mr={3} bg="brand.400" />
<MotionBox
pos="absolute"
rounded="full"
w={2}
h={2}
bg="brand.400"
animate={{ scale: [1, 6], opacity: [0.5, 0] }}
transition={{
duration: 2,
ease: "easeInOut",
repeat: Infinity,
}}
/>
<TagLabel
color="gold.200"
textTransform="uppercase"
fontStyle="italic"
overflow="visible"
fontSize="md"
lineHeight={5}
fontWeight="bold"
>
Live
</TagLabel>
</Tag>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { useRef } from "react"
import { Box, BoxProps } from "@chakra-ui/react"
import { useSize } from "@chakra-ui/react-use-size"
import seasonCountdownBackground from "#/assets/images/season-countdown-section-background.png"
import seasonCountdownForeground from "#/assets/images/season-countdown-section-foreground.png"
import {
MotionValue,
motion,
useScroll,
useSpring,
useTransform,
useTime,
wrap,
} from "framer-motion"

export function SeasonCountdownSectionBackground(props: BoxProps) {
const containerRef = useRef(null)
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["center start", "start end"],
})
const smoothScrollYProgress = useSpring(scrollYProgress, {
damping: 10,
stiffness: 90,
mass: 0.75,
}) as MotionValue<number>
const foregroundParallax = useTransform(
smoothScrollYProgress,
[0, 1],
["25%", "65%"],
)
const time = useTime()
// Seed value is wrapped to prevent infinite increment causing potential memory leaks
const seed = useTransform(time, (value) => wrap(0, 2137, Math.floor(value)))

const size = useSize(containerRef)

return (
<Box as="svg" ref={containerRef} w="full" h="full" rounded="2xl" {...props}>
<defs>
<filter
id="noise-filter"
x="-20%"
y="-20%"
width="140%"
height="140%"
filterUnits="objectBoundingBox"
primitiveUnits="userSpaceOnUse"
colorInterpolationFilters="linearRGB"
>
<motion.feTurbulence
type="fractalNoise"
baseFrequency="0.119"
numOctaves="4"
seed={seed}
stitchTiles="stitch"
x="0%"
y="0%"
width="100%"
height="100%"
result="turbulence"
/>
<feSpecularLighting
surfaceScale="4"
specularConstant="3"
specularExponent="20"
lightingColor="#0600ff"
x="0%"
y="0%"
width="100%"
height="100%"
in="turbulence"
result="specularLighting"
>
<feDistantLight azimuth="3" elevation="107" />
</feSpecularLighting>
<feColorMatrix
type="saturate"
values="0"
x="0%"
y="0%"
width="100%"
height="100%"
in="specularLighting"
result="colormatrix"
/>
</filter>
</defs>
<Box
as="rect"
x="0"
y="0"
style={{ width: size?.width }}
h="full"
fill="brand.400"
/>
<Box as="g" mixBlendMode="overlay">
<Box
as="image"
href={seasonCountdownBackground}
style={{ width: size?.width }}
h="full"
preserveAspectRatio="xMinYMin slice"
/>
<Box
as={motion.image}
href={seasonCountdownForeground}
w="full"
y={foregroundParallax}
preserveAspectRatio="xMinYMin slice"
/>
</Box>
<Box
as="rect"
mixBlendMode="soft-light"
// TOOD: Investigate width update delay
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to address it in a separate PR? Or maybe it's already addressed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a barely noticeable issue. Most of ppl won't even notice.
I gave it a shot and it looks like we can't do much with it. It's browser specific and depends on how the browser (Chromium in Ledger Live's case) processes SVG transformations.
Let's leave it for possible future consideration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to spend more time on the investigation in the future let's create an issue and add it to the list at #154 EPIC.
If there is nothing more we can do let's live with it and remove the TODO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

style={{ width: size?.width }}
h="full"
fill="#0600ff"
filter="url(#noise-filter)"
opacity={0.64}
/>
</Box>
)
}
3 changes: 3 additions & 0 deletions dapp/src/components/shared/SeasonCountdownSection/index.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider moving the SeasonCountdownSection dir to the pages/LandingPages/components because it is rather not a shared component but specific for landing page.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ref commit: a26e6ea

Copy link
Contributor

@kkosiorowska kkosiorowska May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it should be in the pages/LandingPages directory but I am not convinced about nesting it in the components directory. More info here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to change the name of this directory. More information here. SeasonCountdownSection as a directory has no exports and likely will never export a component with this name. If we can let's use lowercasing directory names like this that are purely categorical.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./LiveTag"
export * from "./CountdownTimer"
export * from "./SeasonCountdownSectionBackground"
45 changes: 45 additions & 0 deletions dapp/src/pages/LandingPage/SeasonCountdownSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react"
import { Box, VStack, Heading, Text } from "@chakra-ui/react"

import {
LiveTag,
SeasonCountdownSectionBackground,
CountdownTimer,
} from "#/components/shared/SeasonCountdownSection"

const MOCK_SEASON_DUE_TIMESTAMP = new Date(2024, 3, 20).getTime() / 1000

export default function SeasonCountdownSection() {
return (
<Box display="grid" sx={{ ">*": { gridArea: "-1 / -1" } }}>
<VStack
spacing={0}
px={10}
pt={15}
pb={20}
textAlign="center"
color="white"
>
<LiveTag mb={10} />
<Heading fontSize="5xl" fontWeight="bold" mb={4}>
Season 1. Pre-launch staking
</Heading>
<Text fontSize="lg" fontWeight="medium" mb={10}>
Season 1 users that stake bitcoin before Acre launches earn the <br />
highest rewards and first access to upcoming Seasons.
</Text>
<CountdownTimer timestamp={MOCK_SEASON_DUE_TIMESTAMP} />
</VStack>
<SeasonCountdownSectionBackground
pos="absolute"
left="50%"
translateX="-50%"
transform="auto"
w="calc(100% - 2 * 2.5rem)" // 100% - 2 * 40px
maxW="125rem" // 2000px
maxH="43rem" // 688px
zIndex={-1}
/>
</Box>
)
}
6 changes: 4 additions & 2 deletions dapp/src/pages/LandingPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React from "react"
import { Flex } from "@chakra-ui/react"
import SeasonCountdownSection from "./SeasonCountdownSection"
import HeroSection from "./HeroSection"

export default function LandingPage() {
return (
<Flex
w="full"
flexFlow="column"
px={10}
px={16} // 40px + 24px
pb={10}
maxW="100.625rem"
maxW="87.25rem" // 1268px + 2 * (40px + 24px)
mx="auto"
>
<HeroSection />
<SeasonCountdownSection />
</Flex>
Comment on lines -10 to 20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand what's going on here. 😅 I don't quite understand why we set maxW.

Why can't we do it like this? 🤔

    <Flex flexDirection="column">
      <HeroSection />
      <SeasonCountdownSection />
    </Flex>

)
}
Loading
Loading