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 all 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.
127 changes: 127 additions & 0 deletions dapp/src/components/shared/SeasonCountdownSection/CountdownTimer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
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"
import { Tuple } from "#/types"

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: Tuple<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)

const parsedCountdown = useMemo(
() =>
Object.entries(countdown).reduce<[string, Tuple<number>][]>(
(accumulator, currentValue) => {
const [key, stringValue] = currentValue

if (key === "seconds") return accumulator

const value = +stringValue
const parsedValue = (
value > 0
? Array.from(value.toString().padStart(2, "0")).map(Number)
: Array(2).fill(0)
) as Tuple<number>

return [...accumulator, [key, parsedValue]]
},
[],
),
[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 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"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we don't need to nest these components in the components directory. I have a feeling that this may cause some confusion. Please check this thread. I believe that if the LandingPage folder grows we will think about some refactoring on structure.

File renamed without changes.
45 changes: 45 additions & 0 deletions dapp/src/pages/LandingPage/components/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, 30).getTime() / 1000
Copy link
Contributor

Choose a reason for hiding this comment

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

We can do it later but just flagging. We would probably want to save this as an env variable. Then save this value in constants.

VITE_SEASON_START_DATE="2024-03-30"

Example solution for VITE_REFERRAL:

export const REFERRAL = import.meta.env.VITE_REFERRAL


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>
Comment on lines +24 to +26
Copy link
Contributor

Choose a reason for hiding this comment

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

We would probably want to use an already prepared component for this. I know that style guide is not yet ready but I believe that it will make it easier for us to standardize later.

Suggested change
<Heading fontSize="5xl" fontWeight="bold" mb={4}>
Season 1. Pre-launch staking
</Heading>
<H3 fontWeight="bold" mb={4}>
Season 1. Pre-launch staking
</H3>

<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>
Comment on lines +27 to +30
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar situation as above.

Suggested change
<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>
<TextLg mb={10}>
Season 1 users that stake bitcoin before Acre launches earn the <br />
highest rewards and first access to upcoming Seasons.
</TextLg>

<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>
)
}
2 changes: 2 additions & 0 deletions dapp/src/pages/LandingPage/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as HeroSection } from "./HeroSection"
export { default as SeasonCountdownSection } from "./SeasonCountdownSection"
10 changes: 7 additions & 3 deletions dapp/src/pages/LandingPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import React from "react"
import { Flex } from "@chakra-ui/react"
import HeroSection from "./HeroSection"
import {
SeasonCountdownSection,
HeroSection,
} from "#/pages/LandingPage/components"

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