From f8180ac2259c543349b6b176c38d581e9f18d821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Mon, 22 Jan 2024 17:29:11 +0100 Subject: [PATCH 01/13] work --- components/front-page/Topics.tsx | 43 +++++++ components/markets/MarketsList.tsx | 15 ++- layouts/DefaultLayout.tsx | 4 +- lib/cms/sanity/index.ts | 4 + lib/cms/topics.ts | 54 ++++++++ lib/util/getPlaiceHolders.ts | 12 ++ package.json | 1 + pages/index.tsx | 36 ++++-- pages/markets/index.tsx | 32 ++++- pages/topics/[topic].tsx | 197 +++++++++++++++++++++++++++++ yarn.lock | 21 +++ 11 files changed, 400 insertions(+), 19 deletions(-) create mode 100644 components/front-page/Topics.tsx create mode 100644 lib/cms/topics.ts create mode 100644 lib/util/getPlaiceHolders.ts create mode 100644 pages/topics/[topic].tsx diff --git a/components/front-page/Topics.tsx b/components/front-page/Topics.tsx new file mode 100644 index 000000000..2bd677302 --- /dev/null +++ b/components/front-page/Topics.tsx @@ -0,0 +1,43 @@ +import { CmsTopicHeader } from "lib/cms/topics"; +import Image from "next/image"; +import Link from "next/link"; + +export const Topics = ({ + topics, + imagePlaceholders, +}: { + topics: CmsTopicHeader[]; + imagePlaceholders: string[]; +}) => { + return ( + <> + {topics.map((topic, index) => ( + +
+ {`Image +
+
+

{topic.title}

+
+ + ))} + + ); +}; diff --git a/components/markets/MarketsList.tsx b/components/markets/MarketsList.tsx index ecaf26212..39e8b29bf 100644 --- a/components/markets/MarketsList.tsx +++ b/components/markets/MarketsList.tsx @@ -14,9 +14,13 @@ import useMarketsUrlQuery from "lib/hooks/useMarketsUrlQuery"; import { filterTypes } from "lib/constants/market-filter"; import { ZTG } from "lib/constants"; import { useMarketsStats } from "lib/hooks/queries/useMarketsStats"; +import { CmsTopicHeader } from "lib/cms/topics"; +import { Topics } from "components/front-page/Topics"; export type MarketsListProps = { className?: string; + cmsTopics: CmsTopicHeader[]; + cmsTopicPlaceholders: string[]; }; const useChangeQuery = ( @@ -55,7 +59,11 @@ const useChangeQuery = ( }, [withLiquidityOnly]); }; -const MarketsList = ({ className = "" }: MarketsListProps) => { +const MarketsList = ({ + className = "", + cmsTopics, + cmsTopicPlaceholders, +}: MarketsListProps) => { const [filters, setFilters] = useState(); const [orderBy, setOrderBy] = useState(); const [withLiquidityOnly, setWithLiquidityOnly] = useState(); @@ -96,11 +104,16 @@ const MarketsList = ({ className = "" }: MarketsListProps) => { data-testid="marketsList" id={"market-list"} > +
+ +
+ +
{markets?.map((market) => { const volume = market.volume; diff --git a/layouts/DefaultLayout.tsx b/layouts/DefaultLayout.tsx index 15ade3505..ce5169e1d 100644 --- a/layouts/DefaultLayout.tsx +++ b/layouts/DefaultLayout.tsx @@ -29,6 +29,7 @@ const greyBackgroundPageRoutes = [ "/markets", "/create-account", "/deposit", + "/topics", ]; const DefaultLayout: FC = ({ children }) => { @@ -47,7 +48,8 @@ const DefaultLayout: FC = ({ children }) => { return (
url, + "marketIds": markets[].marketId, +}`; + +export const getCmsTopicHeaders = async (): Promise => { + const data = await sanity.fetch( + groq`*[_type == "topic"] | order(_createdAt desc) ${topicHeaderFields}`, + ); + + return data; +}; + +const topicFullFields = groq`{ + title, + description, + "slug": slug.current, + "thumbnail": thumbnail.asset->url, + "banner": banner, + "marketIds": markets[].marketId +}`; + +export const getCmsFullTopic = async ( + slug: string, +): Promise => { + const data = await sanity.fetch( + groq`*[_type == "topic" && slug.current == "${slug}"] ${topicFullFields}`, + ); + + return data[0]; +}; diff --git a/lib/util/getPlaiceHolders.ts b/lib/util/getPlaiceHolders.ts new file mode 100644 index 000000000..f6a46dc61 --- /dev/null +++ b/lib/util/getPlaiceHolders.ts @@ -0,0 +1,12 @@ +import { + IGetPlaiceholderOptions, + IGetPlaiceholderReturn, + getPlaiceholder, +} from "plaiceholder"; + +export const getPlaiceholders = ( + paths: string[], + options?: IGetPlaiceholderOptions, +): Promise => { + return Promise.all(paths.map((path) => getPlaiceholder(path, options))); +}; diff --git a/package.json b/package.json index ceb2a4ad3..3d347e342 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "next": "^13.4.19", "next-absolute-url": "^1.2.2", "next-qrcode": "^2.5.1", + "next-sanity-image": "^6.1.1", "object-hash": "^2.2.0", "plaiceholder": "^2.5.0", "pure-react-carousel": "^1.27.8", diff --git a/pages/index.tsx b/pages/index.tsx index d2ad0f4df..eda5acc2f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -10,12 +10,14 @@ import { NewsSection } from "components/front-page/News"; import PopularCategories, { CATEGORIES, } from "components/front-page/PopularCategories"; +import { Topics } from "components/front-page/Topics"; import WatchHow from "components/front-page/WatchHow"; import { IndexedMarketCardData } from "components/markets/market-card"; import MarketScroll from "components/markets/MarketScroll"; import { GraphQLClient } from "graphql-request"; import { getCmsMarketMetadataForAllMarkets } from "lib/cms/markets"; import { getCmsNews, CmsNews } from "lib/cms/news"; +import { CmsTopicHeader, getCmsTopicHeaders } from "lib/cms/topics"; import { endpointOptions, environment, graphQlEndpoint } from "lib/constants"; import getFeaturedMarkets from "lib/gql/featured-markets"; import { getNetworkStats } from "lib/gql/get-network-stats"; @@ -27,7 +29,9 @@ import { ZtgPriceHistory, } from "lib/hooks/queries/useAssetUsdPrice"; import { categoryCountsKey } from "lib/hooks/queries/useCategoryCounts"; +import { getPlaiceholders } from "lib/util/getPlaiceHolders"; import { NextPage } from "next"; +import Image from "next/image"; import Link from "next/link"; import path from "path"; import { @@ -36,13 +40,6 @@ import { IGetPlaiceholderReturn, } from "plaiceholder"; -const getPlaiceholders = ( - paths: string[], - options?: IGetPlaiceholderOptions, -): Promise => { - return Promise.all(paths.map((path) => getPlaiceholder(path, options))); -}; - export async function getStaticProps() { const client = new GraphQLClient(graphQlEndpoint); const sdk = await create({ @@ -51,7 +48,10 @@ export async function getStaticProps() { storage: ZeitgeistIpfs(), }); - const news = await getCmsNews(); + const [news, cmsTpoics] = await Promise.all([ + getCmsNews(), + getCmsTopicHeaders(), + ]); const [ featuredMarkets, @@ -59,6 +59,7 @@ export async function getStaticProps() { bannerPlaceholder, categoryPlaceholders, newsImagePlaceholders, + topicImagePlaceholders, stats, ztgHistory, chainProperties, @@ -74,6 +75,10 @@ export async function getStaticProps() { news.map((slide) => slide.image ?? ""), { size: 16 }, ), + getPlaiceholders( + cmsTpoics.map((topic) => topic.thumbnail ?? ""), + { size: 16 }, + ), getNetworkStats(sdk), getZTGHistory(), sdk.api.rpc.system.properties(), @@ -110,9 +115,11 @@ export async function getStaticProps() { bannerPlaceholder: bannerPlaceholder.base64 ?? "", categoryPlaceholders: categoryPlaceholders.map((c) => c.base64) ?? [], newsImagePlaceholders: newsImagePlaceholders.map((c) => c.base64) ?? [], + topicImagePlaceholders: topicImagePlaceholders.map((c) => c.base64) ?? [], stats, ztgHistory, chainProperties: chainProperties.toPrimitive(), + cmsTpoics, }, revalidate: environment === "production" @@ -127,10 +134,12 @@ const IndexPage: NextPage<{ trendingMarkets: IndexedMarketCardData[]; categoryPlaceholders: string[]; newsImagePlaceholders: string[]; + topicImagePlaceholders: string[]; bannerPlaceholder: string; stats: { marketCount: number; tradersCount: number; volumeUsd: number }; ztgHistory: ZtgPriceHistory; chainProperties: GenericChainProperties; + cmsTpoics: CmsTopicHeader[]; }> = ({ news, trendingMarkets, @@ -138,9 +147,11 @@ const IndexPage: NextPage<{ bannerPlaceholder, categoryPlaceholders, newsImagePlaceholders, + topicImagePlaceholders, stats, ztgHistory, chainProperties, + cmsTpoics, }) => { return ( <> @@ -156,11 +167,10 @@ const IndexPage: NextPage<{ chainProperties={chainProperties} /> -
- +
diff --git a/pages/markets/index.tsx b/pages/markets/index.tsx index 7273138a1..4aa32462a 100644 --- a/pages/markets/index.tsx +++ b/pages/markets/index.tsx @@ -4,16 +4,38 @@ import { QueryClient, dehydrate } from "@tanstack/query-core"; import { getCmsMarketMetadataForAllMarkets } from "lib/cms/markets"; import { marketCmsDatakeyForMarket } from "lib/hooks/queries/cms/useMarketCmsMetadata"; import { environment } from "lib/constants"; +import { CmsTopicHeader, getCmsTopicHeaders } from "lib/cms/topics"; +import { getPlaiceholders } from "lib/util/getPlaiceHolders"; -const MarketsPage: NextPage = () => { - return ; +const MarketsPage: NextPage = ({ + cmsTopics, + cmsTopicPlaceholders, +}: { + cmsTopics: CmsTopicHeader[]; + cmsTopicPlaceholders: string[]; +}) => { + return ( + + ); }; export async function getStaticProps() { const queryClient = new QueryClient(); - const cmsData = await getCmsMarketMetadataForAllMarkets(); - for (const marketCmsData of cmsData) { + const [cmsMarketMetaData, cmsTopics] = await Promise.all([ + getCmsMarketMetadataForAllMarkets(), + getCmsTopicHeaders(), + ]); + + const cmsTopicPlaceholders = await getPlaiceholders( + cmsTopics.map((topic) => topic.thumbnail ?? ""), + { size: 16 }, + ).then((plh) => plh.map((c) => c.base64) ?? []); + + for (const marketCmsData of cmsMarketMetaData) { if (marketCmsData.marketId) { queryClient.setQueryData( marketCmsDatakeyForMarket(marketCmsData.marketId), @@ -25,6 +47,8 @@ export async function getStaticProps() { return { props: { dehydratedState: dehydrate(queryClient), + cmsTopics, + cmsTopicPlaceholders, }, revalidate: environment === "production" diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx new file mode 100644 index 000000000..1b0e5cf39 --- /dev/null +++ b/pages/topics/[topic].tsx @@ -0,0 +1,197 @@ +import { PortableText } from "@portabletext/react"; +import { ZTG, ZeitgeistIpfs, create } from "@zeitgeistpm/sdk"; +import { isNotNull } from "@zeitgeistpm/utility/dist/null"; +import MarketCard, { + IndexedMarketCardData, +} from "components/markets/market-card"; +import Decimal from "decimal.js"; +import { sanity, sanityImageBuilder } from "lib/cms/sanity"; +import { + CmsTopicFullTopic, + getCmsFullTopic, + getCmsTopicHeaders, +} from "lib/cms/topics"; +import { MarketOutcome, MarketOutcomes } from "lib/types/markets"; +import { endpointOptions, graphQlEndpoint } from "lib/constants"; + +import { getCurrentPrediction } from "lib/util/assets"; +import { NextPage } from "next"; +import { useNextSanityImage } from "next-sanity-image"; +import Image from "next/image"; +import Link from "next/link"; + +export async function getStaticPaths() { + // const client = new GraphQLClient(graphQlEndpoint); + // const marketIds = await getRecentMarketIds(client); + + // const paths = marketIds.map((market) => ({ + // params: { marketid: market.toString() }, + // })); + + const cmsTopics = await getCmsTopicHeaders(); + + const paths = cmsTopics.map((topic) => ({ + params: { topic: topic.slug }, + })); + + return { paths, fallback: "blocking" }; +} + +export async function getStaticProps({ + params, +}: { + params: { topic: string }; +}) { + const sdk = await create({ + provider: endpointOptions.map((e) => e.value), + indexer: graphQlEndpoint, + storage: ZeitgeistIpfs(), + }); + + const cmsTopic = await getCmsFullTopic(params.topic); + + const { markets } = await sdk.indexer.markets({ + where: { + marketId_in: cmsTopic.marketIds, + }, + }); + + let marketCardsData = markets + .map((market) => { + if (!market || !market.categories) return; + + const marketCategories: MarketOutcomes = market.categories + .map((category, index) => { + const asset = market.assets[index]; + + if (!asset) return; + + const marketCategory: MarketOutcome = { + name: category.name ?? "", + assetId: market.outcomeAssets[index], + price: asset.price, + }; + + return marketCategory; + }) + .filter(isNotNull); + + const prediction = getCurrentPrediction(market.assets, market); + + const marketCardData: IndexedMarketCardData = { + marketId: market.marketId, + question: market.question ?? "", + creation: market.creation, + img: market.img ?? "", + prediction: prediction, + creator: market.creator, + volume: Number(new Decimal(market?.volume ?? 0).div(ZTG).toFixed(0)), + baseAsset: market.baseAsset, + outcomes: marketCategories, + pool: market.pool ?? null, + neoPool: market.neoPool, + marketType: market.marketType as any, + tags: market.tags?.filter(isNotNull), + status: market.status, + scalarType: (market.scalarType ?? null) as "number" | "date" | null, + endDate: market.period.end, + }; + + return marketCardData; + }) + .filter(isNotNull); + + marketCardsData.sort((a, b) => { + return ( + cmsTopic.marketIds?.findIndex((m) => m === a.marketId) - + cmsTopic.marketIds?.findIndex((m) => m === b.marketId) + ); + }); + + return { + props: { + cmsTopic: cmsTopic ?? null, + markets: marketCardsData, + }, + }; +} + +const TopicPage: NextPage<{ + cmsTopic: CmsTopicFullTopic; + markets: IndexedMarketCardData[]; +}> = ({ cmsTopic, markets }) => { + const [marketOne, marketTwo, marketThree, marketFour, ...restMarkets] = + markets; + + const banner = sanityImageBuilder.image(cmsTopic.banner).url() ?? ""; + + return ( +
+ {cmsTopic.banner && ( +
+ +
+ )} + +
+

{cmsTopic.title}

+ +
+ All Markets +
+ +
+ +
+ +
+ + {markets.length > 4 ? ( + <> +
+
+
+ + +
+
+ + +
+
+
+ {restMarkets.map((market) => ( + + ))} +
+
+ +
+ {markets.map((market) => ( + + ))} +
+ + ) : ( + <> +
+ {markets.map((market) => ( + + ))} +
+ + )} +
+ ); +}; + +export default TopicPage; diff --git a/yarn.lock b/yarn.lock index 7b48d7809..1ba9236d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2447,6 +2447,13 @@ __metadata: languageName: node linkType: hard +"@sanity/image-url@npm:^1.0.2": + version: 1.0.2 + resolution: "@sanity/image-url@npm:1.0.2" + checksum: fbd2a6e7bd1e9b053c5a8d953c35a17f5d00dcfd792ce115fd7dfb237cbff03c7830fdf64e1ae8c6e00b58c8cabc2673f546d587454359ee02be2e2f8a39bd1e + languageName: node + linkType: hard + "@scure/base@npm:1.1.1": version: 1.1.1 resolution: "@scure/base@npm:1.1.1" @@ -4390,6 +4397,7 @@ __metadata: next: ^13.4.19 next-absolute-url: ^1.2.2 next-qrcode: ^2.5.1 + next-sanity-image: ^6.1.1 next-transpile-modules: ^9.1.0 object-hash: ^2.2.0 plaiceholder: ^2.5.0 @@ -9644,6 +9652,19 @@ __metadata: languageName: node linkType: hard +"next-sanity-image@npm:^6.1.1": + version: 6.1.1 + resolution: "next-sanity-image@npm:6.1.1" + dependencies: + "@sanity/image-url": ^1.0.2 + peerDependencies: + "@sanity/client": ^5.0.0 || ^6.0.0 + next: ^13.0.0 || ^14.0.0 + react: ^18.0.0 + checksum: b0a4a3400a46bb70cef121ed6ad29bb060cf187508ffaff75a7a98a143f9cc188ea221916ab63cf730e5cd0fd24b51f37fcb833d936e57c19be006c64a6b1785 + languageName: node + linkType: hard + "next-tick@npm:^1.1.0": version: 1.1.0 resolution: "next-tick@npm:1.1.0" From 6462905b16f8c6f5c3265e19cebf948fc30ae7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Tue, 23 Jan 2024 13:11:00 +0100 Subject: [PATCH 02/13] work --- components/front-page/HeroBanner.tsx | 2 +- components/front-page/Topics.tsx | 106 +++++++++++++++++------ components/markets/market-card/index.tsx | 2 +- components/ui/Carousel.tsx | 106 +++++++++++++++++++++++ lib/cms/topics.ts | 83 +++++++++++++++++- package.json | 2 + pages/index.tsx | 78 ++++++++++++++--- pages/topics/[topic].tsx | 65 +++----------- styles/index.css | 23 ++++- yarn.lock | 30 +++++++ 10 files changed, 399 insertions(+), 98 deletions(-) create mode 100644 components/ui/Carousel.tsx diff --git a/components/front-page/HeroBanner.tsx b/components/front-page/HeroBanner.tsx index 8fb1bc899..fac44c7a6 100644 --- a/components/front-page/HeroBanner.tsx +++ b/components/front-page/HeroBanner.tsx @@ -24,7 +24,7 @@ export const HeroBanner = ({ const prctChange = ((latestPrice - firstPrice) / firstPrice) * 100; return ( -
+

diff --git a/components/front-page/Topics.tsx b/components/front-page/Topics.tsx index 2bd677302..620f6093e 100644 --- a/components/front-page/Topics.tsx +++ b/components/front-page/Topics.tsx @@ -1,43 +1,97 @@ +import Carousel from "components/ui/Carousel"; import { CmsTopicHeader } from "lib/cms/topics"; +import { chunk, isObject, isString } from "lodash-es"; import Image from "next/image"; import Link from "next/link"; export const Topics = ({ topics, imagePlaceholders, + selectedTopic, + onClick, }: { topics: CmsTopicHeader[]; imagePlaceholders: string[]; + selectedTopic?: CmsTopicHeader | string; + onClick?: (topic: CmsTopicHeader) => void; }) => { return ( <> - {topics.map((topic, index) => ( - -
- {`Image +
+ {topics.map((topic, index) => ( +
onClick?.(topic)} + > +
+ {`Image +
+
+

{topic.title}

+
-
-

{topic.title}

-
- - ))} + ))} +
+
+ ( +
+ {topics.map((topic, index) => ( + +
+ {`Image +
+
+

+ {topic.title} +

+
+ + ))} +
+ ))} + /> +
); }; diff --git a/components/markets/market-card/index.tsx b/components/markets/market-card/index.tsx index 206c787c6..65d8f6fca 100644 --- a/components/markets/market-card/index.tsx +++ b/components/markets/market-card/index.tsx @@ -242,7 +242,7 @@ export const MarketCard = ({
; + options?: EmblaOptionsType; +}; + +const Carousel: React.FC = (props) => { + const { slides, options } = props; + const [emblaRef, emblaApi] = useEmblaCarousel(options); + + const { + prevBtnDisabled, + nextBtnDisabled, + onPrevButtonClick, + onNextButtonClick, + } = usePrevNextButtons(emblaApi); + + return ( +
+
+ + +
+ +
+
+
+ {slides.map((slide, index) => ( +
+
+ {slide} +
+
+ ))} +
+
+
+
+ ); +}; + +type UsePrevNextButtonsType = { + prevBtnDisabled: boolean; + nextBtnDisabled: boolean; + onPrevButtonClick: () => void; + onNextButtonClick: () => void; +}; + +export const usePrevNextButtons = ( + emblaApi: EmblaCarouselType | undefined, +): UsePrevNextButtonsType => { + const [prevBtnDisabled, setPrevBtnDisabled] = useState(true); + const [nextBtnDisabled, setNextBtnDisabled] = useState(true); + + const onPrevButtonClick = useCallback(() => { + if (!emblaApi) return; + emblaApi.scrollPrev(); + }, [emblaApi]); + + const onNextButtonClick = useCallback(() => { + if (!emblaApi) return; + emblaApi.scrollNext(); + }, [emblaApi]); + + const onSelect = useCallback((emblaApi: EmblaCarouselType) => { + setPrevBtnDisabled(!emblaApi.canScrollPrev()); + setNextBtnDisabled(!emblaApi.canScrollNext()); + }, []); + + useEffect(() => { + if (!emblaApi) return; + + onSelect(emblaApi); + emblaApi.on("reInit", onSelect); + emblaApi.on("select", onSelect); + }, [emblaApi, onSelect]); + + return { + prevBtnDisabled, + nextBtnDisabled, + onPrevButtonClick, + onNextButtonClick, + }; +}; + +export default Carousel; diff --git a/lib/cms/topics.ts b/lib/cms/topics.ts index 6a95dcd6b..0d9717af3 100644 --- a/lib/cms/topics.ts +++ b/lib/cms/topics.ts @@ -1,7 +1,17 @@ -import groq from "groq"; import type { PortableTextBlock } from "@portabletext/types"; -import { sanity } from "./sanity"; import { SanityImageSource } from "@sanity/image-url/lib/types/types"; +import { ZTG } from "@zeitgeistpm/sdk"; +import { isNotNull } from "@zeitgeistpm/utility/dist/null"; +import { IndexedMarketCardData } from "components/markets/market-card"; +import Decimal from "decimal.js"; +import groq from "groq"; +import { sanity } from "./sanity"; + +import { ZeitgeistIndexer } from "@zeitgeistpm/indexer"; + +import { MarketOutcome, MarketOutcomes } from "lib/types/markets"; + +import { getCurrentPrediction } from "lib/util/assets"; export type CmsTopicHeader = { title: string; @@ -52,3 +62,72 @@ export const getCmsFullTopic = async ( return data[0]; }; + +export const marketsForTopic = async ( + topic: CmsTopicFullTopic | CmsTopicHeader, + indexer: ZeitgeistIndexer, + opts?: { + limit?: number; + }, +) => { + const { markets } = await indexer.markets({ + where: { + marketId_in: topic.marketIds, + }, + limit: opts?.limit, + }); + + let marketCardsData = markets + .map((market) => { + if (!market || !market.categories) return; + + const marketCategories: MarketOutcomes = market.categories + .map((category, index) => { + const asset = market.assets[index]; + + if (!asset) return; + + const marketCategory: MarketOutcome = { + name: category.name ?? "", + assetId: market.outcomeAssets[index], + price: asset.price, + }; + + return marketCategory; + }) + .filter(isNotNull); + + const prediction = getCurrentPrediction(market.assets, market); + + const marketCardData: IndexedMarketCardData = { + marketId: market.marketId, + question: market.question ?? "", + creation: market.creation, + img: market.img ?? "", + prediction: prediction, + creator: market.creator, + volume: Number(new Decimal(market?.volume ?? 0).div(ZTG).toFixed(0)), + baseAsset: market.baseAsset, + outcomes: marketCategories, + pool: market.pool ?? null, + neoPool: market.neoPool, + marketType: market.marketType as any, + tags: market.tags?.filter(isNotNull), + status: market.status, + scalarType: (market.scalarType ?? null) as "number" | "date" | null, + endDate: market.period.end, + }; + + return marketCardData; + }) + .filter(isNotNull); + + marketCardsData.sort((a, b) => { + return ( + topic.marketIds?.findIndex((m) => m === a.marketId) - + topic.marketIds?.findIndex((m) => m === b.marketId) + ); + }); + + return marketCardsData; +}; diff --git a/package.json b/package.json index 3d347e342..8d81ba69c 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,8 @@ "boring-avatars": "^1.6.1", "decimal.js": "^10.4.3", "dotenv": "^9.0.2", + "embla-carousel": "^8.0.0-rc19", + "embla-carousel-react": "^8.0.0-rc19", "fathom-client": "^3.2.0", "flexsearch": "^0.7.21", "font-color-contrast": "^11.1.0", diff --git a/pages/index.tsx b/pages/index.tsx index eda5acc2f..ff9c78640 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,3 +1,4 @@ +import { Disclosure } from "@headlessui/react"; import { GenericChainProperties } from "@polkadot/types"; import { dehydrate, QueryClient } from "@tanstack/react-query"; import { create, ZeitgeistIpfs } from "@zeitgeistpm/sdk"; @@ -12,12 +13,18 @@ import PopularCategories, { } from "components/front-page/PopularCategories"; import { Topics } from "components/front-page/Topics"; import WatchHow from "components/front-page/WatchHow"; -import { IndexedMarketCardData } from "components/markets/market-card"; +import MarketCard, { + IndexedMarketCardData, +} from "components/markets/market-card"; import MarketScroll from "components/markets/MarketScroll"; import { GraphQLClient } from "graphql-request"; import { getCmsMarketMetadataForAllMarkets } from "lib/cms/markets"; import { getCmsNews, CmsNews } from "lib/cms/news"; -import { CmsTopicHeader, getCmsTopicHeaders } from "lib/cms/topics"; +import { + CmsTopicHeader, + getCmsTopicHeaders, + marketsForTopic, +} from "lib/cms/topics"; import { endpointOptions, environment, graphQlEndpoint } from "lib/constants"; import getFeaturedMarkets from "lib/gql/featured-markets"; import { getNetworkStats } from "lib/gql/get-network-stats"; @@ -33,12 +40,14 @@ import { getPlaiceholders } from "lib/util/getPlaiceHolders"; import { NextPage } from "next"; import Image from "next/image"; import Link from "next/link"; +import { useRouter } from "next/router"; import path from "path"; import { getPlaiceholder, IGetPlaiceholderOptions, IGetPlaiceholderReturn, } from "plaiceholder"; +import { useState } from "react"; export async function getStaticProps() { const client = new GraphQLClient(graphQlEndpoint); @@ -48,7 +57,7 @@ export async function getStaticProps() { storage: ZeitgeistIpfs(), }); - const [news, cmsTpoics] = await Promise.all([ + const [news, cmsTopics] = await Promise.all([ getCmsNews(), getCmsTopicHeaders(), ]); @@ -64,6 +73,7 @@ export async function getStaticProps() { ztgHistory, chainProperties, marketsCmsData, + topicsMarkets, ] = await Promise.all([ getFeaturedMarkets(client, sdk), getTrendingMarkets(client, sdk), @@ -76,13 +86,21 @@ export async function getStaticProps() { { size: 16 }, ), getPlaiceholders( - cmsTpoics.map((topic) => topic.thumbnail ?? ""), + cmsTopics.map((topic) => topic.thumbnail ?? ""), { size: 16 }, ), getNetworkStats(sdk), getZTGHistory(), sdk.api.rpc.system.properties(), getCmsMarketMetadataForAllMarkets(), + Promise.all( + cmsTopics.map((topic) => + marketsForTopic(topic, sdk.indexer, { limit: 3 }).then((markets) => ({ + topic, + markets, + })), + ), + ), ]); const queryClient = new QueryClient(); @@ -119,7 +137,8 @@ export async function getStaticProps() { stats, ztgHistory, chainProperties: chainProperties.toPrimitive(), - cmsTpoics, + cmsTopics, + topicsMarkets, }, revalidate: environment === "production" @@ -139,7 +158,11 @@ const IndexPage: NextPage<{ stats: { marketCount: number; tradersCount: number; volumeUsd: number }; ztgHistory: ZtgPriceHistory; chainProperties: GenericChainProperties; - cmsTpoics: CmsTopicHeader[]; + cmsTopics: CmsTopicHeader[]; + topicsMarkets: { + topic: CmsTopicHeader; + markets: IndexedMarketCardData[]; + }[]; }> = ({ news, trendingMarkets, @@ -151,8 +174,13 @@ const IndexPage: NextPage<{ stats, ztgHistory, chainProperties, - cmsTpoics, + cmsTopics, + topicsMarkets, }) => { + const router = useRouter(); + const topicSlug = (router.query?.topic as string) ?? cmsTopics[0]?.slug; + const topic = topicsMarkets.find((t) => t.topic.slug === topicSlug); + return ( <>
-
- +
+
+ { + router.push( + { query: { ...router.query, topic: topic.slug } }, + undefined, + { shallow: true }, + ); + }} + imagePlaceholders={topicImagePlaceholders} + /> +
+ + {topic && topic.topic.marketIds && ( + <> +
+ {topic.markets.map((market) => ( + + ))} +
+ +
+ Go to {topic.topic.title} Markets + ({topic.topic.marketIds?.length ?? 0}) +
+ + + )}
{featuredMarkets.length > 0 && ( diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx index 1b0e5cf39..5287b90a3 100644 --- a/pages/topics/[topic].tsx +++ b/pages/topics/[topic].tsx @@ -6,10 +6,13 @@ import MarketCard, { } from "components/markets/market-card"; import Decimal from "decimal.js"; import { sanity, sanityImageBuilder } from "lib/cms/sanity"; +import { ZeitgeistIndexer } from "@zeitgeistpm/indexer"; import { CmsTopicFullTopic, + CmsTopicHeader, getCmsFullTopic, getCmsTopicHeaders, + marketsForTopic, } from "lib/cms/topics"; import { MarketOutcome, MarketOutcomes } from "lib/types/markets"; import { endpointOptions, graphQlEndpoint } from "lib/constants"; @@ -56,57 +59,7 @@ export async function getStaticProps({ }, }); - let marketCardsData = markets - .map((market) => { - if (!market || !market.categories) return; - - const marketCategories: MarketOutcomes = market.categories - .map((category, index) => { - const asset = market.assets[index]; - - if (!asset) return; - - const marketCategory: MarketOutcome = { - name: category.name ?? "", - assetId: market.outcomeAssets[index], - price: asset.price, - }; - - return marketCategory; - }) - .filter(isNotNull); - - const prediction = getCurrentPrediction(market.assets, market); - - const marketCardData: IndexedMarketCardData = { - marketId: market.marketId, - question: market.question ?? "", - creation: market.creation, - img: market.img ?? "", - prediction: prediction, - creator: market.creator, - volume: Number(new Decimal(market?.volume ?? 0).div(ZTG).toFixed(0)), - baseAsset: market.baseAsset, - outcomes: marketCategories, - pool: market.pool ?? null, - neoPool: market.neoPool, - marketType: market.marketType as any, - tags: market.tags?.filter(isNotNull), - status: market.status, - scalarType: (market.scalarType ?? null) as "number" | "date" | null, - endDate: market.period.end, - }; - - return marketCardData; - }) - .filter(isNotNull); - - marketCardsData.sort((a, b) => { - return ( - cmsTopic.marketIds?.findIndex((m) => m === a.marketId) - - cmsTopic.marketIds?.findIndex((m) => m === b.marketId) - ); - }); + let marketCardsData = await marketsForTopic(cmsTopic, sdk.indexer); return { props: { @@ -124,7 +77,7 @@ const TopicPage: NextPage<{ markets; const banner = sanityImageBuilder.image(cmsTopic.banner).url() ?? ""; - + console.log(cmsTopic.banner); return (
{cmsTopic.banner && ( @@ -151,9 +104,11 @@ const TopicPage: NextPage<{
-
- -
+ {cmsTopic.description && ( +
+ +
+ )} {markets.length > 4 ? ( <> diff --git a/styles/index.css b/styles/index.css index 26dc1d8d1..005753616 100644 --- a/styles/index.css +++ b/styles/index.css @@ -360,7 +360,7 @@ tr td:first-child { } .ztg-transition { - @apply transition duration-200 ease-in-out; + @apply transition duration-300 ease-[cubic-bezier(.35,.29,0,1.31)]; } @keyframes rotation { @@ -801,3 +801,24 @@ tr td:first-child { input:-webkit-autofill { -webkit-box-shadow: 0 0 0 30px theme("colors.nyanza-base") inset !important; } + +.embla { + --slide-spacing: 1rem; + --slide-size: 100%; + --slide-height: 19rem; +} +.embla__viewport { + overflow: hidden; +} +.embla__container { + backface-visibility: hidden; + display: flex; + touch-action: pan-y; + margin-left: calc(var(--slide-spacing) * -1); +} +.embla__slide { + flex: 0 0 var(--slide-size); + min-width: 0; + padding-left: var(--slide-spacing); + position: relative; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 1ba9236d3..8f6e84868 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4381,6 +4381,8 @@ __metadata: cross-env: ^7.0.3 decimal.js: ^10.4.3 dotenv: ^9.0.2 + embla-carousel: ^8.0.0-rc19 + embla-carousel-react: ^8.0.0-rc19 fathom-client: ^3.2.0 flexsearch: ^0.7.21 font-color-contrast: ^11.1.0 @@ -6232,6 +6234,34 @@ __metadata: languageName: node linkType: hard +"embla-carousel-react@npm:^8.0.0-rc19": + version: 8.0.0-rc19 + resolution: "embla-carousel-react@npm:8.0.0-rc19" + dependencies: + embla-carousel: 8.0.0-rc19 + embla-carousel-reactive-utils: 8.0.0-rc19 + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 + checksum: b870dad1180ea741d61e905d188a7c197716d7b90abcfa7adbd7072a6763051acd7511ca10e3a17c9a2c66482281e6d824a0af507c0f70eee14b9b5d600bb7ab + languageName: node + linkType: hard + +"embla-carousel-reactive-utils@npm:8.0.0-rc19": + version: 8.0.0-rc19 + resolution: "embla-carousel-reactive-utils@npm:8.0.0-rc19" + peerDependencies: + embla-carousel: 8.0.0-rc19 + checksum: b66c1601e75c8482af70a7b8476ac98b60995f6b759b9c76ae70a47192c0b14021aa2b4eadadb207d20485c16ef53f51f10148e8c5d8dfbf34538b62154e294a + languageName: node + linkType: hard + +"embla-carousel@npm:8.0.0-rc19, embla-carousel@npm:^8.0.0-rc19": + version: 8.0.0-rc19 + resolution: "embla-carousel@npm:8.0.0-rc19" + checksum: de531482ef3fe8b71e9f114cbed9066f0c3bcf261cdfdfb3686961409c02765b65ad137ab262f690bc1f5b4837e066f6bf9e749e19ef24555769559f01bbba5f + languageName: node + linkType: hard + "emoji-regex@npm:^10.2.1": version: 10.3.0 resolution: "emoji-regex@npm:10.3.0" From f26522c1122d1e4ed062f9110add56b6d2017667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Tue, 23 Jan 2024 13:54:03 +0100 Subject: [PATCH 03/13] order topics by document order rank in cms data --- lib/cms/topics.ts | 4 +-- package.json | 4 +-- yarn.lock | 79 ++++++++++++++++++++--------------------------- 3 files changed, 38 insertions(+), 49 deletions(-) diff --git a/lib/cms/topics.ts b/lib/cms/topics.ts index 0d9717af3..2fd60a11d 100644 --- a/lib/cms/topics.ts +++ b/lib/cms/topics.ts @@ -38,7 +38,7 @@ const topicHeaderFields = groq`{ export const getCmsTopicHeaders = async (): Promise => { const data = await sanity.fetch( - groq`*[_type == "topic"] | order(_createdAt desc) ${topicHeaderFields}`, + groq`*[_type == "topic"] | order(orderRank) ${topicHeaderFields}`, ); return data; @@ -57,7 +57,7 @@ export const getCmsFullTopic = async ( slug: string, ): Promise => { const data = await sanity.fetch( - groq`*[_type == "topic" && slug.current == "${slug}"] ${topicFullFields}`, + groq`*[_type == "topic" && slug.current == "${slug}"] | order(orderRank) ${topicFullFields}`, ); return data[0]; diff --git a/package.json b/package.json index 8d81ba69c..af0077929 100644 --- a/package.json +++ b/package.json @@ -41,11 +41,11 @@ "@web3auth/modal": "^7.0.4", "@yornaath/batshit": "^0.8.0", "@yornaath/batshit-devtools-react": "^0.5.4", - "@zeitgeistpm/augment-api": "2.24.0", + "@zeitgeistpm/augment-api": "3.1.0", "@zeitgeistpm/avatara-nft-sdk": "^1.3.1", "@zeitgeistpm/avatara-react": "^1.3.2", "@zeitgeistpm/avatara-util": "^1.2.0", - "@zeitgeistpm/sdk": "2.51.0", + "@zeitgeistpm/sdk": "3.1.0", "@zeitgeistpm/utility": "^2.24.1", "axios": "^0.21.4", "boring-avatars": "^1.6.1", diff --git a/yarn.lock b/yarn.lock index 8f6e84868..cf5d1d676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4175,25 +4175,14 @@ __metadata: languageName: node linkType: hard -"@zeitgeistpm/augment-api@npm:2.24.0": - version: 2.24.0 - resolution: "@zeitgeistpm/augment-api@npm:2.24.0" - peerDependencies: - "@polkadot/api-base": "*" - "@polkadot/rpc-core": "*" - "@polkadot/types": "*" - checksum: 45079ef8856891b449da9ee4e2a21e2c9cdfb45c7d8818d045a133da42d138ecf7257c9850a056a3b5222b146cc6358a82a8ea2d535e740108561fb4235ed0c7 - languageName: node - linkType: hard - -"@zeitgeistpm/augment-api@npm:^2.26.0": - version: 2.26.0 - resolution: "@zeitgeistpm/augment-api@npm:2.26.0" +"@zeitgeistpm/augment-api@npm:3.1.0, @zeitgeistpm/augment-api@npm:^3.1.0": + version: 3.1.0 + resolution: "@zeitgeistpm/augment-api@npm:3.1.0" peerDependencies: "@polkadot/api-base": "*" "@polkadot/rpc-core": "*" "@polkadot/types": "*" - checksum: e18fa3394246ec2d877dc91df532af03896ea85dd136741bf987c6e8a37ce664a67a1012e211394a01bfc452a520e9c48deb64aee7a1ae9a9e9745c4c7b580e1 + checksum: 40a3bbe7b49d8bb3761cf447eee3d970dd8f9cef841ea06a81b35c1870e5ad0c690fca723fd7ef44a0083dd1fe7cce9f3f035e3d9aa3cd45c7296ae1b936ea9b languageName: node linkType: hard @@ -4263,40 +4252,40 @@ __metadata: languageName: node linkType: hard -"@zeitgeistpm/indexer@npm:^3.22.0": - version: 3.22.0 - resolution: "@zeitgeistpm/indexer@npm:3.22.0" +"@zeitgeistpm/indexer@npm:^4.1.0": + version: 4.1.0 + resolution: "@zeitgeistpm/indexer@npm:4.1.0" dependencies: graphql: ^16.6.0 graphql-request: ^5.0.0 graphql-tag: ^2.12.6 - checksum: cf66a5a00343d8a76cb9a6745e41efc457ab1da6e3a3cec8a0e2a68edc8bee2cee2e0dcd36fc68dcaa22bf7d6671f8375822ebff8ff5b613cf9142b23cabe9e1 + checksum: 1f1a19fe293f40b0f1e608fe0f3d4e61cf264564e8566605964b5f22b02961bcc881b96cc9989ca43e02ff4dd8fe1dc9ac0bc188f161e23dc635cd9b8e5f545b languageName: node linkType: hard -"@zeitgeistpm/rpc@npm:^2.15.0": - version: 2.15.0 - resolution: "@zeitgeistpm/rpc@npm:2.15.0" +"@zeitgeistpm/rpc@npm:^3.1.0": + version: 3.1.0 + resolution: "@zeitgeistpm/rpc@npm:3.1.0" dependencies: - "@zeitgeistpm/augment-api": ^2.26.0 - "@zeitgeistpm/utility": ^2.25.0 + "@zeitgeistpm/augment-api": ^3.1.0 + "@zeitgeistpm/utility": ^3.1.0 peerDependencies: "@polkadot/api": "*" "@polkadot/keyring": "*" "@polkadot/types": "*" - checksum: 2ffe91ca3101d688aa5da2e4dccf31b6c87dfc1c6994c7a8b9ae6a1c64cc274db65273f544ff32441f9801b431af2f499f02482a18669cbc0572a2f1b893144e + checksum: e04907aeddc22c8b3253dfc5771da47e2569cf54350e2278e41d6d2c3b43f4317153aedea7e93990f3f4ea5f135f9b0239364d31f5ddb456cdedf4208159ba01 languageName: node linkType: hard -"@zeitgeistpm/sdk@npm:2.51.0": - version: 2.51.0 - resolution: "@zeitgeistpm/sdk@npm:2.51.0" +"@zeitgeistpm/sdk@npm:3.1.0": + version: 3.1.0 + resolution: "@zeitgeistpm/sdk@npm:3.1.0" dependencies: - "@zeitgeistpm/augment-api": ^2.26.0 - "@zeitgeistpm/indexer": ^3.22.0 - "@zeitgeistpm/rpc": ^2.15.0 - "@zeitgeistpm/utility": ^2.25.0 - "@zeitgeistpm/web3.storage": ^2.16.0 + "@zeitgeistpm/augment-api": ^3.1.0 + "@zeitgeistpm/indexer": ^4.1.0 + "@zeitgeistpm/rpc": ^3.1.0 + "@zeitgeistpm/utility": ^3.1.0 + "@zeitgeistpm/web3.storage": ^3.1.0 cids: ^1.1.9 decimal.js: ^10.4.3 human-object-diff: ^3.0.0 @@ -4310,7 +4299,7 @@ __metadata: "@polkadot/api": "*" "@polkadot/types": "*" "@polkadot/util": "*" - checksum: d24e1e394d4af2b93382c76bd5ee04bb9e9258be93e4ded55eeae9ec48709eafff91960b15aa5f9c670ad9ceda02d9095aa882c08b9877a908e989c82f890017 + checksum: 896f4594f0d5536a113a8ecb659a7e7a1b0bea8f913f5c4c5b97eee2436109a556e07afab2be86f4df399d4de1b52fa0cbc4fe6e8b8b48d079fcaef589940e4f languageName: node linkType: hard @@ -4368,11 +4357,11 @@ __metadata: "@web3auth/modal": ^7.0.4 "@yornaath/batshit": ^0.8.0 "@yornaath/batshit-devtools-react": ^0.5.4 - "@zeitgeistpm/augment-api": 2.24.0 + "@zeitgeistpm/augment-api": 3.1.0 "@zeitgeistpm/avatara-nft-sdk": ^1.3.1 "@zeitgeistpm/avatara-react": ^1.3.2 "@zeitgeistpm/avatara-util": ^1.2.0 - "@zeitgeistpm/sdk": 2.51.0 + "@zeitgeistpm/sdk": 3.1.0 "@zeitgeistpm/utility": ^2.24.1 autoprefixer: 10.2.5 axios: ^0.21.4 @@ -4460,9 +4449,9 @@ __metadata: languageName: node linkType: hard -"@zeitgeistpm/utility@npm:^2.25.0": - version: 2.25.0 - resolution: "@zeitgeistpm/utility@npm:2.25.0" +"@zeitgeistpm/utility@npm:^3.1.0": + version: 3.1.0 + resolution: "@zeitgeistpm/utility@npm:3.1.0" dependencies: decimal.js: ^10.4.3 lodash.omit: ^4.5.0 @@ -4472,15 +4461,15 @@ __metadata: "@polkadot/api": "*" "@polkadot/types": "*" "@polkadot/util": "*" - checksum: eb74e58bc28bfac1eb315f9b008b563e4ae8d1fa70b133d9ef9d16c4fdc7cf7aa731f1d155a5bf26a336b693fb0aea773f558bc60a6a1ec999289b42852fc190 + checksum: d4424be9856e556b09328ea78bcd6ed3cb1eb677ce683a0bac9d5f9f75d4e8f75cf0c9afc44039644746f3abc969412aac19c09338641b210ae12dc5cd2241f1 languageName: node linkType: hard -"@zeitgeistpm/web3.storage@npm:^2.16.0": - version: 2.16.0 - resolution: "@zeitgeistpm/web3.storage@npm:2.16.0" +"@zeitgeistpm/web3.storage@npm:^3.1.0": + version: 3.1.0 + resolution: "@zeitgeistpm/web3.storage@npm:3.1.0" dependencies: - "@zeitgeistpm/utility": ^2.25.0 + "@zeitgeistpm/utility": ^3.1.0 cids: ^1.1.9 ipfs-http-client: ^60.0.0 ipfs-only-hash: ^4.0.0 @@ -4489,7 +4478,7 @@ __metadata: up: ^1.0.2 peerDependencies: "@polkadot/util": "*" - checksum: 3cafd120a3a68c510949369998035eaefbd48c27d6a98efe26e3c92e1e6b1bd429a5e47b314e35f88dff05d03b9b275ebbed40d4fa60494f1ea815a4c58dd2de + checksum: ac408edb67cca9297c84f1aa461463d9c6b1cdd8e97149fdb45e8291e9e15a2690250e5692be3c4a64a48714acc4a5c9bcf3427f75b70887c2a35bb543c6d6f3 languageName: node linkType: hard From 8802823f53835065433aa82b9a1eff1dc591c035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Wed, 24 Jan 2024 12:53:09 +0100 Subject: [PATCH 04/13] work --- components/front-page/Topics.tsx | 11 ++++++- lib/cms/topics.ts | 21 ++++++------- lib/util/assets.ts | 4 +++ pages/topics/[topic].tsx | 52 +++++++++++--------------------- 4 files changed, 41 insertions(+), 47 deletions(-) diff --git a/components/front-page/Topics.tsx b/components/front-page/Topics.tsx index 620f6093e..278b1815b 100644 --- a/components/front-page/Topics.tsx +++ b/components/front-page/Topics.tsx @@ -3,6 +3,7 @@ import { CmsTopicHeader } from "lib/cms/topics"; import { chunk, isObject, isString } from "lodash-es"; import Image from "next/image"; import Link from "next/link"; +import { useRouter } from "next/router"; export const Topics = ({ topics, @@ -15,6 +16,8 @@ export const Topics = ({ selectedTopic?: CmsTopicHeader | string; onClick?: (topic: CmsTopicHeader) => void; }) => { + const router = useRouter(); + return ( <>
@@ -30,7 +33,13 @@ export const Topics = ({ : "bg-white" } `} - onClick={() => onClick?.(topic)} + onClick={() => { + if (onClick) { + onClick(topic); + } else { + router.push(`/topics/${topic.slug}`); + } + }} >
=> { - const data = await sanity.fetch( +export const getCmsFullTopic = async (slug: string): Promise => { + const data = await sanity.fetch( groq`*[_type == "topic" && slug.current == "${slug}"] | order(orderRank) ${topicFullFields}`, ); @@ -64,7 +59,7 @@ export const getCmsFullTopic = async ( }; export const marketsForTopic = async ( - topic: CmsTopicFullTopic | CmsTopicHeader, + topic: CmsTopicFull | CmsTopicHeader, indexer: ZeitgeistIndexer, opts?: { limit?: number; @@ -97,6 +92,10 @@ export const marketsForTopic = async ( }) .filter(isNotNull); + if (market.assets.length < 2) { + console.log(market); + } + const prediction = getCurrentPrediction(market.assets, market); const marketCardData: IndexedMarketCardData = { diff --git a/lib/util/assets.ts b/lib/util/assets.ts index f6a3046ca..737009f4e 100644 --- a/lib/util/assets.ts +++ b/lib/util/assets.ts @@ -14,6 +14,10 @@ export const getCurrentPrediction = ( ): { name: string; price: number; percentage: number } => { const totalPrice = assets.reduce((acc, asset) => acc + asset.price, 0); + if (assets?.length < 2) { + return { name: "N/A", price: 0, percentage: 0 }; + } + if (market?.marketType?.categorical) { let [highestPrice, highestPriceIndex] = [0, 0]; assets.sort( diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx index 5287b90a3..d09e26fe9 100644 --- a/pages/topics/[topic].tsx +++ b/pages/topics/[topic].tsx @@ -1,36 +1,21 @@ import { PortableText } from "@portabletext/react"; -import { ZTG, ZeitgeistIpfs, create } from "@zeitgeistpm/sdk"; -import { isNotNull } from "@zeitgeistpm/utility/dist/null"; +import { ZeitgeistIpfs, create } from "@zeitgeistpm/sdk"; import MarketCard, { IndexedMarketCardData, } from "components/markets/market-card"; -import Decimal from "decimal.js"; -import { sanity, sanityImageBuilder } from "lib/cms/sanity"; -import { ZeitgeistIndexer } from "@zeitgeistpm/indexer"; +import { sanityImageBuilder } from "lib/cms/sanity"; import { - CmsTopicFullTopic, - CmsTopicHeader, + CmsTopicFull, getCmsFullTopic, getCmsTopicHeaders, marketsForTopic, } from "lib/cms/topics"; -import { MarketOutcome, MarketOutcomes } from "lib/types/markets"; import { endpointOptions, graphQlEndpoint } from "lib/constants"; - -import { getCurrentPrediction } from "lib/util/assets"; import { NextPage } from "next"; -import { useNextSanityImage } from "next-sanity-image"; import Image from "next/image"; import Link from "next/link"; export async function getStaticPaths() { - // const client = new GraphQLClient(graphQlEndpoint); - // const marketIds = await getRecentMarketIds(client); - - // const paths = marketIds.map((market) => ({ - // params: { marketid: market.toString() }, - // })); - const cmsTopics = await getCmsTopicHeaders(); const paths = cmsTopics.map((topic) => ({ @@ -52,13 +37,6 @@ export async function getStaticProps({ }); const cmsTopic = await getCmsFullTopic(params.topic); - - const { markets } = await sdk.indexer.markets({ - where: { - marketId_in: cmsTopic.marketIds, - }, - }); - let marketCardsData = await marketsForTopic(cmsTopic, sdk.indexer); return { @@ -70,27 +48,31 @@ export async function getStaticProps({ } const TopicPage: NextPage<{ - cmsTopic: CmsTopicFullTopic; + cmsTopic: CmsTopicFull; markets: IndexedMarketCardData[]; }> = ({ cmsTopic, markets }) => { const [marketOne, marketTwo, marketThree, marketFour, ...restMarkets] = markets; - const banner = sanityImageBuilder.image(cmsTopic.banner).url() ?? ""; - console.log(cmsTopic.banner); + const banner = cmsTopic.banner + ? sanityImageBuilder.image(cmsTopic.banner) + : undefined; + return (
- {cmsTopic.banner && ( + {banner && (
)} From 5517c93f633bfe8f5514e1b43ce2df588b1c5fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Wed, 24 Jan 2024 13:00:24 +0100 Subject: [PATCH 05/13] Update [topic].tsx --- pages/topics/[topic].tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx index d09e26fe9..e9d93a153 100644 --- a/pages/topics/[topic].tsx +++ b/pages/topics/[topic].tsx @@ -68,7 +68,7 @@ const TopicPage: NextPage<{ fill objectFit="cover" placeholder="blur" - blurDataURL={banner.blur(300).url()} + blurDataURL={banner.blur(130).url()} className="rounded-lg transition-all" objectPosition={`${(cmsTopic.banner.hotspot?.x ?? 0.5) * 100}% ${ (cmsTopic.banner.hotspot?.y ?? 0.5) * 100 From b05b16ff4eaeeb60a87c54835d0c5bd3f61d35da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Wed, 24 Jan 2024 13:19:58 +0100 Subject: [PATCH 06/13] blurdata --- lib/cms/topics.ts | 2 ++ pages/topics/[topic].tsx | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/cms/topics.ts b/lib/cms/topics.ts index 646edd128..687682fc5 100644 --- a/lib/cms/topics.ts +++ b/lib/cms/topics.ts @@ -23,6 +23,7 @@ export type CmsTopicFull = { slug: string; thumbnail: string; banner: SanityImageObject; + bannerBlurData: string; marketIds: number[]; }; @@ -47,6 +48,7 @@ const topicFullFields = groq`{ "slug": slug.current, "thumbnail": thumbnail.asset->url, "banner": banner, + "bannerBlurData": banner.asset->metadata.lqip, "marketIds": markets[].marketId }`; diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx index e9d93a153..63d5758a4 100644 --- a/pages/topics/[topic].tsx +++ b/pages/topics/[topic].tsx @@ -58,6 +58,15 @@ const TopicPage: NextPage<{ ? sanityImageBuilder.image(cmsTopic.banner) : undefined; + let blur = {}; + + if (cmsTopic.banner && cmsTopic.bannerBlurData) { + blur = { + placeholder: "blur", + blurDataURL: cmsTopic.bannerBlurData, + }; + } + return (
{banner && ( @@ -67,12 +76,11 @@ const TopicPage: NextPage<{ src={banner.url()} fill objectFit="cover" - placeholder="blur" - blurDataURL={banner.blur(130).url()} className="rounded-lg transition-all" objectPosition={`${(cmsTopic.banner.hotspot?.x ?? 0.5) * 100}% ${ (cmsTopic.banner.hotspot?.y ?? 0.5) * 100 }%`} + {...blur} />
)} From b31aa967adf7713c63b1c20e65c103fe12e6214a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Mon, 29 Jan 2024 11:08:59 +0100 Subject: [PATCH 07/13] same animation on topic card hover as on market card hover --- components/front-page/Topics.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/front-page/Topics.tsx b/components/front-page/Topics.tsx index 278b1815b..4d1cddabb 100644 --- a/components/front-page/Topics.tsx +++ b/components/front-page/Topics.tsx @@ -25,7 +25,7 @@ export const Topics = ({
Date: Wed, 31 Jan 2024 14:44:05 +0100 Subject: [PATCH 08/13] Update lib/cms/topics.ts Co-authored-by: Tom Robiquet --- lib/cms/topics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cms/topics.ts b/lib/cms/topics.ts index 687682fc5..cadcde3fd 100644 --- a/lib/cms/topics.ts +++ b/lib/cms/topics.ts @@ -74,7 +74,7 @@ export const marketsForTopic = async ( limit: opts?.limit, }); - let marketCardsData = markets + const marketCardsData = markets .map((market) => { if (!market || !market.categories) return; From 84807a3767e55e126df5e6233ab44eeabdaa34ad Mon Sep 17 00:00:00 2001 From: Yornaath Date: Wed, 31 Jan 2024 14:44:13 +0100 Subject: [PATCH 09/13] Update pages/topics/[topic].tsx Co-authored-by: Tom Robiquet --- pages/topics/[topic].tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx index 63d5758a4..b4c496ac1 100644 --- a/pages/topics/[topic].tsx +++ b/pages/topics/[topic].tsx @@ -37,7 +37,7 @@ export async function getStaticProps({ }); const cmsTopic = await getCmsFullTopic(params.topic); - let marketCardsData = await marketsForTopic(cmsTopic, sdk.indexer); + const marketCardsData = await marketsForTopic(cmsTopic, sdk.indexer); return { props: { From 2e1ba2c4f97c119b06a72a81fdfb3257023762a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Fri, 2 Feb 2024 10:40:55 +0100 Subject: [PATCH 10/13] feature flag topics --- components/markets/MarketsList.tsx | 8 ++-- pages/index.tsx | 62 +++++++++++++++--------------- pages/topics/[topic].tsx | 5 +++ 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/components/markets/MarketsList.tsx b/components/markets/MarketsList.tsx index 39e8b29bf..425207cad 100644 --- a/components/markets/MarketsList.tsx +++ b/components/markets/MarketsList.tsx @@ -104,9 +104,11 @@ const MarketsList = ({ data-testid="marketsList" id={"market-list"} > -
- -
+ {process.env.NEXT_PUBLIC_SHOW_TOPICS === "true" && ( +
+ +
+ )} -
-
- { - router.push( - { query: { ...router.query, topic: topic.slug } }, - undefined, - { shallow: true }, - ); - }} - imagePlaceholders={topicImagePlaceholders} - /> -
+ {process.env.NEXT_PUBLIC_SHOW_TOPICS === "true" && ( +
+
+ { + router.push( + { query: { ...router.query, topic: topic.slug } }, + undefined, + { shallow: true }, + ); + }} + imagePlaceholders={topicImagePlaceholders} + /> +
- {topic && topic.topic.marketIds && ( - <> -
- {topic.markets.map((market) => ( - - ))} -
- -
- Go to {topic.topic.title} Markets - ({topic.topic.marketIds?.length ?? 0}) + {topic && topic.topic.marketIds && ( + <> +
+ {topic.markets.map((market) => ( + + ))}
- - - )} -
+ +
+ Go to {topic.topic.title}{" "} + Markets ({topic.topic.marketIds?.length ?? 0}) +
+ + + )} +
+ )} {featuredMarkets.length > 0 && (
diff --git a/pages/topics/[topic].tsx b/pages/topics/[topic].tsx index b4c496ac1..a6620fe6c 100644 --- a/pages/topics/[topic].tsx +++ b/pages/topics/[topic].tsx @@ -14,6 +14,7 @@ import { endpointOptions, graphQlEndpoint } from "lib/constants"; import { NextPage } from "next"; import Image from "next/image"; import Link from "next/link"; +import NotFoundPage from "pages/404"; export async function getStaticPaths() { const cmsTopics = await getCmsTopicHeaders(); @@ -51,6 +52,10 @@ const TopicPage: NextPage<{ cmsTopic: CmsTopicFull; markets: IndexedMarketCardData[]; }> = ({ cmsTopic, markets }) => { + if (process.env.NEXT_PUBLIC_SHOW_TOPICS !== "true") { + return ; + } + const [marketOne, marketTwo, marketThree, marketFour, ...restMarkets] = markets; From 37e51ae4354c029da344fe65d145d3c5c4774c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Fri, 2 Feb 2024 10:42:22 +0100 Subject: [PATCH 11/13] evn examples --- .env.example | 4 ++++ lib/cms/topics.ts | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 9deb1c62c..bb01b4c2d 100644 --- a/.env.example +++ b/.env.example @@ -48,6 +48,10 @@ NEXT_PUBLIC_OTHER_TAGS=["Coindesk"] NEXT_PUBLIC_COIN_GECKO_API_KEY= +# sanity cms config NEXT_PUBLIC_SANITY_PROJECT_ID="4wbnjof1" NEXT_PUBLIC_SANITY_VERSION="2022-03-07" +# enable topics +NEXT_PUBLIC_SHOW_TOPICS=true + diff --git a/lib/cms/topics.ts b/lib/cms/topics.ts index cadcde3fd..f1bfebd90 100644 --- a/lib/cms/topics.ts +++ b/lib/cms/topics.ts @@ -94,10 +94,6 @@ export const marketsForTopic = async ( }) .filter(isNotNull); - if (market.assets.length < 2) { - console.log(market); - } - const prediction = getCurrentPrediction(market.assets, market); const marketCardData: IndexedMarketCardData = { From 94ae07f186f935934475e12e522550db0b57da9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Fri, 2 Feb 2024 10:47:30 +0100 Subject: [PATCH 12/13] fetch stats(liq and participants) for topic market cards --- lib/cms/topics.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/cms/topics.ts b/lib/cms/topics.ts index f1bfebd90..fe2e130f6 100644 --- a/lib/cms/topics.ts +++ b/lib/cms/topics.ts @@ -9,6 +9,7 @@ import { sanity } from "./sanity"; import { ZeitgeistIndexer } from "@zeitgeistpm/indexer"; import { MarketOutcome, MarketOutcomes } from "lib/types/markets"; import { getCurrentPrediction } from "lib/util/assets"; +import { getMarketsStats } from "lib/gql/markets-stats"; export type CmsTopicHeader = { title: string; @@ -74,6 +75,11 @@ export const marketsForTopic = async ( limit: opts?.limit, }); + const stats = await getMarketsStats( + indexer.client, + markets.map((m) => m.marketId), + ); + const marketCardsData = markets .map((market) => { if (!market || !market.categories) return; @@ -113,6 +119,9 @@ export const marketsForTopic = async ( status: market.status, scalarType: (market.scalarType ?? null) as "number" | "date" | null, endDate: market.period.end, + numParticipants: stats.find((s) => s.marketId === market.marketId) + ?.participants, + liquidity: stats.find((s) => s.marketId === market.marketId)?.liquidity, }; return marketCardData; From f882610cad141525a01316ba6000f755518b902d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Andre=20Tangen=20=40gorillatron?= Date: Fri, 2 Feb 2024 10:49:59 +0100 Subject: [PATCH 13/13] use link for topic cards when linking to topic page --- components/front-page/Topics.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/front-page/Topics.tsx b/components/front-page/Topics.tsx index 4d1cddabb..4b0653684 100644 --- a/components/front-page/Topics.tsx +++ b/components/front-page/Topics.tsx @@ -22,8 +22,9 @@ export const Topics = ({ <>
{topics.map((topic, index) => ( -
{ + onClick={(e) => { if (onClick) { + e.preventDefault(); onClick(topic); } else { router.push(`/topics/${topic.slug}`); @@ -60,7 +62,7 @@ export const Topics = ({

{topic.title}

-
+ ))}