mango-v4-ui/utils/contentful.ts

206 lines
5.3 KiB
TypeScript

import {
Asset,
AssetLink,
createClient,
Entry,
EntryFieldTypes,
EntrySkeletonType,
} from 'contentful'
import { Document as RichTextDocument } from '@contentful/rich-text-types'
interface TypeTokenFields {
tokenName: EntryFieldTypes.Symbol
slug: EntryFieldTypes.Symbol
seoTitle: EntryFieldTypes.Symbol
seoDescription: EntryFieldTypes.Text
description?: EntryFieldTypes.RichText
tags: EntryFieldTypes.Array<
EntryFieldTypes.Symbol<
| 'AI'
| 'Bridged (Portal)'
| 'DeFi'
| 'DePIN'
| 'Derivatives'
| 'Domains'
| 'Exchange'
| 'Gaming'
| 'Governance'
| 'Infrastructure'
| 'Layer 1'
| 'Liquid Staking'
| 'Meme'
| 'Payments'
| 'Social'
| 'Stablecoin'
>
>
websiteUrl?: EntryFieldTypes.Symbol
twitterUrl?: EntryFieldTypes.Symbol
whitepaper?: EntryFieldTypes.Symbol
mint: EntryFieldTypes.Symbol
coingeckoId: EntryFieldTypes.Symbol
symbol: EntryFieldTypes.Symbol
spotSymbol: EntryFieldTypes.Symbol
perpSymbol?: EntryFieldTypes.Symbol
ethMint?: EntryFieldTypes.Symbol
erc20TokenDecimals?: EntryFieldTypes.Integer
}
type TypeTokenSkeleton = EntrySkeletonType<TypeTokenFields, 'token'>
type TokenPageEntry = Entry<TypeTokenSkeleton, undefined, string>
export interface TokenPage {
tokenName: string
symbol: string
slug: string
description: RichTextDocument | undefined
tags: string[]
websiteUrl?: string
twitterUrl?: string
mint: string
ethMint: string | undefined
coingeckoId: string
seoTitle: string
seoDescription: string
perpSymbol: string | undefined
spotSymbol: string
lastModified: string
erc20TokenDecimals: number | undefined
}
function parseContentfulContentImage(
asset?: Asset<undefined, string> | { sys: AssetLink },
): ContentImage | null {
if (!asset) {
return null
}
if (!('fields' in asset)) {
return null
}
return {
src: asset.fields.file?.url || '',
alt: asset.fields.description || '',
width: asset.fields.file?.details.image?.width || 0,
height: asset.fields.file?.details.image?.height || 0,
}
}
function parseContentfulTokenPage(
tokenPageEntry?: TokenPageEntry,
): TokenPage | null {
if (!tokenPageEntry) {
return null
}
return {
tokenName: tokenPageEntry.fields.tokenName,
symbol: tokenPageEntry.fields.symbol,
slug: tokenPageEntry.fields.slug,
description: tokenPageEntry.fields.description || undefined,
tags: tokenPageEntry.fields.tags || [],
websiteUrl: tokenPageEntry.fields.websiteUrl || undefined,
twitterUrl: tokenPageEntry.fields.twitterUrl || undefined,
mint: tokenPageEntry.fields.mint,
ethMint: tokenPageEntry.fields.ethMint || undefined,
coingeckoId: tokenPageEntry.fields.coingeckoId,
seoTitle: tokenPageEntry.fields.seoTitle,
seoDescription: tokenPageEntry.fields.seoDescription,
perpSymbol: tokenPageEntry.fields.perpSymbol || undefined,
spotSymbol: tokenPageEntry.fields.spotSymbol,
lastModified: tokenPageEntry.sys.updatedAt,
erc20TokenDecimals: tokenPageEntry.fields.erc20TokenDecimals || undefined,
}
}
function parseContentfulAppAnnouncement(
homePageAnnouncementEntry?: AppAnnouncementEntry,
): AppAnnouncement | null {
if (!homePageAnnouncementEntry) {
return null
}
return {
title: homePageAnnouncementEntry.fields.title || '',
description: homePageAnnouncementEntry.fields.description || '',
linkPath: homePageAnnouncementEntry.fields.linkPath || '',
image: parseContentfulContentImage(homePageAnnouncementEntry.fields.image),
}
}
export interface ContentImage {
src: string
alt: string
width: number
height: number
}
export interface AppAnnouncement {
title: string
description?: string
image: ContentImage | null
linkPath: string
}
export interface TypeAppAnnouncementFields {
title: EntryFieldTypes.Symbol
description?: EntryFieldTypes.Symbol
linkPath: EntryFieldTypes.Symbol
image?: EntryFieldTypes.AssetLink
}
export type TypeAppAnnouncementSkeleton = EntrySkeletonType<
TypeAppAnnouncementFields,
'appAnnouncement'
>
type AppAnnouncementEntry = Entry<
TypeAppAnnouncementSkeleton,
undefined,
string
>
export async function fetchCMSTokenPage(
symbol: string | undefined,
): Promise<TokenPage[]> {
const client = createClient({
space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID!,
accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN!,
})
const tokenPagesResult = await client.getEntries({
content_type: 'token',
'fields.symbol[in]': symbol,
include: 2,
order: ['fields.tokenName'],
})
const parsedTokenPages = tokenPagesResult.items.map(
(tokenPageEntry) =>
parseContentfulTokenPage(tokenPageEntry as TokenPageEntry) as TokenPage,
)
return parsedTokenPages
}
export async function fetchCMSAnnounements(): Promise<AppAnnouncement[]> {
const client = createClient({
space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID!,
accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN!,
})
const announcementsResult =
await client.getEntries<TypeAppAnnouncementSkeleton>({
content_type: 'appAnnouncement',
include: 2,
order: ['-sys.createdAt'],
limit: 3,
})
return announcementsResult.items.map(
(appAnnouncementEntry) =>
parseContentfulAppAnnouncement(appAnnouncementEntry) as AppAnnouncement,
)
}