Skip to content

Commit

Permalink
feat(newsletter): create a newsletter page + script to get rss feed u…
Browse files Browse the repository at this point in the history
…rls from Substack
  • Loading branch information
mrcalexandre committed Oct 21, 2024
1 parent 70d4910 commit 811b6b6
Show file tree
Hide file tree
Showing 17 changed files with 231 additions and 1 deletion.
49 changes: 49 additions & 0 deletions src/components/common/NewsletterSubscribe.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
import RssIcon from "@/components/icons/RssIcon.astro";
---

<div class="mx-auto max-w-3xl py-8">
<h2 class="mb-4 text-2xl font-semibold text-gray-900 dark:text-white">
Subscribe to Full-Time Curious
</h2>
<p class="mb-6 text-gray-700 dark:text-gray-300">
Subscribe to receive the latest updates from the Full-Time Curious
newsletter.
</p>

<form
action="https://fulltimecurious.substack.com/subscribe"
class="flex space-x-4"
method="get"
target="_blank">
<div class="flex-grow">
<input
class="form-input w-full rounded-lg border border-dashed bg-white px-4 py-3 text-gray-900 placeholder-gray-600 transition duration-300 ease-out focus:border-gray-300 focus:outline-none dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 dark:focus:border-gray-600 sm:text-sm"
id="email"
name="email"
placeholder="Enter your email"
required
type="email"
/>
<p class="mt-2 hidden text-sm text-red-600" id="email-error">
Please fill out this field.
</p>
</div>

<input
class="cursor-pointer rounded-lg bg-blue-600 px-5 py-3 text-white shadow-sm transition duration-300 ease-out hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
type="submit"
value="Subscribe"
/>
</form>

<div class="mt-6">
<a href="https://fulltimecurious.substack.com/feed" target="_blank">
<button
class="flex w-full items-center justify-center space-x-2 rounded-lg border px-4 py-3 text-gray-900 shadow-sm transition duration-300 ease-out hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-300 dark:text-gray-100 dark:hover:bg-gray-700 dark:focus:ring-gray-600">
<RssIcon />
<span>Subscribe to RSS</span>
</button>
</a>
</div>
</div>
2 changes: 1 addition & 1 deletion src/components/layout/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const menuitems = [
title: "Blog",
},
{
path: "https://fulltimecurious.substack.com/",
path: "/newsletter",
title: "Newsletter",
},
{
Expand Down
27 changes: 27 additions & 0 deletions src/components/newsletter/NewsletterItem.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
import FormattedDate from "@/components/common/FormattedDate.astro";
import ArrowRightIcon from "@/components/icons/ArrowRightIcon.astro";
const { entry } = Astro.props;
---

<a
class="border-zinc/15 hover:bg-zinc/5 h-entry u-url group relative flex items-center gap-4 rounded-lg border border-dashed px-4 py-3 shadow-sm transition-colors duration-300 ease-in-out hover:bg-black/5 dark:border-white/20 dark:hover:bg-white/5"
href={`${entry.data.link}`}
target="_blank">
<div class="flex flex-1 flex-col truncate">
<div class="p-name text-md truncate font-serif font-semibold">
{entry.data.title}
</div>
<div class="text-md e-content truncate opacity-60">
{entry.data.description}
</div>
</div>

<div class="text-sm italic">
<span class="dt-published">
<FormattedDate date={new Date(entry.data.pubDate)} />
</span>
</div>
<ArrowRightIcon />
</a>
10 changes: 10 additions & 0 deletions src/content/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ const booksCollection = defineCollection({
}),
});

const newslettersCollection = defineCollection({
type: "data",
schema: z.object({
title: z.string(),
date: z.date(),
description: z.string(),
link: z.string().url()
}),
});

const postsCollection = defineCollection({
type: "content",
schema: z.object({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "First week of work",
"link": "https://fulltimecurious.substack.com/p/full-time-curious-with-alexandre-96c",
"pubDate": "Sun, 21 Apr 2019 16:22:41 GMT",
"title": "Full-Time Curious with Alexandre #2"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "A new beginning ...",
"link": "https://fulltimecurious.substack.com/p/full-time-curious-with-alexandre-039",
"pubDate": "Sun, 14 Apr 2019 16:42:44 GMT",
"title": "Full-Time Curious with Alexandre #1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "dating fatigue, third place, teens mental health, and how to laundry money",
"link": "https://fulltimecurious.substack.com/p/back-to-school-season-back-to-routines",
"pubDate": "Thu, 12 Sep 2024 06:01:13 GMT",
"title": "Back to school season, back to routines"
}
6 changes: 6 additions & 0 deletions src/content/newsletters/2_summer_is_here__kind_of.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Personal web, djing, thrillers, and stories learned",
"link": "https://fulltimecurious.substack.com/p/summer-is-here-kind-of",
"pubDate": "Fri, 05 Jul 2024 06:01:44 GMT",
"title": "Summer is here, kind of"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "slow life, creating your own web, learning and exploring",
"link": "https://fulltimecurious.substack.com/p/reading-running-and-spending-quality",
"pubDate": "Fri, 03 May 2024 06:02:18 GMT",
"title": "reading, running and spending quality time"
}
6 changes: 6 additions & 0 deletions src/content/newsletters/4_unfolding_a_new_chapter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "human curation, the birth of the detective and the ugly truth behind dating apps",
"link": "https://fulltimecurious.substack.com/p/unfolding-a-new-chapter",
"pubDate": "Fri, 08 Mar 2024 12:00:58 GMT",
"title": "Unfolding a new chapter"
}
6 changes: 6 additions & 0 deletions src/content/newsletters/5_happy_new_year__2024_is_here_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "What I consumed the past 2 months to finish 2023",
"link": "https://fulltimecurious.substack.com/p/happy-new-year-2024-is-here",
"pubDate": "Tue, 02 Jan 2024 07:00:38 GMT",
"title": "Happy New Year, 2024 is here!"
}
6 changes: 6 additions & 0 deletions src/content/newsletters/6_slowing_down_and_being_present.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Slowness, Barbie, AI, jazz, MI6, Jim Carrey and more",
"link": "https://fulltimecurious.substack.com/p/slowing-down-and-being-present",
"pubDate": "Thu, 02 Nov 2023 07:00:57 GMT",
"title": "Slowing down and being present"
}
6 changes: 6 additions & 0 deletions src/content/newsletters/7_look_up_at_the_sky_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Disconnected Japan walk, ultra running, and cigars factory readers",
"link": "https://fulltimecurious.substack.com/p/look-up-at-the-sky",
"pubDate": "Sun, 08 Oct 2023 06:01:08 GMT",
"title": "Look Up At The Sky!"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Alternative futures, hidden cameras in Airbnb, information diet, how Lego became Lego, Ibiza life, and much more ...",
"link": "https://fulltimecurious.substack.com/p/curiosity-for-curation-why-i-am-starting",
"pubDate": "Fri, 01 Sep 2023 06:30:06 GMT",
"title": "Curiosity for curation, why I am starting again"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Moving week &#128075;",
"link": "https://fulltimecurious.substack.com/p/full-time-curious-with-alexandre-d48",
"pubDate": "Sun, 28 Apr 2019 19:01:59 GMT",
"title": "Full-Time Curious with Alexandre #3"
}
32 changes: 32 additions & 0 deletions src/pages/newsletter.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
import NewsletterSubscribe from "@/components/common/NewsletterSubscribe.astro";
import NewsletterItem from "@/components/newsletter/NewsletterItem.astro";
import BaseLayout from "@/layouts/BaseLayout.astro";
import { getCollection } from "astro:content";
const allNewsletters = (await getCollection("newsletters")).sort(
(a, b) => new Date(b.data.pubDate) - new Date(a.data.pubDate)
);
const pageTitle = "Newsletter";
const pageSubtitle = "Receive my curation bimonthly";
---

<BaseLayout pageSubtitle={pageSubtitle} pageTitle={pageTitle}>
<div class="space-y-8">
<NewsletterSubscribe />
<div class="space-y-4">
{
(
<ul class="flex flex-col gap-4">
{allNewsletters.map((post) => (
<li>
<NewsletterItem entry={post} />
</li>
))}
</ul>
)
}
</div>
</div>
</BaseLayout>
46 changes: 46 additions & 0 deletions src/utils/newsletter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fs from "fs";
import fetch from "node-fetch";
import xml2js from "xml2js";

// Fetch the RSS feed and parse it into JSON
async function fetchAndParseRSSFeed() {

// URL of the Substack RSS feed
const url = "https://fulltimecurious.substack.com/feed";

// Fetch the RSS feed data
const response = await fetch(url);
const xml = await response.text(); // Convert the response to text (XML format)

// Parse the XML into a JavaScript object
const parser = new xml2js.Parser();
const result = await parser.parseStringPromise(xml);

// Ensure the directory exists
const dir = "./src/content/newsletters/";
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}

// Map through each newsletter issue and save each as a separate JSON file
const newsletters = result.rss.channel[0].item;

newsletters.forEach((newsletter, index) => {

// Extract the data for each newsletter
const sanitizedTitle = newsletter.title[0].toLowerCase().replace(/[^a-zA-Z0-9]/g, "_"); // Clean title for file name

const newsletterData = {
description: newsletter.description[0],
link: newsletter.link[0],
pubDate: newsletter.pubDate[0],
title: newsletter.title[0],
};

// Save the newsletter data as a JSON file
fs.writeFileSync(`${dir}/${index + 1}_${sanitizedTitle}.json`, JSON.stringify(newsletterData, null, 2));
});
}

// Execute the function and handle any errors
fetchAndParseRSSFeed().catch(console.error);

0 comments on commit 811b6b6

Please sign in to comment.