diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/copy-link.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/copy-link.js | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/copy-link.js b/packages/meshbay-hub/src/meshbay_hub/static/copy-link.js new file mode 100644 index 0000000..ad6c3be --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/static/copy-link.js @@ -0,0 +1,67 @@ +import { t } from './i18n.js'; +import * as platform from './platform.js'; +import { groupLinkRoute } from './group-link.js'; + +/** + * "Copy link": the `#/name@owner/path` address of a file or folder + * (group-link.js), on this hub, put on the clipboard. + * + * The hub's origin, never the page's: in the desktop application the page is + * `app://meshbay`, and a link that only opens inside one person's application + * is not a link. + */ + +/** The full link to `path` inside `group` (`{ name, owner_username }`). */ +export function groupItemLink(group, path) { + return platform.hubOrigin() + '/#' + groupLinkRoute(group, path); +} + +/** An entry's path inside its group: its folder, root first, then its name. */ +export function entryPath(entry, dir = entry.path) { + return [dir, entry.name].filter(Boolean).join('/'); +} + +// The async clipboard first; the old command where it is refused — a page +// without focus, a WebView without the permission. Neither needs to read +// anything back. +async function _write(text) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { /* fall through */ } + const area = document.createElement('textarea'); + area.value = text; + area.setAttribute('readonly', ''); + area.style.cssText = 'position:fixed;top:0;left:0;opacity:0;pointer-events:none'; + document.body.appendChild(area); + area.select(); + let ok = false; + try { ok = document.execCommand('copy'); } catch { ok = false; } + area.remove(); + return ok; +} + +let _note = null; +let _noteTimer = null; + +// Said once and gone, in the playlists' note (style.css `.playlist-note`). +// Appended to the body rather than rendered by a view, because the views that +// copy include modals and a context menu that has already closed. +function _say(text) { + if (!_note) { + _note = document.createElement('div'); + _note.className = 'playlist-note copy-link-note'; + _note.setAttribute('role', 'status'); + } + _note.textContent = text; + document.body.appendChild(_note); + clearTimeout(_noteTimer); + _noteTimer = setTimeout(() => { if (_note) _note.remove(); }, 2500); +} + +/** Copy `url`, and say whether it worked. The link itself is shown if not. */ +export async function copyLink(url) { + if (!url) return; + const ok = await _write(url); + _say(ok ? t('link.copied') : t('link.copy_failed', { url })); +} |