Render Jitsi (and other sticky widgets) in PiP container, so it can be dragged and the "jump to room functionality" is provided (#7450)
Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
This commit is contained in:
parent
8b01b68fa3
commit
ef95644e23
13 changed files with 396 additions and 342 deletions
|
@ -508,8 +508,13 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
|
||||
// Also wrap the PersistedElement in a div to fix the height, otherwise
|
||||
// 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)
|
||||
// otherwise there are issues that the PiP view is drawn UNDER another widget (Persistent app) when dragged around.
|
||||
const zIndexAboveOtherPersistentElements = 101;
|
||||
|
||||
appTileBody = <div className="mx_AppTile_persistedWrapper">
|
||||
<PersistedElement zIndex={this.props.miniMode ? 10 : 9}persistKey={this.persistKey}>
|
||||
<PersistedElement zIndex={this.props.miniMode ? zIndexAboveOtherPersistentElements : 9} persistKey={this.persistKey}>
|
||||
{ appTileBody }
|
||||
</PersistedElement>
|
||||
</div>;
|
||||
|
@ -545,15 +550,15 @@ export default class AppTile extends React.Component<IProps, IState> {
|
|||
if (!this.props.hideMaximiseButton) {
|
||||
const widgetIsMaximised = WidgetLayoutStore.instance.
|
||||
isInContainer(this.props.room, this.props.app, Container.Center);
|
||||
const className = classNames({
|
||||
"mx_AppTileMenuBar_iconButton": true,
|
||||
"mx_AppTileMenuBar_iconButton_minWidget": widgetIsMaximised,
|
||||
"mx_AppTileMenuBar_iconButton_maxWidget": !widgetIsMaximised,
|
||||
});
|
||||
maxMinButton = <AccessibleButton
|
||||
className={
|
||||
"mx_AppTileMenuBar_iconButton"
|
||||
+ (widgetIsMaximised
|
||||
? " mx_AppTileMenuBar_iconButton_minWidget"
|
||||
: " mx_AppTileMenuBar_iconButton_maxWidget")
|
||||
}
|
||||
className={className}
|
||||
title={
|
||||
widgetIsMaximised ? _t('Close'): _t('Maximise widget')
|
||||
widgetIsMaximised ? _t('Close') : _t('Maximise widget')
|
||||
}
|
||||
onClick={this.onMaxMinWidgetClick}
|
||||
/>;
|
||||
|
|
|
@ -184,7 +184,7 @@ export default class PersistedElement extends React.Component<IProps> {
|
|||
width: parentRect.width + 'px',
|
||||
height: parentRect.height + 'px',
|
||||
});
|
||||
}, 100, { trailing: true, leading: true });
|
||||
}, 16, { trailing: true, leading: true });
|
||||
|
||||
public render(): JSX.Element {
|
||||
return <div ref={this.collectChildContainer} />;
|
||||
|
|
|
@ -16,141 +16,79 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EventSubscription } from 'fbemitter';
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from '../../../stores/ActiveWidgetStore';
|
||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
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 {
|
||||
// none
|
||||
persistentWidgetId: string;
|
||||
pointerEvents?: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
roomId: string;
|
||||
persistentWidgetId: string;
|
||||
rightPanelPhase?: RightPanelPhases;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.elements.PersistentApp")
|
||||
export default class PersistentApp extends React.Component<IProps, IState> {
|
||||
private roomStoreToken: EventSubscription;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
roomId: RoomViewStore.getRoomId(),
|
||||
persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(),
|
||||
rightPanelPhase: RightPanelStore.instance.currentCard.phase,
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||
ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate);
|
||||
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
if (this.roomStoreToken) {
|
||||
this.roomStoreToken.remove();
|
||||
}
|
||||
ActiveWidgetStore.instance.removeListener(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate);
|
||||
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership);
|
||||
}
|
||||
MatrixClientPeg.get().off("Room.myMembership", this.onMyMembership);
|
||||
}
|
||||
|
||||
private onRoomViewStoreUpdate = (): void => {
|
||||
if (RoomViewStore.getRoomId() === this.state.roomId) return;
|
||||
this.setState({
|
||||
roomId: RoomViewStore.getRoomId(),
|
||||
});
|
||||
};
|
||||
|
||||
private onRightPanelStoreUpdate = () => {
|
||||
this.setState({
|
||||
rightPanelPhase: RightPanelStore.instance.currentCard.phase,
|
||||
});
|
||||
};
|
||||
|
||||
private onActiveWidgetStoreUpdate = (): void => {
|
||||
this.setState({
|
||||
persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(),
|
||||
});
|
||||
};
|
||||
|
||||
private onMyMembership = async (room: Room, membership: string): Promise<void> => {
|
||||
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.state.persistentWidgetId);
|
||||
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.props.persistentWidgetId);
|
||||
if (membership !== "join") {
|
||||
// we're not in the room anymore - delete
|
||||
if (room .roomId === persistentWidgetInRoomId) {
|
||||
ActiveWidgetStore.instance.destroyPersistentWidget(this.state.persistentWidgetId);
|
||||
if (room.roomId === persistentWidgetInRoomId) {
|
||||
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.persistentWidgetId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const wId = this.state.persistentWidgetId;
|
||||
const wId = this.props.persistentWidgetId;
|
||||
if (wId) {
|
||||
const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(wId);
|
||||
|
||||
const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId);
|
||||
|
||||
// Sanity check the room - the widget may have been destroyed between render cycles, and
|
||||
// thus no room is associated anymore.
|
||||
if (!persistentWidgetInRoom) return null;
|
||||
|
||||
const wls = WidgetLayoutStore.instance;
|
||||
|
||||
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
|
||||
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
|
||||
key={app.id}
|
||||
app={app}
|
||||
fullWidth={true}
|
||||
room={persistentWidgetInRoom}
|
||||
userId={MatrixClientPeg.get().credentials.userId}
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
miniMode={true}
|
||||
showMenubar={false}
|
||||
/>;
|
||||
}
|
||||
// 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
|
||||
key={app.id}
|
||||
app={app}
|
||||
fullWidth={true}
|
||||
room={persistentWidgetInRoom}
|
||||
userId={MatrixClientPeg.get().credentials.userId}
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
miniMode={true}
|
||||
showMenubar={false}
|
||||
pointerEvents={this.props.pointerEvents}
|
||||
/>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue