+
+ {
+ 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/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..a6620fe6c
--- /dev/null
+++ b/pages/topics/[topic].tsx
@@ -0,0 +1,147 @@
+import { PortableText } from "@portabletext/react";
+import { ZeitgeistIpfs, create } from "@zeitgeistpm/sdk";
+import MarketCard, {
+ IndexedMarketCardData,
+} from "components/markets/market-card";
+import { sanityImageBuilder } from "lib/cms/sanity";
+import {
+ CmsTopicFull,
+ getCmsFullTopic,
+ getCmsTopicHeaders,
+ marketsForTopic,
+} from "lib/cms/topics";
+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();
+
+ 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 marketCardsData = await marketsForTopic(cmsTopic, sdk.indexer);
+
+ return {
+ props: {
+ cmsTopic: cmsTopic ?? null,
+ markets: marketCardsData,
+ },
+ };
+}
+
+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;
+
+ const banner = cmsTopic.banner
+ ? sanityImageBuilder.image(cmsTopic.banner)
+ : undefined;
+
+ let blur = {};
+
+ if (cmsTopic.banner && cmsTopic.bannerBlurData) {
+ blur = {
+ placeholder: "blur",
+ blurDataURL: cmsTopic.bannerBlurData,
+ };
+ }
+
+ return (
+
+ {banner && (
+
+
+
+ )}
+
+
+
{cmsTopic.title}
+
+
+ All Markets
+
+
+
+
+ {cmsTopic.description && (
+
+ )}
+
+ {markets.length > 4 ? (
+ <>
+
+
+
+ {restMarkets.map((market) => (
+
+ ))}
+
+
+
+
+ {markets.map((market) => (
+
+ ))}
+
+ >
+ ) : (
+ <>
+
+ {markets.map((market) => (
+
+ ))}
+
+ >
+ )}
+
+ );
+};
+
+export default TopicPage;
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 b13c2ad04..4e95a7528 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"
@@ -4363,6 +4370,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
@@ -4379,6 +4388,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
@@ -6197,6 +6207,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"
@@ -9617,6 +9655,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"