Put a feed's address at the top of its settings, and drop the shared box (#167)

Asked for after the redesign went out: the box of settings the same for everyone (address, folder,
Directory category) held what a person reading the feed cannot and should not change. The address
moves to the top, under the feed's name, with its copy button, read-only except to an admin, who
can still follow a feed that moved from its failing banner. The folder and the category leave the
dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 12:32:29 +00:00
parent ecbc253894
commit 4c1d230044
3 changed files with 13 additions and 41 deletions

View File

@@ -482,8 +482,7 @@ function wireChips(box){
};
}
/// Your settings for one feed, and the feed's own, which are everyone's and an admin's to change
/// (#167). What it says is what will happen, in this feed's numbers: the download limit was a bare
/// Your settings for one feed, under its address (#167). What it says is what will happen, in this feed's numbers: the download limit was a bare
/// box whose blank, number and 0 were explained in bold small print.
function settingsModal(f, newUrl?: string){
const kids=S.feeds.filter(c=>c.group===f.id).length;
@@ -502,6 +501,10 @@ function settingsModal(f, newUrl?: string){
const sw=(input: string, text: string, cls='')=>`<label class="switch${cls}"><span>${text}</span><span class="sw">${input}<i></i></span></label>`;
openModal(`<div class="fsetbody">
<header class="fsethead">${artHTML(f.image,name)}<div><h3>${esc(name)}</h3><p>${whose}</p></div></header>
<div class="field faddr"><label for="surl">Address</label>
<div class="inline"><input type="text" id="surl" value="${esc(newUrl||f.url)}" spellcheck="false"${admin?'':' readonly'}>
<button type="button" class="btn ico" id="scopy" title="Copy the address" aria-label="Copy the address">${ICON.copy}</button></div>
${newUrl?'<p class="moved">The feed says it has moved here. Save to follow it.</p>':''}</div>
${sw(`<input type="checkbox" role="switch" id="sauto"${f.auto_download?' checked':''}>`,'Download automatically',' lead')}
<div class="fdeps${f.auto_download?'':' off'}" id="sdeps"><div><div class="fdepsin">
<div class="tabs seg" role="radiogroup" aria-label="How many to download">${
@@ -518,23 +521,7 @@ function settingsModal(f, newUrl?: string){
<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
with what Settings hides everywhere.</p></div>`}
<section class="fshared"><h4>The same for everyone who reads it</h4>
${newUrl?'<p class="moved">The feed says it has moved here. Save to follow it.</p>':''}
<div class="field"><label for="surl">Address</label>
<div class="inline"><input type="text" id="surl" value="${esc(newUrl||f.url)}" spellcheck="false"${admin?'':' readonly'}>
<button type="button" class="btn ico" id="scopy" title="Copy the address" aria-label="Copy the address">${ICON.copy}</button></div>
${admin?'<span class="hint">Changing it keeps every item and download, for when a key in it changes.</span>':''}</div>
${admin?`<div class="field"><label for="sfolder">Folder</label>
<input type="text" id="sfolder" value="${esc(f.folder||'')}" placeholder="${esc(name)}">
<span class="hint">Where its files are saved. One copy serves everyone.</span></div>
<div class="field"><label for="scat">Directory category</label>
<input type="text" id="scat" list="scats" value="${esc(f.category||'')}" placeholder="${esc(f.feed_category||'None')}">
<datalist id="scats"></datalist>
<span class="hint">${f.feed_category
? `The feed names its own, ${esc(f.feed_category)}, and the Directory uses that.`
: 'The feed names none, so the Directory files it here. Pick one already listed where it fits.'}</span></div>`
:'<p class="hint">Only an admin changes these.</p>'}
</section></div>
</div>
<div class="cardacts fsetfoot"><button class="btn ico" onclick="closeModal()" title="Cancel" aria-label="Cancel">${ICON.close}</button>
<button class="btn ico primary" id="ssave" title="Save" aria-label="Save">${ICON.check}</button></div>`, false, 'fset');
@@ -605,10 +592,6 @@ function settingsModal(f, newUrl?: string){
wireChips($('#skw'));
if($('#sblock')) wireChips($('#sblock'));
$('#scopy').onclick=()=>copyText($('#surl').value,$('#scopy'));
// Offer the categories the Directory already shows, so a blog about games joins Games rather
// than starting a second chip beside it.
if($('#scats')) api('/api/directory').then(rows=>{ $('#scats').innerHTML=[...new Set((rows||[])
.flatMap(p=>[p.category,p.subcategory]).filter(Boolean))].sort().map(c=>`<option value="${esc(c)}">`).join(''); }).catch(()=>{});
// Opened to follow a feed that moved: the new address is what there is to look at.
if(newUrl){ $('#surl').scrollIntoView({block:'center'}); $('#surl').focus(); }
@@ -619,12 +602,9 @@ function settingsModal(f, newUrl?: string){
max_new_per_check:mode==='default'?null:mode==='all'?0:newest,
auto_download:$('#sauto').checked, allow_explicit:$('#sexp').checked};
if($('#sblock')) patch.blocked=chipWords($('#sblock'));
// The shared half is an admin's to change, and the API refuses it from anyone else.
if(admin){
patch.url=$('#surl').value.trim();
patch.folder=$('#sfolder').value.trim()||null;
patch.category=$('#scat').value.trim()||null;
}
// The address is the same for everyone: an admin's to change, for a feed that moved, and the
// API refuses it from anyone else. The folder and category are not this dialog's at all.
if(admin) patch.url=$('#surl').value.trim();
await api(`/api/feeds/${encodeURIComponent(f.id)}`,{method:'PATCH',body:JSON.stringify(patch)});
closeModal(); toast('Saved. It applies from the next check.');
await loadFeeds(true); renderFeed(); loadEntries();