aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/music-app.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-05 12:23:18 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-05 12:23:18 +0200
commit1d94a92936abb3d37a8f9bfb36c8850246fee15d (patch)
treef07aa4f65277d703ac64a84882b0c559bf379f30 /packages/meshbay-hub/src/meshbay_hub/static/music-app.js
parent8f25294b0f6bc3f292442edd69a2e149f0717b52 (diff)
downloadmeshbay-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/music-app.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/music-app.js10
1 files changed, 8 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js
index f2c4374..9adb1e1 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js
@@ -11,6 +11,7 @@ import { Menu, MenuDots, useMenu } from './menu.js';
import { PlaylistMenuButton, NameModal, usePlaylists } from './playlist-menu.js';
import * as P from './playlists.js';
import { CastButton } from './cast-session.js';
+import { copyLink } from './copy-link.js';
// -- Music --------------------------------------------------------------------
//
@@ -564,7 +565,7 @@ function FlatList({ items, onPlayQueue, onMenu }) {
function MusicApp({
groupId, transportRef, gekRef, status, entries, availableEntries,
musicDirectories, musicbrainzConfig, onPlayQueue, userId,
- hideFilter, userPrefs, pageResetKey,
+ hideFilter, userPrefs, pageResetKey, linkFor,
}) {
const [mode, setMode] = useState(loadViewMode);
const [filter, setFilter] = useState('');
@@ -618,6 +619,9 @@ function MusicApp({
// argument slot for one.
const onMenu = useCallback((e, tracks, startIndex) => {
if (!tracks || !tracks.length) return;
+ // One track's menu โ€” a row's, whose dots are how a phone opens it. An
+ // album's carries all its tracks and gets no link: Files has the folder's.
+ const link = tracks.length === 1 && linkFor ? linkFor(tracks[0]) : null;
openAt(e, [
{ label: t('music.menu_play'), icon: 'play',
onSelect: () => onPlayQueue(tracks, startIndex || 0) },
@@ -650,8 +654,10 @@ function MusicApp({
},
],
},
+ ...(link ? [{ divider: true },
+ { label: t('link.copy'), icon: 'link', onSelect: () => copyLink(link) }] : []),
]);
- }, [openAt, onPlayQueue, playlists, addToPlaylist]);
+ }, [openAt, onPlayQueue, playlists, addToPlaylist, linkFor]);
useEffect(() => { setMode(loadViewMode()); }, [groupId]);
useEffect(() => { setFilter(''); }, [groupId]);