|
1 | 1 | /** |
2 | | - * Fetch and display GitHub Discussions RSS feed for Weekly Updates |
| 2 | + * Fetch and display GitHub Discussions for Weekly Updates using GraphQL API |
3 | 3 | */ |
4 | 4 | (function() { |
5 | 5 | 'use strict'; |
6 | 6 |
|
7 | | - const RSS_FEED_URL = 'https://github.com/fvutils/fvutils.github.io/discussions.rss'; |
8 | | - const CATEGORY_SLUG = 'weekly-updates'; |
| 7 | + const GRAPHQL_API = 'https://api.github.com/graphql'; |
| 8 | + const OWNER = 'fvutils'; |
| 9 | + const REPO = 'fvutils.github.io'; |
| 10 | + const CATEGORY_ID = 'DIC_kwDOHMTiU84C2BrA'; |
9 | 11 | const MAX_ITEMS = 5; |
10 | 12 |
|
11 | 13 | /** |
12 | | - * Fetch and parse the RSS feed |
| 14 | + * Fetch discussions using GitHub GraphQL API |
13 | 15 | */ |
14 | 16 | async function fetchDiscussions() { |
| 17 | + const query = ` |
| 18 | + query { |
| 19 | + repository(owner: "${OWNER}", name: "${REPO}") { |
| 20 | + discussions(first: ${MAX_ITEMS}, categoryId: "${CATEGORY_ID}", orderBy: {field: CREATED_AT, direction: DESC}) { |
| 21 | + nodes { |
| 22 | + number |
| 23 | + title |
| 24 | + url |
| 25 | + createdAt |
| 26 | + bodyText |
| 27 | + } |
| 28 | + } |
| 29 | + } |
| 30 | + } |
| 31 | + `; |
| 32 | + |
15 | 33 | try { |
16 | | - const response = await fetch(RSS_FEED_URL); |
| 34 | + const response = await fetch(GRAPHQL_API, { |
| 35 | + method: 'POST', |
| 36 | + headers: { |
| 37 | + 'Content-Type': 'application/json', |
| 38 | + }, |
| 39 | + body: JSON.stringify({ query }) |
| 40 | + }); |
| 41 | + |
17 | 42 | if (!response.ok) { |
18 | 43 | throw new Error(`HTTP error! status: ${response.status}`); |
19 | 44 | } |
| 45 | + |
| 46 | + const data = await response.json(); |
20 | 47 |
|
21 | | - const text = await response.text(); |
22 | | - const parser = new DOMParser(); |
23 | | - const xmlDoc = parser.parseFromString(text, 'text/xml'); |
24 | | - |
25 | | - // Check for parsing errors |
26 | | - const parserError = xmlDoc.querySelector('parsererror'); |
27 | | - if (parserError) { |
28 | | - throw new Error('XML parsing error'); |
| 48 | + if (data.errors) { |
| 49 | + console.error('GraphQL errors:', data.errors); |
| 50 | + return null; |
29 | 51 | } |
30 | | - |
31 | | - return parseRSSItems(xmlDoc); |
| 52 | + |
| 53 | + return data.data.repository.discussions.nodes; |
32 | 54 | } catch (error) { |
33 | 55 | console.error('Error fetching discussions:', error); |
34 | 56 | return null; |
35 | 57 | } |
36 | 58 | } |
37 | 59 |
|
38 | 60 | /** |
39 | | - * Parse RSS items and filter for Weekly Updates category |
40 | | - */ |
41 | | - function parseRSSItems(xmlDoc) { |
42 | | - const items = xmlDoc.querySelectorAll('item'); |
43 | | - const discussions = []; |
44 | | - |
45 | | - items.forEach(item => { |
46 | | - const link = item.querySelector('link')?.textContent || ''; |
47 | | - const title = item.querySelector('title')?.textContent || ''; |
48 | | - const pubDate = item.querySelector('pubDate')?.textContent || ''; |
49 | | - const description = item.querySelector('description')?.textContent || ''; |
50 | | - |
51 | | - // Filter by category slug in the link |
52 | | - // Discussion links look like: https://github.com/fvutils/fvutils.github.io/discussions/categories/weekly-updates/... |
53 | | - if (link.includes(`/categories/${CATEGORY_SLUG}`)) { |
54 | | - discussions.push({ |
55 | | - title: title, |
56 | | - link: link, |
57 | | - date: new Date(pubDate), |
58 | | - description: description |
59 | | - }); |
60 | | - } |
61 | | - }); |
62 | | - |
63 | | - // Sort by date (newest first) and limit to MAX_ITEMS |
64 | | - discussions.sort((a, b) => b.date - a.date); |
65 | | - return discussions.slice(0, MAX_ITEMS); |
66 | | - } |
67 | | - |
68 | | - /** |
69 | | - * Format date as readable string |
| 61 | + * Get excerpt from body text |
70 | 62 | */ |
71 | | - function formatDate(date) { |
72 | | - const options = { year: 'numeric', month: 'long', day: 'numeric' }; |
73 | | - return date.toLocaleDateString('en-US', options); |
74 | | - } |
75 | | - |
76 | | - /** |
77 | | - * Extract plain text excerpt from HTML description |
78 | | - */ |
79 | | - function getExcerpt(html, maxWords = 30) { |
80 | | - const tempDiv = document.createElement('div'); |
81 | | - tempDiv.innerHTML = html; |
82 | | - const text = tempDiv.textContent || tempDiv.innerText || ''; |
| 63 | + function getExcerpt(text, maxWords = 30) { |
83 | 64 | const words = text.trim().split(/\s+/); |
84 | 65 |
|
85 | 66 | if (words.length > maxWords) { |
|
115 | 96 | `; |
116 | 97 |
|
117 | 98 | discussions.forEach(discussion => { |
| 99 | + const date = new Date(discussion.createdAt); |
| 100 | + const formattedDate = date.toLocaleDateString('en-US', { |
| 101 | + year: 'numeric', |
| 102 | + month: 'long', |
| 103 | + day: 'numeric' |
| 104 | + }); |
| 105 | + |
118 | 106 | html += ` |
119 | 107 | <li class="news-item"> |
120 | | - <div class="news-date">${formatDate(discussion.date)}</div> |
121 | | - <h3 class="news-title"><a href="${discussion.link}">${discussion.title}</a></h3> |
122 | | - <p class="news-excerpt">${getExcerpt(discussion.description)}</p> |
| 108 | + <div class="news-date">${formattedDate}</div> |
| 109 | + <h3 class="news-title"><a href="${discussion.url}">${discussion.title}</a></h3> |
| 110 | + <p class="news-excerpt">${getExcerpt(discussion.bodyText)}</p> |
123 | 111 | </li> |
124 | 112 | `; |
125 | 113 | }); |
|
0 commit comments