Add a feed without Popular; a phone shows an item's files above its notes
- The Add a feed dialog no longer lists Popular; the sidebar has it (#30). - On a phone the files, with play and delete, come before the show notes. Below them, long notes buried the delete button and it looked missing on iOS (#21). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,9 @@ The long form, with what was wrong before and how it was found, is in
|
|||||||
- A feed that fails to check gets a red ! in the feed list, and its page says why, in place of a
|
- A feed that fails to check gets a red ! in the feed list, and its page says why, in place of a
|
||||||
pop-up per failure that everyone saw during a scan of every feed.
|
pop-up per failure that everyone saw during a scan of every feed.
|
||||||
- The theme is chosen in Settings only; the button beside the iPodderX name is gone.
|
- The theme is chosen in Settings only; the button beside the iPodderX name is gone.
|
||||||
|
- Add a feed asks only for the feed; Popular and Directory in the sidebar are where you browse.
|
||||||
|
- On a phone, an item's files, with play and delete, sit above its show notes rather than below
|
||||||
|
them, where long notes left them looking missing.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -770,9 +770,10 @@ test('Popular lists what everyone here reads, but never a private feed', async (
|
|||||||
await pick('tabs', 'All').click();
|
await pick('tabs', 'All').click();
|
||||||
await expect(tiles).toHaveCount(dir.length);
|
await expect(tiles).toHaveCount(dir.length);
|
||||||
|
|
||||||
// Add a feed opened over Directory fills its own list, not the pane behind it.
|
// Add a feed is for an address; Popular and Directory are where you browse (issue #30).
|
||||||
await piper.locator('#addFeed').click();
|
await piper.locator('#addFeed').click();
|
||||||
await expect(piper.locator('#modalCard .childrow', { hasText: 'Test Show' })).toBeVisible();
|
await expect(piper.locator('#nurl')).toBeVisible();
|
||||||
|
await expect(piper.locator('#modalCard .childrow')).toHaveCount(0);
|
||||||
await expect(tiles).toHaveCount(dir.length);
|
await expect(tiles).toHaveCount(dir.length);
|
||||||
await piper.locator('#modalCard button[title="Cancel"]').click();
|
await piper.locator('#modalCard button[title="Cancel"]').click();
|
||||||
|
|
||||||
@@ -1022,3 +1023,20 @@ test('the pinned heading sits over its pins, and the page is set in Inter', asyn
|
|||||||
expect((await page.request.get('/inter.woff2')).headers()['content-type']).toBe('font/woff2');
|
expect((await page.request.get('/inter.woff2')).headers()['content-type']).toBe('font/woff2');
|
||||||
expect(await page.evaluate(() => document.fonts.ready.then(() => document.fonts.check('14px Inter')))).toBe(true);
|
expect(await page.evaluate(() => document.fonts.ready.then(() => document.fonts.check('14px Inter')))).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('on a phone', () => {
|
||||||
|
test.use({ viewport: { width: 390, height: 844 }, hasTouch: true, isMobile: true });
|
||||||
|
|
||||||
|
test('a downloaded file can be deleted, from above the show notes', async ({ page }) => {
|
||||||
|
await page.locator('#burger').click();
|
||||||
|
await page.locator('.feed', { hasText: 'Test Show' }).click();
|
||||||
|
await page.locator('.tabs button', { hasText: 'Downloaded' }).first().click();
|
||||||
|
await page.locator('.ep').first().click();
|
||||||
|
const del = page.locator('#detail [data-a="del"]');
|
||||||
|
await expect(del).toBeInViewport();
|
||||||
|
// Below a long set of notes it was screens down and looked missing (issue #21).
|
||||||
|
expect(await page.locator('#detail').evaluate(d =>
|
||||||
|
!!(d.querySelector('.encbox').compareDocumentPosition(d.querySelector('.dbody')) & Node.DOCUMENT_POSITION_FOLLOWING)))
|
||||||
|
.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ input:focus,select:focus{outline:0;border-color:var(--accent)}
|
|||||||
.dt{font-size:18px;font-weight:650;margin:0 0 5px;line-height:1.3;overflow-wrap:anywhere}
|
.dt{font-size:18px;font-weight:650;margin:0 0 5px;line-height:1.3;overflow-wrap:anywhere}
|
||||||
.dmeta{color:var(--faint);font-size:12.5px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
|
.dmeta{color:var(--faint);font-size:12.5px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
|
||||||
.dmeta .btn{padding:3px 9px;font-size:12px}
|
.dmeta .btn{padding:3px 9px;font-size:12px}
|
||||||
|
#detail .encbox{margin:0 0 10px}
|
||||||
.dbody{font-size:14.5px;line-height:1.65;overflow-wrap:anywhere;color:var(--fg)}
|
.dbody{font-size:14.5px;line-height:1.65;overflow-wrap:anywhere;color:var(--fg)}
|
||||||
.dbody img{max-width:100%;height:auto;border-radius:6px}
|
.dbody img{max-width:100%;height:auto;border-radius:6px}
|
||||||
.dbody a{color:var(--accent)}
|
.dbody a{color:var(--accent)}
|
||||||
@@ -549,7 +550,7 @@ input[type=range]::-moz-range-thumb{width:12px;height:12px;border:0;border-radiu
|
|||||||
|
|
||||||
/* One pane at a time: the list, then the item over it. */
|
/* One pane at a time: the list, then the item over it. */
|
||||||
#split{grid-template-columns:1fr;grid-template-rows:1fr;grid-template-areas:"list"}
|
#split{grid-template-columns:1fr;grid-template-rows:1fr;grid-template-areas:"list"}
|
||||||
/* Title and date only; the files follow the item's text in the reader instead. */
|
/* Title and date only; the files sit above the item's text in the reader instead. */
|
||||||
#files,.ephead,.ep .fd,.ep .file,.ep .size,.ep .rowacts{display:none}
|
#files,.ephead,.ep .fd,.ep .file,.ep .size,.ep .rowacts{display:none}
|
||||||
.ep,#split.one .ep{grid-template-columns:20px 20px minmax(0,1fr) auto}
|
.ep,#split.one .ep{grid-template-columns:20px 20px minmax(0,1fr) auto}
|
||||||
#grab{display:none}
|
#grab{display:none}
|
||||||
@@ -1396,7 +1397,9 @@ function showDetail(e){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const encs=e.enclosures.map(encBox).join('')||'<p class="empty">No files</p>';
|
const encs=e.enclosures.map(encBox).join('')||'<p class="empty">No files</p>';
|
||||||
// A phone has no room for the files pane, so the files follow the text there instead.
|
// A phone has no room for the files pane, so the files go above the text there instead.
|
||||||
|
// Below it, a long set of show notes pushed play and delete screens down, and on an iPhone
|
||||||
|
// it looked as if a downloaded file could not be deleted at all (issue #21).
|
||||||
const narrow=!!window.matchMedia?.('(max-width:820px)')?.matches;
|
const narrow=!!window.matchMedia?.('(max-width:820px)')?.matches;
|
||||||
const f=S.feeds.find(x=>x.id===e.feed_id);
|
const f=S.feeds.find(x=>x.id===e.feed_id);
|
||||||
const num=[e.season?`S${e.season}`:'',e.episode?`E${e.episode}`:''].filter(Boolean).join('');
|
const num=[e.season?`S${e.season}`:'',e.episode?`E${e.episode}`:''].filter(Boolean).join('');
|
||||||
@@ -1414,8 +1417,8 @@ function showDetail(e){
|
|||||||
${e.link?`<a class="btn ico" href="${esc(e.link)}" target="_blank" rel="noopener noreferrer"
|
${e.link?`<a class="btn ico" href="${esc(e.link)}" target="_blank" rel="noopener noreferrer"
|
||||||
title="Open the original" aria-label="Open the original">${ICON.open}</a>`:''}
|
title="Open the original" aria-label="Open the original">${ICON.open}</a>`:''}
|
||||||
</div>
|
</div>
|
||||||
<div class="dbody">${(e.description&&e.description.trim())||'<em>No show notes.</em>'}</div>
|
${narrow?encs:''}
|
||||||
${narrow?encs:''}`;
|
<div class="dbody">${(e.description&&e.description.trim())||'<em>No show notes.</em>'}</div>`;
|
||||||
if(files) files.innerHTML=narrow?'':`<div class="fhd">Files</div>${encs}`;
|
if(files) files.innerHTML=narrow?'':`<div class="fhd">Files</div>${encs}`;
|
||||||
// description was sanitized server-side with ammonia before it ever reached here
|
// description was sanitized server-side with ammonia before it ever reached here
|
||||||
$('#dback').onclick=()=>showDetail(null);
|
$('#dback').onclick=()=>showDetail(null);
|
||||||
@@ -1794,12 +1797,9 @@ $('#addFeed').onclick=()=>{
|
|||||||
<div class="field"><label>Keywords (optional, comma separated)</label>
|
<div class="field"><label>Keywords (optional, comma separated)</label>
|
||||||
<input type="text" id="nkw"><span class="hint">Only items matching a keyword are downloaded.</span></div>
|
<input type="text" id="nkw"><span class="hint">Only items matching a keyword are downloaded.</span></div>
|
||||||
<label class="check"><input type="checkbox" id="nexp"> Allow items marked explicit</label>
|
<label class="check"><input type="checkbox" id="nexp"> Allow items marked explicit</label>
|
||||||
<div class="field"><label>Popular on this server</label>
|
|
||||||
<div class="childlist" id="npopular"><p class="hint">Loading…</p></div></div>
|
|
||||||
<div class="cardacts"><button class="btn ico" onclick="closeModal()" title="Cancel" aria-label="Cancel">${ICON.close}</button>
|
<div class="cardacts"><button class="btn ico" onclick="closeModal()" title="Cancel" aria-label="Cancel">${ICON.close}</button>
|
||||||
<button class="btn ico primary" id="nsave" title="Add feed" aria-label="Add feed">${ICON.plus}</button></div>`);
|
<button class="btn ico primary" id="nsave" title="Add feed" aria-label="Add feed">${ICON.plus}</button></div>`);
|
||||||
$('#nurl').focus();
|
$('#nurl').focus();
|
||||||
listFeeds('/api/popular',$('#npopular'));
|
|
||||||
$('#nsave').onclick=async()=>{
|
$('#nsave').onclick=async()=>{
|
||||||
const url=$('#nurl').value.trim(); if(!url) return;
|
const url=$('#nurl').value.trim(); if(!url) return;
|
||||||
$('#nsave').disabled=true; $('#nsave').title='Adding…';
|
$('#nsave').disabled=true; $('#nsave').title='Adding…';
|
||||||
@@ -1814,8 +1814,7 @@ $('#addFeed').onclick=()=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// What everyone here reads, you included, as a place to start. The rows carry an id, never a
|
// What everyone here reads, you included, as a place to start. The rows carry an id, never a
|
||||||
// URL, so a key in someone's feed address never reaches this page. The caller hands over the
|
// URL, so a key in someone's feed address never reaches this page.
|
||||||
// box: looked up by id, the Add a feed dialog's list landed in the Directory pane behind it.
|
|
||||||
const NONE_LISTED='<p class="hint">Nothing yet. Feeds people here subscribe to show up here.</p>';
|
const NONE_LISTED='<p class="hint">Nothing yet. Feeds people here subscribe to show up here.</p>';
|
||||||
async function listFeeds(url,box){
|
async function listFeeds(url,box){
|
||||||
let rows=[];
|
let rows=[];
|
||||||
|
|||||||
Reference in New Issue
Block a user