diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx index 809da069ba..d9acba8524 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx @@ -19,13 +19,14 @@ import { EventType } from "matrix-js-sdk/src/@types/event"; import { _t } from "../../../../../languageHandler"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; -import AccessibleButton from "../../../elements/AccessibleButton"; +import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton"; import RoomUpgradeDialog from "../../../dialogs/RoomUpgradeDialog"; import Modal from "../../../../../Modal"; import dis from "../../../../../dispatcher/dispatcher"; import { Action } from "../../../../../dispatcher/actions"; import CopyableText from "../../../elements/CopyableText"; import { ViewRoomPayload } from "../../../../../dispatcher/payloads/ViewRoomPayload"; +import SettingsStore from "../../../../../settings/SettingsStore"; interface IProps { roomId: string; @@ -46,9 +47,11 @@ interface IState { } export default class AdvancedRoomSettingsTab extends React.Component { - public constructor(props, context) { + public constructor(props: IProps, context: any) { super(props, context); + const msc3946ProcessDynamicPredecessor = SettingsStore.getValue("feature_dynamic_room_predecessors"); + this.state = { // This is eventually set to the value of room.getRecommendedVersion() upgradeRecommendation: null, @@ -60,11 +63,10 @@ export default class AdvancedRoomSettingsTab extends React.Component = {}; - const createEvent = room.currentState.getStateEvents(EventType.RoomCreate, ""); - const predecessor = createEvent ? createEvent.getContent().predecessor : null; - if (predecessor && predecessor.room_id) { - additionalStateChanges.oldRoomId = predecessor.room_id; - additionalStateChanges.oldEventId = predecessor.event_id; + const predecessor = room.findPredecessor(msc3946ProcessDynamicPredecessor); + if (predecessor) { + additionalStateChanges.oldRoomId = predecessor.roomId; + additionalStateChanges.oldEventId = predecessor.eventId; } this.setState({ @@ -75,12 +77,12 @@ export default class AdvancedRoomSettingsTab extends React.Component { + private upgradeRoom = (): void => { const room = MatrixClientPeg.get().getRoom(this.props.roomId); Modal.createDialog(RoomUpgradeDialog, { room }); }; - private onOldRoomClicked = (e): void => { + private onOldRoomClicked = (e: ButtonEvent): void => { e.preventDefault(); e.stopPropagation(); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 45db7cb23f..6d315bf38d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -955,6 +955,8 @@ "New group call experience": "New group call experience", "Live Location Sharing": "Live Location Sharing", "Temporary implementation. Locations persist in room history.": "Temporary implementation. Locations persist in room history.", + "Dynamic room predecessors": "Dynamic room predecessors", + "Enable MSC3946 (to support late-arriving room archives)": "Enable MSC3946 (to support late-arriving room archives)", "Favourite Messages": "Favourite Messages", "Under active development.": "Under active development.", "Force 15s voice broadcast chunk length": "Force 15s voice broadcast chunk length", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index bfd40e96ce..fdd3a857c1 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -439,6 +439,15 @@ export const SETTINGS: { [setting: string]: ISetting } = { shouldWarn: true, default: false, }, + "feature_dynamic_room_predecessors": { + isFeature: true, + labsGroup: LabGroup.Rooms, + supportedLevels: LEVELS_FEATURE, + displayName: _td("Dynamic room predecessors"), + description: _td("Enable MSC3946 (to support late-arriving room archives)"), + shouldWarn: true, + default: false, + }, "feature_favourite_messages": { isFeature: true, labsGroup: LabGroup.Messaging, diff --git a/test/components/views/settings/tabs/room/AdvancedRoomSettingsTab-test.tsx b/test/components/views/settings/tabs/room/AdvancedRoomSettingsTab-test.tsx index dafd555966..cb4b373e4f 100644 --- a/test/components/views/settings/tabs/room/AdvancedRoomSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/AdvancedRoomSettingsTab-test.tsx @@ -26,6 +26,7 @@ import { mkEvent, mkStubRoom, stubClient } from "../../../../../test-utils"; import dis from "../../../../../../src/dispatcher/dispatcher"; import { Action } from "../../../../../../src/dispatcher/actions"; import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg"; +import SettingsStore from "../../../../../../src/settings/SettingsStore"; jest.mock("../../../../../../src/dispatcher/dispatcher"); @@ -43,6 +44,12 @@ describe("AdvancedRoomSettingsTab", () => { cli = MatrixClientPeg.get(); room = mkStubRoom(roomId, "test room", cli); mocked(cli.getRoom).mockReturnValue(room); + mocked(dis.dispatch).mockReset(); + mocked(room.findPredecessor).mockImplementation((msc3946: boolean) => + msc3946 + ? { roomId: "old_room_id_via_predecessor", eventId: null } + : { roomId: "old_room_id", eventId: "tombstone_event_id" }, + ); }); it("should render as expected", () => { @@ -71,6 +78,17 @@ describe("AdvancedRoomSettingsTab", () => { room: room.roomId, }); + // Because we're mocking Room.findPredecessor, it may not be necessary + // to provide the actual event here, but we do need the create event, + // and in future this may be needed, so included for symmetry. + const predecessorEvent = mkEvent({ + event: true, + user: "@a:b.com", + type: EventType.RoomPredecessor, + content: { predecessor_room_id: "old_room_id_via_predecessor" }, + room: room.roomId, + }); + type GetStateEvents2Args = (eventType: EventType | string, stateKey: string) => MatrixEvent | null; const getStateEvents = jest.spyOn( @@ -82,6 +100,8 @@ describe("AdvancedRoomSettingsTab", () => { switch (eventType) { case EventType.RoomCreate: return createEvent; + case EventType.RoomPredecessor: + return predecessorEvent; default: return null; } @@ -101,4 +121,26 @@ describe("AdvancedRoomSettingsTab", () => { metricsViaKeyboard: false, }); }); + + describe("When MSC3946 support is enabled", () => { + beforeEach(() => { + jest.spyOn(SettingsStore, "getValue") + .mockReset() + .mockImplementation((settingName) => settingName === "feature_dynamic_room_predecessors"); + }); + + it("should link to predecessor room via MSC3946 if enabled", async () => { + mockStateEvents(room); + const tab = renderTab(); + const link = await tab.findByText("View older messages in test room."); + fireEvent.click(link); + expect(dis.dispatch).toHaveBeenCalledWith({ + action: Action.ViewRoom, + event_id: null, + room_id: "old_room_id_via_predecessor", + metricsTrigger: "WebPredecessorSettings", + metricsViaKeyboard: false, + }); + }); + }); });