Properly handle persistent widgets when room is left (#7724)
This commit is contained in:
parent
d72469663d
commit
ec92102fe3
4 changed files with 130 additions and 103 deletions
|
@ -18,14 +18,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import React, { createRef } from 'react';
|
import React, { ContextType, createRef } from 'react';
|
||||||
import classNames from 'classnames';
|
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 { EventSubscription } from 'fbemitter';
|
||||||
|
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
|
||||||
import AccessibleButton from './AccessibleButton';
|
import AccessibleButton from './AccessibleButton';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import AppPermission from './AppPermission';
|
import AppPermission from './AppPermission';
|
||||||
|
@ -49,12 +48,14 @@ import { OwnProfileStore } from '../../../stores/OwnProfileStore';
|
||||||
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
|
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
|
||||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
app: IApp;
|
app: IApp;
|
||||||
// If room is not specified then it is an account level widget
|
// If room is not specified then it is an account level widget
|
||||||
// which bypasses permission prompts as it was added explicitly by that user
|
// which bypasses permission prompts as it was added explicitly by that user
|
||||||
room: Room;
|
room?: Room;
|
||||||
threadId?: string | null;
|
threadId?: string | null;
|
||||||
// Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer.
|
// Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer.
|
||||||
// This should be set to true when there is only one widget in the app drawer, otherwise it should be false.
|
// This should be set to true when there is only one widget in the app drawer, otherwise it should be false.
|
||||||
|
@ -102,6 +103,9 @@ interface IState {
|
||||||
|
|
||||||
@replaceableComponent("views.elements.AppTile")
|
@replaceableComponent("views.elements.AppTile")
|
||||||
export default class AppTile extends React.Component<IProps, IState> {
|
export default class AppTile extends React.Component<IProps, IState> {
|
||||||
|
public static contextType = MatrixClientContext;
|
||||||
|
context: ContextType<typeof MatrixClientContext>;
|
||||||
|
|
||||||
public static defaultProps: Partial<IProps> = {
|
public static defaultProps: Partial<IProps> = {
|
||||||
waitForIframeLoad: true,
|
waitForIframeLoad: true,
|
||||||
showMenubar: true,
|
showMenubar: true,
|
||||||
|
@ -128,10 +132,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
this.persistKey = getPersistKey(this.props.app.id);
|
this.persistKey = getPersistKey(this.props.app.id);
|
||||||
try {
|
try {
|
||||||
this.sgWidget = new StopGapWidget(this.props);
|
this.sgWidget = new StopGapWidget(this.props);
|
||||||
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
this.setupSgListeners();
|
||||||
this.sgWidget.on("ready", this.onWidgetReady);
|
|
||||||
// emits when the capabilites have been setup or changed
|
|
||||||
this.sgWidget.on("capabilitiesNotified", this.onWidgetCapabilitiesNotified);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.log("Failed to construct widget", e);
|
logger.log("Failed to construct widget", e);
|
||||||
this.sgWidget = null;
|
this.sgWidget = null;
|
||||||
|
@ -164,26 +165,42 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onWidgetLayoutChange = () => {
|
private onWidgetLayoutChange = () => {
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.room.roomId);
|
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(this.props.app.id);
|
||||||
const app = this.props.app;
|
const isVisibleOnScreen = WidgetLayoutStore.instance.isVisibleOnScreen(this.props.room, this.props.app.id);
|
||||||
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(app.id);
|
|
||||||
const isVisibleOnScreen = WidgetLayoutStore.instance.isVisibleOnScreen(room, app.id);
|
|
||||||
if (!isVisibleOnScreen && !isActiveWidget) {
|
if (!isVisibleOnScreen && !isActiveWidget) {
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(app.id);
|
this.endWidgetActions();
|
||||||
PersistedElement.destroyElement(this.persistKey);
|
|
||||||
this.sgWidget?.stopMessaging();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomViewStoreUpdate = () => {
|
private onRoomViewStoreUpdate = () => {
|
||||||
if (this.props.room.roomId == RoomViewStore.getRoomId()) return;
|
if (this.props.room.roomId == RoomViewStore.getRoomId()) return;
|
||||||
const app = this.props.app;
|
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(this.props.app.id);
|
||||||
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(app.id);
|
|
||||||
// Stop the widget if it's not the active (persistent) widget and it's not a user widget
|
// Stop the widget if it's not the active (persistent) widget and it's not a user widget
|
||||||
if (!isActiveWidget && !this.props.userWidget) {
|
if (!isActiveWidget && !this.props.userWidget) {
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(app.id);
|
this.endWidgetActions();
|
||||||
PersistedElement.destroyElement(this.persistKey);
|
}
|
||||||
this.sgWidget?.stopMessaging();
|
};
|
||||||
|
|
||||||
|
private onUserLeftRoom() {
|
||||||
|
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(this.props.app.id);
|
||||||
|
if (isActiveWidget) {
|
||||||
|
// We just left the room that the active widget was from.
|
||||||
|
if (RoomViewStore.getRoomId() !== this.props.room.roomId) {
|
||||||
|
// If we are not actively looking at the room then destroy this widget entirely.
|
||||||
|
this.endWidgetActions();
|
||||||
|
} else if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||||
|
// If this was a Jitsi then reload to end call.
|
||||||
|
this.reload();
|
||||||
|
} else {
|
||||||
|
// Otherwise just cancel its persistence.
|
||||||
|
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMyMembership = (room: Room, membership: string): void => {
|
||||||
|
if (membership === "leave" && room.roomId === this.props.room.roomId) {
|
||||||
|
this.onUserLeftRoom();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -247,6 +264,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
const emitEvent = WidgetLayoutStore.emissionForRoom(this.props.room);
|
const emitEvent = WidgetLayoutStore.emissionForRoom(this.props.room);
|
||||||
WidgetLayoutStore.instance.on(emitEvent, this.onWidgetLayoutChange);
|
WidgetLayoutStore.instance.on(emitEvent, this.onWidgetLayoutChange);
|
||||||
|
this.context.on("Room.myMembership", this.onMyMembership);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||||
|
@ -262,6 +280,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
const emitEvent = WidgetLayoutStore.emissionForRoom(this.props.room);
|
const emitEvent = WidgetLayoutStore.emissionForRoom(this.props.room);
|
||||||
WidgetLayoutStore.instance.off(emitEvent, this.onWidgetLayoutChange);
|
WidgetLayoutStore.instance.off(emitEvent, this.onWidgetLayoutChange);
|
||||||
|
this.context.off("Room.myMembership", this.onMyMembership);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.roomStoreToken?.remove();
|
this.roomStoreToken?.remove();
|
||||||
|
@ -269,12 +288,27 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady);
|
OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setupSgListeners() {
|
||||||
|
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
||||||
|
this.sgWidget.on("ready", this.onWidgetReady);
|
||||||
|
// emits when the capabilites have been setup or changed
|
||||||
|
this.sgWidget.on("capabilitiesNotified", this.onWidgetCapabilitiesNotified);
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopSgListeners() {
|
||||||
|
if (!this.sgWidget) return;
|
||||||
|
this.sgWidget.off("preparing", this.onWidgetPreparing);
|
||||||
|
this.sgWidget.off("ready", this.onWidgetReady);
|
||||||
|
this.sgWidget.off("capabilitiesNotified", this.onWidgetCapabilitiesNotified);
|
||||||
|
}
|
||||||
|
|
||||||
private resetWidget(newProps: IProps): void {
|
private resetWidget(newProps: IProps): void {
|
||||||
this.sgWidget?.stopMessaging();
|
this.sgWidget?.stopMessaging();
|
||||||
|
this.stopSgListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.sgWidget = new StopGapWidget(newProps);
|
this.sgWidget = new StopGapWidget(newProps);
|
||||||
this.sgWidget.on("preparing", this.onWidgetPreparing);
|
this.setupSgListeners();
|
||||||
this.sgWidget.on("ready", this.onWidgetReady);
|
|
||||||
this.startWidget();
|
this.startWidget();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("Failed to construct widget", e);
|
logger.error("Failed to construct widget", e);
|
||||||
|
@ -288,14 +322,18 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private startMessaging() {
|
||||||
|
try {
|
||||||
|
this.sgWidget?.startMessaging(this.iframe);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Failed to start widget", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private iframeRefChange = (ref: HTMLIFrameElement): void => {
|
private iframeRefChange = (ref: HTMLIFrameElement): void => {
|
||||||
this.iframe = ref;
|
this.iframe = ref;
|
||||||
if (ref) {
|
if (ref) {
|
||||||
try {
|
this.startMessaging();
|
||||||
this.sgWidget?.startMessaging(ref);
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Failed to start widget", e);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.resetWidget(this.props);
|
this.resetWidget(this.props);
|
||||||
}
|
}
|
||||||
|
@ -364,24 +402,31 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = (payload): void => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (payload.widgetId === this.props.app.id) {
|
switch (payload.action) {
|
||||||
switch (payload.action) {
|
case 'm.sticker':
|
||||||
case 'm.sticker':
|
if (payload.widgetId === this.props.app.id &&
|
||||||
if (this.sgWidget.widgetApi.hasCapability(MatrixCapabilities.StickerSending)) {
|
this.sgWidget.widgetApi.hasCapability(MatrixCapabilities.StickerSending)
|
||||||
dis.dispatch({
|
) {
|
||||||
action: 'post_sticker_message',
|
dis.dispatch({
|
||||||
data: {
|
action: 'post_sticker_message',
|
||||||
...payload.data,
|
data: {
|
||||||
threadId: this.props.threadId,
|
...payload.data,
|
||||||
},
|
threadId: this.props.threadId,
|
||||||
});
|
},
|
||||||
dis.dispatch({ action: 'stickerpicker_close' });
|
});
|
||||||
} else {
|
dis.dispatch({ action: 'stickerpicker_close' });
|
||||||
logger.warn('Ignoring sticker message. Invalid capability');
|
} else {
|
||||||
}
|
logger.warn('Ignoring sticker message. Invalid capability');
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
|
case "after_leave_room":
|
||||||
|
if (payload.room_id === this.props.room?.roomId) {
|
||||||
|
// call this before we get it echoed down /sync, so it doesn't hang around as long and look jarring
|
||||||
|
this.onUserLeftRoom();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -436,17 +481,25 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private reload() {
|
||||||
|
this.endWidgetActions().then(() => {
|
||||||
|
// reset messaging
|
||||||
|
this.resetWidget(this.props);
|
||||||
|
this.startMessaging();
|
||||||
|
|
||||||
|
if (this.iframe) {
|
||||||
|
// Reload iframe
|
||||||
|
this.iframe.src = this.sgWidget.embedUrl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO replace with full screen interactions
|
// TODO replace with full screen interactions
|
||||||
private onPopoutWidgetClick = (): void => {
|
private onPopoutWidgetClick = (): void => {
|
||||||
// Ensure Jitsi conferences are closed on pop-out, to not confuse the user to join them
|
// Ensure Jitsi conferences are closed on pop-out, to not confuse the user to join them
|
||||||
// twice from the same computer, which Jitsi can have problems with (audio echo/gain-loop).
|
// twice from the same computer, which Jitsi can have problems with (audio echo/gain-loop).
|
||||||
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||||
this.endWidgetActions().then(() => {
|
this.reload();
|
||||||
if (this.iframe) {
|
|
||||||
// Reload iframe
|
|
||||||
this.iframe.src = this.sgWidget.embedUrl;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Using Object.assign workaround as the following opens in a new window instead of a new tab.
|
// Using Object.assign workaround as the following opens in a new window instead of a new tab.
|
||||||
// window.open(this._getPopoutUrl(), '_blank', 'noopener=yes');
|
// window.open(this._getPopoutUrl(), '_blank', 'noopener=yes');
|
||||||
|
@ -507,7 +560,7 @@ export default class AppTile extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
} else if (!this.state.hasPermissionToLoad) {
|
} else if (!this.state.hasPermissionToLoad) {
|
||||||
// only possible for room widgets, can assert this.props.room here
|
// only possible for room widgets, can assert this.props.room here
|
||||||
const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
|
const isEncrypted = this.context.isRoomEncrypted(this.props.room.roomId);
|
||||||
appTileBody = (
|
appTileBody = (
|
||||||
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
<div className={appTileBodyClass} style={appTileBodyStyles}>
|
||||||
<AppPermission
|
<AppPermission
|
||||||
|
|
|
@ -15,73 +15,51 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, { ContextType } from 'react';
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
|
||||||
|
|
||||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
|
||||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import AppTile from "./AppTile";
|
import AppTile from "./AppTile";
|
||||||
|
import { IApp } from '../../../stores/WidgetStore';
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
persistentWidgetId: string;
|
persistentWidgetId: string;
|
||||||
pointerEvents?: string;
|
pointerEvents?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
|
||||||
roomId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@replaceableComponent("views.elements.PersistentApp")
|
@replaceableComponent("views.elements.PersistentApp")
|
||||||
export default class PersistentApp extends React.Component<IProps, IState> {
|
export default class PersistentApp extends React.Component<IProps> {
|
||||||
constructor(props: IProps) {
|
public static contextType = MatrixClientContext;
|
||||||
super(props);
|
context: ContextType<typeof MatrixClientContext>;
|
||||||
|
|
||||||
this.state = {
|
private get app(): IApp {
|
||||||
roomId: RoomViewStore.getRoomId(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount(): void {
|
|
||||||
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
|
||||||
MatrixClientPeg.get().off("Room.myMembership", this.onMyMembership);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onMyMembership = async (room: Room, membership: string): Promise<void> => {
|
|
||||||
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.props.persistentWidgetId);
|
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.props.persistentWidgetId);
|
||||||
if (membership !== "join") {
|
const persistentWidgetInRoom = this.context.getRoom(persistentWidgetInRoomId);
|
||||||
// we're not in the room anymore - delete
|
|
||||||
if (room.roomId === persistentWidgetInRoomId) {
|
// get the widget data
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.persistentWidgetId);
|
const appEvent = WidgetUtils.getRoomWidgets(persistentWidgetInRoom).find((ev) => {
|
||||||
}
|
return ev.getStateKey() === ActiveWidgetStore.instance.getPersistentWidgetId();
|
||||||
}
|
});
|
||||||
};
|
return WidgetUtils.makeAppConfig(
|
||||||
|
appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(),
|
||||||
|
persistentWidgetInRoomId, appEvent.getId(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const wId = this.props.persistentWidgetId;
|
const app = this.app;
|
||||||
if (wId) {
|
if (app) {
|
||||||
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(wId);
|
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.props.persistentWidgetId);
|
||||||
const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId);
|
const persistentWidgetInRoom = this.context.getRoom(persistentWidgetInRoomId);
|
||||||
|
|
||||||
// get the widget data
|
|
||||||
const appEvent = WidgetUtils.getRoomWidgets(persistentWidgetInRoom).find((ev) => {
|
|
||||||
return ev.getStateKey() === ActiveWidgetStore.instance.getPersistentWidgetId();
|
|
||||||
});
|
|
||||||
const app = WidgetUtils.makeAppConfig(
|
|
||||||
appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(),
|
|
||||||
persistentWidgetInRoomId, appEvent.getId(),
|
|
||||||
);
|
|
||||||
return <AppTile
|
return <AppTile
|
||||||
key={app.id}
|
key={app.id}
|
||||||
app={app}
|
app={app}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
room={persistentWidgetInRoom}
|
room={persistentWidgetInRoom}
|
||||||
userId={MatrixClientPeg.get().credentials.userId}
|
userId={this.context.credentials.userId}
|
||||||
creatorUserId={app.creatorUserId}
|
creatorUserId={app.creatorUserId}
|
||||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||||
waitForIframeLoad={app.waitForIframeLoad}
|
waitForIframeLoad={app.waitForIframeLoad}
|
||||||
|
|
|
@ -236,7 +236,6 @@ export default class PipView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
// Accepts a persistentWidgetId to be able to skip awaiting the setState for persistentWidgetId
|
// Accepts a persistentWidgetId to be able to skip awaiting the setState for persistentWidgetId
|
||||||
public updateShowWidgetInPip(persistentWidgetId = this.state.persistentWidgetId) {
|
public updateShowWidgetInPip(persistentWidgetId = this.state.persistentWidgetId) {
|
||||||
let userIsPartOfTheRoom = false;
|
|
||||||
let fromAnotherRoom = false;
|
let fromAnotherRoom = false;
|
||||||
let notVisible = false;
|
let notVisible = false;
|
||||||
if (persistentWidgetId) {
|
if (persistentWidgetId) {
|
||||||
|
@ -248,16 +247,13 @@ export default class PipView extends React.Component<IProps, IState> {
|
||||||
if (persistentWidgetInRoom) {
|
if (persistentWidgetInRoom) {
|
||||||
const wls = WidgetLayoutStore.instance;
|
const wls = WidgetLayoutStore.instance;
|
||||||
notVisible = !wls.isVisibleOnScreen(persistentWidgetInRoom, persistentWidgetId);
|
notVisible = !wls.isVisibleOnScreen(persistentWidgetInRoom, persistentWidgetId);
|
||||||
userIsPartOfTheRoom = persistentWidgetInRoom.getMyMembership() == "join";
|
|
||||||
fromAnotherRoom = this.state.viewedRoomId !== persistentWidgetInRoomId;
|
fromAnotherRoom = this.state.viewedRoomId !== persistentWidgetInRoomId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 || notVisible;
|
||||||
(fromAnotherRoom && userIsPartOfTheRoom) ||
|
|
||||||
(notVisible && userIsPartOfTheRoom);
|
|
||||||
|
|
||||||
this.setState({ showWidgetInPip, persistentWidgetId });
|
this.setState({ showWidgetInPip, persistentWidgetId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,9 +64,8 @@ import { arrayFastClone } from "../../utils/arrays";
|
||||||
|
|
||||||
interface IAppTileProps {
|
interface IAppTileProps {
|
||||||
// Note: these are only the props we care about
|
// Note: these are only the props we care about
|
||||||
|
|
||||||
app: IWidget;
|
app: IWidget;
|
||||||
room: Room;
|
room?: Room; // without a room it is a user widget
|
||||||
userId: string;
|
userId: string;
|
||||||
creatorUserId: string;
|
creatorUserId: string;
|
||||||
waitForIframeLoad: boolean;
|
waitForIframeLoad: boolean;
|
||||||
|
@ -423,6 +422,7 @@ export class StopGapWidget extends EventEmitter {
|
||||||
if (!this.started) return;
|
if (!this.started) return;
|
||||||
WidgetMessagingStore.instance.stopMessaging(this.mockWidget);
|
WidgetMessagingStore.instance.stopMessaging(this.mockWidget);
|
||||||
ActiveWidgetStore.instance.delRoomId(this.mockWidget.id);
|
ActiveWidgetStore.instance.delRoomId(this.mockWidget.id);
|
||||||
|
this.messaging = null;
|
||||||
|
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().off('event', this.onEvent);
|
MatrixClientPeg.get().off('event', this.onEvent);
|
||||||
|
|
Loading…
Reference in a new issue