diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/app.js | 55 |
1 files changed, 46 insertions, 9 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js index 6f97ca5..bdab271 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js @@ -21,6 +21,7 @@ import { } from './hub-client.js'; import { startIdleWatch, markActive } from './idle.js'; import { GroupPage } from './group-page.js'; +import { parseGroupLink, groupLinkRoute, findLinkedGroup } from './group-link.js'; import { lazy } from './lazy.js'; import { ConnectionPool } from './connection-pool.js'; import { MusicPlayerBar } from './music-player.js'; @@ -766,6 +767,9 @@ function App() { const [needsHub, setNeedsHub] = useState( platform.isNative && !platform.hubBase()); const [groups, setGroups] = useState([]); + // Whether `/mine` has answered for this sign-in. A `#/name@owner` link can + // only be called unknown once the list it is looked up in has arrived. + const [groupsLoaded, setGroupsLoaded] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); @@ -1008,11 +1012,13 @@ function App() { if (!user) { nodeKeyAskedForRef.current = null; setGroups([]); setNotifications([]); setUnreadCount(0); setHasNodeKey(false); + setGroupsLoaded(false); return; } hubFetch('/v1/groups/mine', { token: user.token }) .then(data => setGroups(data.groups || [])) - .catch(() => setGroups([])); + .catch(() => setGroups([])) + .finally(() => setGroupsLoaded(true)); hubFetch('/v1/users/me/preferences', { token: user.token }) .then(async (prefs) => { setUserPrefs(prefs || {}); @@ -1233,11 +1239,36 @@ function App() { // out as the only way back. const refreshAuth = useCallback(() => refreshAccessToken(), []); + // A group is reached by its id (`#/group/<id>`, what every link inside the + // application uses) or by its handle (`#/name@owner[/path]`, the one a person + // types or shares — group-link.js). Both open the same page; `groupRoute` is + // the first form whichever was used, so the sidebar and the page see one. + const groupLink = route.startsWith('/group/') ? null : parseGroupLink(route); + const linkedGroup = groupLink ? findLinkedGroup(groups, groupLink) : null; + const groupId = route.startsWith('/group/') ? route.slice(7) + : linkedGroup ? linkedGroup.id : null; + const groupRoute = groupId ? '/group/' + groupId : route; + const shownGroup = groupId ? groups.find(g => g.id === groupId) : null; + const linkPath = groupLink && linkedGroup ? groupLink.path : ''; + // The address shows the handle, which is the link worth copying. `replace`, + // so this is not a history entry; and only once the path a link named has + // been acted on (`onLinkOpened`), so a reload does not download the file a + // second time. + const shownGroupRoute = shownGroup ? groupLinkRoute(shownGroup) : null; + useEffect(() => { + if (shownGroupRoute && !linkPath && route !== shownGroupRoute) { + window.location.replace('#' + shownGroupRoute); + } + }, [route, shownGroupRoute, linkPath]); + const onLinkOpened = useCallback(() => { + if (shownGroupRoute) window.location.replace('#' + shownGroupRoute); + }, [shownGroupRoute]); + let page; // A desktop build with no hub configured cannot do anything at all, so it - // asks before showing a sign-in form that could not work. Deliberately not - // defaulted to meshbay.org: a client that picks its own hub is a client that - // can be pointed at one. + // asks before showing a sign-in form that could not work. The field comes + // filled with meshbay.org but is still asked: a client that picks its own hub + // is a client that can be pointed at one. if (needsHub) { page = html`<${FirstRunPage} onSet=${() => setNeedsHub(false)} />`; } else if (!deviceTried) { @@ -1282,17 +1313,23 @@ function App() { }} />`; } else if (route === '/node' && platform.capabilities.nodeAdmin && hasNodeKey) { page = html`<${LazyNodePage} groups=${groups} token=${user.token} username=${user.username} />`; - } else if (route.startsWith('/group/')) { - const groupId = route.slice(7); - const group = groups.find(g => g.id === groupId); + } else if (groupId) { page = html`<${GroupPage} - groupId=${groupId} group=${group} token=${user.token} + groupId=${groupId} group=${shownGroup} token=${user.token} + openPath=${linkPath} onLinkOpened=${onLinkOpened} username=${user.username} userId=${user.userId} userPrefs=${userPrefs} onRefreshAuth=${refreshAuth} onJoined=${dismissGroupNotifications} onGroupUpdated=${updateGroup} onPresence=${notePresence} onLeft=${handleLeftGroup} onPlayQueue=${handlePlayQueue} onStopMusic=${handleStopMusic} />`; + } else if (groupLink) { + // Not among this account's groups — or not yet known to be. The same words + // whether it does not exist or is someone else's: the list it was looked + // up in is this account's own, so there is nothing else to tell. + page = groupsLoaded + ? html`<div class="page-content"><p class="page-message">${t('group.link_unknown')}</p></div>` + : html`<div class="page-content"><p class="page-message"><span class="spinner"></span></p></div>`; } else if (route === '/admin') { page = (user.role === 'moderator' || user.role === 'admin') ? html`<${LazyAdminPage} token=${user.token} role=${user.role} />` @@ -1334,7 +1371,7 @@ function App() { groups=${groups} presence=${presence} indexProgressPct=${indexProgressPct} - route=${route} + route=${groupRoute} menuOpen=${menuOpen} role=${user.role} allowPublicGroups=${allowPublicGroups} |