Skip to content

Commit 45105d0

Browse files
committed
fix: harden feed directory instance handling
1 parent c099588 commit 45105d0

2 files changed

Lines changed: 66 additions & 25 deletions

File tree

src/components/FeedDirectory.astro

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const staticFeedUrls = configs.map((config) => ({
2222
}));
2323
---
2424

25-
<section class="feed-directory not-content" aria-label="Feed directory">
25+
<section class="feed-directory not-content" data-feed-directory aria-label="Feed directory">
2626
<div class="directory-controls">
2727
<div class="search-block">
2828
<label class="sr-only" for="search-input">Search feeds</label>
@@ -62,7 +62,7 @@ const staticFeedUrls = configs.map((config) => ({
6262
</button>
6363
</div>
6464

65-
<div class="instance-editor" id="instance-editor" hidden>
65+
<div class="instance-editor" id="instance-editor">
6666
<div class="instance-editor-row">
6767
<label class="sr-only" for="instance-url-input">Instance URL</label>
6868
<input
@@ -527,6 +527,10 @@ const staticFeedUrls = configs.map((config) => ({
527527
color: var(--sl-color-gray-3);
528528
}
529529

530+
.feed-directory:not([data-enhanced="true"]) .instance-toggle {
531+
display: none;
532+
}
533+
530534
.instance-toggle {
531535
padding: 0;
532536
border: 0;

src/components/feed-directory.js

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,72 @@ function getDefaultInstanceUrl() {
1616
return atob('aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=');
1717
}
1818

19+
function getStorageKey() {
20+
return 'html2rss.feedDirectory.instanceUrl';
21+
}
22+
1923
function getHashParams() {
2024
const hash = window.location.hash || '';
21-
const normalizedHash = hash.startsWith('#!') ? hash.slice(2) : hash.startsWith('#') ? hash.slice(1) : hash;
22-
return new URLSearchParams(normalizedHash);
25+
if (!hash.startsWith('#!')) return new URLSearchParams();
26+
return new URLSearchParams(hash.slice(2));
27+
}
28+
29+
function normalizeParsedInstanceUrl(parsedUrl) {
30+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
31+
return null;
32+
}
33+
34+
parsedUrl.search = '';
35+
parsedUrl.hash = '';
36+
return parsedUrl.toString();
2337
}
2438

2539
function readInstanceUrlFromHash(defaultInstanceUrl) {
2640
const candidate = getHashParams().get('url');
2741
if (!candidate) return defaultInstanceUrl;
2842

2943
try {
30-
const parsedUrl = new URL(candidate);
31-
parsedUrl.search = '';
32-
parsedUrl.hash = '';
33-
return parsedUrl.toString();
44+
return normalizeParsedInstanceUrl(new URL(candidate)) || defaultInstanceUrl;
3445
} catch {
3546
return defaultInstanceUrl;
3647
}
3748
}
3849

39-
function writeInstanceUrlToHash(instanceUrl, defaultInstanceUrl) {
40-
const params = getHashParams();
41-
if (instanceUrl && instanceUrl !== defaultInstanceUrl) {
42-
params.set('url', instanceUrl);
43-
} else {
44-
params.delete('url');
50+
function readInstanceUrlFromStorage(defaultInstanceUrl) {
51+
try {
52+
const candidate = window.localStorage.getItem(getStorageKey());
53+
if (!candidate) return defaultInstanceUrl;
54+
return normalizeInstanceUrl(candidate) || defaultInstanceUrl;
55+
} catch {
56+
return defaultInstanceUrl;
4557
}
58+
}
4659

47-
const nextHash = params.toString();
48-
const nextUrl = nextHash
49-
? `${window.location.pathname}${window.location.search}#!${nextHash}`
50-
: `${window.location.pathname}${window.location.search}`;
51-
window.history.replaceState({}, '', nextUrl);
60+
function writeInstanceUrl(instanceUrl, defaultInstanceUrl) {
61+
try {
62+
if (instanceUrl && instanceUrl !== defaultInstanceUrl) {
63+
window.localStorage.setItem(getStorageKey(), instanceUrl);
64+
} else {
65+
window.localStorage.removeItem(getStorageKey());
66+
}
67+
} catch {
68+
// Ignore storage failures and keep the current page usable.
69+
}
70+
71+
if (window.location.hash.startsWith('#!')) {
72+
const nextUrl = `${window.location.pathname}${window.location.search}`;
73+
window.history.replaceState({}, '', nextUrl);
74+
}
75+
}
76+
77+
function readInitialInstanceUrl(defaultInstanceUrl) {
78+
const hashInstanceUrl = readInstanceUrlFromHash(defaultInstanceUrl);
79+
if (hashInstanceUrl !== defaultInstanceUrl) {
80+
writeInstanceUrl(hashInstanceUrl, defaultInstanceUrl);
81+
return hashInstanceUrl;
82+
}
83+
84+
return readInstanceUrlFromStorage(defaultInstanceUrl);
5285
}
5386

5487
function fuzzyMatch(text, query) {
@@ -78,10 +111,7 @@ function normalizeInstanceUrl(value) {
78111
if (!trimmed) return null;
79112

80113
try {
81-
const parsedUrl = new URL(trimmed);
82-
parsedUrl.search = '';
83-
parsedUrl.hash = '';
84-
return parsedUrl.toString();
114+
return normalizeParsedInstanceUrl(new URL(trimmed));
85115
} catch {
86116
return null;
87117
}
@@ -195,6 +225,11 @@ function setupInstanceEditor(
195225
const apply = document.querySelector('[data-apply-instance]');
196226
if (!toggle || !editor || !input || !apply) return;
197227

228+
const directory = document.querySelector('[data-feed-directory]');
229+
if (directory) {
230+
directory.dataset.enhanced = 'true';
231+
}
232+
198233
const setExpanded = (expanded) => {
199234
editor.hidden = !expanded;
200235
toggle.setAttribute('aria-expanded', String(expanded));
@@ -222,12 +257,14 @@ function setupInstanceEditor(
222257
setCurrentInstanceUrl(normalized);
223258
input.value = normalized;
224259
updateInstanceSummary(normalized);
225-
writeInstanceUrlToHash(normalized, defaultInstanceUrl);
260+
writeInstanceUrl(normalized, defaultInstanceUrl);
226261
updateFeedUrls(normalized);
227262
setExpanded(false);
228263
setInstanceFeedback('Using your custom instance.', 'success');
229264
};
230265

266+
setExpanded(false);
267+
231268
apply.addEventListener('click', applyInstance);
232269
input.addEventListener('keydown', (event) => {
233270
if (event.key === 'Enter') {
@@ -313,7 +350,7 @@ function setupCopyButtons() {
313350

314351
function initializeFeedDirectory() {
315352
const defaultInstanceUrl = getDefaultInstanceUrl();
316-
let currentInstanceUrl = readInstanceUrlFromHash(defaultInstanceUrl);
353+
let currentInstanceUrl = readInitialInstanceUrl(defaultInstanceUrl);
317354
const searchInput = document.getElementById('search-input');
318355
const feedItems = Array.from(document.querySelectorAll('[data-domain]'));
319356
const instanceInput = document.getElementById('instance-url-input');

0 commit comments

Comments
 (0)