Merge pull request #393 from blockworks-foundation/cms-announcements
add announcements to overview
This commit is contained in:
commit
bd468bbe80
|
@ -15,6 +15,7 @@ import ConnectEmptyState from '@components/shared/ConnectEmptyState'
|
|||
import CreateAccountModal from '@components/modals/CreateAccountModal'
|
||||
import { FaceSmileIcon } from '@heroicons/react/20/solid'
|
||||
import Button from '@components/shared/Button'
|
||||
import Announcements from './Announcements'
|
||||
|
||||
const EMPTY_STATE_WRAPPER_CLASSES =
|
||||
'flex h-[180px] flex-col justify-center pb-4 md:h-full'
|
||||
|
@ -116,6 +117,7 @@ const AccountOverview = () => {
|
|||
<AccountHeroStats accountValue={accountValue} />
|
||||
</div>
|
||||
</div>
|
||||
<Announcements />
|
||||
<Explore />
|
||||
{showCreateAccountModal ? (
|
||||
<CreateAccountModal
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/20/solid'
|
||||
import { useViewport } from 'hooks/useViewport'
|
||||
import { ReactNode, useRef } from 'react'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import Slider from 'react-slick'
|
||||
import Image from 'next/image'
|
||||
import { usePlausible } from 'next-plausible'
|
||||
import Link from 'next/link'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { AppAnnouncement, fetchCMSAnnounements } from 'utils/contentful'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { SHOW_ANNOUNCEMENTS_KEY } from 'utils/constants'
|
||||
import { LinkButton } from '@components/shared/Button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Announcements = () => {
|
||||
const { width } = useViewport()
|
||||
const { t } = useTranslation('account')
|
||||
const [showAnnouncements, setShowAnnouncements] = useLocalStorageState(
|
||||
SHOW_ANNOUNCEMENTS_KEY,
|
||||
true,
|
||||
)
|
||||
|
||||
const { data: announcements } = useQuery(
|
||||
['announcements-data'],
|
||||
() => fetchCMSAnnounements(),
|
||||
{
|
||||
cacheTime: 1000 * 60 * 15,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
retry: 3,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
)
|
||||
|
||||
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 announcements?.length && showAnnouncements ? (
|
||||
<div className="px-2 pt-10 md:px-4">
|
||||
<div className="flex items-center justify-center">
|
||||
{showArrows ? (
|
||||
<button
|
||||
className="mr-4 flex h-8 w-8 items-center justify-center rounded-full border-2 border-th-bkg-4"
|
||||
onClick={prevSlide}
|
||||
>
|
||||
<ChevronLeftIcon className="h-5 w-5 text-th-fgd-1" />
|
||||
</button>
|
||||
) : null}
|
||||
<div className={` ${showArrows ? 'w-[calc(100%-120px)]' : 'w-full'}`}>
|
||||
<Slider ref={sliderRef} {...sliderSettings}>
|
||||
{announcements.map((announcement, i) => (
|
||||
<div className="px-2" key={announcement.title + i}>
|
||||
<Announcement data={announcement} />
|
||||
</div>
|
||||
))}
|
||||
</Slider>
|
||||
</div>
|
||||
{showArrows ? (
|
||||
<button
|
||||
className="ml-4 flex h-8 w-8 items-center justify-center rounded-full border-2 border-th-bkg-4"
|
||||
onClick={nextSlide}
|
||||
>
|
||||
<ChevronRightIcon className="h-5 w-5 text-th-fgd-1" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-2 flex justify-center px-2 md:justify-end">
|
||||
<LinkButton
|
||||
className="text-xs font-normal text-th-fgd-3"
|
||||
onClick={() => setShowAnnouncements(false)}
|
||||
>
|
||||
{t('hide-announcements')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default Announcements
|
||||
|
||||
const classNames =
|
||||
'bg-th-bkg-2 p-4 rounded-lg block w-full md:hover:bg-th-bkg-3'
|
||||
|
||||
const AnnouncementWrapper = ({
|
||||
children,
|
||||
isExternal,
|
||||
path,
|
||||
}: {
|
||||
children: ReactNode
|
||||
isExternal: boolean
|
||||
path: string
|
||||
}) => {
|
||||
const telemetry = usePlausible()
|
||||
|
||||
const trackClick = () => {
|
||||
telemetry('announcement', {
|
||||
props: {
|
||||
path: path,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return isExternal ? (
|
||||
<a
|
||||
className={classNames}
|
||||
href={path}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={trackClick}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
) : (
|
||||
<Link className={classNames} href={path} onClick={trackClick} shallow>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const Announcement = ({ data }: { data: AppAnnouncement }) => {
|
||||
const { 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="shrink-0 rounded-full"
|
||||
src={`https:${imageSrc}`}
|
||||
alt={imageAlt}
|
||||
height={48}
|
||||
width={48}
|
||||
/>
|
||||
) : null}
|
||||
<div>
|
||||
<p className="block font-display text-sm text-th-fgd-1">{title}</p>
|
||||
<p className="block text-sm text-th-fgd-3">{description}</p>
|
||||
</div>
|
||||
</span>
|
||||
{/* <ChevronRightIcon className="ml-3 h-6 w-6 text-th-fgd-4 flex-shrink-0" /> */}
|
||||
</AnnouncementWrapper>
|
||||
)
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import { useEffect, useMemo, useState } from 'react'
|
||||
import PerpMarketsTable from './PerpMarketsTable'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import RecentGainersLosers from './RecentGainersLosers'
|
||||
import Spot from './Spot'
|
||||
|
@ -8,11 +7,13 @@ import useBanks from 'hooks/useBanks'
|
|||
import TabsText from '@components/shared/TabsText'
|
||||
import useFollowedAccounts from 'hooks/useFollowedAccounts'
|
||||
import FollowedAccounts from './FollowedAccounts'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { SHOW_ANNOUNCEMENTS_KEY } from 'utils/constants'
|
||||
|
||||
const Explore = () => {
|
||||
const { t } = useTranslation(['common'])
|
||||
const { banks } = useBanks()
|
||||
const { data: followedAccounts } = useFollowedAccounts()
|
||||
const [showAnnouncements] = useLocalStorageState(SHOW_ANNOUNCEMENTS_KEY, true)
|
||||
const perpStats = mangoStore((s) => s.perpStats.data)
|
||||
const [activeTab, setActiveTab] = useState('tokens')
|
||||
|
||||
|
@ -37,12 +38,12 @@ const Explore = () => {
|
|||
}, [banks, followedAccounts])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="px-4 pt-10 md:px-6">
|
||||
<div className={showAnnouncements ? 'pt-4' : 'pt-10'}>
|
||||
{/* <div className="px-4 pt-10 md:px-6">
|
||||
<h2 className="mb-4 text-center text-lg md:text-left">
|
||||
{t('explore')}
|
||||
</h2>
|
||||
</div>
|
||||
</div> */}
|
||||
<RecentGainersLosers />
|
||||
<div className="z-10 w-max px-4 pt-8 md:px-6">
|
||||
<div
|
||||
|
@ -62,7 +63,7 @@ const Explore = () => {
|
|||
<div className="pb-20 md:pb-0">
|
||||
<TabContent activeTab={activeTab} />
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { useCallback } from 'react'
|
|||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
NOTIFICATION_POSITION_KEY,
|
||||
SHOW_ANNOUNCEMENTS_KEY,
|
||||
SIZE_INPUT_UI_KEY,
|
||||
TRADE_CHART_UI_KEY,
|
||||
TRADE_LAYOUT_KEY,
|
||||
|
@ -25,6 +26,7 @@ import {
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import { CUSTOM_SKINS } from 'utils/theme'
|
||||
import { SETTINGS_BUTTON_TITLE_CLASSES } from './AccountSettings'
|
||||
import Switch from '@components/forms/Switch'
|
||||
|
||||
const NOTIFICATION_POSITIONS = [
|
||||
'bottom-left',
|
||||
|
@ -89,6 +91,11 @@ const DisplaySettings = () => {
|
|||
|
||||
const [, setTradeLayout] = useLocalStorageState(TRADE_LAYOUT_KEY, 'chartLeft')
|
||||
|
||||
const [showAnnouncements, setShowAnnouncements] = useLocalStorageState(
|
||||
SHOW_ANNOUNCEMENTS_KEY,
|
||||
true,
|
||||
)
|
||||
|
||||
// add nft skins to theme selection list
|
||||
useEffect(() => {
|
||||
if (nfts.length) {
|
||||
|
@ -236,6 +243,13 @@ const DisplaySettings = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-t border-th-bkg-3 p-4">
|
||||
<p className={SETTINGS_BUTTON_TITLE_CLASSES}>{t('announcements')}</p>
|
||||
<Switch
|
||||
checked={showAnnouncements}
|
||||
onChange={() => setShowAnnouncements(!showAnnouncements)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ const nextConfig = {
|
|||
'arweave.net',
|
||||
'www.dual.finance',
|
||||
'shdw-drive.genesysgo.net',
|
||||
'images.ctfassets.net',
|
||||
],
|
||||
},
|
||||
reactStrictMode: true,
|
||||
|
|
|
@ -79,9 +79,11 @@
|
|||
"react-nice-dates": "3.1.0",
|
||||
"react-number-format": "4.9.2",
|
||||
"react-responsive-pagination": "2.2.3",
|
||||
"react-slick": "0.30.2",
|
||||
"react-tsparticles": "2.2.4",
|
||||
"recharts": "2.5.0",
|
||||
"remark-gfm": "4.0.0",
|
||||
"slick-carousel": "1.8.1",
|
||||
"three": "^0.155.0",
|
||||
"tsparticles": "2.2.4",
|
||||
"walktour": "5.1.1",
|
||||
|
@ -106,6 +108,7 @@
|
|||
"@types/react": "18.0.3",
|
||||
"@types/react-dom": "18.0.0",
|
||||
"@types/react-grid-layout": "1.3.2",
|
||||
"@types/react-slick": "0.23.13",
|
||||
"@types/recharts": "1.8.24",
|
||||
"@typescript-eslint/eslint-plugin": "5.43.0",
|
||||
"autoprefixer": "10.4.13",
|
||||
|
|
|
@ -2,6 +2,8 @@ import '../styles/globals.css'
|
|||
import 'react-nice-dates/build/style.css'
|
||||
import '../styles/datepicker.css'
|
||||
import 'driver.js/dist/driver.css'
|
||||
import 'slick-carousel/slick/slick.css'
|
||||
import 'slick-carousel/slick/slick-theme.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"follow": "Follow",
|
||||
"followed-accounts": "Followed Accounts",
|
||||
"funding-chart": "Funding Chart",
|
||||
"hide-announcements": "Hide Announcements",
|
||||
"init-health": "Init Health",
|
||||
"maint-health": "Maint Health",
|
||||
"health-contributions": "Health Contributions",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"all": "All",
|
||||
"amount": "Amount",
|
||||
"amount-owed": "Amount Owed",
|
||||
"announcements": "Announcements",
|
||||
"asked-sign-transaction": "You'll be asked to sign a transaction",
|
||||
"asset-liability-weight": "Asset/Liability Weights",
|
||||
"asset-liability-weight-desc": "Asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral. Liability weight does the opposite (adds to the value of the liability in your health calculation).",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"follow": "Follow",
|
||||
"followed-accounts": "Followed Accounts",
|
||||
"funding-chart": "Funding Chart",
|
||||
"hide-announcements": "Hide Announcements",
|
||||
"init-health": "Init Health",
|
||||
"maint-health": "Maint Health",
|
||||
"health-contributions": "Health Contributions",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"all": "All",
|
||||
"amount": "Amount",
|
||||
"amount-owed": "Amount Owed",
|
||||
"announcements": "Announcements",
|
||||
"asked-sign-transaction": "You'll be asked to sign a transaction",
|
||||
"asset-liability-weight": "Asset/Liability Weights",
|
||||
"asset-liability-weight-desc": "Asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral. Liability weight does the opposite (adds to the value of the liability in your health calculation).",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"follow": "Seguir",
|
||||
"followed-accounts": "Contas Seguidas",
|
||||
"funding-chart": "Gráfico de Financiamento",
|
||||
"hide-announcements": "Hide Announcements",
|
||||
"init-health": "Saúde Inicial",
|
||||
"maint-health": "Saúde de Manutenção",
|
||||
"health-contributions": "Contribuições para a Saúde",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"all": "Todos",
|
||||
"amount": "Quantia",
|
||||
"amount-owed": "Quantia Devida",
|
||||
"announcements": "Announcements",
|
||||
"asked-sign-transaction": "Você será solicitado a assinar uma transação",
|
||||
"asset-liability-weight": "Pesos de Ativo/Passivo",
|
||||
"asset-liability-weight-desc": "O peso do ativo aplica um desconto ao valor do colateral no cálculo da saúde da sua conta. Quanto menor o peso do ativo, menos o ativo conta como colateral. O peso do passivo faz o oposto (adiciona ao valor do passivo no cálculo da saúde).",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"follow": "Follow",
|
||||
"followed-accounts": "Followed Accounts",
|
||||
"funding-chart": "Funding Chart",
|
||||
"hide-announcements": "Hide Announcements",
|
||||
"init-health": "Init Health",
|
||||
"maint-health": "Maint Health",
|
||||
"health-contributions": "Health Contributions",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"all": "All",
|
||||
"amount": "Amount",
|
||||
"amount-owed": "Amount Owed",
|
||||
"announcements": "Announcements",
|
||||
"asked-sign-transaction": "You'll be asked to sign a transaction",
|
||||
"asset-liability-weight": "Asset/Liability Weights",
|
||||
"asset-liability-weight-desc": "Asset weight applies a haircut to the value of the collateral in your account health calculation. The lower the asset weight, the less the asset counts towards collateral. Liability weight does the opposite (adds to the value of the liability in your health calculation).",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"follow": "关注",
|
||||
"followed-accounts": "你关注的帐户",
|
||||
"funding-chart": "资金费图表",
|
||||
"hide-announcements": "Hide Announcements",
|
||||
"health-contributions": "健康度贡献",
|
||||
"init-health": "初始健康度",
|
||||
"init-health-contribution": "初始健康贡献",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"all": "全部",
|
||||
"amount": "数量",
|
||||
"amount-owed": "欠款",
|
||||
"announcements": "Announcements",
|
||||
"asked-sign-transaction": "你会被要求签署交易",
|
||||
"asset-liability-weight": "资产/债务权重",
|
||||
"asset-liability-weight-desc": "资产权重在账户健康计算中对质押品价值进行扣减。资产权重越低,资产对质押品的影响越小。债务权重恰恰相反(在健康计算中增加债务价值)。",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"follow": "關注",
|
||||
"followed-accounts": "你關注的帳戶",
|
||||
"funding-chart": "資金費圖表",
|
||||
"hide-announcements": "Hide Announcements",
|
||||
"health-contributions": "健康度貢獻",
|
||||
"init-health": "初始健康度",
|
||||
"init-health-contribution": "初始健康貢獻",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"all": "全部",
|
||||
"amount": "數量",
|
||||
"amount-owed": "欠款",
|
||||
"announcements": "Announcements",
|
||||
"asked-sign-transaction": "你會被要求簽署交易",
|
||||
"asset-liability-weight": "資產/債務權重",
|
||||
"asset-liability-weight-desc": "資產權重在賬戶健康計算中對質押品價值進行扣減。資產權重越低,資產對質押品的影響越小。債務權重恰恰相反(在健康計算中增加債務價值)。",
|
||||
|
|
|
@ -88,6 +88,8 @@ export const NON_RESTRICTED_JURISDICTION_KEY = 'non-restricted-jurisdiction-0.1'
|
|||
export const FILTER_ORDERS_FOR_MARKET_KEY = 'filterOrdersForMarket-0.1'
|
||||
export const FILTER_HISTORY_FOR_MARKET_KEY = 'filterHistoryForMarket-0.1'
|
||||
|
||||
export const SHOW_ANNOUNCEMENTS_KEY = 'showAnnouncements-0.1'
|
||||
|
||||
// Unused
|
||||
export const PROFILE_CATEGORIES = [
|
||||
'borrower',
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import {
|
||||
Asset,
|
||||
AssetLink,
|
||||
createClient,
|
||||
Entry,
|
||||
EntryFieldTypes,
|
||||
|
@ -66,6 +68,25 @@ export interface TokenPage {
|
|||
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 {
|
||||
|
@ -93,6 +114,53 @@ function parseContentfulTokenPage(
|
|||
}
|
||||
}
|
||||
|
||||
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[]> {
|
||||
|
@ -115,3 +183,23 @@ export async function fetchCMSTokenPage(
|
|||
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
|
50
yarn.lock
50
yarn.lock
|
@ -3627,6 +3627,13 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-slick@0.23.13":
|
||||
version "0.23.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-slick/-/react-slick-0.23.13.tgz#037434e73a58063047b121e08565f7185d811f36"
|
||||
integrity sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.9":
|
||||
version "4.4.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.9.tgz#12a1a1b5b8791067198149867b0823fbace31579"
|
||||
|
@ -6160,6 +6167,11 @@ enhanced-resolve@^5.12.0:
|
|||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
|
||||
enquire.js@^2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814"
|
||||
integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==
|
||||
|
||||
entities@^4.2.0, entities@^4.3.0, entities@^4.4.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
|
@ -8551,6 +8563,13 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
|
|||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
|
||||
|
||||
json2mq@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
|
||||
integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==
|
||||
dependencies:
|
||||
string-convert "^0.2.0"
|
||||
|
||||
json5@1.0.2, json5@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
|
||||
|
@ -8717,6 +8736,11 @@ lodash.clonedeep@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||
|
||||
lodash.isequal@4.5.0, lodash.isequal@^4.0.0, lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
|
@ -10548,6 +10572,17 @@ react-simple-animate@3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/react-simple-animate/-/react-simple-animate-3.0.2.tgz#67f29b0c64155d2dfd540c1c74f634ef521536a8"
|
||||
integrity sha512-GDpznDMpIHHdH6L6AZo9nNry6Xsq7forKmd6rwigMSuxe4FS9ihK0HSC1odB4WWKbA87S8VL7EVR9JeTRVkbQA==
|
||||
|
||||
react-slick@0.30.2:
|
||||
version "0.30.2"
|
||||
resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.30.2.tgz#b28e992f9c519bb516a0af8d37e82cb59fee08ce"
|
||||
integrity sha512-XvQJi7mRHuiU3b9irsqS9SGIgftIfdV5/tNcURTb5LdIokRA5kIIx3l4rlq2XYHfxcSntXapoRg/GxaVOM1yfg==
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
enquire.js "^2.1.6"
|
||||
json2mq "^0.2.0"
|
||||
lodash.debounce "^4.0.8"
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
|
||||
react-smooth@^2.0.2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.4.tgz#95187126265970a1490e2aea5690365203ee555f"
|
||||
|
@ -10822,6 +10857,11 @@ requires-port@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
||||
|
||||
resize-observer-polyfill@^1.5.0:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
|
@ -11192,6 +11232,11 @@ slash@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||
|
||||
slick-carousel@1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/slick-carousel/-/slick-carousel-1.8.1.tgz#a4bfb29014887bb66ce528b90bd0cda262cc8f8d"
|
||||
integrity sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==
|
||||
|
||||
smart-buffer@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
|
||||
|
@ -11354,6 +11399,11 @@ strict-uri-encode@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||
integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
|
||||
|
||||
string-convert@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97"
|
||||
integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==
|
||||
|
||||
string-length@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
|
||||
|
|
Loading…
Reference in New Issue