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

Homepage announcements #21

Merged
merged 3 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 14 additions & 2 deletions app/(pages)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import HomePage from '../components/home/HomePage'
import { fetchAppStatsData, fetchMangoMarketData } from '../utils/mango'
import { fetchTokenPages } from '../../contentful/tokenPage'
import { draftMode } from 'next/headers'
import { fetchHomePageAnnouncements } from '../../contentful/homePageAnnouncement'

const metaTitle = 'Mango Markets | Safer. Smarter. Faster.'
const metaDescription =
Expand Down Expand Up @@ -35,14 +36,25 @@ async function Page() {
const tokensPromise = fetchTokenPages({
preview: draftMode().isEnabled,
})
const [appStatsData, markets, tokens] = await Promise.all([
const announcementsPromise = fetchHomePageAnnouncements({
preview: draftMode().isEnabled,
})
const [appStatsData, markets, tokens, announcements] = await Promise.all([
appStatsDataPromise,
marketsPromise,
tokensPromise,
announcementsPromise,
])

return (
<HomePage appStatsData={appStatsData} markets={markets} tokens={tokens} />
<div>
<HomePage
announcements={announcements}
appStatsData={appStatsData}
markets={markets}
tokens={tokens}
/>
</div>
)
}

Expand Down
57 changes: 57 additions & 0 deletions app/components/home/Announcement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Image from 'next/image'
import { HomePageAnnouncement } from '../../../contentful/homePageAnnouncement'
// import { ChevronRightIcon } from '@heroicons/react/20/solid'
import { ReactNode } from 'react'
import Link from 'next/link'

const AnnouncementWrapper = ({
children,
isExternal,
path,
}: {
children: ReactNode
isExternal: boolean
path: string
}) => {
const classNames =
'border border-th-bkg-4 py-3 px-4 rounded-lg flex items-center justify-between md:hover:bg-th-bkg-3'
return isExternal ? (
<a className={classNames} href={path} rel="noopener noreferrer">
{children}
</a>
) : (
<Link className={classNames} href={path} shallow>
{children}
</Link>
)
}

const Announcement = ({ data }: { data: HomePageAnnouncement }) => {
const { category, linkPath, description, image, title } = data
const imageSrc = image?.src
const imageAlt = image?.alt || 'CTA Image'
const isExtenalLink = linkPath.includes('http')
return (
<AnnouncementWrapper isExternal={isExtenalLink} path={linkPath}>
<span className="flex items-center space-x-3">
{imageSrc ? (
<Image
className="flex-shrink-0 rounded-full"
src={`https:${imageSrc}`}
alt={imageAlt}
height={48}
width={48}
/>
) : null}
<div>
<p className="mb-1 text-xs text-th-active leading-none">{category}</p>
<p className="text-th-fgd-2 text-sm block font-display">{title}</p>
<p className="text-sm block text-th-fgd-3">{description}</p>
</div>
</span>
{/* <ChevronRightIcon className="ml-3 h-6 w-6 text-th-fgd-4 flex-shrink-0" /> */}
</AnnouncementWrapper>
)
}

export default Announcement
112 changes: 109 additions & 3 deletions app/components/home/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {
ArrowPathRoundedSquareIcon,
BoltIcon,
BuildingLibraryIcon,
ChevronLeftIcon,
ChevronRightIcon,
CurrencyDollarIcon,
DevicePhoneMobileIcon,
FaceFrownIcon,
MegaphoneIcon,
NoSymbolIcon,
QuestionMarkCircleIcon,
RocketLaunchIcon,
Expand Down Expand Up @@ -43,6 +45,10 @@ import Link from 'next/link'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import SheenLoader from '../shared/SheenLoader'
import { HomePageAnnouncement } from '../../../contentful/homePageAnnouncement'
import Announcement from './Announcement'
import Slider from 'react-slick'
import { breakpoints, useViewport } from '../../hooks/useViewport'
dayjs.extend(relativeTime)

type Markets = {
Expand Down Expand Up @@ -72,10 +78,12 @@ const MOBILE_IMAGE_CLASSES =
'core-image h-[240px] w-[240px] sm:h-[300px] sm:w-[300px] md:h-[480px] md:w-[480px] mb-6 lg:mb-0'

const HomePage = ({
announcements,
appStatsData,
markets,
tokens,
}: {
announcements: HomePageAnnouncement[]
appStatsData: AppStatsData
markets: Markets
tokens: TokenPageWithData[]
Expand All @@ -85,6 +93,7 @@ const HomePage = ({
const swapPanel = useRef<HTMLDivElement>(null)
const coreFeatures = useRef<HTMLDivElement>(null)
const build = useRef<HTMLDivElement>(null)
const { width } = useViewport()
const numberOfMarkets =
(markets?.spot.length || 0) + (markets?.perp.length || 0)

Expand Down Expand Up @@ -276,9 +285,54 @@ const HomePage = ({
return () => ctx.revert() // <- Cleanup!
}, [])

const sliderSettings = {
arrows: false,
dots: false,
infinite: true,
slidesToShow: 3,
slidesToScroll: 1,
cssEase: 'linear',
responsive: [
{
breakpoint: breakpoints.xl,
settings: {
slidesToShow: 2,
},
},
{
breakpoint: breakpoints.lg,
settings: {
slidesToShow: 1,
},
},
],
}

const sliderRef = useRef<Slider | null>(null)

const nextSlide = () => {
if (sliderRef.current) {
sliderRef.current.slickNext()
}
}

const prevSlide = () => {
if (sliderRef.current) {
sliderRef.current.slickPrev()
}
}

const slides = width >= breakpoints.xl ? 3 : width >= breakpoints.lg ? 2 : 1
const showArrows = announcements?.length
? slides < announcements.length
: false

return (
<>
<SectionWrapper className="overflow-hidden h-[760px] lg:h-auto">
<SectionWrapper
className="overflow-hidden h-[760px] lg:h-[600px] lg:flex lg:items-center py-12 lg:py-0"
noPaddingY
>
<div className="grid grid-cols-12" ref={topSection}>
<div className="col-span-12 lg:col-span-5 mb-12 lg:mb-0 relative z-10">
<h1 className="mb-6 text-center lg:text-left">
Expand All @@ -299,7 +353,7 @@ const HomePage = ({
<div className="col-span-12 lg:col-span-7 relative h-full flex justify-center">
<BlackSphere />
<img
className="w-3/4 lg:w-full absolute h-auto lg:-right-40 lg:top-1/2 lg:transform lg:-translate-y-1/2 xl:right-0 xl:w-[740px]"
className="w-3/4 absolute h-auto lg:-right-40 lg:top-1/2 lg:transform lg:-translate-y-1/2 xl:right-0 lg:w-full xl:w-[740px]"
src="/images/new/trade-desktop.png"
alt=""
/>
Expand All @@ -311,6 +365,58 @@ const HomePage = ({
</div>
</div>
</SectionWrapper>
{announcements?.length ? (
<SectionWrapper
className="mt-0 lg:mt-6 py-6 bg-th-bkg-2 xl:rounded-xl px-6"
noPaddingY
noPaddingX={showArrows}
>
<div
className={`mb-6 flex items-center justify-center ${
showArrows ? 'md:px-6 lg:px-14' : ''
}`}
>
<div className="mr-3 flex items-center justify-center w-7 h-7 bg-th-active rounded-full">
<MegaphoneIcon className="w-4 h-4 text-th-bkg-1 -rotate-[30deg]" />
</div>
<h2 className="text-xl">Latest news</h2>
</div>
<div className="flex items-center">
{showArrows ? (
<button
className="mr-4 flex items-center justify-center w-8 h-8 border-2 border-th-bkg-4 rounded-full"
onClick={prevSlide}
>
<ChevronLeftIcon className="w-5 h-5 text-th-fgd-1" />
</button>
) : null}
<div
className={` ${showArrows ? 'w-[calc(100%-88px)]' : 'w-full'}`}
>
<Slider ref={sliderRef} {...sliderSettings}>
{announcements.map((announcement, i) => (
<div
className={
i !== announcements.length - 1 ? 'pr-3' : 'pr-[1px]'
}
key={announcement.title + i}
>
<Announcement data={announcement} />
</div>
))}
</Slider>
</div>
{showArrows ? (
<button
className="ml-1 flex items-center justify-center w-8 h-8 border-2 border-th-bkg-4 rounded-full"
onClick={nextSlide}
>
<ChevronRightIcon className="w-5 h-5 text-th-fgd-1" />
</button>
) : null}
</div>
</SectionWrapper>
) : null}
<SectionWrapper className="mt-10 md:mt-0">
<div className="grid grid-cols-6 gap-4 lg:gap-6" ref={callouts}>
<IconWithText
Expand Down Expand Up @@ -588,7 +694,7 @@ const BlackSphere = () => {
if (!mounted) return <div className="sphere w-56 h-56" />
return (
<img
className={`sphere absolute -top-16 -left-6 sm:left-6 w-56 h-auto xl:-left-12 ${
className={`sphere absolute -left-6 sm:left-6 w-56 h-auto xl:-left-12 ${
theme === 'Dark' ? '' : 'opacity-0'
}`}
src="/images/new/black-sphere.png"
Expand Down
2 changes: 2 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import './styles/global.css'
import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'
import LayoutWrapper from './components/LayoutWrapper'
import ExitDraftModeLink from './components/ExitDraftModeLink'
import { draftMode } from 'next/headers'
Expand Down
57 changes: 57 additions & 0 deletions contentful/homePageAnnouncement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { TypeHomePageAnnouncementSkeleton } from './types'
import { Entry } from 'contentful'
import contentfulClient from './contentfulClient'
import { ContentImage, parseContentfulContentImage } from './contentImage'

type HomePageAnnouncementEntry = Entry<
TypeHomePageAnnouncementSkeleton,
undefined,
string
>

export interface HomePageAnnouncement {
title: string
description?: string
image: ContentImage | null
linkPath: string
category: string
}

export function parseContentfulHomePageAnnouncement(
homePageAnnouncementEntry?: HomePageAnnouncementEntry,
): HomePageAnnouncement | null {
if (!homePageAnnouncementEntry) {
return null
}

return {
title: homePageAnnouncementEntry.fields.title || '',
description: homePageAnnouncementEntry.fields.description || '',
linkPath: homePageAnnouncementEntry.fields.linkPath || '',
image: parseContentfulContentImage(homePageAnnouncementEntry.fields.image),
category: homePageAnnouncementEntry.fields.category,
}
}

interface FetchHomePageAnnouncementsOptions {
preview: boolean
}
export async function fetchHomePageAnnouncements({
preview,
}: FetchHomePageAnnouncementsOptions): Promise<HomePageAnnouncement[]> {
const contentful = contentfulClient({ preview })

const homePageAnnouncementsResult =
await contentful.getEntries<TypeHomePageAnnouncementSkeleton>({
content_type: 'homePageAnnouncement',
include: 2,
limit: 3,
})

return homePageAnnouncementsResult.items.map(
(homePageAnnouncementEntry) =>
parseContentfulHomePageAnnouncement(
homePageAnnouncementEntry,
) as HomePageAnnouncement,
)
}
24 changes: 24 additions & 0 deletions contentful/types/TypeHomePageAnnouncement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type {
ChainModifiers,
Entry,
EntryFieldTypes,
EntrySkeletonType,
LocaleCode,
} from 'contentful'

export interface TypeHomePageAnnouncementFields {
title: EntryFieldTypes.Symbol
description?: EntryFieldTypes.Symbol
linkPath: EntryFieldTypes.Symbol
image?: EntryFieldTypes.AssetLink
category: EntryFieldTypes.Symbol<'Announcement' | 'New Listing' | 'News'>
}

export type TypeHomePageAnnouncementSkeleton = EntrySkeletonType<
TypeHomePageAnnouncementFields,
'homePageAnnouncement'
>
export type TypeHomePageAnnouncement<
Modifiers extends ChainModifiers,
Locales extends LocaleCode,
> = Entry<TypeHomePageAnnouncementSkeleton, Modifiers, Locales>
5 changes: 5 additions & 0 deletions contentful/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export type {
TypeCallToActionFields,
TypeCallToActionSkeleton,
} from './TypeCallToAction'
export type {
TypeHomePageAnnouncement,
TypeHomePageAnnouncementFields,
TypeHomePageAnnouncementSkeleton,
} from './TypeHomePageAnnouncement'
export type {
TypeInlineTextPrice,
TypeInlineTextPriceFields,
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
"react-dom": "^18.2.0",
"react-flip-numbers": "^3.0.8",
"react-i18next": "^13.5.0",
"recharts": "^2.10.0"
"react-slick": "^0.30.1",
"recharts": "^2.10.0",
"slick-carousel": "^1.8.1"
},
"devDependencies": {
"@testing-library/react": "^14.1.2",
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.tsbuildinfo

Large diffs are not rendered by default.

Loading
Loading