Share a feed, an item or a file (#48)
A share button in the feed's header, beside an item's "Open the original", and on each file. It uses the Web Share API where the browser has it, which is the share sheet on a phone, and copies the link where it does not. A file is shared by the publisher's address, not /media/, which only someone signed in here can open. Paid feeds carry the subscriber's access in their address, Patreon's in a token, so sharing one gives the subscription away. An address that looks like that asks first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ const esc = s => (s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>
|
||||
// the button's own colour. To add one, copy the path from svgs/<style>/<name>.svg at the same tag.
|
||||
const fa=(box,body)=>`<svg class="i" viewBox="${box}" aria-hidden="true">${body}</svg>`;
|
||||
const ICON={
|
||||
share:fa('0 0 512 512','<path fill="currentColor" d="M384 192c53 0 96-43 96-96s-43-96-96-96-96 43-96 96c0 5.4 .5 10.8 1.3 16L159.6 184.1c-16.9-15-39.2-24.1-63.6-24.1-53 0-96 43-96 96s43 96 96 96c24.4 0 46.6-9.1 63.6-24.1L289.3 400c-.9 5.2-1.3 10.5-1.3 16 0 53 43 96 96 96s96-43 96-96-43-96-96-96c-24.4 0-46.6 9.1-63.6 24.1L190.7 272c.9-5.2 1.3-10.5 1.3-16s-.5-10.8-1.3-16l129.7-72.1c16.9 15 39.2 24.1 63.6 24.1z"/>'), // solid/share-nodes
|
||||
plus:fa('0 0 448 512','<path fill="currentColor" d="M256 64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 160-160 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l160 0 0 160c0 17.7 14.3 32 32 32s32-14.3 32-32l0-160 160 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-160 0 0-160z"/>'), // solid/plus
|
||||
circleMinus:fa('0 0 512 512','<path fill="currentColor" d="M512 256A256 256 0 1 0 0 256a256 256 0 1 0 512 0zM184 232l144 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-144 0c-13.3 0-24-10.7-24-24s10.7-24 24-24z"/>'), // solid/circle-minus, for Unsubscribe
|
||||
play:fa('0 0 448 512','<path fill="currentColor" d="M91.2 36.9c-12.4-6.8-27.4-6.5-39.6 .7S32 57.9 32 72l0 368c0 14.1 7.5 27.2 19.6 34.4s27.2 7.5 39.6 .7l336-184c12.8-7 20.8-20.5 20.8-35.1s-8-28.1-20.8-35.1l-336-184z"/>'), // solid/play
|
||||
@@ -92,6 +93,27 @@ async function copyText(text,btn){
|
||||
}
|
||||
}
|
||||
|
||||
/// The system's share sheet where there is one (phones, Safari, Edge), the clipboard elsewhere.
|
||||
/// Asks first when the address looks like it carries your own access to a paid or private
|
||||
/// feed: a Patreon feed's token, or a key, token or password in it, which would hand whoever
|
||||
/// gets it your subscription (issue #48).
|
||||
///
|
||||
/// ponytail: a guess from the address. A private feed whose key has another name is shared
|
||||
/// without asking; add its pattern here when one turns up.
|
||||
async function share(title: string, url: string, btn?){
|
||||
if(/patreon\.com\/|\/\/[^/]*@|[?&][^=]*(auth|token|key|secret|pass|sig)[^=]*=/i.test(url)
|
||||
&& !confirm('This address looks like it includes your own access to the feed, and anyone you '
|
||||
+'send it to could use it as you.\n\nShare it anyway?')) return;
|
||||
if(navigator.share){
|
||||
// Cancelling the sheet rejects too, and is not a failure worth a word.
|
||||
try{ await navigator.share({title, url}); }
|
||||
catch(e){ if(e.name!=='AbortError') toast(e.message,true); }
|
||||
return;
|
||||
}
|
||||
await copyText(url,btn);
|
||||
toast('Link copied');
|
||||
}
|
||||
|
||||
function toast(msg: string, bad?: boolean){
|
||||
const t=document.createElement('div');
|
||||
t.className='toast'+(bad?' bad':''); t.textContent=msg;
|
||||
|
||||
Reference in New Issue
Block a user