Keep a show from an OPML when the OPML drops it (#280)

A show inside a subscribed OPML went when the OPML stopped listing it
(orphaned only if it had downloads), and went for you when you left the
OPML. Keep, in its settings, is a per-person flag on the subscription:
when the OPML drops a kept show, or nobody subscribes to the OPML any more,
it becomes a catalogue entry outside the OPML, taking the OPML's settings
while it is there, and stays on the lists of those who kept it. Whoever had
it only through the OPML loses it as before. Leaving the OPML yourself
leaves what you kept on your list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 21:37:01 +00:00
parent 36a820f283
commit 2c4ba7896e
11 changed files with 197 additions and 3 deletions

View File

@@ -578,6 +578,9 @@ function settingsModal(f: Feed, newUrl?: string){
const kids=S.feeds.filter(c=>c.group===f.id).length;
const isGroup=kids>0;
const parent=f.managed?S.feeds.find(p=>p.id===f.group):null;
// A show from an OPML or a Patreon creator, which you may keep should the list drop it (#280).
const list=f.managed&&!!f.group;
const listName=parent?(parent.title||parent.id):f.group||'';
const admin=!!(S.me&&S.me.admin);
const name=f.title||f.id;
// Blank follows the server's default; 0 is every item (#166).
@@ -616,6 +619,9 @@ function settingsModal(f: Feed, newUrl?: string){
<div class="dpslide"><input type="range" id="sdpause" min="200" max="500" step="100" value="${f.deadair_pause||300}"${f.deadair?'':' disabled'}></div>
<div class="dpends" aria-hidden="true"><span>Brisk</span><span>Relaxed</span></div>
<p class="cap">The silence kept where a long one is cut. More is easier on the ear; less saves more time.</p></div></div></div>
${list?`<div class="ffield">${sw(`<input type="checkbox" role="switch" id="skept"${f.kept?' checked':''}>`,'Keep',' lead')}
<p class="cap">Stays on your list as a show of its own if ${esc(listName)} stops listing it, or if you
unsubscribe from ${esc(listName)}.</p></div>`:''}
${isGroup?'':`<div class="ffield"><label class="flabel" for="sblockin">Hide items that mention</label>
<div class="chips" id="sblock">${chipsHTML(f.blocked||[],'sblockin')}</div>
<p class="cap">Whole words or phrases. Hidden from your list and not downloaded for you, along
@@ -713,6 +719,8 @@ function settingsModal(f: Feed, newUrl?: string){
auto_download:$<HTMLInputElement>('#sauto').checked, allow_explicit:$<HTMLInputElement>('#sexp').checked, deadair:$<HTMLInputElement>('#sdead').checked,
deadair_pause:Number($<HTMLInputElement>('#sdpause').value)};
if($('#sblock')) patch.blocked=chipWords($('#sblock'));
const kept=$<HTMLInputElement>('#skept');
if(kept&&kept.checked!==!!f.kept) patch.kept=kept.checked;
// Sent only when it changed: anyone may mark a feed private, but only an admin unmark it.
const priv=$<HTMLInputElement>('#sprivate');
if(!priv.disabled&&priv.checked!==!!f.private) patch.private=priv.checked;