From 40a9babf55c9e0f964868b01199cad3b98e60c36 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Sat, 24 Jan 2026 23:10:42 +0100 Subject: [PATCH] feat: add news page and data file for RSS feed reader - newsActivity.js: Fetch items/feeds/status from /rssapi/api - news.njk: News page with list/card/full view modes and feed filtering Co-Authored-By: Claude Opus 4.5 --- _data/newsActivity.js | 99 ++++++++++++++ news.njk | 312 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 411 insertions(+) create mode 100644 _data/newsActivity.js create mode 100644 news.njk diff --git a/_data/newsActivity.js b/_data/newsActivity.js new file mode 100644 index 0000000..47499b8 --- /dev/null +++ b/_data/newsActivity.js @@ -0,0 +1,99 @@ +/** + * News/RSS Activity Data + * Fetches from Indiekit's endpoint-rss public API + */ + +import EleventyFetch from "@11ty/eleventy-fetch"; + +const INDIEKIT_URL = process.env.SITE_URL || "https://example.com"; + +/** + * Fetch from Indiekit's public RSS API endpoint + */ +async function fetchFromIndiekit(endpoint) { + try { + const url = `${INDIEKIT_URL}/rssapi/api/${endpoint}`; + console.log(`[newsActivity] Fetching from Indiekit: ${url}`); + const data = await EleventyFetch(url, { + duration: "15m", + type: "json", + }); + console.log(`[newsActivity] Indiekit ${endpoint} success`); + return data; + } catch (error) { + console.log( + `[newsActivity] Indiekit API unavailable for ${endpoint}: ${error.message}` + ); + return null; + } +} + +export default async function () { + try { + console.log("[newsActivity] Fetching RSS feed data..."); + + // Fetch all data from Indiekit API + const [itemsRes, feedsRes, statusRes] = await Promise.all([ + fetchFromIndiekit("items?limit=50"), + fetchFromIndiekit("feeds"), + fetchFromIndiekit("status"), + ]); + + // Check if we got data + const hasData = itemsRes?.items?.length || feedsRes?.feeds?.length; + + if (!hasData) { + console.log("[newsActivity] No data available from Indiekit"); + return { + items: [], + feeds: [], + status: null, + lastUpdated: null, + source: "unavailable", + }; + } + + console.log( + `[newsActivity] Got ${itemsRes?.items?.length || 0} items from ${feedsRes?.feeds?.length || 0} feeds` + ); + + // Create a map of feed IDs to feed info for quick lookup + const feedMap = new Map(); + for (const feed of feedsRes?.feeds || []) { + feedMap.set(feed.id, feed); + } + + // Enhance items with additional feed info + const items = (itemsRes?.items || []).map((item) => { + const feed = feedMap.get(item.feedId); + return { + ...item, + feedInfo: feed + ? { + title: feed.title, + siteUrl: feed.siteUrl, + imageUrl: feed.imageUrl, + } + : null, + }; + }); + + return { + items, + feeds: feedsRes?.feeds || [], + pagination: itemsRes?.pagination || null, + status: statusRes || null, + lastUpdated: statusRes?.lastSync || new Date().toISOString(), + source: "indiekit", + }; + } catch (error) { + console.error("[newsActivity] Error:", error.message); + return { + items: [], + feeds: [], + status: null, + lastUpdated: null, + source: "error", + }; + } +} diff --git a/news.njk b/news.njk new file mode 100644 index 0000000..af26274 --- /dev/null +++ b/news.njk @@ -0,0 +1,312 @@ +--- +layout: layouts/base.njk +title: News Feed +permalink: /news/ +withSidebar: true +--- +
+
+

News Feed

+

+ Aggregated content from my favorite feeds +

+ {% if newsActivity.lastUpdated %} +

+ Last updated: {{ newsActivity.lastUpdated | date("PPpp") }} +

+ {% endif %} +
+ + {# View Mode and Filter Controls #} +
+ {# View Mode Buttons #} +
+ + + +
+ + {# Feed Filter Dropdown #} + {% if newsActivity.feeds.length > 1 %} +
+ + + + +
+ {% endif %} +
+ + {# Stats Bar #} + {% if newsActivity.status %} +
+
+ Feeds: + {{ newsActivity.status.stats.feedsCount }} +
+
+ Items: + {{ newsActivity.status.stats.itemsCount }} +
+ {% if newsActivity.status.status == 'syncing' %} +
+ + + + + Syncing... +
+ {% endif %} +
+ {% endif %} + + {# Items List #} + {% if newsActivity.items.length %} +
+ {# List View #} +
+ {% for item in newsActivity.items %} +
+ {% if item.imageUrl %} + + {% endif %} + +
+

+ {{ item.title }} +

+ + {% if item.description %} +

+ {{ item.description }} +

+ {% endif %} + +
+ {% if item.feedTitle %} + + {% if item.feedInfo and item.feedInfo.imageUrl %} + + {% endif %} + {{ item.feedTitle }} + + {% endif %} + + {% if item.author %} + by {{ item.author }} + {% endif %} + + {% if item.pubDate %} + + {% endif %} + + {% if item.categories.length %} + + {% endif %} +
+
+
+ {% endfor %} +
+ + {# Card View #} +
+ {% for item in newsActivity.items %} +
+ {% if item.imageUrl %} +
+ +
+ {% endif %} + +
+

+ {{ item.title }} +

+ + {% if item.description %} +

+ {{ item.description }} +

+ {% endif %} + +
+ + {% if item.feedTitle %}{{ item.feedTitle }}{% endif %} + + {% if item.pubDate %} + + {% endif %} +
+
+
+ {% endfor %} +
+ + {# Full/Expanded View #} +
+ {% for item in newsActivity.items %} +
+ {% if item.imageUrl %} +
+ +
+ {% endif %} + +
+ {# Meta bar #} +
+ {% if item.feedInfo %} + + {% if item.feedInfo.imageUrl %} + + {% endif %} + {{ item.feedTitle }} + + {% endif %} + + {% if item.author %} + by {{ item.author }} + {% endif %} + + {% if item.pubDate %} + + {% endif %} +
+ +

+ {{ item.title }} +

+ + {% if item.description %} +

+ {{ item.description }} +

+ {% endif %} + +
+ + Read More + + + + + + {% if item.categories.length %} +
+ {% for cat in item.categories %} + + {{ cat }} + + {% endfor %} +
+ {% endif %} +
+
+
+ {% endfor %} +
+
+ {% else %} +
+ + + +

No news items yet.

+

Add some RSS feeds to get started.

+
+ {% endif %} +