History based navigation with new right panel store (#7398)

Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
This commit is contained in:
Timo 2022-01-05 17:25:41 +01:00 committed by GitHub
parent 6f89267a31
commit 4ab3470184
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 248 additions and 252 deletions

View file

@ -15,7 +15,6 @@ limitations under the License.
*/
import { logger } from "matrix-js-sdk/src/logger";
import { EventSubscription } from 'fbemitter';
import defaultDispatcher from '../../dispatcher/dispatcher';
import { pendingVerificationRequestForUser } from '../../verification';
@ -31,7 +30,6 @@ import {
convertToStatePanel,
convertToStorePanel,
IRightPanelForRoom,
convertCardToStore,
} from './RightPanelStoreIPanelState';
import { MatrixClientPeg } from "../../MatrixClientPeg";
// import RoomViewStore from '../RoomViewStore';
@ -63,7 +61,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
private viewedRoomId: string;
private isViewingRoom?: boolean;
private dispatcherRefRightPanelStore: string;
private roomStoreToken: EventSubscription;
private isReady = false;
private global?: IRightPanelForRoom = null;
private byRoom: {
@ -76,6 +74,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
}
protected async onReady(): Promise<any> {
this.isReady = true;
// TODO RightPanelStore (will be addressed when dropping groups): This should be used instead of the onDispatch callback when groups are removed.
// RoomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequestUpdate);
@ -90,9 +89,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
}
protected async onNotReady(): Promise<any> {
if (this.roomStoreToken) {
this.roomStoreToken.remove();
}
this.isReady = false;
MatrixClientPeg.get().off("crypto.verification.request", this.onVerificationRequestUpdate);
// TODO RightPanelStore (will be addressed when dropping groups): User this instead of the dispatcher.
// RoomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
@ -140,8 +137,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
// Setters
public setCard(card: IRightPanelCard, allowClose = true, roomId?: string) {
const rId = roomId ?? this.viewedRoomId;
// this was previously a very multifunctional command:
// Toggle panel: if the same phase is send but without a state
// This function behaves as following:
// Update state: if the same phase is send but with a state
// Set right panel and erase history: if a "different to the current" phase is send (with or without a state)
const redirect = this.getVerificationRedirect(card);
@ -149,32 +145,29 @@ export default class RightPanelStore extends ReadyWatchingStore {
const cardState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
// Checks for wrong SetRightPanelPhase requests
if (!this.isPhaseActionIsValid(targetPhase)) return;
if (!this.isPhaseActionValid(targetPhase)) return;
if (targetPhase === this.currentCard?.phase &&
allowClose &&
(this.compareCards({ phase: targetPhase, state: cardState }, this.currentCard) || !cardState)
) {
// Toggle panel: a toggle command needs to fullfil the following:
// - the same phase
// - the panel can be closed
// - does not contain any state information (state)
if (targetPhase != RightPanelPhases.EncryptionPanel) {
this.togglePanel(rId);
}
return;
} else if ((targetPhase === this.currentCardForRoom(rId)?.phase && !!cardState)) {
if ((targetPhase === this.currentCardForRoom(rId)?.phase && !!cardState)) {
// Update state: set right panel with a new state but keep the phase (dont know it this is ever needed...)
const hist = this.byRoom[rId]?.history ?? [];
hist[hist.length - 1].state = cardState;
this.emitAndUpdateSettings();
return;
} else if (targetPhase !== this.currentCard?.phase) {
}
if (targetPhase !== this.currentCard?.phase) {
// Set right panel and erase history.
this.setRightPanelCache({ phase: targetPhase, state: cardState ?? {} }, rId);
}
}
public setCards(cards: IRightPanelCard[], allowClose = true, roomId: string = null) {
const rId = roomId ?? this.viewedRoomId;
const history = cards.map(c => ({ phase: c.phase, state: c.state ?? {} }));
this.byRoom[rId] = { history, isOpen: true };
this.emitAndUpdateSettings();
}
public pushCard(
card: IRightPanelCard,
allowClose = true,
@ -186,7 +179,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
const pState = redirect?.state ?? (Object.keys(card.state ?? {}).length === 0 ? null : card.state);
// Checks for wrong SetRightPanelPhase requests
if (!this.isPhaseActionIsValid(targetPhase)) return;
if (!this.isPhaseActionValid(targetPhase)) return;
let roomCache = this.byRoom[rId];
if (!!roomCache) {
@ -236,10 +229,6 @@ export default class RightPanelStore extends ReadyWatchingStore {
}
}
private compareCards(a: IRightPanelCard, b: IRightPanelCard): boolean {
return JSON.stringify(convertCardToStore(a)) == JSON.stringify(convertCardToStore(b));
}
private emitAndUpdateSettings() {
const storePanelGlobal = convertToStorePanel(this.global);
SettingsStore.setValue("RightPanel.phasesGlobal", null, SettingLevel.DEVICE, storePanelGlobal);
@ -257,10 +246,8 @@ export default class RightPanelStore extends ReadyWatchingStore {
}
private setRightPanelCache(card: IRightPanelCard, roomId?: string) {
this.byRoom[roomId ?? this.viewedRoomId] = {
history: [{ phase: card.phase, state: card.state ?? {} }],
isOpen: true,
};
const history = [{ phase: card.phase, state: card.state ?? {} }];
this.byRoom[roomId ?? this.viewedRoomId] = { history, isOpen: true };
this.emitAndUpdateSettings();
}
@ -282,7 +269,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
return null;
}
private isPhaseActionIsValid(targetPhase) {
private isPhaseActionValid(targetPhase) {
if (!RightPanelPhases[targetPhase]) {
logger.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
return false;
@ -305,6 +292,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
private onVerificationRequestUpdate = () => {
const { member } = this.currentCard.state;
if (!member) return;
const pendingRequest = pendingVerificationRequestForUser(member);
if (pendingRequest) {
this.currentCard.state.verificationRequest = pendingRequest;
@ -312,13 +300,13 @@ export default class RightPanelStore extends ReadyWatchingStore {
}
};
onRoomViewStoreUpdate() {
onRoomViewStoreUpdate = () => {
// TODO: use this function instead of the onDispatch (the whole onDispatch can get removed!) as soon groups are removed
// this.viewedRoomId = RoomViewStore.getRoomId();
// this.isViewingRoom = true; // Is viewing room will of course be removed when removing groups
// // load values from byRoomCache with the viewedRoomId.
// this.loadCacheFromSettings();
}
};
onDispatch(payload: ActionPayload) {
switch (payload.action) {
@ -347,9 +335,9 @@ export default class RightPanelStore extends ReadyWatchingStore {
_this.viewedRoomId = payload.room_id;
_this.isViewingRoom = payload.action == Action.ViewRoom;
// load values from byRoomCache with the viewedRoomId.
if (!!_this.roomStoreToken) {
// skip loading here since we need the client to be ready to get the events form the ids of the settings
// this loading will be done in the onReady function
if (_this.isReady) {
// we need the client to be ready to get the events form the ids of the settings
// the loading will be done in the onReady function (to catch up with the changes done here before it was ready)
// all the logic in this case is not necessary anymore as soon as groups are dropped and we use: onRoomViewStoreUpdate
_this.loadCacheFromSettings();
_this.emitAndUpdateSettings();

View file

@ -89,48 +89,43 @@ export function convertToStatePanel(storeRoom: IRightPanelForRoomStored, room: R
}
export function convertCardToStore(panelState: IRightPanelCard): IRightPanelCardStored {
const panelStateThisRoomStored = { ...panelState.state } as any;
if (!!panelState?.state?.threadHeadEvent?.getId()) {
panelStateThisRoomStored.threadHeadEventId = panelState.state.threadHeadEvent.getId();
}
if (!!panelState?.state?.memberInfoEvent?.getId()) {
panelStateThisRoomStored.memberInfoEventId = panelState.state.memberInfoEvent.getId();
}
if (!!panelState?.state?.initialEvent?.getId()) {
panelStateThisRoomStored.initialEventId = panelState.state.initialEvent.getId();
}
if (!!panelState?.state?.member?.userId) {
panelStateThisRoomStored.memberId = panelState.state.member.userId;
}
delete panelStateThisRoomStored.threadHeadEvent;
delete panelStateThisRoomStored.initialEvent;
delete panelStateThisRoomStored.memberInfoEvent;
delete panelStateThisRoomStored.verificationRequest;
delete panelStateThisRoomStored.verificationRequestPromise;
delete panelStateThisRoomStored.member;
const state = panelState.state ?? {};
const stateStored: IRightPanelCardStateStored = {
groupId: state.groupId,
groupRoomId: state.groupRoomId,
widgetId: state.widgetId,
spaceId: state.spaceId,
isInitialEventHighlighted: state.isInitialEventHighlighted,
threadHeadEventId: !!state?.threadHeadEvent?.getId() ?
panelState.state.threadHeadEvent.getId() : undefined,
memberInfoEventId: !!state?.memberInfoEvent?.getId() ?
panelState.state.memberInfoEvent.getId() : undefined,
initialEventId: !!state?.initialEvent?.getId() ?
panelState.state.initialEvent.getId() : undefined,
memberId: !!state?.member?.userId ?
panelState.state.member.userId : undefined,
};
const storedCard = { state: panelStateThisRoomStored as IRightPanelCardStored, phase: panelState.phase };
return storedCard as IRightPanelCardStored;
return { state: stateStored, phase: panelState.phase };
}
function convertStoreToCard(panelStateStore: IRightPanelCardStored, room: Room): IRightPanelCard {
const panelStateThisRoom = { ...panelStateStore?.state } as any;
if (!!panelStateThisRoom.threadHeadEventId) {
panelStateThisRoom.threadHeadEvent = room.findEventById(panelStateThisRoom.threadHeadEventId);
}
if (!!panelStateThisRoom.memberInfoEventId) {
panelStateThisRoom.memberInfoEvent = room.findEventById(panelStateThisRoom.memberInfoEventId);
}
if (!!panelStateThisRoom.initialEventId) {
panelStateThisRoom.initialEvent = room.findEventById(panelStateThisRoom.initialEventId);
}
if (!!panelStateThisRoom.memberId) {
panelStateThisRoom.member = room.getMember(panelStateThisRoom.memberId);
}
delete panelStateThisRoom.threadHeadEventId;
delete panelStateThisRoom.initialEventId;
delete panelStateThisRoom.memberInfoEventId;
delete panelStateThisRoom.memberId;
const stateStored = panelStateStore.state ?? {};
const state: IRightPanelCardState = {
groupId: stateStored.groupId,
groupRoomId: stateStored.groupRoomId,
widgetId: stateStored.widgetId,
spaceId: stateStored.spaceId,
isInitialEventHighlighted: stateStored.isInitialEventHighlighted,
threadHeadEvent: !!stateStored?.threadHeadEventId ?
room.findEventById(stateStored.threadHeadEventId) : undefined,
memberInfoEvent: !!stateStored?.memberInfoEventId ?
room.findEventById(stateStored.memberInfoEventId) : undefined,
initialEvent: !!stateStored?.initialEventId ?
room.findEventById(stateStored.initialEventId) : undefined,
member: !!stateStored?.memberId ?
room.getMember(stateStored.memberId) : undefined,
};
return { state: panelStateThisRoom as IRightPanelCardState, phase: panelStateStore.phase } as IRightPanelCard;
return { state: state, phase: panelStateStore.phase };
}

View file

@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { _t } from "../../languageHandler";
// These are in their own file because of circular imports being a problem.
export enum RightPanelPhases {
// Room stuff
@ -44,6 +46,17 @@ export enum RightPanelPhases {
ThreadPanel = "ThreadPanel",
}
export function backLabelForPhase(phase: RightPanelPhases) {
switch (phase) {
case RightPanelPhases.ThreadPanel: return _t("Threads");
case RightPanelPhases.Timeline: return _t("Back to chat");
case RightPanelPhases.RoomSummary: return _t("Room information");
case RightPanelPhases.RoomMemberList: return _t("Room members");
case RightPanelPhases.ThreadView: return _t("Back to thread");
}
return null;
}
// These are the phases that are safe to persist (the ones that don't require additional
// arguments).
export const RIGHT_PANEL_PHASES_NO_ARGS = [