feat(links): Copy Lotus Link permalinks, in-app recognition, /home redirect for joined rooms, via= alias, OIDC deep-link redirect (#130)
CI / Build & Quality Checks (push) Successful in 2m4s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 6s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 2m11s

matrix.to cannot target this deployment (Cinny adapter hard-codes
app.cinny.in; web-instance[] is Element-only, allowlisted), so add a
"Copy Lotus Link" next to every Copy Link (message menu, space header
menu, sidebar space tab) producing https://<this origin>/home/<room>/<event>
?viaServers=… via plugins/lotus-permalink.ts. Lotus links in messages are
rewritten to their matrix.to form inside the HTML parser so they render as
room/event mentions and navigate in place.

/home/<room> for a joined room that belongs to a space or Direct now
redirects to its own route (was a preview card with a View button; also
the form matrix.to → Cinny links use). ?via= is accepted as an alias of
?viaServers= (what the matrix.to Cinny adapter emits). A deep link
visited while logged out is now honoured after an OIDC login: the OIDC
callback reloads at the app root, which discarded the stored path — the
index loader consumes it via the shared takeAfterLoginPath().

Verified end-to-end with Playwright on a local Synapse: logged-out cold
link → login → lands on the event under the space route; menu copies the
expected link; a pasted Lotus link renders as a mention and jumps in
place; both space menus copy the space link.

Closes #130

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-18 00:18:45 -04:00
co-authored by Claude Opus 5
parent d929143f7d
commit f528e5e440
14 changed files with 396 additions and 22 deletions
+29 -1
View File
@@ -39,7 +39,13 @@ import {
NavItemContent,
NavLink,
} from '../../../components/nav';
import { getSpaceLobbyPath, getSpaceRoomPath, getSpaceSearchPath } from '../../pathUtils';
import {
getOriginBaseUrl,
getSpaceLobbyPath,
getSpaceRoomPath,
getSpaceSearchPath,
} from '../../pathUtils';
import { useClientConfig } from '../../../hooks/useClientConfig';
import { getCanonicalAliasOrRoomId, isRoomAlias } from '../../../utils/matrix';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import {
@@ -70,6 +76,7 @@ import { useStateEvent } from '../../../hooks/useStateEvent';
import { Membership, StateEvent } from '../../../../types/matrix/room';
import { stopPropagation } from '../../../utils/keyboard';
import { getMatrixToRoom } from '../../../plugins/matrix-to';
import { getLotusSpacePermalink } from '../../../plugins/lotus-permalink';
import { getViaServers } from '../../../plugins/via-servers';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
@@ -93,6 +100,7 @@ type SpaceMenuProps = {
};
const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClose }, ref) => {
const mx = useMatrixClient();
const { hashRouter } = useClientConfig();
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
const [developerTools] = useSetting(settingsAtom, 'developerTools');
const roomToParents = useAtomValue(roomToParentsAtom);
@@ -125,6 +133,16 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
requestClose();
};
// [Gitea #130] direct link into this deployment
const handleCopyLotusLink = () => {
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, room.roomId);
const viaServers = isRoomAlias(roomIdOrAlias) ? undefined : getViaServers(room);
copyToClipboard(
getLotusSpacePermalink(getOriginBaseUrl(hashRouter), roomIdOrAlias, viaServers),
);
requestClose();
};
const handleInvite = () => {
setInvitePrompt(true);
};
@@ -189,6 +207,16 @@ const SpaceMenu = forwardRef<HTMLDivElement, SpaceMenuProps>(({ room, requestClo
Copy Link
</Text>
</MenuItem>
<MenuItem
onClick={handleCopyLotusLink}
size="300"
after={<Icon size="100" src={Icons.Link} />}
radii="300"
>
<Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
Copy Lotus Link
</Text>
</MenuItem>
<MenuItem
onClick={handleRoomSettings}
size="300"