-
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 #59 from oxen-io/foundation_site
Session Foundation Website
- Loading branch information
Showing
217 changed files
with
12,604 additions
and
1,118 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
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,15 @@ | ||
import type { ReactNode } from 'react'; | ||
import '@session/ui/styles'; | ||
import type { Metadata } from 'next'; | ||
|
||
export const metadata: Metadata = { | ||
title: 'STF | Sanity Studio', | ||
}; | ||
|
||
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,126 @@ | ||
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'; | ||
import type { Metadata, ResolvingMetadata } from 'next'; | ||
import { generateSanityMetadata } from '@session/sanity-cms/lib/metadata'; | ||
import { getFileBySlug } from '@session/sanity-cms/queries/getFile'; | ||
import FileDownload from '@session/sanity-cms/components/SanityFileDownload'; | ||
import { getTranslations } from 'next-intl/server'; | ||
|
||
/** | ||
* 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 generateMetadata( | ||
{ params }: PageProps, | ||
parent: ResolvingMetadata | ||
): Promise<Metadata> { | ||
const slug = params.slug; | ||
if (!slug) { | ||
logger.warn(`No slug provided for metadata generation`); | ||
return {}; | ||
} | ||
|
||
if ( | ||
NEXTJS_EXPLICIT_IGNORED_ROUTES.includes(slug) || | ||
NEXTJS_IGNORED_PATTERNS.some((pattern) => slug.includes(pattern)) | ||
) { | ||
return {}; | ||
} | ||
|
||
logger.info(`Generating metadata for slug ${slug}`); | ||
|
||
const page = await getPageBySlug({ client, slug }); | ||
|
||
if (!page) { | ||
logger.warn(`No page found for slug ${slug}`); | ||
return {}; | ||
} | ||
const parentMetadata = await parent; | ||
return generateSanityMetadata(client, { | ||
seo: page.seo, | ||
parentMetadata, | ||
type: 'website', | ||
}); | ||
} | ||
|
||
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) { | ||
const file = await getFileBySlug({ | ||
client, | ||
slug, | ||
}); | ||
|
||
if (file?.src && file?.fileName) { | ||
const fileDictionary = await getTranslations('fileDownload'); | ||
return ( | ||
<FileDownload | ||
fileName={file.fileName} | ||
src={file.src} | ||
strings={{ | ||
fetching: fileDictionary('fetching'), | ||
clickToDownload: fileDictionary('clickToDownload'), | ||
clickToDownloadAria: fileDictionary('clickToDownloadAria'), | ||
openPdfInNewTab: fileDictionary('openPdfInNewTab'), | ||
openPdfInNewTabAria: fileDictionary('openPdfInNewTabAria'), | ||
}} | ||
/> | ||
); | ||
} | ||
|
||
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> | ||
); | ||
} |
Oops, something went wrong.