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:
parent
ac6177053a
commit
f6effc52fd
4 changed files with 53 additions and 19 deletions
|
@ -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();
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue