Fix active Jitsi calls (and other active widgets) not being visible on screen, by showing them in PiP if they are not visible in any other container (#7435)

Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
This commit is contained in:
Timo 2022-01-11 11:25:21 +01:00 committed by GitHub
parent ac6177053a
commit f6effc52fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 19 deletions

View file

@ -25,6 +25,10 @@ import WidgetUtils from '../../../utils/WidgetUtils';
import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import AppTile from "./AppTile"; import AppTile from "./AppTile";
import { Container, WidgetLayoutStore } from '../../../stores/widgets/WidgetLayoutStore';
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
interface IProps { interface IProps {
// none // none
@ -33,6 +37,7 @@ interface IProps {
interface IState { interface IState {
roomId: string; roomId: string;
persistentWidgetId: string; persistentWidgetId: string;
rightPanelPhase?: RightPanelPhases;
} }
@replaceableComponent("views.elements.PersistentApp") @replaceableComponent("views.elements.PersistentApp")
@ -45,12 +50,14 @@ export default class PersistentApp extends React.Component<IProps, IState> {
this.state = { this.state = {
roomId: RoomViewStore.getRoomId(), roomId: RoomViewStore.getRoomId(),
persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(), persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(),
rightPanelPhase: RightPanelStore.instance.currentCard.phase,
}; };
} }
public componentDidMount(): void { public componentDidMount(): void {
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate); ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership); MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
} }
@ -59,6 +66,7 @@ export default class PersistentApp extends React.Component<IProps, IState> {
this.roomStoreToken.remove(); this.roomStoreToken.remove();
} }
ActiveWidgetStore.instance.removeListener(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate); ActiveWidgetStore.instance.removeListener(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate);
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership); MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership);
} }
@ -71,6 +79,12 @@ export default class PersistentApp extends React.Component<IProps, IState> {
}); });
}; };
private onRightPanelStoreUpdate = () => {
this.setState({
rightPanelPhase: RightPanelStore.instance.currentCard.phase,
});
};
private onActiveWidgetStoreUpdate = (): void => { private onActiveWidgetStoreUpdate = (): void => {
this.setState({ this.setState({
persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(), persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(),
@ -88,8 +102,9 @@ export default class PersistentApp extends React.Component<IProps, IState> {
}; };
public render(): JSX.Element { public render(): JSX.Element {
if (this.state.persistentWidgetId) { const wId = this.state.persistentWidgetId;
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.state.persistentWidgetId); if (wId) {
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(wId);
const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId); const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId);
@ -97,8 +112,24 @@ export default class PersistentApp extends React.Component<IProps, IState> {
// thus no room is associated anymore. // thus no room is associated anymore.
if (!persistentWidgetInRoom) return null; if (!persistentWidgetInRoom) return null;
const myMembership = persistentWidgetInRoom.getMyMembership(); const wls = WidgetLayoutStore.instance;
if (this.state.roomId !== persistentWidgetInRoomId && myMembership === "join") {
const userIsPartOfTheRoom = persistentWidgetInRoom.getMyMembership() == "join";
const fromAnotherRoom = this.state.roomId !== persistentWidgetInRoomId;
const notInRightPanel =
!(this.state.rightPanelPhase == RightPanelPhases.Widget &&
wId == RightPanelStore.instance.currentCard.state?.widgetId);
const notInCenterContainer =
!wls.getContainerWidgets(persistentWidgetInRoom, Container.Center).some((app) => app.id == wId);
const notInTopContainer =
!wls.getContainerWidgets(persistentWidgetInRoom, Container.Top).some(app => app.id == wId);
if (
// 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.
(fromAnotherRoom && userIsPartOfTheRoom) ||
(notInRightPanel && notInCenterContainer && notInTopContainer && userIsPartOfTheRoom)
) {
// get the widget data // get the widget data
const appEvent = WidgetUtils.getRoomWidgets(persistentWidgetInRoom).find((ev) => { const appEvent = WidgetUtils.getRoomWidgets(persistentWidgetInRoom).find((ev) => {
return ev.getStateKey() === ActiveWidgetStore.instance.getPersistentWidgetId(); return ev.getStateKey() === ActiveWidgetStore.instance.getPersistentWidgetId();

View file

@ -29,13 +29,14 @@ import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import ContextMenu, { ChevronFace } from "../../structures/ContextMenu"; import ContextMenu, { ChevronFace } from "../../structures/ContextMenu";
import { WidgetType } from "../../../widgets/WidgetType"; import { WidgetType } from "../../../widgets/WidgetType";
import { Action } from "../../../dispatcher/actions";
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore"; import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { ActionPayload } from '../../../dispatcher/payloads'; import { ActionPayload } from '../../../dispatcher/payloads';
import ScalarAuthClient from '../../../ScalarAuthClient'; import ScalarAuthClient from '../../../ScalarAuthClient';
import GenericElementContextMenu from "../context_menus/GenericElementContextMenu"; import GenericElementContextMenu from "../context_menus/GenericElementContextMenu";
import { IApp } from "../../../stores/WidgetStore"; import { IApp } from "../../../stores/WidgetStore";
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { UPDATE_EVENT } from '../../../stores/AsyncStore';
// This should be below the dialog level (4000), but above the rest of the UI (1000-2000). // This should be below the dialog level (4000), but above the rest of the UI (1000-2000).
// We sit in a context menu, so this should be given to the context menu. // We sit in a context menu, so this should be given to the context menu.
@ -139,6 +140,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
// Track updates to widget state in account data // Track updates to widget state in account data
MatrixClientPeg.get().on('accountData', this.updateWidget); MatrixClientPeg.get().on('accountData', this.updateWidget);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
// Initialise widget state from current account data // Initialise widget state from current account data
this.updateWidget(); this.updateWidget();
} }
@ -146,7 +148,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
public componentWillUnmount(): void { public componentWillUnmount(): void {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
if (client) client.removeListener('accountData', this.updateWidget); if (client) client.removeListener('accountData', this.updateWidget);
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
window.removeEventListener('resize', this.onResize); window.removeEventListener('resize', this.onResize);
if (this.dispatcherRef) { if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
@ -204,7 +206,6 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
case "stickerpicker_close": case "stickerpicker_close":
this.props.setShowStickers(false); this.props.setShowStickers(false);
break; break;
case Action.AfterRightPanelPhaseChange:
case "show_left_panel": case "show_left_panel":
case "hide_left_panel": case "hide_left_panel":
this.props.setShowStickers(false); this.props.setShowStickers(false);
@ -212,6 +213,10 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
} }
}; };
private onRightPanelStoreUpdate = () => {
this.props.setShowStickers(false);
};
private defaultStickerpickerContent(): JSX.Element { private defaultStickerpickerContent(): JSX.Element {
return ( return (
<AccessibleButton onClick={this.launchManageIntegrations} <AccessibleButton onClick={this.launchManageIntegrations}

View file

@ -197,13 +197,16 @@ export default class CallPreview extends React.Component<IProps, IState> {
draggable={pipMode} draggable={pipMode}
onDoubleClick={this.onDoubleClick} onDoubleClick={this.onDoubleClick}
> >
{ ({ onStartMoving, onResize }) => <CallView {
onMouseDownOnHeader={onStartMoving} ({ onStartMoving, onResize }) =>
call={this.state.primaryCall} <CallView
secondaryCall={this.state.secondaryCall} onMouseDownOnHeader={onStartMoving}
pipMode={pipMode} call={this.state.primaryCall}
onResize={onResize} secondaryCall={this.state.secondaryCall}
/> } pipMode={pipMode}
onResize={onResize}
/>
}
</PictureInPictureDragger> </PictureInPictureDragger>
); );

View file

@ -102,11 +102,6 @@ export enum Action {
*/ */
ViewRoomDelta = "view_room_delta", ViewRoomDelta = "view_room_delta",
/**
* Trigged after the phase of the right panel is set. Should be used with AfterRightPanelPhaseChangePayload.
*/
AfterRightPanelPhaseChange = "after_right_panel_phase_change",
/** /**
* Opens the modal dial pad * Opens the modal dial pad
*/ */