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:
14
web/app.css
14
web/app.css
@@ -1066,6 +1066,8 @@ input[type=range]::-moz-range-thumb{width:12px;height:12px;border:0;border-radiu
|
||||
.fsethead .art{width:56px;height:56px;border-radius:12px;font-size:18px}
|
||||
.fsethead h3{margin:0;font-size:19px;line-height:1.25;letter-spacing:-.01em;overflow-wrap:anywhere}
|
||||
.fsethead p{margin:3px 0 0;font-size:12.5px;line-height:1.45;color:var(--dim)}
|
||||
.faddr{margin-bottom:18px}
|
||||
.moved{margin:2px 0 0;font-size:13px;color:var(--accent)}
|
||||
/* On or off, drawn as iOS draws it, as the app is on a phone (#53). The box is the real control,
|
||||
unseen over the track, so a click, a tap, the keyboard and a screen reader all reach it. */
|
||||
.switch{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:38px;font-size:14px;font-weight:500;cursor:pointer}
|
||||
@@ -1115,13 +1117,6 @@ input[type=range]::-moz-range-thumb{width:12px;height:12px;border:0;border-radiu
|
||||
.chipx:hover{background:var(--raise);color:var(--fg)}
|
||||
.chipx .i{width:9px;height:9px}
|
||||
.chips .chipin{flex:1 0 8ch;width:auto;min-width:0;padding:4px 6px;border:0;background:none}
|
||||
/* The feed's own settings, one copy for everyone and an admin's to change, set apart on the
|
||||
page's own ground. */
|
||||
.fshared{margin-top:20px;padding:14px 16px 4px;border:1px solid var(--line);border-radius:10px;background:var(--bg)}
|
||||
.fshared h4{margin:0 0 12px;font-size:13px;font-weight:600}
|
||||
.fshared>.hint{margin:-6px 0 12px;font-size:12.5px;color:var(--dim)}
|
||||
.fshared .field input{background:var(--panel)}
|
||||
.moved{margin:-4px 0 12px;font-size:13px;color:var(--accent)}
|
||||
#toasts{position:fixed;bottom:88px;right:18px;display:flex;flex-direction:column;gap:8px;z-index:60}
|
||||
.toast{
|
||||
background:var(--raise);border:1px solid var(--line);border-radius:9px;
|
||||
@@ -1339,7 +1334,7 @@ body:has(#player.on) #status{padding-bottom:3px}
|
||||
.fsetbody{padding:20px 16px 24px}
|
||||
.card.fset .fsetfoot{padding:12px 16px calc(12px + var(--safe-b))}
|
||||
.fsethead .art{width:64px;height:64px;border-radius:14px}
|
||||
.fsethead p,.wcap,.cap,.fshared>.hint{font-size:14px}
|
||||
.fsethead p,.wcap,.cap{font-size:14px}
|
||||
.switch,.flabel{font-size:17px}
|
||||
.switch{min-height:44px}
|
||||
.sw{width:51px;height:31px}
|
||||
@@ -1352,7 +1347,6 @@ body:has(#player.on) #status{padding-bottom:3px}
|
||||
.chips{background:var(--panel2);border-color:var(--edge);border-radius:10px}
|
||||
.chip{background:var(--panel);font-size:15px;padding:5px 4px 5px 12px}
|
||||
.chipx{width:26px;height:26px}
|
||||
.fshared{border-color:var(--edge);border-radius:14px}
|
||||
.fdeps{margin-left:12px}
|
||||
.fdepsin{padding-left:14px}
|
||||
.toast{border-radius:14px}
|
||||
@@ -1490,8 +1484,6 @@ body:has(#player.on) #status{padding-bottom:3px}
|
||||
:root[data-theme="glass"] .sidefoot button{border-radius:10px}
|
||||
}
|
||||
:root[data-theme="glass"] .btn.primary{box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
|
||||
/* A feed's shared settings let the glass through, a shade deeper than the card. */
|
||||
:root[data-theme="glass"] .fshared{background:color-mix(in srgb,var(--bg) 55%,transparent);border-color:var(--glass-edge)}
|
||||
:root[data-theme="glass"] .card.fset .fsetfoot{border-top-color:var(--glass-edge);background:color-mix(in srgb,var(--panel) 60%,transparent)}
|
||||
/* On a phone the rim all round a small capsule or bar read as the outline #53 took away, so the
|
||||
light only catches the top edge there, and the bars at the top and bottom have none. */
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user