diff --git a/bun.lockb b/bun.lockb index 2b54452..0f32659 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/config/template.html b/config/template.html index 414a0fa..67930a1 100644 --- a/config/template.html +++ b/config/template.html @@ -5,34 +5,132 @@ - 📰 Carter's RSS Feeds + Carter's RSS Feeds + -

Carter's RSS Feeds

- - {% for group, feeds in data %} -

{{ group }}

- {% for feed in feeds %} -
- - {{ feed.title }} - ({{ feed.feed }}) - - -
- {% endfor %} {% endfor %} {% if errors | length > 0 %} + + {% endfor %} + + {% if errors | length > 0 %}

Errors

There were errors trying to parse these feeds:

{% endif %} - -
-
-

Last updated {{ now }}.

-

- Powered by - Bubo Reader (v{{ info.version }}), a project by George Mandis. ❤️ -

+ diff --git a/package.json b/package.json index 88ac0b2..824b921 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ }, "license": "MIT", "dependencies": { + "@feelinglovelynow/get-relative-time": "^1.1.2", "chalk": "^5.2.0", - "javascript-time-ago": "^2.5.10", "node-fetch": "^3.3.1", "nunjucks": "^3.2.4", "rss-parser": "^3.13.0" diff --git a/public/manifest.json b/public/manifest.json index 060df0b..8c93e43 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,16 +1,18 @@ { - "name": "Carter's RSS Feeds", - "short_name": "Carter's RSS Feeds", - "start_url": ".", - "scope": ".", - "display": "standalone", - "background_color": "#FCF5E4", - "theme_color": "#FCF5E4", - "description": "Updates from RSS feeds that Carter likes", - "id": "/", - "icons": [{ - "sizes": "any", - "src": "news-emoji.svg", - "type": "image/svg+xml" - }] + "name": "Carter's RSS Feeds", + "short_name": "Carter's RSS Feeds", + "start_url": ".", + "scope": ".", + "display": "standalone", + "background_color": "#FCF5E4", + "theme_color": "#FCF5E4", + "description": "Updates from RSS feeds that Carter likes", + "id": "/", + "icons": [ + { + "sizes": "any", + "src": "news-emoji.svg", + "type": "image/svg+xml" + } + ] } diff --git a/public/style.css b/public/style.css index 20982a2..5092d7c 100644 --- a/public/style.css +++ b/public/style.css @@ -12,6 +12,17 @@ body { color: var(--color-text); } +main { + display: grid; + gap: 1em; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); +} + +section ul { + max-height: 250px; + overflow-y: auto; +} + .inline-icon { height: 1em; vertical-align: text-bottom; @@ -19,6 +30,7 @@ body { summary { cursor: pointer; + text-overflow: ellipsis; } summary, @@ -42,13 +54,18 @@ details:has(li.has-recent) { } } -details, + .errors li { word-break: break-all; } .feed-url { color: #aaa; + white-space: nowrap; + display: inline-block; + text-overflow: ellipsis; + width: 100%; + overflow: hidden; } .article-timestamp, diff --git a/src/index.ts b/src/index.ts index 1aa3e3b..7d8a414 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,9 +72,20 @@ const finishBuild: () => void = async () => { process.stdout.write("\nDone fetching everything!\n"); + // sort all the categories and the feeds alphabetically + const sortedFeeds: Feeds = {}; + const sortedKeys = Object.keys(contentFromAllFeeds).sort((a, b) => + a.localeCompare(b), + ); + for (const key of sortedKeys) { + sortedFeeds[key] = contentFromAllFeeds[key].sort((a, b) => + a.title.localeCompare(b.title), + ); + } + // generate the static HTML output from our template renderer const output = render({ - data: contentFromAllFeeds, + data: sortedFeeds, errors: errors, info: buboInfo, }); @@ -105,48 +116,48 @@ const processFeed = feed: string; startTime: number; }) => - async (response: Response): Promise => { - const body = await parseFeed(response); - //skip to the next one if this didn't work out - if (!body) return; + async (response: Response): Promise => { + const body = await parseFeed(response); + //skip to the next one if this didn't work out + if (!body) return; - try { - const contents: FeedItem = ( - typeof body === "string" ? await parser.parseString(body) : body - ) as FeedItem; + try { + const contents: FeedItem = ( + typeof body === "string" ? await parser.parseString(body) : body + ) as FeedItem; - contents.feed = feed; - contents.title = getTitle(contents); - contents.link = getLink(contents); + contents.feed = feed; + contents.title = getTitle(contents); + contents.link = getLink(contents); - // try to normalize date attribute naming - for (const item of contents.items) { - item.timestamp = getTimestamp(item); - item.title = getTitle(item); - item.link = getLink(item); - const timestamp = new Date(Number.parseInt(item.timestamp)); - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - item.isRecent = timestamp > yesterday; + // try to normalize date attribute naming + for (const item of contents.items) { + item.timestamp = getTimestamp(item); + item.title = getTitle(item); + item.link = getLink(item); + const timestamp = new Date(Number.parseInt(item.timestamp)); + const eightHoursAgo = new Date(); + eightHoursAgo.setHours(eightHoursAgo.getHours() - 8); + item.isRecent = timestamp > eightHoursAgo; + } + + contents.hasRecent = contents.items.some((item) => item.isRecent); + + contentFromAllFeeds[group].push(contents as object); + process.stdout.write( + `${success("Successfully fetched:")} ${feed} - ${benchmark(startTime)}\n`, + ); + } catch (err) { + process.stdout.write( + `${error("Error processing:")} ${feed} - ${benchmark( + startTime, + )}\n${err}\n`, + ); + errors.push(`Error processing: ${feed}\n\t${err}`); } - contents.hasRecent = contents.items.some((item) => item.isRecent); - - contentFromAllFeeds[group].push(contents as object); - process.stdout.write( - `${success("Successfully fetched:")} ${feed} - ${benchmark(startTime)}\n`, - ); - } catch (err) { - process.stdout.write( - `${error("Error processing:")} ${feed} - ${benchmark( - startTime, - )}\n${err}\n`, - ); - errors.push(`Error processing: ${feed}\n\t${err}`); - } - - finishBuild(); - }; + finishBuild(); + }; // go through each group of feeds and process const processFeeds = () => { diff --git a/src/renderer.ts b/src/renderer.ts index 1dbda63..6ac0eeb 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -7,18 +7,15 @@ import nunjucks from "nunjucks"; const env: nunjucks.Environment = nunjucks.configure({ autoescape: true }); import { readFile } from "node:fs/promises"; +import { getRelativeTime } from "@feelinglovelynow/get-relative-time"; import type { Feeds, JSONValue } from "./@types/bubo"; -import TimeAgo from "javascript-time-ago"; -import en from "javascript-time-ago/locale/en"; -TimeAgo.addDefaultLocale(en); -const timeFormatter = new TimeAgo("en-US"); /** * Global filters for my Nunjucks templates */ env.addFilter("relative", (dateString): string => { const date: Date = new Date(Number.parseInt(dateString)); - return !Number.isNaN(date.getTime()) ? timeFormatter.format(date) : dateString; + return !Number.isNaN(date.getTime()) ? getRelativeTime(date) : dateString; }); env.addFilter("formatTime", (dateString): string => {