From 7b5a4adcfb3a2ac7f2f46ae5bda88d20fadc3455 Mon Sep 17 00:00:00 2001
From: Waled Khatiz
Date: Mon, 26 Aug 2024 00:09:13 +1000
Subject: [PATCH 4/6] fix: CardWrapper typo
---
src/components/theme-card.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/theme-card.tsx b/src/components/theme-card.tsx
index 988bbce..3e29af1 100644
--- a/src/components/theme-card.tsx
+++ b/src/components/theme-card.tsx
@@ -3,7 +3,7 @@ import { getThemeAuthorLink, ZenTheme } from "@/lib/themes";
import styled from "styled-components";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog";import { Button } from "./ui/button";
-const ThemeCardWrapepr = styled.div`
+const ThemeCardWrapper = styled.div`
`;
export default function ThemeCard({
@@ -12,7 +12,7 @@ export default function ThemeCard({
theme: ZenTheme;
}) {
return (
- {
+ {
if (event.target instanceof HTMLAnchorElement) return;
window.open(`/themes/${theme.id}`, "_self");
}} className="flex flex-col justify-start p-5 rounded-lg shadow-sm bg-muted dark:bg-muted/50 border border-grey-900 dark:border-muted w-full hover:shadow-lg transition duration-300 ease-in-out hover:bg-muted/100 hover:border-blue-500 cursor-pointer select-none ">
@@ -35,6 +35,6 @@ export default function ThemeCard({
{theme.description}
-
+
);
}
From 2e94c80dc4b01daeab04f92bc429800de810a939 Mon Sep 17 00:00:00 2001
From: Daniel Masterson
Date: Sun, 25 Aug 2024 15:48:38 +0100
Subject: [PATCH 5/6] feat: Add RSS 2.0 Feed
---
package-lock.json | 31 +++++++++++++++
package.json | 1 +
src/app/feed.xml/route.ts | 81 +++++++++++++++++++++++++++++++++++++++
src/app/layout.tsx | 1 +
4 files changed, 114 insertions(+)
create mode 100644 src/app/feed.xml/route.ts
diff --git a/package-lock.json b/package-lock.json
index 9a79331..845ad22 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27,6 +27,7 @@
"clsx": "^2.1.1",
"cobe": "^0.6.3",
"dotenv": "^16.4.5",
+ "feed": "^4.2.2",
"framer-motion": "^11.3.24",
"lucide-react": "^0.400.0",
"next": "14.2.4",
@@ -7780,6 +7781,18 @@
"bser": "2.1.1"
}
},
+ "node_modules/feed": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz",
+ "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xml-js": "^1.6.11"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -13396,6 +13409,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/sax": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
+ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+ "license": "ISC"
+ },
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -15453,6 +15472,18 @@
}
}
},
+ "node_modules/xml-js": {
+ "version": "1.6.11",
+ "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
+ "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
+ "license": "MIT",
+ "dependencies": {
+ "sax": "^1.2.4"
+ },
+ "bin": {
+ "xml-js": "bin/cli.js"
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/package.json b/package.json
index 921dc8b..fa53fd6 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"clsx": "^2.1.1",
"cobe": "^0.6.3",
"dotenv": "^16.4.5",
+ "feed": "^4.2.2",
"framer-motion": "^11.3.24",
"lucide-react": "^0.400.0",
"next": "14.2.4",
diff --git a/src/app/feed.xml/route.ts b/src/app/feed.xml/route.ts
new file mode 100644
index 0000000..c1fe957
--- /dev/null
+++ b/src/app/feed.xml/route.ts
@@ -0,0 +1,81 @@
+import { Feed } from "feed";
+import { releaseNoteIsAlpha, releaseNotes } from "@/lib/release-notes";
+import type { ReleaseNote } from "@/lib/release-notes";
+
+export async function GET() {
+ releaseNotes[0].date
+
+ const feed = new Feed({
+ id: "https://www.zen-browser.app/release-notes",
+ link: "https://www.zen-browser.app/release-notes",
+ title: "Zen Browser Release Notes",
+ description: "Release Notes for the Zen Browser",
+ language: "en",
+ favicon: "https://www.zen-browser.app/favicon.ico",
+ copyright: `Zen Browser © ${new Date().getFullYear()} - Made with ❤️ by the Zen team.`,
+ updated: formatRssDate(releaseNotes[0].date),
+ });
+
+ for (const releaseNote of releaseNotes) {
+ feed.addItem({
+ title: `Release notes for version ${releaseNote.version}`,
+ id: `https://www.zen-browser.app/release-notes/${releaseNote.version}`,
+ link: `https://www.zen-browser.app/release-notes/${releaseNote.version}`,
+ date: formatRssDate(releaseNote.date),
+ description: releaseNote.extra,
+ content: formatReleaseNote(releaseNote),
+ });
+ }
+
+ return new Response(feed.rss2(), {
+ headers: {
+ 'Content-Type': 'application/xml; charset=utf-8',
+ },
+ });
+}
+
+function formatRssDate(date: string) {
+ // NOTE: This is assuming the format day/month/year. If release notes change to ISO format, this will need to be updated.
+ const splitDate = date.split("/");
+ const year = Number(splitDate[2]);
+ const month = Number(splitDate[1]) - 1;
+ const day = Number(splitDate[0]);
+ return new Date(year, month, day);
+}
+
+function formatReleaseNote(releaseNote: ReleaseNote) {
+ let content = "If you encounter any issues, please report them on the issues page . Thanks everyone for your feedback! ❤️
";
+
+ if(releaseNote.extra) {
+ content += `${releaseNote.extra.replace(/(\n)/g, " ")}
`
+ }
+
+ if(releaseNote.breakingChanges) {
+ content += `⚠️ Breaking changes `
+ content += ``
+ for (const breakingChange of releaseNote.breakingChanges) {
+ content += `${breakingChange} `
+ }
+ content += ` `
+ }
+
+ if(releaseNote.features) {
+ content += `⭐ Features `
+ content += ``
+ for (const feature of releaseNote.features) {
+ content += `${feature} `
+ }
+ content += ` `
+ }
+
+ if(releaseNote.fixes) {
+ content += `✓ Fixes `
+ content += ``
+ for (const fix of releaseNote.fixes) {
+ content += `${fix.description} `
+ }
+ content += ` `
+ }
+
+ return content;
+}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 13fbbc8..fd2a59b 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -21,6 +21,7 @@ export default function RootLayout({
+
Date: Sun, 25 Aug 2024 19:59:19 +0100
Subject: [PATCH 6/6] feat: Limit RSS Feed to return a maximum of 20 items
---
src/app/feed.xml/route.ts | 57 +++++++++++++++++++++++++++++----------
1 file changed, 43 insertions(+), 14 deletions(-)
diff --git a/src/app/feed.xml/route.ts b/src/app/feed.xml/route.ts
index c1fe957..4d761f1 100644
--- a/src/app/feed.xml/route.ts
+++ b/src/app/feed.xml/route.ts
@@ -1,9 +1,23 @@
import { Feed } from "feed";
-import { releaseNoteIsAlpha, releaseNotes } from "@/lib/release-notes";
+import { releaseNotes } from "@/lib/release-notes";
import type { ReleaseNote } from "@/lib/release-notes";
+// Force feed.xml to be cached as static and remain constant for the lifetime of the current site build.
+// The supplied releaseNotes array is constant per build, so this will always be the latest release notes.
+export const dynamic = "force-static";
+
+/** The default number of entries to include in the RSS feed. */
+const RSS_ENTRY_LIMIT = 20;
+
+/**
+ * Handles the GET request for the `feed.xml` endpoint.
+ * @returns The RSS feed for the Zen Browser release notes.
+ */
export async function GET() {
- releaseNotes[0].date
+ // Just in case the release notes array is empty for whatever reason.
+ const latestDate = releaseNotes.length > 0
+ ? formatRssDate(releaseNotes[0].date)
+ : new Date();
const feed = new Feed({
id: "https://www.zen-browser.app/release-notes",
@@ -13,10 +27,10 @@ export async function GET() {
language: "en",
favicon: "https://www.zen-browser.app/favicon.ico",
copyright: `Zen Browser © ${new Date().getFullYear()} - Made with ❤️ by the Zen team.`,
- updated: formatRssDate(releaseNotes[0].date),
+ updated: latestDate,
});
- for (const releaseNote of releaseNotes) {
+ for (const releaseNote of releaseNotes.slice(0, RSS_ENTRY_LIMIT)) {
feed.addItem({
title: `Release notes for version ${releaseNote.version}`,
id: `https://www.zen-browser.app/release-notes/${releaseNote.version}`,
@@ -34,23 +48,38 @@ export async function GET() {
});
}
-function formatRssDate(date: string) {
- // NOTE: This is assuming the format day/month/year. If release notes change to ISO format, this will need to be updated.
- const splitDate = date.split("/");
- const year = Number(splitDate[2]);
- const month = Number(splitDate[1]) - 1;
+/**
+ * Formats a date string in the format day/month/year.
+ *
+ * Note: If release notes change to ISO format, this will need to be updated.
+ * @param dateStr The date string to format.
+ * @returns The passed in date string as a Date object.
+ */
+function formatRssDate(dateStr: string) {
+ const splitDate = dateStr.split("/");
+ if (splitDate.length !== 3) {
+ throw new Error("Invalid date format");
+ }
+
const day = Number(splitDate[0]);
+ const month = Number(splitDate[1]) - 1;
+ const year = Number(splitDate[2]);
return new Date(year, month, day);
}
+/**
+ * Formats the release note entry for use as the content of the RSS feed.
+ * @param releaseNote The release note to format.
+ * @returns The formatted release note as a HTML string.
+ */
function formatReleaseNote(releaseNote: ReleaseNote) {
let content = "If you encounter any issues, please report them on the issues page . Thanks everyone for your feedback! ❤️
";
- if(releaseNote.extra) {
+ if (releaseNote.extra) {
content += `${releaseNote.extra.replace(/(\n)/g, " ")}
`
}
- if(releaseNote.breakingChanges) {
+ if (releaseNote.breakingChanges) {
content += `⚠️ Breaking changes `
content += ``
for (const breakingChange of releaseNote.breakingChanges) {
@@ -59,7 +88,7 @@ function formatReleaseNote(releaseNote: ReleaseNote) {
content += ` `
}
- if(releaseNote.features) {
+ if (releaseNote.features) {
content += `⭐ Features `
content += ``
for (const feature of releaseNote.features) {
@@ -68,7 +97,7 @@ function formatReleaseNote(releaseNote: ReleaseNote) {
content += ` `
}
- if(releaseNote.fixes) {
+ if (releaseNote.fixes) {
content += `✓ Fixes `
content += ``
for (const fix of releaseNote.fixes) {
@@ -78,4 +107,4 @@ function formatReleaseNote(releaseNote: ReleaseNote) {
}
return content;
-}
\ No newline at end of file
+}