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
92 lines
3.4 KiB
TypeScript
92 lines
3.4 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
getLotusRoomPermalink,
|
|
getLotusSpacePermalink,
|
|
lotusPermalinkToMatrixTo,
|
|
parseLotusPermalink,
|
|
} from './lotus-permalink';
|
|
|
|
const BASE = 'https://chat.lotusguild.org/';
|
|
|
|
test('builds room / event / space permalinks with encoded ids and viaServers', () => {
|
|
assert.equal(
|
|
getLotusRoomPermalink(BASE, '!abc:matrix.lotusguild.org', '$ev1', ['matrix.lotusguild.org']),
|
|
'https://chat.lotusguild.org/home/!abc%3Amatrix.lotusguild.org/%24ev1?viaServers=matrix.lotusguild.org',
|
|
);
|
|
assert.equal(
|
|
getLotusRoomPermalink(BASE, '#general:matrix.lotusguild.org'),
|
|
'https://chat.lotusguild.org/home/%23general%3Amatrix.lotusguild.org',
|
|
);
|
|
assert.equal(
|
|
getLotusSpacePermalink('https://chat.lotusguild.org', '#guild:matrix.lotusguild.org'),
|
|
'https://chat.lotusguild.org/%23guild%3Amatrix.lotusguild.org',
|
|
);
|
|
});
|
|
|
|
test('parses its own output back (encoded and decoded forms)', () => {
|
|
const link = getLotusRoomPermalink(BASE, '!abc:lotus', '$ev1', ['a.org', 'b.org']);
|
|
assert.deepEqual(parseLotusPermalink(BASE, link), {
|
|
roomIdOrAlias: '!abc:lotus',
|
|
eventId: '$ev1',
|
|
viaServers: ['a.org', 'b.org'],
|
|
});
|
|
assert.deepEqual(parseLotusPermalink(BASE, 'https://chat.lotusguild.org/home/!abc:lotus/$ev1'), {
|
|
roomIdOrAlias: '!abc:lotus',
|
|
eventId: '$ev1',
|
|
viaServers: undefined,
|
|
});
|
|
assert.deepEqual(parseLotusPermalink(BASE, 'https://chat.lotusguild.org/direct/!dm:lotus/'), {
|
|
roomIdOrAlias: '!dm:lotus',
|
|
viaServers: undefined,
|
|
});
|
|
});
|
|
|
|
test('space routes: lobby → the space, nested room → the room', () => {
|
|
assert.deepEqual(parseLotusPermalink(BASE, 'https://chat.lotusguild.org/%23guild%3Alotus/'), {
|
|
roomIdOrAlias: '#guild:lotus',
|
|
viaServers: undefined,
|
|
});
|
|
assert.deepEqual(
|
|
parseLotusPermalink(BASE, 'https://chat.lotusguild.org/%23guild%3Alotus/!room%3Alotus/%24e'),
|
|
{ roomIdOrAlias: '!room:lotus', eventId: '$e', viaServers: undefined },
|
|
);
|
|
});
|
|
|
|
test('accepts the matrix.to Cinny adapter form (?via=a,b)', () => {
|
|
assert.deepEqual(
|
|
parseLotusPermalink(BASE, 'https://chat.lotusguild.org/home/!r:x?via=a.org,b.org'),
|
|
{
|
|
roomIdOrAlias: '!r:x',
|
|
viaServers: ['a.org', 'b.org'],
|
|
},
|
|
);
|
|
});
|
|
|
|
test('rejects other origins, non-room routes and malformed ids', () => {
|
|
assert.equal(parseLotusPermalink(BASE, 'https://app.cinny.in/home/!r:x'), undefined);
|
|
assert.equal(parseLotusPermalink(BASE, 'https://chat.lotusguild.org/home/settings'), undefined);
|
|
assert.equal(parseLotusPermalink(BASE, 'https://chat.lotusguild.org/explore/lotus'), undefined);
|
|
assert.equal(
|
|
parseLotusPermalink(BASE, 'https://chat.lotusguild.org/home/!r:x/notanevent'),
|
|
undefined,
|
|
);
|
|
assert.equal(
|
|
parseLotusPermalink(BASE, 'https://chat.lotusguild.org.evil.com/home/!r:x'),
|
|
undefined,
|
|
);
|
|
assert.equal(parseLotusPermalink(BASE, 'https://chat.lotusguild.org'), undefined);
|
|
});
|
|
|
|
test('rewrites to the matrix.to form the existing mention pipeline understands', () => {
|
|
assert.equal(
|
|
lotusPermalinkToMatrixTo(BASE, 'https://chat.lotusguild.org/home/!r:x/$e?viaServers=a.org'),
|
|
'https://matrix.to/#/!r:x/$e?via=a.org',
|
|
);
|
|
assert.equal(
|
|
lotusPermalinkToMatrixTo(BASE, 'https://chat.lotusguild.org/%23guild%3Ax/'),
|
|
'https://matrix.to/#/#guild:x',
|
|
);
|
|
assert.equal(lotusPermalinkToMatrixTo(BASE, 'https://example.org/home/!r:x'), undefined);
|
|
});
|