Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-forum-event-timeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

Fix the forum room "Event Timeline" developer entry bouncing straight back to the forum view.
8 changes: 8 additions & 0 deletions src/app/components/page/PersistentRoomHost.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ describe('PersistentRoomHost', () => {
);
});

it('keeps a forum room on the timeline route when the timeline param asks for it', () => {
renderHost(`/home/${encodeURIComponent(FORUM_ROOM_ID)}/?timeline=true`);
expect(screen.getByTestId('room-timeline')).toBeInTheDocument();
expect(screen.getByTestId('pathname')).toHaveTextContent(
`/home/${encodeURIComponent(FORUM_ROOM_ID)}/`
);
});

it('preloads the last visited room on a list route without redirecting', () => {
renderHost('/home', { home: FORUM_ROOM_ID });
expect(screen.getByTestId('room-timeline')).toBeInTheDocument();
Expand Down
1 change: 1 addition & 0 deletions src/app/features/forum/ForumHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export function ForumHeader({ room, showProfile, powerLevels }: ForumHeaderProps
fill="None"
onClick={menu.triggerProps.onClick}
ref={triggerRef}
aria-label="More Options"
aria-pressed={!!menu.anchor}
>
{composerIcon(DotsThreeOutlineVerticalIcon, {
Expand Down
16 changes: 12 additions & 4 deletions src/app/features/forum/ForumMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import { settingsAtom } from '$state/settings';
import { markAsRead } from '$utils/notifications';
import { copyToClipboard } from '$utils/dom';
import { getCanonicalAliasOrRoomId, isRoomAlias } from '$utils/matrix';
import { getHomeRoomPath, getDirectRoomPath, getSpaceRoomPath } from '$pages/pathUtils';
import {
getHomeRoomPath,
getDirectRoomPath,
getSpaceRoomPath,
withSearchParam,
} from '$pages/pathUtils';
import { ROOM_TIMELINE_SEARCH_PARAM } from '$pages/paths';
import { getMatrixToRoom } from '$plugins/matrix-to';
import { getViaServers } from '$plugins/via-servers';
import {
Expand Down Expand Up @@ -77,14 +83,16 @@ export const ForumMenu = forwardRef<HTMLDivElement, ForumMenuProps>(

const handleOpenTimeline = () => {
const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, room.roomId);
let path: string;
if (parentSpace) {
const spaceIdOrAlias = getCanonicalAliasOrRoomId(mx, parentSpace.roomId);
navigate(getSpaceRoomPath(spaceIdOrAlias, roomIdOrAlias));
path = getSpaceRoomPath(spaceIdOrAlias, roomIdOrAlias);
} else if (isDirectRoom) {
navigate(getDirectRoomPath(roomIdOrAlias));
path = getDirectRoomPath(roomIdOrAlias);
} else {
navigate(getHomeRoomPath(roomIdOrAlias));
path = getHomeRoomPath(roomIdOrAlias);
}
navigate(withSearchParam(path, { [ROOM_TIMELINE_SEARCH_PARAM]: 'true' }));
requestClose();
};

Expand Down
8 changes: 6 additions & 2 deletions src/app/pages/client/RoomRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { ForumView } from '$features/forum';
import { Room } from '$features/room';
import { useRoom } from '$hooks/useRoom';
Expand All @@ -11,6 +11,7 @@ import {
getSpaceForumPath,
getSpaceRoomPath,
} from '$pages/pathUtils';
import { ROOM_TIMELINE_SEARCH_PARAM } from '$pages/paths';
import { CustomRoomType } from '$types/matrix/room';

export type RoomRouteSection = 'home' | 'direct' | 'space';
Expand All @@ -34,7 +35,10 @@ export function RoomGate({
}: RoomGateProps) {
const room = useRoom();
const navigate = useNavigate();
const isForum = room.getType() === CustomRoomType.Forum;
const [searchParams] = useSearchParams();
// Lets the developer tools open a forum room's timeline without being redirected back.
const timelineRequested = !forum && searchParams.get(ROOM_TIMELINE_SEARCH_PARAM) === 'true';
const isForum = room.getType() === CustomRoomType.Forum && !timelineRequested;

useEffect(() => {
if (isForum === forum) return;
Expand Down
3 changes: 3 additions & 0 deletions src/app/pages/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export const SEARCH_PATH_SEGMENT = 'search/';
export type RoomSearchParams = {
/* comma separated string of servers */
viaServers?: string;
/* "true" keeps a forum room on the timeline route instead of the forum view */
timeline?: string;
};
export const ROOM_TIMELINE_SEARCH_PARAM = 'timeline';
export const ROOM_PATH_SEGMENT = ':roomIdOrAlias/:eventId?/';
export const ROOM_FORUM_PATH_SEGMENT = ':roomIdOrAlias/forum/:eventId?/';

Expand Down
69 changes: 69 additions & 0 deletions tests/e2e/forum-event-timeline.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { test, expect } from './fixtures/test';
import { createRoom, sendText } from './fixtures/continuwuity';
import { AppShell } from './pages/AppShell';
import { homeserverBaseUrl, loginAsFreshUser } from './fixtures/session';

const FORUM_ROOM_TYPE = 'pl.chrome.forum';

test.describe('forum event timeline', () => {
test('opens the event timeline of a forum room and stays on it', async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== 'desktop', 'desktop-focused');
test.setTimeout(300_000);
const storageStatePath = testInfo.project.use.storageState as string;
const hsBaseUrl = await homeserverBaseUrl(storageStatePath);
const tag = `evtl-${process.pid}-${Date.now().toString(36)}`;
const app = new AppShell(page);
const user = await loginAsFreshUser(page, hsBaseUrl, `${tag}-u`);

// The entry only exists with developer tools on.
await page.addInitScript(() => {
localStorage.setItem('settings', JSON.stringify({ developerTools: true }));
});

const forumId = await createRoom(hsBaseUrl, user.accessToken, {
name: `${tag} Forum`,
preset: 'private_chat',
creation_content: { type: FORUM_ROOM_TYPE },
});
await sendText(hsBaseUrl, user.accessToken, forumId, `${tag} topic one`, 1);

await page.goto('/');
await expect(page.getByText(`${tag} Forum`).first()).toBeVisible({ timeout: 180_000 });
await app.openRoom(`${tag} Forum`);
await expect(page).toHaveURL(/\/forum\/?$/);
await expect(page.getByText(`${tag} topic one`).first()).toBeVisible();

// The forum header's options button, not the room nav item's.
const options = page.getByRole('button', { name: 'More Options' });
await expect(options).toHaveCount(2);
await options.last().click();
await page.getByRole('button', { name: 'Event Timeline' }).click();

await expect(page.getByText(`${tag} topic one`).first()).toBeVisible();
await page.waitForTimeout(3000);
await expect(page).not.toHaveURL(/\/forum\/?$/);
await expect(page.getByText(`${tag} topic one`).first()).toBeVisible();
});

test('keeps the timeline route of a forum room when opened cold', async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== 'desktop', 'desktop-focused');
test.setTimeout(300_000);
const storageStatePath = testInfo.project.use.storageState as string;
const hsBaseUrl = await homeserverBaseUrl(storageStatePath);
const tag = `evtlcold-${process.pid}-${Date.now().toString(36)}`;
const user = await loginAsFreshUser(page, hsBaseUrl, `${tag}-u`);

const forumId = await createRoom(hsBaseUrl, user.accessToken, {
name: `${tag} Forum`,
preset: 'private_chat',
creation_content: { type: FORUM_ROOM_TYPE },
});
await sendText(hsBaseUrl, user.accessToken, forumId, `${tag} topic one`, 1);

await page.goto(`/home/${encodeURIComponent(forumId)}/?timeline=true`);
await expect(page.getByText(`${tag} topic one`).first()).toBeVisible({ timeout: 180_000 });
await page.waitForTimeout(3000);
await expect(page).not.toHaveURL(/\/forum\/?$/);
await expect(page.getByText(`${tag} topic one`).first()).toBeVisible();
});
});
Loading