Compare commits

...

2 Commits

Author SHA1 Message Date
saml33 fc19b8f2d0 add zeus logo 2024-04-05 11:58:17 +11:00
saml33 18a3847b70 support mango update content type 2024-04-05 09:38:24 +11:00
14 changed files with 424 additions and 44 deletions

View File

@ -0,0 +1,104 @@
import { Metadata } from 'next'
import { draftMode } from 'next/headers'
import { notFound } from 'next/navigation'
import {
fetchMangoUpdate,
fetchMangoUpdates,
} from '../../../../contentful/mangoUpdate'
import RichText from '../../../components/rich-text/RichText'
import AppCallToAction from '../../../components/shared/AppCallToAction'
import PageHeader from '../../../components/explore/PageHeader'
import dayjs from 'dayjs'
interface MangoUpdatePageParams {
slug: string
}
interface MangoUpdatePageProps {
params: MangoUpdatePageParams
}
// Tell Next.js about all our blog posts so
// they can be statically generated at build time.
export async function generateStaticParams(): Promise<MangoUpdatePageParams[]> {
const mangoUpdates = await fetchMangoUpdates({ preview: false })
return mangoUpdates.map((post) => ({ slug: post.slug }))
}
// For each blog post, tell Next.js which metadata
// (e.g. page title) to display.
export async function generateMetadata(
{ params }: MangoUpdatePageProps,
// parent: ResolvingMetadata,
): Promise<Metadata> {
const mangoUpdate = await fetchMangoUpdate({
slug: params.slug,
preview: draftMode().isEnabled,
})
if (!mangoUpdate) {
return notFound()
}
return {
title: mangoUpdate?.seoTitle || mangoUpdate.postTitle,
description: mangoUpdate?.seoDescription,
openGraph: {
title: mangoUpdate?.seoTitle || mangoUpdate.postTitle,
description: mangoUpdate?.seoDescription,
url: `https://mango.markets/blog/${mangoUpdate.slug}`,
siteName: 'Mango Markets',
locale: 'en_US',
type: 'website',
},
}
}
async function MangoUpdatePage({ params }: MangoUpdatePageProps) {
const mangoUpdate = await fetchMangoUpdate({
slug: params.slug,
preview: draftMode().isEnabled,
})
if (!mangoUpdate) {
return notFound()
}
const {
postTitle,
postContent,
ctaTitle,
ctaDescription,
ctaUrl,
createdAt,
} = mangoUpdate
const ctaData = { ctaTitle, ctaDescription, ctaUrl }
return (
<>
<PageHeader
title={postTitle}
tag={dayjs(createdAt).format('DD MMM YYYY')}
showBack
/>
<div
className={`px-6 lg:px-20 pt-6 md:pt-10 pb-10 md:pb-16 max-w-3xl mx-auto`}
>
<div className="flex flex-col md:flex-row md:justify-center">
<div>
<RichText document={postContent} />
{ctaData?.ctaUrl ? (
<div className="pt-6">
<AppCallToAction data={ctaData} />
</div>
) : null}
</div>
</div>
</div>
</>
)
}
export default MangoUpdatePage

View File

@ -0,0 +1,61 @@
import { draftMode } from 'next/headers'
import { fetchMangoUpdates } from '../../../contentful/mangoUpdate'
import PageHeader from '../../components/explore/PageHeader'
import Link from 'next/link'
import dayjs from 'dayjs'
async function UpdatePage() {
// Fetch blog posts using the content preview
// if draft mode is enabled:
const mangoUpdates = await fetchMangoUpdates({
preview: draftMode().isEnabled,
})
return (
<>
<PageHeader title="Updates" />
<div className="px-6 lg:px-20 py-10 md:py-16 max-w-4xl mx-auto">
{mangoUpdates && mangoUpdates.length ? (
<div className="space-y-6">
{mangoUpdates.map((mangoUpdate) => {
const {
postTitle,
shortenedDescription,
createdAt,
slug,
updateImage,
} = mangoUpdate
return (
<Link
className="flex items-center p-4 md:hover:bg-th-bkg-3 rounded-xl"
key={slug}
href={`/updates/${slug}`}
>
{updateImage?.src ? (
<img
className="mr-6 w-48 h-auto"
src={updateImage.src}
alt={updateImage?.alt}
/>
) : null}
<div>
<p>{dayjs(createdAt).format('DD MMM YYYY')}</p>
<h2 className="mb-2 text-2xl">{postTitle}</h2>
<p>{shortenedDescription}</p>
</div>
</Link>
)
})}
</div>
) : (
<div className="p-6 rounded-xl border border-th-bkg-3">
<p className="text-center">Nothing to see here yet...</p>
</div>
)}
</div>
</>
)
}
export default UpdatePage

View File

@ -1,7 +1,8 @@
import Link from 'next/link'
import { ReactNode } from 'react'
import Twitter from '../icons/Twitter'
import Discord from '../icons/Discord'
import { XIcon } from '../icons/XIcon'
import { Github } from '../icons/Github'
const Footer = () => {
return (
@ -42,6 +43,11 @@ const Footer = () => {
<FooterLink path="/explore/tokens" title="Listed tokens" />
<FooterLink path="/explore/categories" title="Token categories" />
</FooterLinkColumn>
<FooterLinkColumn title="Insights">
{/* <FooterLink path="/updates" title="Updates" /> */}
<FooterLink path="/blog" title="Blog" />
<FooterLink path="/learn" title="Learn" />
</FooterLinkColumn>
<FooterLinkColumn title="Developers">
<FooterLink
path="https://docs.mango.markets"
@ -76,7 +82,7 @@ const Footer = () => {
title="Vote"
/>
</FooterLinkColumn>
<FooterLinkColumn title="Social">
{/* <FooterLinkColumn title="Social">
<FooterLink
icon={<Twitter className="h-4 w-4 mr-2" />}
path="https://twitter.com/mangomarkets"
@ -89,13 +95,39 @@ const Footer = () => {
isExternal
title="Discord"
/>
</FooterLinkColumn>
</FooterLinkColumn> */}
</div>
</div>
<div className="border-t border-th-bkg-3 mt-3 lg:mt-12 pt-6">
<p className="flex justify-center font-mono text-xs opacity-60">
<div className="flex justify-between border-t border-th-bkg-3 mt-3 lg:mt-12 pt-6">
<p className="font-mono text-xs opacity-60">
© Mango DAO 2023. All rights reserved.
</p>
<div className="flex items-center space-x-4">
<a
className="text-th-fgd-4 hover:text-th-fgd-1"
href="https://discord.gg/2uwjsBc5yw"
target="_blank"
rel="noopener noreferrer"
>
<Discord className="h-5 w-5" />
</a>
<a
className="text-th-fgd-4 hover:text-th-fgd-1"
href="https://x.com/mangomarkets"
target="_blank"
rel="noopener noreferrer"
>
<XIcon className="h-5 w-5" />
</a>
<a
className="text-th-fgd-4 hover:text-th-fgd-1"
href="https://github.com/blockworks-foundation"
target="_blank"
rel="noopener noreferrer"
>
<Github className="h-5 w-5" />
</a>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
export const Github = ({ className }: { className?: string }) => {
return (
<svg
className={`${className}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 98 96"
fill="currentColor"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
/>
</svg>
)
}

View File

@ -0,0 +1,14 @@
export const XIcon = ({ className }: { className?: string }) => {
return (
<svg
className={`${className}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
>
<g>
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path>
</g>
</svg>
)
}

View File

@ -9,10 +9,10 @@ import {
} from 'react'
import NavigationItemLink from './NavigationItemLink'
import ButtonLink from '../shared/ButtonLink'
import Twitter from '../icons/Twitter'
import Discord from '../icons/Discord'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
import { XIcon } from '../icons/XIcon'
const DesktopNavigation = () => {
return (
@ -26,8 +26,13 @@ const DesktopNavigation = () => {
/>
</NavigationItemPanel>
</NavigationItem>
<NavigationLink path="/blog" text="Blog" />
<NavigationLink path="/learn" text="Learn" />
<NavigationItem title="Insights">
<NavigationItemPanel>
{/* <NavigationItemLink path="/updates" title="Updates" /> */}
<NavigationItemLink path="/blog" title="Blog" />
<NavigationItemLink path="/learn" title="Learn" />
</NavigationItemPanel>
</NavigationItem>
<NavigationItem title="Developers">
<NavigationItemPanel>
<NavigationItemLink
@ -66,15 +71,7 @@ const DesktopNavigation = () => {
/>
</NavigationItemPanel>
</NavigationItem>
<div className="flex items-center space-x-6">
<a
className="text-th-fgd-4 hover:text-th-fgd-1"
href="https://twitter.com/mangomarkets"
target="_blank"
rel="noopener noreferrer"
>
<Twitter className="h-5 w-5" />
</a>
<div className="flex items-center space-x-4">
<a
className="text-th-fgd-4 hover:text-th-fgd-1"
href="https://discord.gg/2uwjsBc5yw"
@ -83,6 +80,14 @@ const DesktopNavigation = () => {
>
<Discord className="h-5 w-5" />
</a>
<a
className="text-th-fgd-4 hover:text-th-fgd-1"
href="https://x.com/mangomarkets"
target="_blank"
rel="noopener noreferrer"
>
<XIcon className="h-5 w-5" />
</a>
</div>
<ButtonLink linkText="Launch App" path="https://app.mango.markets" />
</div>

View File

@ -3,9 +3,6 @@ import { IconButton } from '../shared/Button'
import { Bars3Icon, XMarkIcon } from '@heroicons/react/20/solid'
import { Transition } from '@headlessui/react'
import NavigationItemLink from './NavigationItemLink'
import Twitter from '../icons/Twitter'
import Discord from '../icons/Discord'
import { NavigationLink } from './DesktopNavigation'
const MobileNavigation = () => {
const [showMenu, setShowMenu] = useState(false)
@ -60,11 +57,23 @@ const MenuPanel = ({
title="Token categories"
/>
</div>
<div className="px-4">
<NavigationLink path="/blog" text="Blog" />
</div>
<div className="px-4">
<NavigationLink path="/learn" text="Learn" />
<div>
<h4 className="px-4 mb-2">Insights</h4>
{/* <NavigationItemLink
path="/updates"
onClick={closeOnClick}
title="Updates"
/> */}
<NavigationItemLink
path="/blog"
onClick={closeOnClick}
title="Blog"
/>
<NavigationItemLink
path="/learn"
onClick={closeOnClick}
title="Learn"
/>
</div>
<div>
<h4 className="px-4 mb-2">Developers</h4>
@ -108,23 +117,6 @@ const MenuPanel = ({
title="Vote"
/>
</div>
<div>
<h4 className="px-4 mb-2">Social</h4>
<NavigationItemLink
icon={<Twitter className="h-4 w-4 mr-2" />}
path="https://twitter.com/mangomarkets"
onClick={closeOnClick}
isExternal
title="Twitter"
/>
<NavigationItemLink
icon={<Discord className="h-4 w-4 mr-2" />}
path="https://discord.gg/2uwjsBc5yw"
onClick={closeOnClick}
isExternal
title="Discord"
/>
</div>
</div>
</div>
<Transition

View File

@ -53,6 +53,7 @@ export const CUSTOM_TOKEN_ICONS: { [key: string]: boolean } = {
wen: true,
wif: true,
$wif: true,
zeus: true,
}
export const DAILY_SECONDS = 86400

100
contentful/mangoUpdate.ts Normal file
View File

@ -0,0 +1,100 @@
import { TypeMangoUpdateSkeleton } from './types'
import { Entry } from 'contentful'
import { Document as RichTextDocument } from '@contentful/rich-text-types'
import contentfulClient from './contentfulClient'
import { ContentImage, parseContentfulContentImage } from './contentImage'
type MangoUpdateEntry = Entry<TypeMangoUpdateSkeleton, undefined, string>
// Our simplified version of a MangoUpdate.
// We don't need all the data that Contentful gives us.
export interface MangoUpdate {
postTitle: string
postContent: RichTextDocument | undefined
updateImage: ContentImage | null
slug: string
createdAt: string
seoTitle: string | undefined
seoDescription: string | undefined
ctaTitle: string | undefined
ctaDescription: string | undefined
ctaUrl: string | undefined
lastModified: string
showOnHomePage: boolean | undefined
shortenedTitle: string | undefined
shortenedDescription: string
}
// A function to transform a Contentful blog post
// into our own MangoUpdate object.
export function parseContentfulMangoUpdate(
mangoUpdateEntry?: MangoUpdateEntry,
): MangoUpdate | null {
if (!mangoUpdateEntry) {
return null
}
return {
postTitle: mangoUpdateEntry.fields.postTitle || '',
postContent: mangoUpdateEntry.fields.postContent || undefined,
updateImage: parseContentfulContentImage(
mangoUpdateEntry.fields.updateImage,
),
slug: mangoUpdateEntry.fields.slug,
createdAt: mangoUpdateEntry.sys.createdAt,
seoTitle: mangoUpdateEntry.fields.seoTitle,
seoDescription: mangoUpdateEntry.fields.seoDescription,
ctaTitle: mangoUpdateEntry.fields.ctaTitle,
ctaDescription: mangoUpdateEntry.fields.ctaDescription,
ctaUrl: mangoUpdateEntry.fields.ctaUrl,
lastModified: mangoUpdateEntry.sys.updatedAt,
showOnHomePage: mangoUpdateEntry.fields.showOnHomePage,
shortenedTitle: mangoUpdateEntry.fields.shortenedTitle,
shortenedDescription: mangoUpdateEntry.fields.shortenedDescription,
}
}
// A function to fetch all blog posts.
// Optionally uses the Contentful content preview.
interface FetchMangoUpdatesOptions {
preview: boolean
}
export async function fetchMangoUpdates({
preview,
}: FetchMangoUpdatesOptions): Promise<MangoUpdate[]> {
const contentful = contentfulClient({ preview })
const mangoUpdatesResult =
await contentful.getEntries<TypeMangoUpdateSkeleton>({
content_type: 'mangoUpdate',
include: 2,
order: ['-sys.createdAt'],
})
return mangoUpdatesResult.items.map(
(mangoUpdateEntry) =>
parseContentfulMangoUpdate(mangoUpdateEntry) as MangoUpdate,
)
}
// A function to fetch a single blog post by its slug.
// Optionally uses the Contentful content preview.
interface FetchMangoUpdateOptions {
slug: string
preview: boolean
}
export async function fetchMangoUpdate({
slug,
preview,
}: FetchMangoUpdateOptions): Promise<MangoUpdate | null> {
const contentful = contentfulClient({ preview })
const mangoUpdatesResult =
await contentful.getEntries<TypeMangoUpdateSkeleton>({
content_type: 'mangoUpdate',
'fields.slug': slug,
include: 2,
})
return parseContentfulMangoUpdate(mangoUpdatesResult.items[0])
}

View File

@ -11,7 +11,7 @@ export interface TypeLearnPostFields {
slug: EntryFieldTypes.Symbol
author?: EntryFieldTypes.Symbol
category: EntryFieldTypes.Symbol<
'Learn to Trade' | 'Listing on Mango' | 'Repost' | 'Spot Trading'
'Guides' | 'Learn to Trade' | 'Listing on Mango' | 'Repost' | 'Spot Trading'
>
authorProfileImage?: EntryFieldTypes.AssetLink
showTableOfContents?: EntryFieldTypes.Boolean

View File

@ -0,0 +1,31 @@
import type {
ChainModifiers,
Entry,
EntryFieldTypes,
EntrySkeletonType,
LocaleCode,
} from 'contentful'
export interface TypeMangoUpdateFields {
postTitle: EntryFieldTypes.Symbol
slug: EntryFieldTypes.Symbol
updateImage?: EntryFieldTypes.AssetLink
postContent: EntryFieldTypes.RichText
showOnHomePage?: EntryFieldTypes.Boolean
shortenedTitle?: EntryFieldTypes.Symbol
shortenedDescription: EntryFieldTypes.Symbol
seoTitle?: EntryFieldTypes.Symbol
seoDescription?: EntryFieldTypes.Text
ctaTitle?: EntryFieldTypes.Symbol
ctaDescription?: EntryFieldTypes.Symbol
ctaUrl?: EntryFieldTypes.Symbol
}
export type TypeMangoUpdateSkeleton = EntrySkeletonType<
TypeMangoUpdateFields,
'mangoUpdate'
>
export type TypeMangoUpdate<
Modifiers extends ChainModifiers,
Locales extends LocaleCode,
> = Entry<TypeMangoUpdateSkeleton, Modifiers, Locales>

View File

@ -28,6 +28,11 @@ export type {
TypeLearnPostFields,
TypeLearnPostSkeleton,
} from './TypeLearnPost'
export type {
TypeMangoUpdate,
TypeMangoUpdateFields,
TypeMangoUpdateSkeleton,
} from './TypeMangoUpdate'
export type {
TypeNewsArticle,
TypeNewsArticleFields,

View File

@ -0,0 +1,19 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_242_2)">
<path d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32Z" fill="#16161B"/>
<path d="M7.64016 8L6 14.3188H12.4636L9.54577 24.0996L24.9547 14.3188H18.7172L20.422 8H7.64016Z" fill="url(#paint0_linear_242_2)"/>
<path d="M20.4112 8H23.0464L21.4265 14.3188H18.7064L20.4112 8Z" fill="white"/>
<path d="M24.9528 14.3149H30.09L9.54175 24.0956L24.9528 14.3149Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_242_2" x1="15.4773" y1="8" x2="15.4773" y2="20.9528" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEB83"/>
<stop offset="0.247213" stop-color="#FFBF83"/>
<stop offset="0.682213" stop-color="#E677E9"/>
<stop offset="1" stop-color="#B283FF"/>
</linearGradient>
<clipPath id="clip0_242_2">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 974 B

File diff suppressed because one or more lines are too long