Skip to content

Commit 4d13857

Browse files
authored
Merge pull request #13806 from ethereum/homepage-atom-feed
Homepage: enable atom feeds
2 parents 2dec337 + 6e205c9 commit 4d13857

File tree

3 files changed

+135
-45
lines changed

3 files changed

+135
-45
lines changed

src/lib/api/fetchRSS.ts

Lines changed: 100 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { parseString } from "xml2js"
22

3-
import type { RSSChannel, RSSItem, RSSResult } from "../types"
3+
import type {
4+
AtomElement,
5+
AtomResult,
6+
RSSChannel,
7+
RSSItem,
8+
RSSResult,
9+
} from "../types"
410
import { isValidDate } from "../utils/date"
511

612
/**
@@ -12,45 +18,101 @@ export const fetchRSS = async (xmlUrl: string | string[]) => {
1218
const urls = Array.isArray(xmlUrl) ? xmlUrl : [xmlUrl]
1319
const allItems: RSSItem[][] = []
1420
for (const url of urls) {
15-
const rssItems = (await fetchXml(url)) as RSSResult
16-
if (!rssItems.rss) continue
17-
const [mainChannel] = rssItems.rss.channel as RSSChannel[]
18-
const [source] = mainChannel.title
19-
const [sourceUrl] = mainChannel.link
20-
const channelImage = mainChannel.image ? mainChannel.image[0].url[0] : ""
21+
const response = (await fetchXml(url)) as RSSResult | AtomResult
22+
if ("rss" in response) {
23+
const [mainChannel] = response.rss.channel as RSSChannel[]
24+
const [source] = mainChannel.title
25+
const [sourceUrl] = mainChannel.link
26+
const channelImage = mainChannel.image ? mainChannel.image[0].url[0] : ""
2127

22-
const parsedRssItems = mainChannel.item
23-
// Filter out items with invalid dates
24-
.filter((item) => {
25-
if (!item.pubDate) return false
26-
const [pubDate] = item.pubDate
27-
return isValidDate(pubDate)
28-
})
29-
// Sort by pubDate (most recent is first in array
30-
.sort((a, b) => {
31-
const dateA = new Date(a.pubDate[0])
32-
const dateB = new Date(b.pubDate[0])
33-
return dateB.getTime() - dateA.getTime()
34-
})
35-
// Map to RSSItem object
36-
.map((item) => {
37-
const getImgSrc = () => {
38-
if (item.enclosure) return item.enclosure[0].$.url
39-
if (item["media:content"]) return item["media:content"][0].$.url
40-
return channelImage
41-
}
42-
return {
43-
pubDate: item.pubDate[0],
44-
title: item.title[0],
45-
link: item.link[0],
46-
imgSrc: getImgSrc(),
47-
source,
48-
sourceUrl,
49-
sourceFeedUrl: url,
50-
} as RSSItem
51-
})
28+
const parsedRssItems = mainChannel.item
29+
// Filter out items with invalid dates
30+
.filter((item) => {
31+
if (!item.pubDate) return false
32+
const [pubDate] = item.pubDate
33+
return isValidDate(pubDate)
34+
})
35+
// Sort by pubDate (most recent is first in array
36+
.sort((a, b) => {
37+
const dateA = new Date(a.pubDate[0])
38+
const dateB = new Date(b.pubDate[0])
39+
return dateB.getTime() - dateA.getTime()
40+
})
41+
// Map to RSSItem object
42+
.map((item) => {
43+
const getImgSrc = () => {
44+
if (item.enclosure) return item.enclosure[0].$.url
45+
if (item["media:content"]) return item["media:content"][0].$.url
46+
return channelImage
47+
}
48+
return {
49+
pubDate: item.pubDate[0],
50+
title: item.title[0],
51+
link: item.link[0],
52+
imgSrc: getImgSrc(),
53+
source,
54+
sourceUrl,
55+
sourceFeedUrl: url,
56+
} as RSSItem
57+
})
5258

53-
allItems.push(parsedRssItems)
59+
allItems.push(parsedRssItems)
60+
} else if ("feed" in response) {
61+
const [source] = response.feed.title
62+
const [sourceUrl] = response.feed.id
63+
const feedImage = response.feed.icon?.[0]
64+
65+
const parsedAtomItems = response.feed.entry
66+
// Filter out items with invalid dates
67+
.filter((entry) => {
68+
if (!entry.updated) return false
69+
const [published] = entry.updated
70+
return isValidDate(published)
71+
})
72+
// Sort by published (most recent is first in array
73+
.sort((a, b) => {
74+
const dateA = new Date(a.updated[0])
75+
const dateB = new Date(b.updated[0])
76+
return dateB.getTime() - dateA.getTime()
77+
})
78+
// Map to RSSItem object
79+
.map((entry) => {
80+
const getString = (el?: AtomElement[]): string => {
81+
if (!el) return ""
82+
const [firstEl] = el
83+
if (typeof firstEl === "string") return firstEl
84+
return firstEl._ || ""
85+
}
86+
const getHref = (): string => {
87+
if (!entry.link) {
88+
console.warn(`No link found for RSS url: ${url}`)
89+
return ""
90+
}
91+
const link = entry.link[0]
92+
if (typeof link === "string") return link
93+
return link.$.href || ""
94+
}
95+
const getImgSrc = (): string => {
96+
const imgRegEx = /https?:\/\/[^"]*?\.(jpe?g|png|webp)/g
97+
const contentMatch = getString(entry.content).match(imgRegEx)
98+
if (contentMatch) return contentMatch[0]
99+
const summaryMatch = getString(entry.summary).match(imgRegEx)
100+
if (summaryMatch) return summaryMatch[0]
101+
return feedImage || ""
102+
}
103+
return {
104+
pubDate: entry.updated[0],
105+
title: getString(entry.title),
106+
link: getHref(),
107+
imgSrc: getImgSrc(),
108+
source,
109+
sourceUrl,
110+
sourceFeedUrl: url,
111+
} as RSSItem
112+
})
113+
114+
allItems.push(parsedAtomItems)
115+
}
54116
}
55117
return allItems as RSSItem[][]
56118
}

src/lib/constants.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,13 @@ export const COMMUNITY_BLOGS: CommunityBlog[] = [
201201
feed: SOLIDITY_FEED,
202202
},
203203
{
204-
name: "Privacy and Scaling Explorations",
205204
href: "https://mirror.xyz/privacy-scaling-explorations.eth",
206-
// feed: "https://mirror.xyz/privacy-scaling-explorations.eth/feed/atom", // Old xml format
205+
feed: "https://mirror.xyz/privacy-scaling-explorations.eth/feed/atom",
206+
},
207+
{
208+
href: "https://stark.mirror.xyz/",
209+
feed: "https://stark.mirror.xyz/feed/atom",
207210
},
208-
// {
209-
// href: "https://stark.mirror.xyz/",
210-
// feed: "https://stark.mirror.xyz/feed/atom", // Old xml format
211-
// },
212-
// TODO: Add support for old xml format, re-enable above when ready
213211
]
214212

215213
export const BLOG_FEEDS = COMMUNITY_BLOGS.map(({ feed }) => feed).filter(

src/lib/types.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,36 @@ export type RSSResult = {
803803
}
804804
}
805805

806+
export type AtomElement =
807+
| string
808+
| {
809+
_?: string // children
810+
$: {
811+
href?: string
812+
}
813+
}
814+
export type AtomEntry = {
815+
id: string[]
816+
title: AtomElement[]
817+
updated: string[]
818+
content?: AtomElement[]
819+
link?: AtomElement[]
820+
summary?: AtomElement[]
821+
}
822+
823+
export type AtomResult = {
824+
feed: {
825+
id: string[]
826+
title: string[]
827+
updated: string[]
828+
generator: string[]
829+
link: string[]
830+
subtitle: string[]
831+
icon?: string[]
832+
entry: AtomEntry[]
833+
}
834+
}
835+
806836
export type CommunityBlog = {
807837
href: string
808838
} & ({ name: string; feed?: string } | { name?: string; feed: string })

0 commit comments

Comments
 (0)