diff --git a/.env.example b/.env.example index cf2dd01..4cfc4be 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,17 @@ # rss feed which should be a link to an xml file RSS_FEED_URL="" + # renders all episodes or just "coming soon" COMING_SOON_ENABLED="true" + # Adds the DECENTRALIZED banner for funsies -# DECENTRALIZED="true" \ No newline at end of file +# DECENTRALIZED="true" + +# GraphQL api token +BLOG_API_TOKEN="" + +# defaults to https://api.github.com/graphql +# BLOG_GRAPHQL_ENDPOINT="" + +# Blog github branch to pull content from defaults to "main" +# BLOG_BRANCH="" \ No newline at end of file diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index dfcfe87..8f98976 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -11,7 +11,10 @@ jobs: runs-on: ubuntu-latest environment: gh-pages steps: - - uses: actions/setup-node@v3 + - name: 'Setup Node' + uses: actions/setup-node@v3 + with: + node-version: 18 - name: Checkout 🛎️ uses: actions/checkout@v3 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..9020bd1 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.5.0 diff --git a/app.config.ts b/app.config.ts index fb8e676..37a3b63 100644 --- a/app.config.ts +++ b/app.config.ts @@ -4,6 +4,10 @@ export const DECENTRALIZED = process.env.DECENTRALIZED === 'true'; export const BUILD_TS = process.env.BUILD_TS ? new Date(Number(process.env.BUILD_TS) * 1000) : new Date(); +export const BLOG_API_TOKEN = process.env.BLOG_API_TOKEN || ''; +export const BLOG_GRAPHQL_ENDPOINT = + process.env.BLOG_GRAPHQL_ENDPOINT || 'https://api.github.com/graphql'; +export const BLOG_BRANCH = process.env.BLOG_BRANCH || 'main'; if (!COMING_SOON_ENABLED && !RSS_FEED_URL) throw new Error( diff --git a/package.json b/package.json index ce43f81..6964f65 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "private": true, "dependencies": { "@types/react": "^17.0.37", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", "next": "^12.0.7", + "next-mdx-remote": "^4.0.3", "next-sitemap": "^1.6.203", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -30,6 +33,7 @@ }, "devDependencies": { "@types/node": "^17.0.35", + "@types/styled-components": "^5.1.25", "serve": "^13.0.2" } } diff --git a/components/AudioPlayer/AudioPlayer.styled.tsx b/src/components/AudioPlayer/AudioPlayer.styled.tsx similarity index 93% rename from components/AudioPlayer/AudioPlayer.styled.tsx rename to src/components/AudioPlayer/AudioPlayer.styled.tsx index 17d33d9..42cfa22 100644 --- a/components/AudioPlayer/AudioPlayer.styled.tsx +++ b/src/components/AudioPlayer/AudioPlayer.styled.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components'; -export const StyledAudioPlayer = styled.div` +export const StyledAudioPlayer = styled.div<{ $active: boolean }>` width: 100%; position: fixed; bottom: 0; diff --git a/components/AudioPlayer/AudioPlayer.tsx b/src/components/AudioPlayer/AudioPlayer.tsx similarity index 100% rename from components/AudioPlayer/AudioPlayer.tsx rename to src/components/AudioPlayer/AudioPlayer.tsx diff --git a/components/AudioPlayer/AudioPlayerCtx.tsx b/src/components/AudioPlayer/AudioPlayerCtx.tsx similarity index 100% rename from components/AudioPlayer/AudioPlayerCtx.tsx rename to src/components/AudioPlayer/AudioPlayerCtx.tsx diff --git a/src/components/Author/Author.styled.ts b/src/components/Author/Author.styled.ts new file mode 100644 index 0000000..95aeeb1 --- /dev/null +++ b/src/components/Author/Author.styled.ts @@ -0,0 +1,43 @@ +import styled from 'styled-components'; + +export const Avatar = styled.div` + padding: 1.6rem; + border: 1px solid white; + display: flex; + align-items: center; + img { + min-width: 100px; + max-width: 100px; + height: 100px; + margin-right: 1.6rem; + border: 1px solid white; + } + h1, + h2 { + margin: 0; + } + h2 { + font-weight: 400; + font-size: 1.6rem; + } +`; + +export const Inner = styled.div` + max-width: 500px; + min-height: 500px; + margin: auto; + padding: 2rem 1.6rem 0; + + @media scree and (min-width: 500px) { + padding: 2rem 0 0; + } + + h3 { + padding-top: 1.6rem; + margin-bottom: 0.8rem; + } + ul { + padding-left: 1.8rem; + margin-top: 0; + } +`; diff --git a/src/components/Author/Author.tsx b/src/components/Author/Author.tsx new file mode 100644 index 0000000..8843ecb --- /dev/null +++ b/src/components/Author/Author.tsx @@ -0,0 +1,43 @@ +import Link from 'next/link'; +import { Author as IAuthor, Post } from '../../lib/blog'; +import { blogAssetRoot } from '../../utils/constants'; +import { Avatar, Inner } from './Author.styled'; +import { BreadCrumbs } from '../BreadCrumbs/BreadCrumbs'; + +interface AuthorProps { + author: IAuthor; + posts: Post[]; +} + +export const Author = (props: AuthorProps) => { + const { author, posts } = props; + return ( +
+ + + + {`${author.name}'s +
+

{author.name}

+

+ {'a.k.a '} + {author.title} +

+
+
+

{author.shortbio}

+

Posts by this author

+
    + {posts.map((post) => ( +
  • + {post.data.title} +
  • + ))} +
+
+
+ ); +}; diff --git a/src/components/Authors/Authors.styled.ts b/src/components/Authors/Authors.styled.ts new file mode 100644 index 0000000..54b6682 --- /dev/null +++ b/src/components/Authors/Authors.styled.ts @@ -0,0 +1,27 @@ +import styled from 'styled-components'; + +export const Inner = styled.div` + max-width: 500px; + margin: 0 auto; + padding: 0 1.6rem; + @media screen and (min-width: 500px) { + padding: 0; + } + h1, + h2 { + text-align: center; + font-weight: 400; + } + h1 { + font-size: 5.2rem; + margin-bottom: 1.2rem; + } + h2 { + margin-top: 0; + font-size: 1.8rem; + } +`; + +export const AuthorsList = styled.div` + min-height: 500px; +`; diff --git a/src/components/Authors/Authors.tsx b/src/components/Authors/Authors.tsx new file mode 100644 index 0000000..4e033f7 --- /dev/null +++ b/src/components/Authors/Authors.tsx @@ -0,0 +1,28 @@ +import Link from 'next/link'; +import { Author } from '../../lib/blog'; +import { Inner, AuthorsList } from './Authors.styled'; +import { BreadCrumbs } from '../BreadCrumbs/BreadCrumbs'; + +interface AuthorsProps { + authors: Author[]; +} + +export const Authors = (props: AuthorsProps) => { + const { authors } = props; + return ( +
+ + +

Authors

+

Can't live with 'em, can't live without 'em

+ + {authors.map((author) => ( +

+ {author.name} +

+ ))} +
+
+
+ ); +}; diff --git a/src/components/Blog/Blog.styled.ts b/src/components/Blog/Blog.styled.ts new file mode 100644 index 0000000..0ae2ab2 --- /dev/null +++ b/src/components/Blog/Blog.styled.ts @@ -0,0 +1,79 @@ +import styled from 'styled-components'; + +export const StyledBlog = styled.section` + background-color: ${(p) => p.theme.background}; + font-family: ${(p) => p.theme.ffJetbrainsMono}; + padding: 0 1.6rem; + @media screen and (min-width: 500px) { + padding: 0 2.4rem; + } +`; + +export const Hero = styled.div` + h1, + h2, + h3, + h5 { + margin: 0; + } + + h1 { + text-indent: -999999px; + font-size: 0.2rem; + padding-bottom: 0.8rem; + img { + display: block; + width: 100%; + } + } + + h2 { + font-size: 1.6rem; + @media screen and (min-width: 500px) { + font-size: 2.4rem; + } + } + + h2, + h3 { + text-align: center; + font-weight: 400; + } + h3 { + font-size: 1.2rem; + padding: 1.6rem 0 3.2rem; + } +`; + +export const Inner = styled.div` + max-width: 700px; + margin: 0 auto; + padding: 1.6rem 0; +`; + +export const ArticleLink = styled.a` + text-decoration: unset; +`; + +export const Article = styled.article` + h3, + h5 { + font-weight: 400; + } + h3 { + margin: 0; + font-size: 3.2rem; + font-style: italic; + text-transform: UPPERCASE; + color: white; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + h5 { + margin: 0; + } + border: 1px solid white; + margin-bottom: 1.6rem; + padding: 0.8rem; +`; diff --git a/src/components/Blog/Blog.tsx b/src/components/Blog/Blog.tsx new file mode 100644 index 0000000..3800ffe --- /dev/null +++ b/src/components/Blog/Blog.tsx @@ -0,0 +1,92 @@ +import Link from 'next/link'; +import { Post, Author } from '../../lib/blog'; +import { StyledBlog, Inner, Article, ArticleLink } from './Blog.styled'; +import { Hero } from './Blog.styled'; +import caveImgPng from './hero-images/chad-syntax-blog-cave.png'; +import caveImgPng2x from './hero-images/chad-syntax-blog-cave@2x.png'; +import caveImgWebp from './hero-images/chad-syntax-blog-cave.webp'; +import caveImgWebp2x from './hero-images/chad-syntax-blog-cave@2x.png'; +import { BreadCrumbs } from '../BreadCrumbs/BreadCrumbs'; +import { useMemo } from 'react'; + +interface BlogProps { + posts: Post[]; + authors: Author[]; +} + +const maxDescCharCount = 275; + +export const Blog = (props: BlogProps) => { + const { posts, authors } = props; + + const authorsMap = useMemo(() => { + const map = new Map(); + for (const author of authors) { + map.set(author.slug, author); + } + return map; + }, [authors]); + + return ( + + + + +

+ Chad $yntax's Blog of infinite Wonders! + + + + Chad $yntax's Blog of infinite Wonders Logo! + +

+

+ A Blog of posts. Posts about software engineering, web development, + and whatever else. +

+

+ *Disclaimer: Wonders non-transferrable, see fine print for details. +

+
+ {posts.length === 0 &&

The cave is... empty? Wtf.

} +
+ {posts.map((post) => { + const { title, author } = post.data; + const targetAuthor = authorsMap.get(author); + let description = post.data.description ?? ''; + if (description.length > maxDescCharCount) { + description = `${description + .slice(0, maxDescCharCount) + .trim()}...`; + } + return ( + + + + + + ); + })} +
+
+
+ ); +}; diff --git a/src/components/Blog/hero-images/chad-syntax-blog-cave.png b/src/components/Blog/hero-images/chad-syntax-blog-cave.png new file mode 100644 index 0000000..d53b704 Binary files /dev/null and b/src/components/Blog/hero-images/chad-syntax-blog-cave.png differ diff --git a/src/components/Blog/hero-images/chad-syntax-blog-cave.webp b/src/components/Blog/hero-images/chad-syntax-blog-cave.webp new file mode 100644 index 0000000..f81ed24 Binary files /dev/null and b/src/components/Blog/hero-images/chad-syntax-blog-cave.webp differ diff --git a/src/components/Blog/hero-images/chad-syntax-blog-cave@2x.png b/src/components/Blog/hero-images/chad-syntax-blog-cave@2x.png new file mode 100644 index 0000000..d69e062 Binary files /dev/null and b/src/components/Blog/hero-images/chad-syntax-blog-cave@2x.png differ diff --git a/src/components/Blog/hero-images/chad-syntax-blog-cave@2x.webp b/src/components/Blog/hero-images/chad-syntax-blog-cave@2x.webp new file mode 100644 index 0000000..2a8acd3 Binary files /dev/null and b/src/components/Blog/hero-images/chad-syntax-blog-cave@2x.webp differ diff --git a/src/components/BlogFooter/BlogFooter.styled.ts b/src/components/BlogFooter/BlogFooter.styled.ts new file mode 100644 index 0000000..b2f9538 --- /dev/null +++ b/src/components/BlogFooter/BlogFooter.styled.ts @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +export const StyledBlogFooter = styled.footer` + font-family: ${(p) => p.theme.ffJetbrainsMono}; + max-width: 700px; + margin: auto; + text-align: center; + padding: 0 1.6rem 3.2rem; + @media screen and (min-width: 700px) { + padding: 0 0 3.2rem; + } +`; + +export const BlogLegalise = styled.p` + font-size: 1rem; + text-transform: uppercase; + text-align: justify; +`; diff --git a/src/components/BlogFooter/BlogFooter.tsx b/src/components/BlogFooter/BlogFooter.tsx new file mode 100644 index 0000000..896e628 --- /dev/null +++ b/src/components/BlogFooter/BlogFooter.tsx @@ -0,0 +1,48 @@ +import Link from 'next/link'; +import { StyledBlogFooter, BlogLegalise } from './BlogFooter.styled'; + +export const BlogFooter = () => ( + + + All Blog Posts + + {' | '} + + Authors + + {' | '} + + Podcast + +
+
+ + Send feedback/questions to query@ttpspodcast.com + +
+ + *Any and all wonders found on the property are exclusively owned by "Chad + $yntax Blog Cave of Wonders! LLC" in perpetuity. By entering the blog cave + you waive all rights to any wonders found within the cave and recognize + the authority of "Chad $yntax Blog Cave of Wonders! LLC" staff on any + company property. By entering the cave you waive your right to bear arms + and must strictly adhere to company policy. Failure to comply may result + in incarceration and/or expulsion from the Blog Cave of Wonders. By + entering the cave you waive all rights to bring suit against "Chad $yntax + Blog Cave of Wonders! LLC" and it's subsidiaries. "Chad $yntax Blog Cave + of Wonders! LLC" reserves the right to restrain any personnel found + exiting the premisis with wonders concealed or purposefully hidden from + staff. +
+
+ "Chad $yntax Blog Cave of Wonders! LLC" also holds exclusive rights to + what are colloquially known as "Sub-Wonders", "Sub-Sub-Wonders", + "Mini-Wonders", "Micro Wonders", and "Chonk Wonders" found within the Blog + Cave. "Chad $yntax Blog Cave of Wonders! LLC" also owns the rights to any + "Wonder" subgroups, archetypes, or categories discovered in the future. +
+
+ Outside food and drink are not allowed within the blog cave. +
+
+); diff --git a/src/components/BlogPost/BlogPost.styled.ts b/src/components/BlogPost/BlogPost.styled.ts new file mode 100644 index 0000000..eb0487d --- /dev/null +++ b/src/components/BlogPost/BlogPost.styled.ts @@ -0,0 +1,45 @@ +import styled from 'styled-components'; + +export const Inner = styled.div` + max-width: 700px; + margin: 0 auto; + padding: 1.6rem 0 0; +`; + +export const BlogPostSection = styled.section` + margin: 0 1.6rem; +`; + +export const BlogPostTitle = styled.h1` + font-size: 4.8rem; + font-style: italic; + text-transform: UPPERCASE; + color: white; + margin: 0 0 1.2rem; +`; + +export const BlogPostSubTitle = styled.h2` + margin-top: 0; + font-weight: 400; +`; + +export const BlogPostByline = styled.p` + margin: 0; + padding-bottom: 1.6rem; + border-bottom: 1px solid ${(p) => p.theme.offWhite}; + font-size: 1.2rem; +`; + +export const BlogPostBody = styled.article` + h1, + h2, + h3, + h4, + h5, + h6 { + font-weight: 400; + } + p { + text-align: justify; + } +`; diff --git a/src/components/BlogPost/BlogPost.tsx b/src/components/BlogPost/BlogPost.tsx new file mode 100644 index 0000000..37142ed --- /dev/null +++ b/src/components/BlogPost/BlogPost.tsx @@ -0,0 +1,49 @@ +import Link from 'next/link'; +import { BreadCrumbs } from '../BreadCrumbs/BreadCrumbs'; +import { + Inner, + BlogPostSection, + BlogPostTitle, + BlogPostSubTitle, + BlogPostByline, + BlogPostBody, +} from './BlogPost.styled'; +import { Post, Author } from '../../lib/blog'; +import { Mdx } from '../Mdx/Mdx'; + +interface BlogPostProps { + post: Post; + author: Author; +} + +export const BlogPost = (props: BlogPostProps) => { + const { post, author } = props; + + const { + data: { date, title, description }, + readTime, + } = post; + + const dateObj = new Date(date); + const pubDate = dateObj.toLocaleDateString(); + const pubTime = dateObj.toLocaleTimeString(); + + return ( + + + + + {title} + {description} + + Published {pubDate} {`@ ${pubTime}`} by  + {author.name} + | + {readTime} + + + + + + ); +}; diff --git a/src/components/BreadCrumbs/BreadCrumbs.styled.ts b/src/components/BreadCrumbs/BreadCrumbs.styled.ts new file mode 100644 index 0000000..d4d995b --- /dev/null +++ b/src/components/BreadCrumbs/BreadCrumbs.styled.ts @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +export const ExitLink = styled.a<{ isLast?: boolean }>` + text-decoration: underline; + position: relative; + color: white; +`; + +export const BreadCrumbsNav = styled.nav` + line-height: 2.8rem; + span { + margin: 0 0.8rem; + } + display: flex; + flex-wrap: wrap; +`; diff --git a/src/components/BreadCrumbs/BreadCrumbs.tsx b/src/components/BreadCrumbs/BreadCrumbs.tsx new file mode 100644 index 0000000..0f33d1a --- /dev/null +++ b/src/components/BreadCrumbs/BreadCrumbs.tsx @@ -0,0 +1,32 @@ +import Link from 'next/link'; +import { Fragment } from 'react'; +import { useRouter } from 'next/router'; +import { ExitLink, BreadCrumbsNav } from './BreadCrumbs.styled'; +import { CopyTag } from '../CopyTag/CopyTag'; + +export const BreadCrumbs = () => { + const { asPath } = useRouter(); + + const otherLinks = asPath.split('/').filter(Boolean); + + return ( + + + < Exit the cave + + {otherLinks.map((otherLink, index) => { + const previous = otherLinks[index - 1]; + const href = `/${previous ? `${previous}/` : ''}${otherLink}`; + const isLast = index === otherLinks.length - 1; + + return ( + + {'/'} + {otherLink} + {isLast && } + + ); + })} + + ); +}; diff --git a/src/components/CaveEntrance/CaveEntrance.styled.tsx b/src/components/CaveEntrance/CaveEntrance.styled.tsx new file mode 100644 index 0000000..ce12ce6 --- /dev/null +++ b/src/components/CaveEntrance/CaveEntrance.styled.tsx @@ -0,0 +1,21 @@ +import styled from 'styled-components'; + +export const StyledCaveEntrance = styled.img` + width: 100px; + height: 100px; + display: block; + margin-bottom: -10px; +`; + +export const CaveLink = styled.a` + position: absolute; + bottom: 0; + right: 0; + z-index: 1; + font-size: 1rem; + line-height: 1rem; + span { + display: block; + text-indent: -999999px; + } +`; diff --git a/src/components/CaveEntrance/CaveEntrance.tsx b/src/components/CaveEntrance/CaveEntrance.tsx new file mode 100644 index 0000000..78c16d3 --- /dev/null +++ b/src/components/CaveEntrance/CaveEntrance.tsx @@ -0,0 +1,15 @@ +import Link from 'next/link'; +import { StyledCaveEntrance, CaveLink } from './CaveEntrance.styled'; +import caveEntrancePng from './blog-cave-entrance.png'; + +export const CaveEntrance = () => ( + + + + Blog + + +); diff --git a/src/components/CaveEntrance/blog-cave-entrance.png b/src/components/CaveEntrance/blog-cave-entrance.png new file mode 100644 index 0000000..558fde8 Binary files /dev/null and b/src/components/CaveEntrance/blog-cave-entrance.png differ diff --git a/src/components/CopyTag/CopyTag.styled.ts b/src/components/CopyTag/CopyTag.styled.ts new file mode 100644 index 0000000..5fa0942 --- /dev/null +++ b/src/components/CopyTag/CopyTag.styled.ts @@ -0,0 +1,9 @@ +import styled from 'styled-components'; + +export const StyledCopyTag = styled.span` + cursor: pointer; + font-size: 1.2rem; + line-height: 2.8rem; + margin-left: 0.8rem; + text-decoration: none; +`; diff --git a/src/components/CopyTag/CopyTag.tsx b/src/components/CopyTag/CopyTag.tsx new file mode 100644 index 0000000..6301ec5 --- /dev/null +++ b/src/components/CopyTag/CopyTag.tsx @@ -0,0 +1,52 @@ +import { useRef, useState, useEffect } from 'react'; +import { StyledCopyTag } from './CopyTag.styled'; + +type NodeJSTimeout = ReturnType; + +interface CopyState { + isCopied: boolean; + copyText: string; +} + +const defaultCopyState = { + isCopied: false, + copyText: '🔗 Copy', +} as const; + +const activeCopyState = { + isCopied: true, + copyText: 'Copied!', +} as const; + +interface CopyTagProps { + hash?: string; +} + +export const CopyTag = (props: CopyTagProps) => { + const { hash } = props; + const [copyState, setCopyState] = useState(defaultCopyState); + const { isCopied, copyText } = copyState; + const timeoutRef = useRef(null); + + useEffect(() => { + if (isCopied) { + clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => { + setCopyState(defaultCopyState); + }, 3000); + } + }, [isCopied]); + + const onClick = async (e) => { + e.preventDefault(); + try { + await navigator.clipboard.writeText(`${location.href}${hash || ''}`); + setCopyState(activeCopyState); + } catch (e) { + setCopyState(defaultCopyState); + alert('Failed to copy to clipboard :('); + } + }; + + return {copyText}; +}; diff --git a/components/Episode/Episode.styled.ts b/src/components/Episode/Episode.styled.ts similarity index 100% rename from components/Episode/Episode.styled.ts rename to src/components/Episode/Episode.styled.ts diff --git a/components/Episode/Episode.tsx b/src/components/Episode/Episode.tsx similarity index 100% rename from components/Episode/Episode.tsx rename to src/components/Episode/Episode.tsx diff --git a/components/Episodes/Episodes.styled.ts b/src/components/Episodes/Episodes.styled.ts similarity index 95% rename from components/Episodes/Episodes.styled.ts rename to src/components/Episodes/Episodes.styled.ts index c2de586..47ef861 100644 --- a/components/Episodes/Episodes.styled.ts +++ b/src/components/Episodes/Episodes.styled.ts @@ -44,12 +44,6 @@ export const Episode = styled.div` font-family: ${(p) => p.theme.ffIbmPlex}; font-size: 2.6rem; margin-bottom: 1.6rem; - a { - color: white; - &:visited { - color: #b0b0b0; - } - } @media screen and (max-width: 768px) { font-size: 2rem; diff --git a/components/Episodes/Episodes.tsx b/src/components/Episodes/Episodes.tsx similarity index 52% rename from components/Episodes/Episodes.tsx rename to src/components/Episodes/Episodes.tsx index e5322f6..71d8fc6 100644 --- a/components/Episodes/Episodes.tsx +++ b/src/components/Episodes/Episodes.tsx @@ -1,5 +1,7 @@ import Link from 'next/link'; import { useState } from 'react'; +import { Hero } from '../Hero/Hero'; +import { PodcastLinks } from '../PodcastLinks/PodcastLinks'; import { StyledEpisodes, EpisodesInner, @@ -19,10 +21,11 @@ const cursorTitleMap = { interface EpisodesProps { episodes: Episode[]; comingSoonEnabled: boolean; + decentralized?: boolean; } export const Episodes = (props: EpisodesProps) => { - const { episodes, comingSoonEnabled } = props; + const { episodes, comingSoonEnabled, decentralized } = props; const [cursorLength, setCursorLength] = useState(10); const [cursor, setCursor] = useState(cursorLength); const [timesClicked, setTimesClicked] = useState(0); @@ -57,31 +60,38 @@ export const Episodes = (props: EpisodesProps) => { ); return ( - - -

Episodes

-
- {currentEpisodes.map((episode) => ( - - - {episode.title} - - - ))} - {!allLoaded && episodes.length > 10 && ( - - load {cursorTitleMap[cursorLength] ?? cursorLength} more - - )} - - - viewing {currentEpisodes.length} / {episodes.length} episodes - - {!allLoaded && ( - load all + <> + + + + +

Episodes

+
+ {currentEpisodes.map((episode) => ( + + + {episode.title} + + + ))} + {!allLoaded && episodes.length > 10 && ( + + load {cursorTitleMap[cursorLength] ?? cursorLength} more + )} -
-
-
+ + + viewing {currentEpisodes.length} / {episodes.length} episodes + + {!allLoaded && ( + load all + )} + + + + ); }; diff --git a/components/Footer/Footer.styled.ts b/src/components/Footer/Footer.styled.ts similarity index 87% rename from components/Footer/Footer.styled.ts rename to src/components/Footer/Footer.styled.ts index 464a55e..7203a49 100644 --- a/components/Footer/Footer.styled.ts +++ b/src/components/Footer/Footer.styled.ts @@ -2,10 +2,8 @@ import styled from 'styled-components'; export const StyledFooter = styled.footer` background-color: ${(p) => p.theme.background}; + position: relative; text-align: center; color: #b0b0b0; padding: 4rem 0 12rem; - a { - color: white; - } `; diff --git a/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx similarity index 93% rename from components/Footer/Footer.tsx rename to src/components/Footer/Footer.tsx index 7abe0c4..57aab7d 100644 --- a/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,4 +1,6 @@ +import Link from 'next/link'; import { StyledFooter } from './Footer.styled'; +import { CaveEntrance } from '../CaveEntrance/CaveEntrance'; export const Footer = () => ( @@ -69,5 +71,6 @@ export const Footer = () => ( github + ); diff --git a/components/Header/Header.styled.ts b/src/components/Header/Header.styled.ts similarity index 100% rename from components/Header/Header.styled.ts rename to src/components/Header/Header.styled.ts diff --git a/components/Header/Header.tsx b/src/components/Header/Header.tsx similarity index 100% rename from components/Header/Header.tsx rename to src/components/Header/Header.tsx diff --git a/components/Hero/Hero.styled.tsx b/src/components/Hero/Hero.styled.tsx similarity index 100% rename from components/Hero/Hero.styled.tsx rename to src/components/Hero/Hero.styled.tsx diff --git a/components/Hero/Hero.tsx b/src/components/Hero/Hero.tsx similarity index 100% rename from components/Hero/Hero.tsx rename to src/components/Hero/Hero.tsx diff --git a/components/Hero/Mailchimp.tsx b/src/components/Hero/Mailchimp.tsx similarity index 100% rename from components/Hero/Mailchimp.tsx rename to src/components/Hero/Mailchimp.tsx diff --git a/components/Hero/ethereum.svg b/src/components/Hero/ethereum.svg similarity index 100% rename from components/Hero/ethereum.svg rename to src/components/Hero/ethereum.svg diff --git a/components/Hero/ttps-art-2048-2048.png b/src/components/Hero/ttps-art-2048-2048.png similarity index 100% rename from components/Hero/ttps-art-2048-2048.png rename to src/components/Hero/ttps-art-2048-2048.png diff --git a/components/Hero/ttps-art-880-880.png b/src/components/Hero/ttps-art-880-880.png similarity index 100% rename from components/Hero/ttps-art-880-880.png rename to src/components/Hero/ttps-art-880-880.png diff --git a/src/components/Layout/BlogLayout.tsx b/src/components/Layout/BlogLayout.tsx new file mode 100644 index 0000000..4cfce54 --- /dev/null +++ b/src/components/Layout/BlogLayout.tsx @@ -0,0 +1,13 @@ +import styled from 'styled-components'; +import { BlogFooter } from '../BlogFooter/BlogFooter'; + +export const StyledBlogLayout = styled.main` + font-family: ${(p) => p.theme.ffJetbrainsMono}; +`; + +export const BlogLayout = ({ children }) => ( + + {children} + + +); diff --git a/src/components/Layout/PodcastLayout.tsx b/src/components/Layout/PodcastLayout.tsx new file mode 100644 index 0000000..c560953 --- /dev/null +++ b/src/components/Layout/PodcastLayout.tsx @@ -0,0 +1,25 @@ +import { ReactNode } from 'react'; +import styled from 'styled-components'; +import { Footer } from '../Footer/Footer'; +import { Header } from '../Header/Header'; + +export const StyledPodcastLayout = styled.main` + font-family: ${(p) => p.theme.ffUbuntu}; + + @media screen and (max-width: 500px) { + padding: 0 0.8rem; + background-color: ${(p) => p.theme.background}; + } +`; + +interface PodcastLayoutProps { + children: ReactNode; +} + +export const PodcastLayout = ({ children }: PodcastLayoutProps) => ( + +
+ {children} +