-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from oxen-io/sanity_cms_schemas_and_components
Sanity Portable Text & Site Schemas
- Loading branch information
Showing
132 changed files
with
5,269 additions
and
244 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
NEXT_PUBLIC_ENV_FLAG= pick from dev, qa, stg, prd | ||
NEXT_PUBLIC_SANITY_DATASET= | ||
NEXT_PUBLIC_SANITY_PROJECT_ID= | ||
SANITY_API_READ_TOKEN= | ||
SANITY_REVALIDATE_SECRET= | ||
NEXT_PUBLIC_SANITY_API_VERSION="2024-09-30" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** @type {import("eslint").Linter.Config} */ | ||
module.exports = { | ||
root: true, | ||
extends: ['@session/eslint-config/next.js'], | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
project: true, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Session Technology Foundation Website | ||
|
||
The Session Technology Foundation Website is a [Next.js](https://nextjs.org/) app. | ||
|
||
## Getting Started | ||
|
||
You can follow the generic instructions in the root [README.md](../../README.md#getting-started) to get started. | ||
|
||
## Development | ||
|
||
Running the app requires several environment variables to be set. See the [.env.local.template](.env.local.template) | ||
file for a list | ||
of required variables. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import type { ReactNode } from 'react'; | ||
import '@session/ui/styles'; | ||
|
||
export default function SanityLayout({ children }: { children: ReactNode }) { | ||
return ( | ||
<html lang="en"> | ||
<body>{children}</body> | ||
</html> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
'use client'; | ||
|
||
import SanityStudio from '@session/sanity-cms/components/SanityStudio'; | ||
import { sanityConfig } from '@/lib/sanity/sanity.config'; | ||
|
||
export default function Studio() { | ||
return <SanityStudio config={sanityConfig} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Loading } from '@session/ui/components/loading'; | ||
import { SanityStudioSSRPage } from '@session/sanity-cms/components/SanityStudioSSRPage'; | ||
import Studio from '@/app/(Sanity)/studio/[[...tool]]/Studio'; | ||
|
||
export default function StudioPage() { | ||
return <SanityStudioSSRPage sanityStudio={<Studio />} suspenseFallback={<Loading />} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { ReactNode } from 'react'; | ||
import { Footer } from '@/components/Footer'; | ||
import { getInitialSiteDataForSSR } from '@/lib/sanity/sanity-server'; | ||
|
||
export default async function UniversalPageLayout({ children }: { children: ReactNode }) { | ||
const { settings } = await getInitialSiteDataForSSR(); | ||
return ( | ||
<> | ||
{children} | ||
<Footer className="max-w-screen-md" {...settings} /> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { getPageBySlug } from '@session/sanity-cms/queries/getPage'; | ||
import { client } from '@/lib/sanity/sanity.client'; | ||
import { getPagesSlugs } from '@session/sanity-cms/queries/getPages'; | ||
import { notFound } from 'next/navigation'; | ||
import { getLandingPageSlug } from '@/lib/sanity/sanity-server'; | ||
import PortableText from '@/components/PortableText'; | ||
import logger from '@/lib/logger'; | ||
import { NEXTJS_EXPLICIT_IGNORED_ROUTES, NEXTJS_IGNORED_PATTERNS } from '@/lib/constants'; | ||
|
||
/** | ||
* Force static rendering and cache the data of a layout or page by forcing `cookies()`, `headers()` | ||
* and `useSearchParams()` to return empty values. | ||
* @see {@link https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic} | ||
*/ | ||
export const dynamic = 'force-static'; | ||
/** | ||
* Dynamic segments not included in generateStaticParams are generated on demand. | ||
* @see {@link https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams} | ||
*/ | ||
export const dynamicParams = true; | ||
|
||
export async function generateStaticParams() { | ||
const pages = await getPagesSlugs({ client }); | ||
const slugs = new Set(pages.map((page) => page.slug.current)); | ||
|
||
const landingPageSlug = await getLandingPageSlug(); | ||
if (landingPageSlug) { | ||
slugs.delete(landingPageSlug); | ||
} else { | ||
console.warn('No landing page set in settings to statically generate'); | ||
} | ||
|
||
const pagesToGenerate = Array.from(slugs); | ||
logger.info(`Generating static params for ${pagesToGenerate.length} pages`); | ||
logger.info(pagesToGenerate); | ||
return pagesToGenerate; | ||
} | ||
|
||
type PageProps = { | ||
params: { slug?: string }; | ||
}; | ||
|
||
export default async function UniversalPage({ params }: PageProps) { | ||
const slug = params.slug; | ||
if (!slug) return notFound(); | ||
|
||
if ( | ||
NEXTJS_EXPLICIT_IGNORED_ROUTES.includes(slug) || | ||
NEXTJS_IGNORED_PATTERNS.some((pattern) => slug.includes(pattern)) | ||
) { | ||
return; | ||
} | ||
|
||
logger.info(`Generating page for slug ${slug}`); | ||
|
||
const page = await getPageBySlug({ | ||
client, | ||
slug, | ||
}); | ||
|
||
if (!page) return notFound(); | ||
|
||
return <PortableText body={page.body} className="max-w-screen-md" wrapperComponent="main" />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import PortableText from '@/components/PortableText'; | ||
import { getLocale, getTranslations } from 'next-intl/server'; | ||
import { Button } from '@session/ui/ui/button'; | ||
import { cn } from '@session/ui/lib/utils'; | ||
import { getLangDir } from 'rtl-detect'; | ||
import Link from 'next/link'; | ||
import { ButtonDataTestId } from '@/testing/data-test-ids'; | ||
import { SANITY_SCHEMA_URL } from '@/lib/constants'; | ||
import type { FormattedPostType } from '@session/sanity-cms/queries/getPost'; | ||
import { notFound } from 'next/navigation'; | ||
import logger from '@/lib/logger'; | ||
import PostInfoBlock from '@/app/(Site)/blog/[slug]/PostInfoBlock'; | ||
import HeadingOutline from '@/app/(Site)/blog/[slug]/HeadingOutline'; | ||
|
||
export type PostProps = { | ||
post: FormattedPostType; | ||
}; | ||
|
||
export default async function BlogPost({ post }: PostProps) { | ||
const blogDictionary = await getTranslations('blog'); | ||
const locale = await getLocale(); | ||
const direction = getLangDir(locale); | ||
|
||
const body = post.body; | ||
|
||
if (!body) { | ||
logger.error(`No body found for post: ${post.slug}`); | ||
return notFound(); | ||
} | ||
|
||
const allH2s = body.filter((block) => block._type === 'block' && block.style === 'h2'); | ||
|
||
const headings: Array<string> = allH2s | ||
.map((block) => | ||
'children' in block && Array.isArray(block.children) ? block.children[0].text : null | ||
) | ||
.filter(Boolean); | ||
|
||
return ( | ||
<article className="mx-auto mb-32 mt-4 flex max-w-screen-xl flex-col"> | ||
<Link href={SANITY_SCHEMA_URL.POST} prefetch> | ||
<Button | ||
data-testid={ButtonDataTestId.Back_To_Blog} | ||
className={cn('text-session-text-black-secondary my-2 gap-2 fill-current px-1')} | ||
size="sm" | ||
rounded="md" | ||
variant="ghost" | ||
> | ||
<span className={cn(direction === 'rtl' && 'rotate-180')}>←</span> | ||
{blogDictionary('backToBlog')} | ||
</Button> | ||
</Link> | ||
<PostInfoBlock | ||
className="w-full" | ||
postInfo={post} | ||
renderWithPriority | ||
mobileImagePosition="below" | ||
/> | ||
<div className="mt-6 flex flex-row justify-center gap-12 md:mt-12"> | ||
<PortableText body={body} className="max-w-screen-md" wrapperComponent="section" /> | ||
{headings.length ? ( | ||
<HeadingOutline headings={headings} title={blogDictionary('inThisArticle')} /> | ||
) : null} | ||
</div> | ||
</article> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use client'; | ||
|
||
import Typography from '@session/ui/components/Typography'; | ||
import { cn } from '@session/ui/lib/utils'; | ||
import { navlinkVariants } from '@session/ui/components/NavLink'; | ||
|
||
function scrollToHeading(text: string) { | ||
document.querySelectorAll('h2').forEach((heading) => { | ||
if (text && heading.textContent && heading.textContent === text) { | ||
heading.scrollIntoView({ | ||
behavior: 'smooth', | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
type HeadingOutlineProps = { | ||
headings: Array<string>; | ||
title: string; | ||
}; | ||
|
||
export default function HeadingOutline({ title, headings }: HeadingOutlineProps) { | ||
return ( | ||
<nav className="wrap sticky top-12 mt-7 hidden h-max w-max max-w-[25vw] lg:block"> | ||
<Typography variant="h2" className="mb-3"> | ||
{title} | ||
</Typography> | ||
<ul className="text-session-text-black-secondary flex flex-col gap-2"> | ||
{headings.map((heading) => ( | ||
<li key={`scroll-to-${heading}`}> | ||
<button | ||
onClick={() => { | ||
scrollToHeading(heading); | ||
}} | ||
className={cn(navlinkVariants({ active: false }), 'w-full text-wrap text-start')} | ||
> | ||
{heading} | ||
</button> | ||
</li> | ||
))} | ||
</ul> | ||
</nav> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { SanityImage } from '@session/sanity-cms/components/SanityImage'; | ||
import { client } from '@/lib/sanity/sanity.client'; | ||
import Typography from '@session/ui/components/Typography'; | ||
import { getLocale } from 'next-intl/server'; | ||
import { cn } from '@session/ui/lib/utils'; | ||
import { safeTry } from '@session/util-js/try'; | ||
import logger from '@/lib/logger'; | ||
import type { FormattedPostType } from '@session/sanity-cms/queries/getPost'; | ||
import type { ReactNode } from 'react'; | ||
|
||
const getLocalizedPosedDate = async (date: Date) => { | ||
const locale = await getLocale(); | ||
return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long', day: 'numeric' }).format( | ||
date | ||
); | ||
}; | ||
export type PostBlockProps = { | ||
postInfo: Pick<FormattedPostType, 'title' | 'summary' | 'featuredImage' | 'author' | 'date'>; | ||
renderWithPriority?: boolean; | ||
mobileImagePosition?: 'above' | 'below'; | ||
columnAlways?: boolean; | ||
className?: string; | ||
children?: ReactNode; | ||
}; | ||
|
||
export default async function PostInfoBlock({ | ||
postInfo, | ||
renderWithPriority, | ||
mobileImagePosition = 'above', | ||
columnAlways, | ||
className, | ||
children, | ||
}: PostBlockProps) { | ||
const { title, summary, featuredImage, author, date } = postInfo; | ||
|
||
let localizedPublishedAt = null; | ||
if (date) { | ||
const [err, res] = await safeTry(getLocalizedPosedDate(date)); | ||
if (err) { | ||
logger.error(err); | ||
localizedPublishedAt = date.toLocaleDateString(); | ||
} else { | ||
localizedPublishedAt = res; | ||
} | ||
} | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
'flex w-full items-center gap-8', | ||
columnAlways ? 'flex-col' : 'md:grid md:grid-cols-2', | ||
mobileImagePosition === 'below' ? 'flex-col-reverse' : 'flex-col', | ||
className | ||
)} | ||
> | ||
<div className="aspect-video w-full overflow-hidden rounded-lg"> | ||
<SanityImage | ||
className="h-full" | ||
client={client} | ||
value={featuredImage} | ||
cover | ||
renderWithPriority={renderWithPriority} | ||
/> | ||
</div> | ||
<div className="flex w-full flex-col gap-2"> | ||
<Typography variant={columnAlways ? 'h2' : 'h1'} className="w-full"> | ||
{title} | ||
</Typography> | ||
<span className="text-session-text-black-secondary inline-flex w-full gap-1"> | ||
{date ? <time dateTime={date.toISOString()}>{localizedPublishedAt}</time> : null} | ||
{date ? '/' : null} | ||
{author?.name ? <address>{author.name}</address> : null} | ||
</span> | ||
{summary ? <p className="w-full">{summary}</p> : null} | ||
{children} | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.