From 2c8dd2ad7a70d567074531b399dfc4174498e400 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 16 Apr 2025 01:53:45 -0500 Subject: [PATCH 1/2] Added /pizza route, added BlueSky to share icons --- assets/bluesky-icon.svg | 7 + components/common/Markdown/remarkGithub.js | 158 ++++++++++--------- components/pages/blog/post/ArticleHeader.jsx | 16 +- components/pages/blog/post/ShareSheet.jsx | 58 +++++-- components/pages/blog/post/index.js | 3 + pages/pizza.js | 46 ++++++ 6 files changed, 193 insertions(+), 95 deletions(-) create mode 100644 assets/bluesky-icon.svg create mode 100644 pages/pizza.js diff --git a/assets/bluesky-icon.svg b/assets/bluesky-icon.svg new file mode 100644 index 0000000..0032b2d --- /dev/null +++ b/assets/bluesky-icon.svg @@ -0,0 +1,7 @@ + + + diff --git a/components/common/Markdown/remarkGithub.js b/components/common/Markdown/remarkGithub.js index bfa42b6..c82fa79 100644 --- a/components/common/Markdown/remarkGithub.js +++ b/components/common/Markdown/remarkGithub.js @@ -12,90 +12,102 @@ const remarkGithub = (options) => { /https:\/\/github\.com\/([\w-]+)\/([\w-]+)\/(issues|pull)\/(\d+)/g; const compareUrlRegex = /https:\/\/github\.com\/([\w-]+)\/([\w-]+)\/compare\/([\w.-]+)\.\.\.([\w.-]+)/g; - const usernameRegex = /@([\w-]+)/g; // Regex to match @username + const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g; + const usernameRegex = /@([\w-]+)(?![@.]\w)/g; // Negative lookahead to prevent matching email addresses return (tree) => { visit(tree, 'text', (node, index, parent) => { - if (!isInsideLink(node, [])) { - const children = []; - let lastIndex = 0; - let match; + // Skip if we're inside a link or if the parent is a link + if (isInsideLink(node, []) || parent?.type === 'link') { + return; + } + + const children = []; + let lastIndex = 0; + let match; - function addText(text, start, end) { - if (start < end) children.push(u('text', text.slice(start, end))); - } + function addText(text, start, end) { + if (start < end) children.push(u('text', text.slice(start, end))); + } - // TODO: Handle my-org/my-repo#123 and my-repo#123 + // First check for email addresses + while ((match = emailRegex.exec(node.value)) !== null) { + const [email] = match; + addText(node.value, lastIndex, match.index); + children.push( + u('link', { url: `mailto:${email}` }, [u('text', email)]) + ); + lastIndex = match.index + email.length; + } - // Handle issue numbers - while ((match = issueRegex.exec(node.value)) !== null) { - const [fullMatch, issueNumber] = match; - addText(node.value, lastIndex, match.index); - children.push( - u( - 'link', - { - url: `https://github.com/${defaultOrg}/${defaultRepo}/issues/${issueNumber}`, - }, - [u('text', `#${issueNumber}`)] - ) - ); - lastIndex = match.index + fullMatch.length; - } + // Handle issue numbers + while ((match = issueRegex.exec(node.value)) !== null) { + const [fullMatch, issueNumber] = match; + addText(node.value, lastIndex, match.index); + children.push( + u( + 'link', + { + url: `https://github.com/${defaultOrg}/${defaultRepo}/issues/${issueNumber}`, + }, + [u('text', `#${issueNumber}`)] + ) + ); + lastIndex = match.index + fullMatch.length; + } - // Handle full GitHub URLs - while ((match = fullUrlRegex.exec(node.value)) !== null) { - const [fullMatch, org, repo, type, number] = match; - addText(node.value, lastIndex, match.index); - const linkText = `${org === defaultOrg ? '' : `${org}/`}${ - org === defaultOrg && repo === defaultRepo ? '' : repo - }#${number}`; - children.push( - u( - 'link', - { url: `https://github.com/${org}/${repo}/${type}/${number}` }, - [u('text', linkText)] - ) - ); - lastIndex = match.index + fullMatch.length; - } + // Handle full GitHub URLs + while ((match = fullUrlRegex.exec(node.value)) !== null) { + const [fullMatch, org, repo, type, number] = match; + addText(node.value, lastIndex, match.index); + const linkText = `${org === defaultOrg ? '' : `${org}/`}${ + org === defaultOrg && repo === defaultRepo ? '' : repo + }#${number}`; + children.push( + u( + 'link', + { url: `https://github.com/${org}/${repo}/${type}/${number}` }, + [u('text', linkText)] + ) + ); + lastIndex = match.index + fullMatch.length; + } - // Handle GitHub compare URLs - while ((match = compareUrlRegex.exec(node.value)) !== null) { - const [fullMatch, org, repo, tag1, tag2] = match; - addText(node.value, lastIndex, match.index); - children.push( - u( - 'link', - { - url: `https://github.com/${org}/${repo}/compare/${tag1}...${tag2}`, - }, - [u('text', `${tag1}...${tag2}`)] - ) - ); - lastIndex = match.index + fullMatch.length; - } + // Handle GitHub compare URLs + while ((match = compareUrlRegex.exec(node.value)) !== null) { + const [fullMatch, org, repo, tag1, tag2] = match; + addText(node.value, lastIndex, match.index); + children.push( + u( + 'link', + { + url: `https://github.com/${org}/${repo}/compare/${tag1}...${tag2}`, + }, + [u('text', `${tag1}...${tag2}`)] + ) + ); + lastIndex = match.index + fullMatch.length; + } - // Handle GitHub usernames - while ((match = usernameRegex.exec(node.value)) !== null) { - const [fullMatch, usernameOrOrg] = match; - addText(node.value, lastIndex, match.index); - children.push( - u('link', { url: `https://github.com/${usernameOrOrg}` }, [ - u('text', fullMatch), - ]) - ); - lastIndex = match.index + fullMatch.length; - } + // Handle GitHub usernames (only if not part of an email) + while ((match = usernameRegex.exec(node.value)) !== null) { + const [fullMatch, usernameOrOrg] = match; + addText(node.value, lastIndex, match.index); + children.push( + u('link', { url: `https://github.com/${usernameOrOrg}` }, [ + u('text', fullMatch), + ]) + ); + lastIndex = match.index + fullMatch.length; + } - addText(node.value, lastIndex, node.value.length); + addText(node.value, lastIndex, node.value.length); - if ( - children.length > 1 || - (children.length === 1 && children[0].type !== 'text') - ) { - parent.children.splice(index, 1, ...children); - } + if ( + children.length > 1 || + (children.length === 1 && children[0].type !== 'text') + ) { + parent.children.splice(index, 1, ...children); } }); }; diff --git a/components/pages/blog/post/ArticleHeader.jsx b/components/pages/blog/post/ArticleHeader.jsx index b2eca67..1f0bae7 100644 --- a/components/pages/blog/post/ArticleHeader.jsx +++ b/components/pages/blog/post/ArticleHeader.jsx @@ -133,12 +133,16 @@ const ArticleHeader = ({ frontmatter, author }) => {
- - {config.categories[frontmatter.category ?? "updates"]?.title ?? config.categories.updates.title} - - - {formatDate(frontmatter.date)} - + {frontmatter.category && ( + + {config.categories[frontmatter.category]?.title ?? config.categories.updates.title} + + )} + {frontmatter.date && ( + + {formatDate(frontmatter.date)} + + )}
diff --git a/components/pages/blog/post/ShareSheet.jsx b/components/pages/blog/post/ShareSheet.jsx index fb35147..f3f0cfe 100644 --- a/components/pages/blog/post/ShareSheet.jsx +++ b/components/pages/blog/post/ShareSheet.jsx @@ -1,5 +1,6 @@ import { useRef } from "react"; import XSvg from '@/assets/x-icon.svg'; +import BlueSkySvg from '@/assets/bluesky-icon.svg'; import FacebookSvg from '@/assets/facebook-icon.svg'; import { Link, Mail } from 'react-feather'; import styled from "styled-components"; @@ -111,6 +112,18 @@ const ShareSheetWrap = styled.div` const ShareSheet = () => { const shareLinkInputRef = useRef(); + + const sanitizeUrl = (url) => { + if (!url) return ''; + try { + const urlObj = new URL(url); + return encodeURIComponent(urlObj.toString()); + } catch (e) { + console.error('Error sanitizing URL:', e); + return ''; + } + }; + const shareViaMail = () => { var subject = 'CodeEdit Blog Post'; var body = window.location.href; @@ -122,12 +135,13 @@ const ShareSheet = () => { }; function shareViaLink() { - navigator.clipboard.writeText(window.location.href).then( - function (data) { - console.log('Copying to clipboard was successful!', data); + const url = window.location.href; + navigator.clipboard.writeText(url).then( + function () { + console.log('Copying to clipboard was successful!'); }, function (err) { - console.log('Could not copy text: ', err); + console.error('Could not copy text: ', err); } ); } @@ -144,7 +158,7 @@ const ShareSheet = () => { aria-label="Share this article via Facebook (opens in new window)" onClick={() => window.open( - `https://www.facebook.com/sharer/sharer.php?u=${window.location.href}` + `https://www.facebook.com/sharer/sharer.php?u=${sanitizeUrl(window.location.href)}` ) } > @@ -153,18 +167,32 @@ const ShareSheet = () => {
  • +
  • + +
  • diff --git a/components/pages/blog/post/index.js b/components/pages/blog/post/index.js index d0edf68..4d02032 100644 --- a/components/pages/blog/post/index.js +++ b/components/pages/blog/post/index.js @@ -44,6 +44,9 @@ const BlogPost = ({ frontmatter, markdownBody, author }) => { } Blog`; const description = frontmatter.description ?? frontmatter.subhead; + console.log(frontmatter); + console.log(markdownBody); + return (
    diff --git a/pages/pizza.js b/pages/pizza.js new file mode 100644 index 0000000..3d95125 --- /dev/null +++ b/pages/pizza.js @@ -0,0 +1,46 @@ +import BlogPost from '@/components/pages/blog/post'; + +export default function Pizza() { + return ( + [!NOTE] +> We reserve the right to only reward completed and meaningful contributions (but we’ll still love you for trying). + +## Leaderboard + +| Name | PRs Merged / 🍕 Received | +|:------|----------:| +| [thecoolwinter](https://github.com/thecoolwinter) | 2 | +| [tom-ludwig](https://github.com/tom-ludwig) | 1 | +| [FastestMolasses](https://github.com/FastestMolasses) | 1 | +| [austincondiff](https://github.com/austincondiff) | 1 | + `} + /> + ); +} From c20a9c67c54d1acfc4edccc8e720b48adacc2f19 Mon Sep 17 00:00:00 2001 From: Austin Condiff Date: Wed, 16 Apr 2025 08:50:36 -0500 Subject: [PATCH 2/2] Put markdown into data file --- data/pizza.md | 37 +++++++++++++++++++++++++++++++ pages/pizza.js | 59 +++++++++++++++----------------------------------- utils/file.js | 12 +++++++++- 3 files changed, 65 insertions(+), 43 deletions(-) create mode 100644 data/pizza.md diff --git a/data/pizza.md b/data/pizza.md new file mode 100644 index 0000000..6c38b98 --- /dev/null +++ b/data/pizza.md @@ -0,0 +1,37 @@ +--- +title: 'You Ship Code, We Ship 🍕!' +image: 'https://media1.tenor.com/m/VWAZosGBC6AAAAAd/hellmo-pizza.gif' +--- + +![Pizza](https://media1.tenor.com/m/VWAZosGBC6AAAAAd/hellmo-pizza.gif) + +At Deep Dish Swift, we're not only here to talk Swift, but to also serve pizza! +Think your code's got the secret sauce? Let's see what you've got. + +We're cooking up a native Mac code editor called [CodeEdit](https://github.com/CodeEditApp/CodeEdit) (you may have heard of it), and if you contribute, we'll send you a pizza — no strings attached, except for cheese. + +Make a pull request, climb the leaderboard, claim your pie. Let's get saucy! + +## Rules + +Here's how to claim your slice: + +1. Comment "I'll take this slice! 🍕" on any issue in [our repos](https://github.com/CodeEditApp) on GitHub. +2. Make a PR solving that issue. +3. Email your name, GitHub username, a link to your merged PR to [codeforpizza@codeedit.app](mailto:codeforpizza@codeedit.app), along with your preferred pizza delivery service. + +Once your PR is merged and you've sent us an email, we'll send you a digital gift card to order a pizza — on us. + +Hurry, this won't last long, [get started](https://github.com/CodeEditApp/CodeEdit/issues)! + +> [!NOTE] +> We reserve the right to only reward completed and meaningful contributions (but we'll still love you for trying). + +## Leaderboard + +| Name | PRs Merged / 🍕 Received | +|:------------------------------------------------------|------------------------:| +| [thecoolwinter](https://github.com/thecoolwinter) | 2 | +| [tom-ludwig](https://github.com/tom-ludwig) | 1 | +| [FastestMolasses](https://github.com/FastestMolasses) | 1 | +| [austincondiff](https://github.com/austincondiff) | 1 | diff --git a/pages/pizza.js b/pages/pizza.js index 3d95125..868ea83 100644 --- a/pages/pizza.js +++ b/pages/pizza.js @@ -1,46 +1,21 @@ +import matter from 'gray-matter'; import BlogPost from '@/components/pages/blog/post'; -export default function Pizza() { - return ( - [!NOTE] -> We reserve the right to only reward completed and meaningful contributions (but we’ll still love you for trying). - -## Leaderboard +export async function getStaticProps() { + const content = await import('@/data/pizza.md'); + const data = await matter(content.default); + + return { + props: { + frontmatter: { + ...data.data, + date: new Date().toISOString(), + }, + markdownBody: data.content, + } + }; +} -| Name | PRs Merged / 🍕 Received | -|:------|----------:| -| [thecoolwinter](https://github.com/thecoolwinter) | 2 | -| [tom-ludwig](https://github.com/tom-ludwig) | 1 | -| [FastestMolasses](https://github.com/FastestMolasses) | 1 | -| [austincondiff](https://github.com/austincondiff) | 1 | - `} - /> - ); +export default function Pizza({ frontmatter, markdownBody }) { + return ; } diff --git a/utils/file.js b/utils/file.js index c49bd72..f2f16db 100644 --- a/utils/file.js +++ b/utils/file.js @@ -27,7 +27,17 @@ export const readFile = async (pathName) => { if (error) { reject(error); } - resolve(JSON.parse(data)); + // If the file is a markdown file, return the raw content + if (pathName.endsWith('.md')) { + resolve(data); + } else { + // Otherwise try to parse as JSON + try { + resolve(JSON.parse(data)); + } catch (e) { + reject(e); + } + } }); }); };