Make widgets not reload (persistent) between center and top container (#7575)
This commit is contained in:
parent
e28d2a2299
commit
9d9b77d5e1
6 changed files with 108 additions and 81 deletions
|
@ -23,6 +23,7 @@ import classNames from 'classnames';
|
||||||
import { MatrixCapabilities } from "matrix-widget-api";
|
import { MatrixCapabilities } from "matrix-widget-api";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { EventSubscription } from 'fbemitter';
|
||||||
|
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
import AccessibleButton from './AccessibleButton';
|
import AccessibleButton from './AccessibleButton';
|
||||||
|
@ -46,6 +47,7 @@ import { IApp } from "../../../stores/WidgetStore";
|
||||||
import { WidgetLayoutStore, Container } from "../../../stores/widgets/WidgetLayoutStore";
|
import { WidgetLayoutStore, Container } from "../../../stores/widgets/WidgetLayoutStore";
|
||||||
import { OwnProfileStore } from '../../../stores/OwnProfileStore';
|
import { OwnProfileStore } from '../../../stores/OwnProfileStore';
|
||||||
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
|
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
|
||||||
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -117,6 +119,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
private persistKey: string;
|
private persistKey: string;
|
||||||
private sgWidget: StopGapWidget;
|
private sgWidget: StopGapWidget;
|
||||||
private dispatcherRef: string;
|
private dispatcherRef: string;
|
||||||
|
private roomStoreToken: EventSubscription;
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -135,9 +138,6 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = this.getNewState(props);
|
this.state = this.getNewState(props);
|
||||||
this.watchUserReady();
|
|
||||||
|
|
||||||
this.allowedWidgetsWatchRef = SettingsStore.watchSetting("allowedWidgets", null, this.onAllowedWidgetsChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private watchUserReady = () => {
|
private watchUserReady = () => {
|
||||||
|
@ -163,6 +163,29 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
return !!currentlyAllowedWidgets[props.app.eventId];
|
return !!currentlyAllowedWidgets[props.app.eventId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onWidgetLayoutChange = () => {
|
||||||
|
const room = MatrixClientPeg.get().getRoom(this.props.room.roomId);
|
||||||
|
const app = this.props.app;
|
||||||
|
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(app.id);
|
||||||
|
const isVisibleOnScreen = WidgetLayoutStore.instance.isVisibleOnScreen(room, app.id);
|
||||||
|
if (!isVisibleOnScreen && !isActiveWidget) {
|
||||||
|
ActiveWidgetStore.instance.destroyPersistentWidget(app.id);
|
||||||
|
PersistedElement.destroyElement(this.persistKey);
|
||||||
|
this.sgWidget?.stopMessaging();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private onRoomViewStoreUpdate = () => {
|
||||||
|
if (this.props.room.roomId == RoomViewStore.getRoomId()) return;
|
||||||
|
const app = this.props.app;
|
||||||
|
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(app.id);
|
||||||
|
if (!isActiveWidget) {
|
||||||
|
ActiveWidgetStore.instance.destroyPersistentWidget(app.id);
|
||||||
|
PersistedElement.destroyElement(this.persistKey);
|
||||||
|
this.sgWidget?.stopMessaging();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set initial component state when the App wUrl (widget URL) is being updated.
|
* Set initial component state when the App wUrl (widget URL) is being updated.
|
||||||
* Component props *must* be passed (rather than relying on this.props).
|
* Component props *must* be passed (rather than relying on this.props).
|
||||||
|
@ -195,7 +218,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
// Force the widget to be non-persistent (able to be deleted/forgotten)
|
// Force the widget to be non-persistent (able to be deleted/forgotten)
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id);
|
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id);
|
||||||
PersistedElement.destroyElement(this.persistKey);
|
PersistedElement.destroyElement(this.persistKey);
|
||||||
if (this.sgWidget) this.sgWidget.stop();
|
this.sgWidget?.stopMessaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ hasPermissionToLoad });
|
this.setState({ hasPermissionToLoad });
|
||||||
|
@ -218,7 +241,11 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
if (this.sgWidget && this.state.hasPermissionToLoad) {
|
if (this.sgWidget && this.state.hasPermissionToLoad) {
|
||||||
this.startWidget();
|
this.startWidget();
|
||||||
}
|
}
|
||||||
|
this.watchUserReady();
|
||||||
|
|
||||||
|
WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.onWidgetLayoutChange);
|
||||||
|
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||||
|
this.allowedWidgetsWatchRef = SettingsStore.watchSetting("allowedWidgets", null, this.onAllowedWidgetsChange);
|
||||||
// Widget action listeners
|
// Widget action listeners
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
@ -227,24 +254,14 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
// Widget action listeners
|
// Widget action listeners
|
||||||
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
||||||
|
|
||||||
// if it's not remaining on screen, get rid of the PersistedElement container
|
WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.onWidgetLayoutChange);
|
||||||
if (!ActiveWidgetStore.instance.getWidgetPersistence(this.props.app.id)) {
|
this.roomStoreToken?.remove();
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id);
|
|
||||||
PersistedElement.destroyElement(this.persistKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sgWidget) {
|
|
||||||
this.sgWidget.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsStore.unwatchSetting(this.allowedWidgetsWatchRef);
|
SettingsStore.unwatchSetting(this.allowedWidgetsWatchRef);
|
||||||
OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady);
|
OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetWidget(newProps: IProps): void {
|
private resetWidget(newProps: IProps): void {
|
||||||
if (this.sgWidget) {
|
this.sgWidget?.stopMessaging();
|
||||||
this.sgWidget.stop();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
this.sgWidget = new StopGapWidget(newProps);
|
this.sgWidget = new StopGapWidget(newProps);
|
||||||
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
||||||
|
@ -266,9 +283,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
this.iframe = ref;
|
this.iframe = ref;
|
||||||
if (ref) {
|
if (ref) {
|
||||||
try {
|
try {
|
||||||
if (this.sgWidget) {
|
this.sgWidget?.startMessaging(ref);
|
||||||
this.sgWidget.start(ref);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("Failed to start widget", e);
|
logger.error("Failed to start widget", e);
|
||||||
}
|
}
|
||||||
|
@ -321,7 +336,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
PersistedElement.destroyElement(this.persistKey);
|
PersistedElement.destroyElement(this.persistKey);
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id);
|
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id);
|
||||||
|
|
||||||
if (this.sgWidget) this.sgWidget.stop({ forceDestroy: true });
|
this.sgWidget?.stopMessaging({ forceDestroy: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWidgetPreparing = (): void => {
|
private onWidgetPreparing = (): void => {
|
||||||
|
@ -532,7 +547,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
// Also wrap the PersistedElement in a div to fix the height, otherwise
|
// Also wrap the PersistedElement in a div to fix the height, otherwise
|
||||||
// AppTile's border is in the wrong place
|
// AppTile's border is in the wrong place
|
||||||
|
|
||||||
// For persistent apps in PiP we want the zIndex to be higher then for other persistent apps (100)
|
// For persisted apps in PiP we want the zIndex to be higher then for other persisted apps (100)
|
||||||
// otherwise there are issues that the PiP view is drawn UNDER another widget (Persistent app) when dragged around.
|
// otherwise there are issues that the PiP view is drawn UNDER another widget (Persistent app) when dragged around.
|
||||||
const zIndexAboveOtherPersistentElements = 101;
|
const zIndexAboveOtherPersistentElements = 101;
|
||||||
|
|
||||||
|
@ -569,6 +584,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let maxMinButton;
|
let maxMinButton;
|
||||||
if (!this.props.hideMaximiseButton) {
|
if (!this.props.hideMaximiseButton) {
|
||||||
const widgetIsMaximised = WidgetLayoutStore.instance.
|
const widgetIsMaximised = WidgetLayoutStore.instance.
|
||||||
|
|
|
@ -189,7 +189,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||||
private onAction = (action: ActionPayload): void => {
|
private onAction = (action: ActionPayload): void => {
|
||||||
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
|
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case 'appsDrawer':
|
case "appsDrawer":
|
||||||
// Note: these booleans are awkward because localstorage is fundamentally
|
// Note: these booleans are awkward because localstorage is fundamentally
|
||||||
// string-based. We also do exact equality on the strings later on.
|
// string-based. We also do exact equality on the strings later on.
|
||||||
if (action.show) {
|
if (action.show) {
|
||||||
|
@ -279,7 +279,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
|
||||||
drawer = <PersistentVResizer
|
drawer = <PersistentVResizer
|
||||||
room={this.props.room}
|
room={this.props.room}
|
||||||
minHeight={100}
|
minHeight={100}
|
||||||
maxHeight={(this.props.maxHeight || !widgetIsMaxmised) ? this.props.maxHeight - 50 : undefined}
|
maxHeight={this.props.maxHeight - 50}
|
||||||
handleClass="mx_AppsContainer_resizerHandle"
|
handleClass="mx_AppsContainer_resizerHandle"
|
||||||
handleWrapperClass="mx_AppsContainer_resizerHandleContainer"
|
handleWrapperClass="mx_AppsContainer_resizerHandleContainer"
|
||||||
className="mx_AppsContainer_resizer"
|
className="mx_AppsContainer_resizer"
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import PictureInPictureDragger from './PictureInPictureDragger';
|
import PictureInPictureDragger from './PictureInPictureDragger';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { Container, WidgetLayoutStore } from '../../../stores/widgets/WidgetLayoutStore';
|
import { WidgetLayoutStore } from '../../../stores/widgets/WidgetLayoutStore';
|
||||||
import CallViewHeader from './CallView/CallViewHeader';
|
import CallViewHeader from './CallView/CallViewHeader';
|
||||||
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from '../../../stores/ActiveWidgetStore';
|
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from '../../../stores/ActiveWidgetStore';
|
||||||
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
|
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
|
||||||
|
@ -242,9 +242,7 @@ export default class PipView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
let userIsPartOfTheRoom = false;
|
let userIsPartOfTheRoom = false;
|
||||||
let fromAnotherRoom = false;
|
let fromAnotherRoom = false;
|
||||||
let notInRightPanel = false;
|
let notVisible = false;
|
||||||
let notInCenterContainer = false;
|
|
||||||
let notInTopContainer = false;
|
|
||||||
if (wId) {
|
if (wId) {
|
||||||
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(wId);
|
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(wId);
|
||||||
const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId);
|
const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId);
|
||||||
|
@ -254,24 +252,16 @@ export default class PipView extends React.Component<IProps, IState> {
|
||||||
if (!persistentWidgetInRoom) return null;
|
if (!persistentWidgetInRoom) return null;
|
||||||
|
|
||||||
const wls = WidgetLayoutStore.instance;
|
const wls = WidgetLayoutStore.instance;
|
||||||
|
notVisible = !wls.isVisibleOnScreen(persistentWidgetInRoom, wId);
|
||||||
userIsPartOfTheRoom = persistentWidgetInRoom.getMyMembership() == "join";
|
userIsPartOfTheRoom = persistentWidgetInRoom.getMyMembership() == "join";
|
||||||
fromAnotherRoom = this.state.viewedRoomId !== persistentWidgetInRoomId;
|
fromAnotherRoom = this.state.viewedRoomId !== persistentWidgetInRoomId;
|
||||||
|
|
||||||
notInRightPanel =
|
|
||||||
!(RightPanelStore.instance.currentCard.phase == RightPanelPhases.Widget &&
|
|
||||||
wId == RightPanelStore.instance.currentCard.state?.widgetId);
|
|
||||||
notInCenterContainer =
|
|
||||||
!wls.getContainerWidgets(persistentWidgetInRoom, Container.Center).some((app) => app.id == wId);
|
|
||||||
notInTopContainer =
|
|
||||||
!wls.getContainerWidgets(persistentWidgetInRoom, 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
|
// 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.
|
// either, because we are viewing a different room OR because it is in none of the possible containers of the room view.
|
||||||
const showWidgetInPip =
|
const showWidgetInPip =
|
||||||
(fromAnotherRoom && userIsPartOfTheRoom) ||
|
(fromAnotherRoom && userIsPartOfTheRoom) ||
|
||||||
(notInRightPanel && notInCenterContainer && notInTopContainer && userIsPartOfTheRoom);
|
(notVisible && userIsPartOfTheRoom);
|
||||||
|
|
||||||
this.setState({ showWidgetInPip });
|
this.setState({ showWidgetInPip });
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { EventSubscription } from 'fbemitter';
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import defaultDispatcher from '../../dispatcher/dispatcher';
|
import defaultDispatcher from '../../dispatcher/dispatcher';
|
||||||
|
@ -21,7 +22,6 @@ import { pendingVerificationRequestForUser } from '../../verification';
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { RightPanelPhases } from "./RightPanelStorePhases";
|
import { RightPanelPhases } from "./RightPanelStorePhases";
|
||||||
import { ActionPayload } from "../../dispatcher/payloads";
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import { Action } from '../../dispatcher/actions';
|
|
||||||
import { SettingLevel } from "../../settings/SettingLevel";
|
import { SettingLevel } from "../../settings/SettingLevel";
|
||||||
import { UPDATE_EVENT } from '../AsyncStore';
|
import { UPDATE_EVENT } from '../AsyncStore';
|
||||||
import { ReadyWatchingStore } from '../ReadyWatchingStore';
|
import { ReadyWatchingStore } from '../ReadyWatchingStore';
|
||||||
|
@ -32,7 +32,7 @@ import {
|
||||||
IRightPanelForRoom,
|
IRightPanelForRoom,
|
||||||
} from './RightPanelStoreIPanelState';
|
} from './RightPanelStoreIPanelState';
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
// import RoomViewStore from '../RoomViewStore';
|
import RoomViewStore from '../RoomViewStore';
|
||||||
|
|
||||||
const GROUP_PHASES = [
|
const GROUP_PHASES = [
|
||||||
RightPanelPhases.GroupMemberList,
|
RightPanelPhases.GroupMemberList,
|
||||||
|
@ -41,12 +41,6 @@ const GROUP_PHASES = [
|
||||||
RightPanelPhases.GroupMemberInfo,
|
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
|
* 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
|
* 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;
|
[roomId: string]: IRightPanelForRoom;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
|
private roomStoreToken: EventSubscription;
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
super(defaultDispatcher);
|
super(defaultDispatcher);
|
||||||
this.dispatcherRefRightPanelStore = defaultDispatcher.register(this.onDispatch);
|
this.dispatcherRefRightPanelStore = defaultDispatcher.register(this.onDispatch);
|
||||||
|
@ -75,9 +71,9 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
|
|
||||||
protected async onReady(): Promise<any> {
|
protected async onReady(): Promise<any> {
|
||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
// TODO RightPanelStore (will be addressed when dropping groups): This should be used instead of the onDispatch callback when groups are removed.
|
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||||
// RoomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
|
||||||
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequestUpdate);
|
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequestUpdate);
|
||||||
|
this.viewedRoomId = RoomViewStore.getRoomId();
|
||||||
this.loadCacheFromSettings();
|
this.loadCacheFromSettings();
|
||||||
this.emitAndUpdateSettings();
|
this.emitAndUpdateSettings();
|
||||||
}
|
}
|
||||||
|
@ -91,8 +87,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
protected async onNotReady(): Promise<any> {
|
protected async onNotReady(): Promise<any> {
|
||||||
this.isReady = false;
|
this.isReady = false;
|
||||||
MatrixClientPeg.get().off("crypto.verification.request", this.onVerificationRequestUpdate);
|
MatrixClientPeg.get().off("crypto.verification.request", this.onVerificationRequestUpdate);
|
||||||
// TODO RightPanelStore (will be addressed when dropping groups): User this instead of the dispatcher.
|
this.roomStoreToken.remove();
|
||||||
// RoomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
@ -373,45 +368,43 @@ 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
|
// 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.viewedRoomId = RoomViewStore.getRoomId();
|
||||||
// this.isViewingRoom = true; // Is viewing room will of course be removed when removing groups
|
this.isViewingRoom = true; // Is viewing room will of course be removed when removing groups
|
||||||
// // load values from byRoomCache with the viewedRoomId.
|
// load values from byRoomCache with the viewedRoomId.
|
||||||
// this.loadCacheFromSettings();
|
this.loadCacheFromSettings();
|
||||||
|
this.emitAndUpdateSettings();
|
||||||
};
|
};
|
||||||
|
|
||||||
onDispatch = (payload: ActionPayload) => {
|
onDispatch = (payload: ActionPayload) => {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'view_group':
|
case 'view_group': {
|
||||||
case Action.ViewRoom: {
|
|
||||||
if (payload.room_id === this.viewedRoomId) break; // skip this transition, probably a permalink
|
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
|
// 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.
|
// 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)) {
|
if (
|
||||||
// switch from group to room
|
this.currentCard?.phase === RightPanelPhases.GroupMemberInfo
|
||||||
this.setRightPanelCache({ phase: RightPanelPhases.RoomMemberList, state: {} });
|
) {
|
||||||
} else if (
|
// switch from room to group
|
||||||
payload.action == "view_group" &&
|
this.setRightPanelCache({ phase: RightPanelPhases.GroupMemberList, state: {} });
|
||||||
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.
|
// 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.
|
// The right panel store always will return the state for the current room.
|
||||||
this.viewedRoomId = payload.room_id;
|
this.viewedRoomId = payload.room_id;
|
||||||
this.isViewingRoom = payload.action == Action.ViewRoom;
|
this.isViewingRoom = false;
|
||||||
// load values from byRoomCache with the viewedRoomId.
|
// load values from byRoomCache with the viewedRoomId.
|
||||||
if (this.isReady) {
|
if (this.isReady) {
|
||||||
// we need the client to be ready to get the events form the ids of the settings
|
// 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)
|
// 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
|
// all the logic in this case is not necessary anymore as soon as groups are dropped and we use: onRoomViewStoreUpdate
|
||||||
this.loadCacheFromSettings();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
if (this.started) return;
|
||||||
const allowedCapabilities = this.appTileProps.whitelistCapabilities || [];
|
const allowedCapabilities = this.appTileProps.whitelistCapabilities || [];
|
||||||
const driver = new StopGapWidgetDriver(allowedCapabilities, this.mockWidget, this.kind, this.roomId);
|
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) {
|
if (!opts?.forceDestroy && ActiveWidgetStore.instance.getPersistentWidgetId() === this.mockWidget.id) {
|
||||||
logger.log("Skipping destroy - persistent widget");
|
logger.log("Skipping destroy - persistent widget");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,6 +27,8 @@ import { SettingLevel } from "../../settings/SettingLevel";
|
||||||
import { arrayFastClone } from "../../utils/arrays";
|
import { arrayFastClone } from "../../utils/arrays";
|
||||||
import { UPDATE_EVENT } from "../AsyncStore";
|
import { UPDATE_EVENT } from "../AsyncStore";
|
||||||
import { compare } from "../../utils/strings";
|
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";
|
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);
|
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 {
|
public canAddToContainer(room: Room, container: Container): boolean {
|
||||||
switch (container) {
|
switch (container) {
|
||||||
case Container.Top: return this.getContainerWidgets(room, container).length < MAX_PINNED;
|
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) {
|
public moveToContainer(room: Room, widget: IApp, toContainer: Container) {
|
||||||
const allWidgets = this.getAllWidgets(room);
|
const allWidgets = this.getAllWidgets(room);
|
||||||
if (!allWidgets.some(([w]) => w.id === widget.id)) return; // invalid
|
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) {
|
switch (toContainer) {
|
||||||
case Container.Right:
|
case Container.Right:
|
||||||
// new "right" widget
|
// new "right" widget
|
||||||
|
@ -448,24 +467,25 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
case Container.Center:
|
case Container.Center:
|
||||||
// new "center" widget => all other widgets go into "right"
|
// new "center" widget => all other widgets go into "right"
|
||||||
for (const w of this.getContainerWidgets(room, Container.Top)) {
|
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)) {
|
for (const w of this.getContainerWidgets(room, Container.Center)) {
|
||||||
this.moveToContainer(room, w, Container.Right);
|
newLayout[w.id] = { container: Container.Right };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Container.Top:
|
case Container.Top:
|
||||||
// new "top" widget => the center widget moves into "right"
|
// new "top" widget => the center widget moves into "right"
|
||||||
if (this.hasMaximisedWidget(room)) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// move widgets into requested container.
|
newLayout[widget.id] = { container: toContainer };
|
||||||
this.updateUserLayout(room, {
|
|
||||||
[widget.id]: { container: toContainer },
|
// move widgets into requested containers.
|
||||||
});
|
this.updateUserLayout(room, newLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasMaximisedWidget(room: Room) {
|
public hasMaximisedWidget(room: Room) {
|
||||||
|
|
Loading…
Reference in a new issue