diff --git a/CHANGELOG.md b/CHANGELOG.md index f644945..ecdbc30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 list of its own; an item with one of those words or phrases in its title or text is hidden from you and is not downloaded on your account. Other people's lists never hide anything from you. +- Share buttons for a feed, an item and a file. On a phone they open the system's share sheet; + elsewhere they copy the link. A link that looks like it carries your own access to a paid + feed asks before it goes anywhere. ### Fixed diff --git a/tests/ui/app.spec.js b/tests/ui/app.spec.js index 5f4b390..5d1b722 100644 --- a/tests/ui/app.spec.js +++ b/tests/ui/app.spec.js @@ -1361,3 +1361,16 @@ test('words in Settings hide the items that mention them', async ({ page }) => { await page.evaluate(() => api('/api/me', { method: 'PATCH', body: JSON.stringify({ blocked: [] }) })); }); + +test('Share hands the feed, the item and the file to the share sheet', async ({ page }) => { + await page.evaluate(() => { window.shared = []; navigator.share = async d => { window.shared.push(d); }; }); + await page.locator('.feed', { hasText: 'Test Show' }).click(); + await page.locator('.tabs button', { hasText: 'All' }).first().click(); + await page.locator('#content .acts [data-a="share"]').click(); + await page.locator('.ep', { hasText: 'Second Episode' }).click(); + await page.locator('.encbox [data-a="share"]').first().click(); + await expect.poll(() => page.evaluate(() => window.shared)).toEqual([ + { title: 'Test Show', url: expect.stringContaining('/show.xml') }, + { title: 'Second Episode', url: expect.stringContaining('/ep1.mp3?2') }, + ]); +}); diff --git a/web/src/feedpage.ts b/web/src/feedpage.ts index 2a1621f..d9ba775 100644 --- a/web/src/feedpage.ts +++ b/web/src/feedpage.ts @@ -31,6 +31,7 @@ function renderFeed(){ + @@ -120,6 +121,7 @@ function renderGroup(f,kids){ + @@ -166,6 +168,7 @@ async function feedAction(a,f){ }catch(e){ toast(e.message,true); } } if(a==='settings') settingsModal(f); + if(a==='share') share(f.title||f.id, f.url, $('#content .acts [data-a="share"]')); if(a==='dl') downloadLatestModal(f); } /// All Subscriptions' own buttons: a feed's, across every feed you read. diff --git a/web/src/items.ts b/web/src/items.ts index e748f62..ec6bb6b 100644 --- a/web/src/items.ts +++ b/web/src/items.ts @@ -249,7 +249,8 @@ function showDetail(e){ ${e.link?`${ICON.open}`:''} + title="Open the original" aria-label="Open the original">${ICON.open} + `:''} ${narrow?encs:''}
${(e.description&&e.description.trim())||'No show notes.'}
`; @@ -273,6 +274,8 @@ function encBox(x){ : 'Delete file'; const delBtn=``; const saveBtn=`${ICON.save}`; + // The publisher's address, not this server's copy, which only someone signed in here can open. + const shareBtn=``; if(x.path && !isPlayable(x)){ // On disk, but not audio or video: view it, keep it, or remove it -- no player. return `
@@ -280,6 +283,7 @@ function encBox(x){ ${size} ${ICON.open} ${saveBtn} + ${shareBtn} ${delBtn}
`; } @@ -291,6 +295,7 @@ function encBox(x){ ${size} ${saveBtn} + ${shareBtn} ${delBtn} `; } @@ -303,6 +308,7 @@ function encBox(x){ ${size} ${x.state==='error'&&x.last_error?`${esc(x.last_error)}`:''} ${viewable?`${ICON.open}`:''} + ${shareBtn} `; } @@ -314,6 +320,7 @@ async function epAction(a: string, e, el, encId?: number){ const path=`/api/entries/${encodeURIComponent(e.feed_id)}/${encodeURIComponent(e.guid)}`; try{ if(a==='play') play(e, encId!=null ? enc : undefined); + if(a==='share') await share(e.title||'', encId!=null ? enc.url : e.link, el); if(a==='flag'){ e.flagged=!e.flagged; await api(path+'/flags',{method:'POST',body:JSON.stringify({flagged:e.flagged})}); redraw(); } if(a==='read'){ await setRead(e,!e.read); redraw(); } if(a==='get'){ diff --git a/web/src/util.ts b/web/src/util.ts index f1a0088..2339449 100644 --- a/web/src/util.ts +++ b/web/src/util.ts @@ -10,6 +10,7 @@ const esc = s => (s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'> // the button's own colour. To add one, copy the path from svgs/