Skip to content

Commit e91cec7

Browse files
committed
feed directory iterate
1 parent fee88f9 commit e91cec7

2 files changed

Lines changed: 110 additions & 98 deletions

File tree

src/components/FeedDirectory.astro

Lines changed: 105 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { configs } from '../data/loadConfigs';
33
import { Icon, LinkButton } from '@astrojs/starlight/components';
44
55
// Simple helper functions
6-
function getFeedUrl(config: any, instanceUrl: string, params: Record<string, string> = {}) {
6+
function getFeedUrl(config: { domain: string; name: string; url_parameters?: Record<string, any> }, instanceUrl: string, params: Record<string, string> = {}) {
77
const baseUrl = instanceUrl.endsWith("/") ? instanceUrl : `${instanceUrl}/`;
88
let url = `${baseUrl}${config.domain}/${config.name}.rss`;
99
@@ -17,26 +17,24 @@ function getFeedUrl(config: any, instanceUrl: string, params: Record<string, str
1717
return url;
1818
}
1919
20-
// Generate static feed URLs
21-
const defaultInstanceUrl = atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
20+
// Don't generate static URLs to avoid exposing instance URL in build
2221
const staticFeedUrls = configs.map(config => ({
2322
...config,
24-
staticFeedUrl: getFeedUrl(config, defaultInstanceUrl)
23+
staticFeedUrl: "#" // Placeholder that will be updated by JavaScript
2524
}));
2625
---
2726

2827
<div class="feed-directory-container">
2928
<!-- Progressive Enhancement: Works without JavaScript -->
3029
<div class="feed-directory__filters">
31-
<label for="instance-url">
30+
<label for="instance-url-input">
3231
Instance URL
3332
</label>
3433
<input
3534
type="text"
3635
name="instance"
37-
id="instance-url"
36+
id="instance-url-input"
3837
class="feed-directory__input"
39-
value={defaultInstanceUrl}
4038
aria-label="Instance URL"
4139
placeholder="https://your-instance.com"
4240
/>
@@ -73,7 +71,7 @@ const staticFeedUrls = configs.map(config => ({
7371

7472
{config.channel?.url && (
7573
<div class="feed-directory__item-source">
76-
<a href={config.channel.url} target="_blank" rel="noopener noreferrer" class="feed-directory__item-link">
74+
<a href={config.channel.url} target="_blank" rel="noopener noreferrer nofollow" class="feed-directory__item-link">
7775
{config.channel.url}
7876
</a>
7977
</div>
@@ -101,6 +99,7 @@ const staticFeedUrls = configs.map(config => ({
10199
<LinkButton
102100
href={config.staticFeedUrl}
103101
target="_blank"
102+
rel="noopener noreferrer nofollow"
104103
data-feed-url
105104
size="sm"
106105
class="feed-directory__action-btn feed-directory__action-btn--rss"
@@ -114,6 +113,7 @@ const staticFeedUrls = configs.map(config => ({
114113
<LinkButton
115114
href={`https://github.com/html2rss/html2rss-configs/blob/master/lib/html2rss/configs/${encodeURIComponent(config.domain)}/${encodeURIComponent(config.name)}.yml`}
116115
target="_blank"
116+
rel="noopener noreferrer nofollow"
117117
variant="secondary"
118118
size="sm"
119119
class="feed-directory__action-btn feed-directory__action-btn--edit"
@@ -168,7 +168,7 @@ const staticFeedUrls = configs.map(config => ({
168168
</div>
169169

170170
<!-- Enhanced functionality with vanilla JavaScript -->
171-
<script>
171+
<script is:inline>
172172
// Simple debounce helper
173173
function debounce(func, wait) {
174174
let timeout;
@@ -196,92 +196,112 @@ const staticFeedUrls = configs.map(config => ({
196196
return queryIndex === lowerQuery.length;
197197
}
198198

199-
// Main functionality
200-
let instanceUrl = document.getElementById('instance-url')?.value || atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
201-
const searchInput = document.getElementById('search-input');
202-
const feedItems = document.querySelectorAll('[data-domain]');
199+
// Main functionality - wait for DOM to be ready
200+
function initializeFeedDirectory() {
201+
const defaultInstanceUrl = atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
202+
let instanceUrl = defaultInstanceUrl;
203+
const searchInput = document.getElementById('search-input');
204+
const feedItems = document.querySelectorAll('[data-domain]');
203205

204-
// Search functionality
205-
if (searchInput) {
206-
searchInput.addEventListener('input', debounce((e) => {
207-
const query = e.target.value.toLowerCase();
208-
feedItems.forEach(item => {
209-
const searchableText = item.dataset.searchable?.toLowerCase() || '';
210-
item.style.display = fuzzyMatch(searchableText, query) ? 'flex' : 'none';
211-
});
212-
}, 150));
213-
}
206+
// Initialize instance URL field with default value
207+
const instanceInput = document.getElementById('instance-url-input');
214208

215-
// Instance URL updates
216-
const instanceInput = document.getElementById('instance-url');
217-
if (instanceInput) {
218-
instanceInput.addEventListener('input', debounce((e) => {
219-
instanceUrl = e.target.value || atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
220-
updateFeedUrls();
221-
}, 300));
222-
}
209+
if (instanceInput) {
210+
instanceInput.value = defaultInstanceUrl;
211+
instanceUrl = defaultInstanceUrl;
212+
}
213+
214+
// Search functionality
215+
if (searchInput) {
216+
searchInput.addEventListener('input', debounce((e) => {
217+
const query = e.target.value.toLowerCase();
218+
feedItems.forEach(item => {
219+
const searchableText = item.dataset.searchable?.toLowerCase() || '';
220+
item.style.display = fuzzyMatch(searchableText, query) ? 'flex' : 'none';
221+
});
222+
}, 150));
223+
}
224+
225+
// Instance URL updates
226+
if (instanceInput) {
227+
instanceInput.addEventListener('input', debounce((e) => {
228+
instanceUrl = e.target.value || defaultInstanceUrl;
229+
updateFeedUrls();
230+
}, 300));
231+
}
223232

224-
// Parameter forms
225-
document.querySelectorAll('.feed-directory__configure-btn').forEach(button => {
226-
button.addEventListener('click', (e) => {
227-
const targetId = e.target.closest('button').dataset.target;
228-
const form = document.getElementById(targetId);
229-
if (!form) return;
233+
// Parameter forms
234+
document.querySelectorAll('.feed-directory__configure-btn').forEach(button => {
235+
button.addEventListener('click', (e) => {
236+
const targetId = e.target.closest('button').dataset.target;
237+
const form = document.getElementById(targetId);
238+
if (!form) return;
230239

231-
const isExpanded = !form.hidden;
232-
form.hidden = isExpanded;
233-
e.target.closest('button').setAttribute('aria-expanded', !isExpanded);
234-
e.target.closest('button').querySelector('span').textContent = isExpanded ? 'Configure' : 'Hide';
240+
const isExpanded = !form.hidden;
241+
form.hidden = isExpanded;
242+
e.target.closest('button').setAttribute('aria-expanded', !isExpanded);
243+
e.target.closest('button').querySelector('span').textContent = isExpanded ? 'Configure' : 'Hide';
235244

236-
if (!isExpanded) updateFeedUrls();
245+
if (!isExpanded) updateFeedUrls();
246+
});
237247
});
238-
});
239248

240-
// Close forms
241-
document.querySelectorAll('[data-close-form]').forEach(button => {
242-
button.addEventListener('click', (e) => {
243-
const form = e.target.closest('.feed-directory__item-params');
244-
const toggle = document.querySelector(`[data-target="${form.id}"]`);
245-
if (!form || !toggle) return;
249+
// Close forms
250+
document.querySelectorAll('[data-close-form]').forEach(button => {
251+
button.addEventListener('click', (e) => {
252+
const form = e.target.closest('.feed-directory__item-params');
253+
const toggle = document.querySelector(`[data-target="${form.id}"]`);
254+
if (!form || !toggle) return;
246255

247-
form.hidden = true;
248-
toggle.setAttribute('aria-expanded', 'false');
249-
toggle.querySelector('span').textContent = 'Configure';
256+
form.hidden = true;
257+
toggle.setAttribute('aria-expanded', 'false');
258+
toggle.querySelector('span').textContent = 'Configure';
259+
});
250260
});
251-
});
252261

253-
// Parameter input updates
254-
document.querySelectorAll('.feed-directory__param-input').forEach(input => {
255-
input.addEventListener('input', debounce(updateFeedUrls, 200));
256-
});
262+
// Parameter input updates
263+
document.querySelectorAll('.feed-directory__param-input').forEach(input => {
264+
input.addEventListener('input', debounce(updateFeedUrls, 200));
265+
});
257266

258-
// Update feed URLs
259-
function updateFeedUrls() {
260-
document.querySelectorAll('[data-feed-url]').forEach(link => {
261-
const item = link.closest('[data-domain]');
262-
if (!item) return;
267+
// Initialize feed URLs on page load
268+
updateFeedUrls();
263269

264-
const domain = item.dataset.domain;
265-
const name = item.dataset.name;
266-
if (!domain || !name) return;
270+
// Update feed URLs
271+
function updateFeedUrls() {
272+
document.querySelectorAll('[data-feed-url]').forEach(link => {
273+
const item = link.closest('[data-domain]');
274+
if (!item) return;
267275

268-
// Get parameters
269-
const params = {};
270-
item.querySelectorAll('[data-param-key]').forEach(input => {
271-
if (input.value) params[input.dataset.paramKey] = input.value;
272-
});
276+
const domain = item.dataset.domain;
277+
const name = item.dataset.name;
278+
if (!domain || !name) return;
273279

274-
// Build URL
275-
let url = instanceUrl.endsWith("/") ? instanceUrl : `${instanceUrl}/`;
276-
url += `${domain}/${name}.rss`;
280+
// Get parameters
281+
const params = {};
282+
item.querySelectorAll('[data-param-key]').forEach(input => {
283+
if (input.value) params[input.dataset.paramKey] = input.value;
284+
});
277285

278-
const queryParams = new URLSearchParams();
279-
Object.entries(params).forEach(([key, value]) => queryParams.append(key, value));
280-
const queryString = queryParams.toString();
281-
if (queryString) url += `?${queryString}`;
286+
// Build URL
287+
let url = instanceUrl.endsWith("/") ? instanceUrl : `${instanceUrl}/`;
288+
url += `${domain}/${name}.rss`;
282289

283-
link.href = url;
284-
});
290+
const queryParams = new URLSearchParams();
291+
Object.entries(params).forEach(([key, value]) => queryParams.append(key, value));
292+
const queryString = queryParams.toString();
293+
if (queryString) url += `?${queryString}`;
294+
295+
link.href = url;
296+
});
297+
}
298+
}
299+
300+
// Initialize when DOM is ready
301+
if (document.readyState === 'loading') {
302+
document.addEventListener('DOMContentLoaded', initializeFeedDirectory);
303+
} else {
304+
initializeFeedDirectory();
285305
}
286306
</script>
287307

@@ -293,18 +313,16 @@ const staticFeedUrls = configs.map(config => ({
293313

294314
/* Filters Section */
295315
.feed-directory__filters {
296-
display: grid;
297-
grid-template-columns: 1fr 1fr;
298-
gap: 1.5rem;
299-
margin-bottom: 2rem;
300-
padding: 1.5rem;
316+
display: flex;
317+
flex-direction: column;
318+
gap: 1rem;
319+
margin-bottom: 1.5rem;
320+
padding: 1rem;
301321
background: hsl(var(--sl-color-gray-2));
302322
border: 1px solid hsl(var(--sl-color-gray-4));
303-
border-radius: 0.75rem;
323+
border-radius: 0.5rem;
304324
}
305325

306-
307-
308326
.feed-directory__input {
309327
width: 100%;
310328
}
@@ -439,17 +457,6 @@ const staticFeedUrls = configs.map(config => ({
439457
border-color: hsl(var(--sl-color-accent));
440458
}
441459

442-
/* Mobile-First Design */
443-
.feed-directory__filters {
444-
display: flex;
445-
flex-direction: column;
446-
gap: 1rem;
447-
margin-bottom: 1.5rem;
448-
padding: 1rem;
449-
background: hsl(var(--sl-color-gray-2));
450-
border: 1px solid hsl(var(--sl-color-gray-4));
451-
border-radius: 0.5rem;
452-
}
453460

454461
.feed-directory__item {
455462
display: flex;

src/content/docs/feed-directory/index.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
---
22
title: 'Feed Directory'
33
description: 'Browse pre-built configurations to create RSS feeds for various websites.'
4+
head:
5+
- tag: meta
6+
attrs:
7+
name: robots
8+
content: noindex
49
---
510

611
# Welcome to the Feed Directory!

0 commit comments

Comments
 (0)