diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-05 12:23:18 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-05 12:23:18 +0200 |
| commit | 1d94a92936abb3d37a8f9bfb36c8850246fee15d (patch) | |
| tree | f07aa4f65277d703ac64a84882b0c559bf379f30 /packages/meshbay-hub/src/meshbay_hub/static/group-page.js | |
| parent | 8f25294b0f6bc3f292442edd69a2e149f0717b52 (diff) | |
| download | meshbay-1d94a92936abb3d37a8f9bfb36c8850246fee15d.tar.gz | |
feat: copy a file's or folder's #/name@owner link from Files, Music, Photos and Search
"Copy link" puts the address group-link.js resolves on the clipboard, on the
hub's origin rather than the page's, so a link copied in the desktop
application is not app://meshbay. Files offers it for one row, from the
right-click menu or the toolbar with one row ticked (a phone's way in);
Music on one track's menu, whose dots a phone has; Photos on a right-clicked
tile and in the lightbox's bar. The video player and the file preview carry
a link button next to Download.
Applications get a `linkFor(entry | folderPath)` prop (MESHBAY_DESIGN.md
§9.2) and offer the action only when it names a link. The group page builds
it from the hub's row; Search from each result's own group and its path
before the merged views prefixed it, and names no link for a folder of the
merged tree, which a group name alone does not identify.
harness/copy_link_probe.py mounts the three applications in Chrome and reads
what reached the clipboard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-page.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/group-page.js | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index a2d6c18..48b1c80 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -6,6 +6,7 @@ import { Icon } from './icon.js'; import { transfers } from './transfers.js'; import { downloadEntry } from './file-utils.js'; import { resolveLinkedPath } from './group-link.js'; +import { copyLink, entryPath, groupItemLink } from './copy-link.js'; import { HUB, session, hubFetch, ensureFreshToken, _loadBundleKey, _loadRecoveryKey, _storeBundleKey, @@ -761,6 +762,14 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, if (onLinkOpened) onLinkOpened(); }, [openPath, status, groupId]); + // The link "Copy link" puts on the clipboard, for an entry or a folder path + // (group-link.js). Null until the hub's row for the group is in: the handle + // is its name and its owner's, and the node knows neither. + const linkFor = useCallback((target) => { + if (!group || !group.name || !group.owner_username || !target) return null; + return groupItemLink(group, typeof target === 'string' ? target : entryPath(target)); + }, [group]); + const refreshIndex = useCallback(async () => { const transport = transportRef.current; if (!transport || !transport.connected) return; @@ -891,7 +900,7 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, entries, availableEntries, nodeDirs, nodeRoots, setEntries, setNodeDirs, setNodeRoots, applyIndex, isNodeAdmin, operatorPaired, attachRoot, attachDir, userId, setError, onPreview, - onRefreshIndex: refreshIndex, onActivity: touchActivity, + onRefreshIndex: refreshIndex, onActivity: touchActivity, linkFor, // Plural everywhere, and built from the registry rather than a list of app // names kept here: Videos and Music read a list, Photos always did, and an // application added to `APPS` gets its own entry without this file @@ -1060,7 +1069,8 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, transportRef=${transportRef} gekRef=${gekRef} onClose=${() => setPreviewEntry(null)} - onDownload=${() => downloadFileForModal(previewEntry)} /> + onDownload=${() => downloadFileForModal(previewEntry)} + onCopyLink=${linkFor(previewEntry) && (() => copyLink(linkFor(previewEntry)))} /> `} ${videoEntry && html` <${VideoPlayer} @@ -1068,7 +1078,8 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, transportRef=${transportRef} gekRef=${gekRef} onClose=${() => setVideoEntry(null)} - onDownload=${() => downloadFileForModal(videoEntry)} /> + onDownload=${() => downloadFileForModal(videoEntry)} + onCopyLink=${linkFor(videoEntry) && (() => copyLink(linkFor(videoEntry)))} /> `} </div> `; |