Make widgets not reload (persistent) between center and top container (#7575)

This commit is contained in:
Timo 2022-01-24 10:24:30 -05:00 committed by GitHub
parent e28d2a2299
commit 9d9b77d5e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 81 deletions

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventSubscription } from 'fbemitter';
import { logger } from "matrix-js-sdk/src/logger";
import defaultDispatcher from '../../dispatcher/dispatcher';
@ -21,7 +22,6 @@ import { pendingVerificationRequestForUser } from '../../verification';
import SettingsStore from "../../settings/SettingsStore";
import { RightPanelPhases } from "./RightPanelStorePhases";
import { ActionPayload } from "../../dispatcher/payloads";
import { Action } from '../../dispatcher/actions';
import { SettingLevel } from "../../settings/SettingLevel";
import { UPDATE_EVENT } from '../AsyncStore';
import { ReadyWatchingStore } from '../ReadyWatchingStore';
@ -32,7 +32,7 @@ import {
IRightPanelForRoom,
} from './RightPanelStoreIPanelState';
import { MatrixClientPeg } from "../../MatrixClientPeg";
// import RoomViewStore from '../RoomViewStore';
import RoomViewStore from '../RoomViewStore';
const GROUP_PHASES = [
RightPanelPhases.GroupMemberList,
@ -41,12 +41,6 @@ const GROUP_PHASES = [
RightPanelPhases.GroupMemberInfo,
];
const MEMBER_INFO_PHASES = [
RightPanelPhases.RoomMemberInfo,
RightPanelPhases.Room3pidMemberInfo,
RightPanelPhases.EncryptionPanel,
];
/**
* A class for tracking the state of the right panel between layouts and
* sessions. This state includes a history for each room. Each history element
@ -68,6 +62,8 @@ export default class RightPanelStore extends ReadyWatchingStore {
[roomId: string]: IRightPanelForRoom;
} = {};
private roomStoreToken: EventSubscription;
private constructor() {
super(defaultDispatcher);
this.dispatcherRefRightPanelStore = defaultDispatcher.register(this.onDispatch);
@ -75,9 +71,9 @@ 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);
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequestUpdate);
this.viewedRoomId = RoomViewStore.getRoomId();
this.loadCacheFromSettings();
this.emitAndUpdateSettings();
}
@ -91,8 +87,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
protected async onNotReady(): Promise<any> {
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);
this.roomStoreToken.remove();
}
// Getters
@ -373,45 +368,43 @@ export default class RightPanelStore extends ReadyWatchingStore {
};
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();
// TODO: only 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();
this.emitAndUpdateSettings();
};
onDispatch = (payload: ActionPayload) => {
switch (payload.action) {
case 'view_group':
case Action.ViewRoom: {
case 'view_group': {
if (payload.room_id === this.viewedRoomId) break; // skip this transition, probably a permalink
// Put group in the same/similar view to what was open from the previously viewed room
// Is contradictory to the new "per room" philosophy but it is the legacy behavior for groups.
if ((this.isViewingRoom ? Action.ViewRoom : "view_group") != payload.action) {
if (payload.action == Action.ViewRoom && MEMBER_INFO_PHASES.includes(this.currentCard?.phase)) {
// switch from group to room
this.setRightPanelCache({ phase: RightPanelPhases.RoomMemberList, state: {} });
} else if (
payload.action == "view_group" &&
this.currentCard?.phase === RightPanelPhases.GroupMemberInfo
) {
// switch from room to group
this.setRightPanelCache({ phase: RightPanelPhases.GroupMemberList, state: {} });
}
if (
this.currentCard?.phase === RightPanelPhases.GroupMemberInfo
) {
// switch from room to group
this.setRightPanelCache({ phase: RightPanelPhases.GroupMemberList, state: {} });
}
// Update the current room here, so that all the other functions dont need to be room dependant.
// The right panel store always will return the state for the current room.
this.viewedRoomId = payload.room_id;
this.isViewingRoom = payload.action == Action.ViewRoom;
this.isViewingRoom = false;
// load values from byRoomCache with the viewedRoomId.
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();
// DO NOT EMIT. Emitting breaks iframe refs by triggering a render
// for the room view and calling the iframe ref changed function
// this.emitAndUpdateSettings();
}
break;
}

View file

@ -255,8 +255,11 @@ export class StopGapWidget extends EventEmitter {
});
}
};
public start(iframe: HTMLIFrameElement) {
/**
* This starts the messaging for the widget if it is not in the state `started` yet.
* @param iframe the iframe the widget should use
*/
public startMessaging(iframe: HTMLIFrameElement): any {
if (this.started) return;
const allowedCapabilities = this.appTileProps.whitelistCapabilities || [];
const driver = new StopGapWidgetDriver(allowedCapabilities, this.mockWidget, this.kind, this.roomId);
@ -407,7 +410,12 @@ export class StopGapWidget extends EventEmitter {
}
}
public stop(opts = { forceDestroy: false }) {
/**
* Stops the widget messaging for if it is started. Skips stopping if it is an active
* widget.
* @param opts
*/
public stopMessaging(opts = { forceDestroy: false }) {
if (!opts?.forceDestroy && ActiveWidgetStore.instance.getPersistentWidgetId() === this.mockWidget.id) {
logger.log("Skipping destroy - persistent widget");
return;

View file

@ -27,6 +27,8 @@ import { SettingLevel } from "../../settings/SettingLevel";
import { arrayFastClone } from "../../utils/arrays";
import { UPDATE_EVENT } from "../AsyncStore";
import { compare } from "../../utils/strings";
import RightPanelStore from "../right-panel/RightPanelStore";
import { RightPanelPhases } from "../right-panel/RightPanelStorePhases";
export const WIDGET_LAYOUT_EVENT_TYPE = "io.element.widgets.layout";
@ -351,6 +353,22 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
return this.getContainerWidgets(room, container).some(w => w.id === widget.id);
}
public isVisibleOnScreen(room: Room, widgetId: string) {
const wId = widgetId;
const inRightPanel =
(RightPanelStore.instance.currentCard.phase == RightPanelPhases.Widget &&
wId == RightPanelStore.instance.currentCard.state?.widgetId);
const inCenterContainer =
this.getContainerWidgets(room, Container.Center).some((app) => app.id == wId);
const inTopContainer =
this.getContainerWidgets(room, Container.Top).some(app => app.id == wId);
// The widget should only be shown as a persistent app (in a floating pip container) if it is not visible on screen
// either, because we are viewing a different room OR because it is in none of the possible containers of the room view.
const isVisibleOnScreen = (inRightPanel || inCenterContainer || inTopContainer);
return isVisibleOnScreen;
}
public canAddToContainer(room: Room, container: Container): boolean {
switch (container) {
case Container.Top: return this.getContainerWidgets(room, container).length < MAX_PINNED;
@ -440,7 +458,8 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
public moveToContainer(room: Room, widget: IApp, toContainer: Container) {
const allWidgets = this.getAllWidgets(room);
if (!allWidgets.some(([w]) => w.id === widget.id)) return; // invalid
// Prepare other containers (potentially move widgets to obay the following rules)
// Prepare other containers (potentially move widgets to obey the following rules)
const newLayout = {};
switch (toContainer) {
case Container.Right:
// new "right" widget
@ -448,24 +467,25 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
case Container.Center:
// new "center" widget => all other widgets go into "right"
for (const w of this.getContainerWidgets(room, Container.Top)) {
this.moveToContainer(room, w, Container.Right);
newLayout[w.id] = { container: Container.Right };
}
for (const w of this.getContainerWidgets(room, Container.Center)) {
this.moveToContainer(room, w, Container.Right);
newLayout[w.id] = { container: Container.Right };
}
break;
case Container.Top:
// new "top" widget => the center widget moves into "right"
if (this.hasMaximisedWidget(room)) {
this.moveToContainer(room, this.getContainerWidgets(room, Container.Center)[0], Container.Right);
const centerWidget = this.getContainerWidgets(room, Container.Center)[0];
newLayout[centerWidget.id] = { container: Container.Right };
}
break;
}
// move widgets into requested container.
this.updateUserLayout(room, {
[widget.id]: { container: toContainer },
});
newLayout[widget.id] = { container: toContainer };
// move widgets into requested containers.
this.updateUserLayout(room, newLayout);
}
public hasMaximisedWidget(room: Room) {