From f31d74eb615f854874f11a71ee577c3ddad2f27f Mon Sep 17 00:00:00 2001 From: saml33 Date: Wed, 21 Feb 2024 13:08:12 +1100 Subject: [PATCH] add announcements to overview --- components/account/AccountOverview.tsx | 7 + components/account/Announcements.tsx | 170 +++++++++++++++++++++++++ next.config.js | 1 + package.json | 3 + pages/_app.tsx | 2 + public/locales/en/common.json | 1 + public/locales/es/common.json | 1 + public/locales/pt/common.json | 1 + public/locales/ru/common.json | 1 + public/locales/zh/common.json | 1 + public/locales/zh_tw/common.json | 1 + utils/contentful.ts | 88 +++++++++++++ yarn.lock | 54 +++++++- 13 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 components/account/Announcements.tsx diff --git a/components/account/AccountOverview.tsx b/components/account/AccountOverview.tsx index 38026e4c..31b3b9a1 100644 --- a/components/account/AccountOverview.tsx +++ b/components/account/AccountOverview.tsx @@ -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,12 @@ const AccountOverview = () => { +
+

+ {t('announcements')} +

+ +
{showCreateAccountModal ? ( { + const { width } = useViewport() + + const { data: announcements } = useQuery( + ['announcements-data'], + () => fetchCMSAnnounements(), + { + cacheTime: 1000 * 60 * 15, + staleTime: 1000 * 60 * 5, + retry: 3, + refetchOnWindowFocus: false, + }, + ) + + console.log(announcements) + + 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(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 ? ( +
+ {showArrows ? ( + + ) : null} +
+ + {announcements.map((announcement, i) => ( +
+ +
+ ))} +
+
+ {showArrows ? ( + + ) : null} +
+ ) : null +} + +export default Announcements + +const classNames = + 'border border-th-bkg-4 py-3 px-4 rounded-lg flex items-center justify-between 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 ? ( + + {children} + + ) : ( + + {children} + + ) +} + +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 ( + + + {imageSrc ? ( + {imageAlt} + ) : null} +
+ {/*

{category}

*/} +

{title}

+

{description}

+
+
+ {/* */} +
+ ) +} diff --git a/next.config.js b/next.config.js index ae5c7f7c..23bfb45c 100644 --- a/next.config.js +++ b/next.config.js @@ -11,6 +11,7 @@ const nextConfig = { 'arweave.net', 'www.dual.finance', 'shdw-drive.genesysgo.net', + 'images.ctfassets.net', ], }, reactStrictMode: true, diff --git a/package.json b/package.json index bc7def71..48dc7b13 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pages/_app.tsx b/pages/_app.tsx index 273ab106..3aef0e1b 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -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 { diff --git a/public/locales/en/common.json b/public/locales/en/common.json index e63073bd..2346b165 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -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).", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index e63073bd..2346b165 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -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).", diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json index 8771d4b4..1405567c 100644 --- a/public/locales/pt/common.json +++ b/public/locales/pt/common.json @@ -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).", diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index e63073bd..2346b165 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -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).", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index fa625865..2fd808c1 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -19,6 +19,7 @@ "all": "全部", "amount": "数量", "amount-owed": "欠款", + "announcements": "Announcements", "asked-sign-transaction": "你会被要求签署交易", "asset-liability-weight": "资产/债务权重", "asset-liability-weight-desc": "资产权重在账户健康计算中对质押品价值进行扣减。资产权重越低,资产对质押品的影响越小。债务权重恰恰相反(在健康计算中增加债务价值)。", diff --git a/public/locales/zh_tw/common.json b/public/locales/zh_tw/common.json index c4ed5aa7..42426a4c 100644 --- a/public/locales/zh_tw/common.json +++ b/public/locales/zh_tw/common.json @@ -19,6 +19,7 @@ "all": "全部", "amount": "數量", "amount-owed": "欠款", + "announcements": "Announcements", "asked-sign-transaction": "你會被要求簽署交易", "asset-liability-weight": "資產/債務權重", "asset-liability-weight-desc": "資產權重在賬戶健康計算中對質押品價值進行扣減。資產權重越低,資產對質押品的影響越小。債務權重恰恰相反(在健康計算中增加債務價值)。", diff --git a/utils/contentful.ts b/utils/contentful.ts index 08ab6e95..b68e7732 100644 --- a/utils/contentful.ts +++ b/utils/contentful.ts @@ -1,4 +1,6 @@ import { + Asset, + AssetLink, createClient, Entry, EntryFieldTypes, @@ -66,6 +68,25 @@ export interface TokenPage { erc20TokenDecimals: number | undefined } +function parseContentfulContentImage( + asset?: Asset | { 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 { @@ -115,3 +183,23 @@ export async function fetchCMSTokenPage( return parsedTokenPages } + +export async function fetchCMSAnnounements(): Promise { + const client = createClient({ + space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID!, + accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN!, + }) + + const announcementsResult = + await client.getEntries({ + content_type: 'appAnnouncement', + include: 2, + order: ['-sys.createdAt'], + limit: 3, + }) + + return announcementsResult.items.map( + (appAnnouncementEntry) => + parseContentfulAppAnnouncement(appAnnouncementEntry) as AppAnnouncement, + ) +} diff --git a/yarn.lock b/yarn.lock index 6294dea4..e72334d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2558,7 +2558,7 @@ dependencies: "@solana/wallet-adapter-base" "^0.9.23" -"@solana/wallet-adapter-solflare@0.6.27": +"@solana/wallet-adapter-solflare@0.6.27", "@solana/wallet-adapter-solflare@^0.6.28": version "0.6.27" resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-solflare/-/wallet-adapter-solflare-0.6.27.tgz#49ba2dfecca4bee048e65d302216d1b732d7e39e" integrity sha512-MBBx9B1pI8ChCT70sgxrmeib1S7G9tRQzfMHqJPdGQ2jGtukY0Puzma2OBsIAsH5Aw9rUUUFZUK+8pzaE+mgAg== @@ -2569,7 +2569,7 @@ "@solflare-wallet/sdk" "^1.3.0" "@wallet-standard/wallet" "^1.0.1" -"@solana/wallet-adapter-solflare@0.6.28", "@solana/wallet-adapter-solflare@^0.6.28": +"@solana/wallet-adapter-solflare@0.6.28": version "0.6.28" resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-solflare/-/wallet-adapter-solflare-0.6.28.tgz#3de42a43220cca361050ebd1755078012a5b0fe2" integrity sha512-iiUQtuXp8p4OdruDawsm1dRRnzUCcsu+lKo8OezESskHtbmZw2Ifej0P99AbJbBAcBw7q4GPI6987Vh05Si5rw== @@ -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"