Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Show chat panel when opening a video room with unread messages #8812

Merged
merged 11 commits into from
Jun 17, 2022
19 changes: 10 additions & 9 deletions src/stores/right-panel/RightPanelStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,21 @@ export default class RightPanelStore extends ReadyWatchingStore {
} = {};
private viewedRoomId: Optional<string>;

private constructor() {
// Public for tests
public constructor() {
robintown marked this conversation as resolved.
Show resolved Hide resolved
super(defaultDispatcher);
}

protected async onReady(): Promise<any> {
protected onReady = async (): Promise<any> => {
robintown marked this conversation as resolved.
Show resolved Hide resolved
this.viewedRoomId = RoomViewStore.instance.getRoomId();
this.matrixClient.on(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
this.loadCacheFromSettings();
this.emitAndUpdateSettings();
}
};

protected async onNotReady(): Promise<any> {
protected onNotReady = async (): Promise<any> => {
this.matrixClient.off(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
}
};

protected onDispatcherAction(payload: ActionPayload) {
if (payload.action !== Action.ActiveRoomChanged) return;
Expand Down Expand Up @@ -141,7 +142,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
const hist = this.byRoom[rId]?.history ?? [];
hist[hist.length - 1].state = cardState;
this.emitAndUpdateSettings();
} else if (targetPhase !== this.currentCard?.phase || !this.byRoom[rId]) {
} else if (targetPhase !== this.currentCardForRoom(rId)?.phase || !this.byRoom[rId]) {
// Set right panel and initialize/erase history
const history = [{ phase: targetPhase, state: cardState ?? {} }];
this.byRoom[rId] = { history, isOpen: true };
Expand All @@ -161,16 +162,16 @@ export default class RightPanelStore extends ReadyWatchingStore {
this.emitAndUpdateSettings();
}

// Appends a card to the history and shows the right panel if not already visible
public pushCard(
card: IRightPanelCard,
allowClose = true,
roomId: string = null,
) {
// This function appends a card to the history and shows the right panel if now already visible.
const rId = roomId ?? this.viewedRoomId;
const redirect = this.getVerificationRedirect(card);
const targetPhase = redirect?.phase ?? card.phase;
const pState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
const pState = redirect?.state ?? card.state ?? {};

// Checks for wrong SetRightPanelPhase requests
if (!this.isPhaseValid(targetPhase, Boolean(rId))) return;
Expand All @@ -183,7 +184,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
} else {
// setup room panel cache with the new card
this.byRoom[rId] = {
history: [{ phase: targetPhase, state: pState ?? {} }],
history: [{ phase: targetPhase, state: pState }],
// if there was no right panel store object the the panel was closed -> keep it closed, except if allowClose==false
isOpen: !allowClose,
};
Expand Down
204 changes: 204 additions & 0 deletions test/stores/right-panel/RightPanelStore-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { mocked, MockedObject } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/client";

import { stubClient } from "../../test-utils";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
import { Action } from "../../../src/dispatcher/actions";
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
import { ActiveRoomChangedPayload } from "../../../src/dispatcher/payloads/ActiveRoomChangedPayload";
import RightPanelStore from "../../../src/stores/right-panel/RightPanelStore";
import { RightPanelPhases } from "../../../src/stores/right-panel/RightPanelStorePhases";
import SettingsStore from "../../../src/settings/SettingsStore";

describe("RightPanelStore", () => {
turt2live marked this conversation as resolved.
Show resolved Hide resolved
// Mock out the settings store so the right panel store can't persist values between tests
jest.spyOn(SettingsStore, "setValue").mockImplementation(async () => {});

let store: RightPanelStore;
let cli: MockedObject<MatrixClient>;
beforeEach(() => {
stubClient();
cli = mocked(MatrixClientPeg.get());

store = new RightPanelStore();
store.useUnitTestClient(cli);
});

const viewRoom = async (roomId: string) => {
const roomChanged = new Promise<void>(resolve => {
const ref = defaultDispatcher.register(payload => {
if (payload.action === Action.ActiveRoomChanged && payload.newRoomId === roomId) {
defaultDispatcher.unregister(ref);
resolve();
}
});
});

defaultDispatcher.dispatch<ActiveRoomChangedPayload>({
action: Action.ActiveRoomChanged,
oldRoomId: null,
newRoomId: roomId,
});

await roomChanged;
};

const setCard = (roomId: string, phase: RightPanelPhases) => store.setCard({ phase }, true, roomId);

describe("isOpen", () => {
it("is false if no rooms are open", () => {
expect(store.isOpen).toEqual(false);
});
it("is false if a room other than the current room is open", async () => {
await viewRoom("!1:example.org");
setCard("!2:example.org", RightPanelPhases.RoomSummary);
expect(store.isOpen).toEqual(false);
});
it("is true if the current room is open", async () => {
await viewRoom("!1:example.org");
setCard("!1:example.org", RightPanelPhases.RoomSummary);
expect(store.isOpen).toEqual(true);
});
});

describe("currentCard", () => {
it("has a phase of null if nothing is open", () => {
expect(store.currentCard.phase).toEqual(null);
});
it("has a phase of null if the panel is open but in another room", async () => {
await viewRoom("!1:example.org");
setCard("!2:example.org", RightPanelPhases.RoomSummary);
expect(store.currentCard.phase).toEqual(null);
});
it("reflects the phase of the current room", async () => {
await viewRoom("!1:example.org");
setCard("!1:example.org", RightPanelPhases.RoomSummary);
expect(store.currentCard.phase).toEqual(RightPanelPhases.RoomSummary);
});
});

describe("setCard", () => {
it("does nothing if given no room ID and not viewing a room", () => {
store.setCard({ phase: RightPanelPhases.RoomSummary }, true);
expect(store.isOpen).toEqual(false);
expect(store.currentCard.phase).toEqual(null);
});
it("does nothing if given an invalid state", async () => {
await viewRoom("!1:example.org");
// Needs a member specified to be valid
store.setCard({ phase: RightPanelPhases.RoomMemberInfo }, true, "!1:example.org");
expect(store.roomPhaseHistory).toEqual([]);
});
it("only creates a single history entry if given the same card twice", async () => {
await viewRoom("!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
expect(store.roomPhaseHistory).toEqual([
{ phase: RightPanelPhases.RoomSummary, state: {} },
]);
});
it("opens the panel in the given room with the correct phase", () => {
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
expect(store.isOpenForRoom("!1:example.org")).toEqual(true);
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomSummary);
});
it("overwrites history if changing the phase", async () => {
await viewRoom("!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomMemberList }, true, "!1:example.org");
expect(store.roomPhaseHistory).toEqual([
{ phase: RightPanelPhases.RoomMemberList, state: {} },
]);
});
});

describe("setCards", () => {
it("overwrites history", async () => {
await viewRoom("!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomMemberList }, true, "!1:example.org");
store.setCards([
{ phase: RightPanelPhases.RoomSummary },
{ phase: RightPanelPhases.PinnedMessages },
], true, "!1:example.org");
expect(store.roomPhaseHistory).toEqual([
{ phase: RightPanelPhases.RoomSummary, state: {} },
{ phase: RightPanelPhases.PinnedMessages, state: {} },
]);
});
});

describe("pushCard", () => {
it("does nothing if given no room ID and not viewing a room", () => {
store.pushCard({ phase: RightPanelPhases.RoomSummary }, true);
expect(store.isOpen).toEqual(false);
expect(store.currentCard.phase).toEqual(null);
});
it("opens the panel in the given room with the correct phase", () => {
store.pushCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
expect(store.isOpenForRoom("!1:example.org")).toEqual(true);
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomSummary);
});
it("appends the phase to any phases that were there before", async () => {
await viewRoom("!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
store.pushCard({ phase: RightPanelPhases.PinnedMessages }, true, "!1:example.org");
expect(store.roomPhaseHistory).toEqual([
{ phase: RightPanelPhases.RoomSummary, state: {} },
{ phase: RightPanelPhases.PinnedMessages, state: {} },
]);
});
});

describe("popCard", () => {
it("removes the most recent card", () => {
store.setCards([
{ phase: RightPanelPhases.RoomSummary },
{ phase: RightPanelPhases.PinnedMessages },
], true, "!1:example.org");
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.PinnedMessages);
store.popCard("!1:example.org");
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomSummary);
});
});

describe("togglePanel", () => {
it("does nothing if the room has no phase to open to", () => {
expect(store.isOpenForRoom("!1:example.org")).toEqual(false);
store.togglePanel("!1:example.org");
expect(store.isOpenForRoom("!1:example.org")).toEqual(false);
});
it("works if a room is specified", () => {
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
expect(store.isOpenForRoom("!1:example.org")).toEqual(true);
store.togglePanel("!1:example.org");
expect(store.isOpenForRoom("!1:example.org")).toEqual(false);
store.togglePanel("!1:example.org");
expect(store.isOpenForRoom("!1:example.org")).toEqual(true);
});
it("operates on the current room if no room is specified", async () => {
await viewRoom("!1:example.org");
store.setCard({ phase: RightPanelPhases.RoomSummary }, true);
expect(store.isOpen).toEqual(true);
store.togglePanel(null);
expect(store.isOpen).toEqual(false);
store.togglePanel(null);
expect(store.isOpen).toEqual(true);
});
});
});