mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 21:24:59 +08:00
Support arbitrary widgets sticking to the screen + sending stickers
Following https://github.com/matrix-org/matrix-react-sdk/pull/5385, it is now possible for a widget to request these capabilities without being a video conference or sticker picker. This commit actually enables this support for those kinds of widgets. This commit also fixes an issue in the URL templating where some variables might get set to 'undefined' - this appears to be a scoping issue, so StopGapWidget now stores the definition alongside the superclass. Fixes https://github.com/vector-im/element-web/issues/15001
This commit is contained in:
parent
5f47077a30
commit
107fa98180
@ -23,7 +23,6 @@ import PropTypes from 'prop-types';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import AccessibleButton from './AccessibleButton';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
import AppPermission from './AppPermission';
|
||||
import AppWarning from './AppWarning';
|
||||
import Spinner from './Spinner';
|
||||
@ -375,19 +374,18 @@ export default class AppTile extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
// if the widget would be allowed to remain on screen, we must put it in
|
||||
// a PersistedElement from the get-go, otherwise the iframe will be
|
||||
// re-mounted later when we do.
|
||||
if (this.props.whitelistCapabilities.includes('m.always_on_screen')) {
|
||||
const PersistedElement = sdk.getComponent("elements.PersistedElement");
|
||||
// Also wrap the PersistedElement in a div to fix the height, otherwise
|
||||
// AppTile's border is in the wrong place
|
||||
appTileBody = <div className="mx_AppTile_persistedWrapper">
|
||||
<PersistedElement persistKey={this._persistKey}>
|
||||
{appTileBody}
|
||||
</PersistedElement>
|
||||
</div>;
|
||||
}
|
||||
|
||||
// all widgets can theoretically be allowed to remain on screen, so we wrap
|
||||
// them all in a PersistedElement from the get-go. If we wait, the iframe will
|
||||
// be re-mounted later, which means the widget has to start over, which is bad.
|
||||
|
||||
// Also wrap the PersistedElement in a div to fix the height, otherwise
|
||||
// AppTile's border is in the wrong place
|
||||
appTileBody = <div className="mx_AppTile_persistedWrapper">
|
||||
<PersistedElement persistKey={this._persistKey}>
|
||||
{appTileBody}
|
||||
</PersistedElement>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,10 +472,6 @@ AppTile.propTypes = {
|
||||
handleMinimisePointerEvents: PropTypes.bool,
|
||||
// Optionally hide the popout widget icon
|
||||
showPopout: PropTypes.bool,
|
||||
// Widget capabilities to allow by default (without user confirmation)
|
||||
// NOTE -- Use with caution. This is intended to aid better integration / UX
|
||||
// basic widget capabilities, e.g. injecting sticker message events.
|
||||
whitelistCapabilities: PropTypes.array,
|
||||
// Is this an instance of a user widget
|
||||
userWidget: PropTypes.bool,
|
||||
};
|
||||
@ -488,7 +482,6 @@ AppTile.defaultProps = {
|
||||
showTitle: true,
|
||||
showPopout: true,
|
||||
handleMinimisePointerEvents: false,
|
||||
whitelistCapabilities: [],
|
||||
userWidget: false,
|
||||
miniMode: false,
|
||||
};
|
||||
|
@ -71,7 +71,6 @@ export default class PersistentApp extends React.Component {
|
||||
appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(),
|
||||
persistentWidgetInRoomId, appEvent.getId(),
|
||||
);
|
||||
const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, persistentWidgetInRoomId);
|
||||
const AppTile = sdk.getComponent('elements.AppTile');
|
||||
return <AppTile
|
||||
key={app.id}
|
||||
@ -82,7 +81,6 @@ export default class PersistentApp extends React.Component {
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
whitelistCapabilities={capWhitelist}
|
||||
miniMode={true}
|
||||
showMenubar={false}
|
||||
/>;
|
||||
|
@ -103,7 +103,6 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
whitelistCapabilities={WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, room.roomId)}
|
||||
/>
|
||||
</BaseCard>;
|
||||
};
|
||||
|
@ -221,7 +221,6 @@ export default class AppsDrawer extends React.Component {
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
whitelistCapabilities={capWhitelist}
|
||||
/>);
|
||||
});
|
||||
|
||||
|
@ -280,7 +280,6 @@ export default class Stickerpicker extends React.Component {
|
||||
showPopout={false}
|
||||
onMinimiseClick={this._onHideStickersClick}
|
||||
handleMinimisePointerEvents={true}
|
||||
whitelistCapabilities={['m.sticker', 'visibility']}
|
||||
userWidget={true}
|
||||
/>
|
||||
</PersistedElement>
|
||||
|
@ -75,8 +75,8 @@ interface IAppTileProps {
|
||||
|
||||
// TODO: Don't use this because it's wrong
|
||||
class ElementWidget extends Widget {
|
||||
constructor(w) {
|
||||
super(w);
|
||||
constructor(private rawDefinition: IWidget) {
|
||||
super(rawDefinition);
|
||||
}
|
||||
|
||||
public get templateUrl(): string {
|
||||
@ -137,12 +137,7 @@ class ElementWidget extends Widget {
|
||||
|
||||
public getCompleteUrl(params: ITemplateParams, asPopout=false): string {
|
||||
return runTemplate(asPopout ? this.popoutTemplateUrl : this.templateUrl, {
|
||||
// we need to supply a whole widget to the template, but don't have
|
||||
// easy access to the definition the superclass is using, so be sad
|
||||
// and gutwrench it.
|
||||
// This isn't a problem when the widget architecture is fixed and this
|
||||
// subclass gets deleted.
|
||||
...super['definition'], // XXX: Private member access
|
||||
...this.rawDefinition,
|
||||
data: this.rawData,
|
||||
}, params);
|
||||
}
|
||||
@ -351,18 +346,39 @@ export class StopGapWidget extends EventEmitter {
|
||||
MatrixClientPeg.get().on('event', this.onEvent);
|
||||
MatrixClientPeg.get().on('Event.decrypted', this.onEventDecrypted);
|
||||
|
||||
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
|
||||
this.messaging.on("action:set_always_on_screen",
|
||||
(ev: CustomEvent<IStickyActionRequest>) => {
|
||||
if (this.messaging.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
|
||||
this.messaging.on(`action:${WidgetApiFromWidgetAction.UpdateAlwaysOnScreen}`,
|
||||
(ev: CustomEvent<IStickyActionRequest>) => {
|
||||
if (this.messaging.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
|
||||
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
|
||||
CountlyAnalytics.instance.trackJoinCall(this.appTileProps.room.roomId, true, true);
|
||||
ActiveWidgetStore.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value);
|
||||
ev.preventDefault();
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); // ack
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (WidgetType.STICKERPICKER.matches(this.mockWidget.type)) {
|
||||
ActiveWidgetStore.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value);
|
||||
ev.preventDefault();
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); // ack
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: Replace this event listener with appropriate driver functionality once the API
|
||||
// establishes a sane way to send events back and forth.
|
||||
this.messaging.on(`action:${WidgetApiFromWidgetAction.SendSticker}`,
|
||||
(ev: CustomEvent<IStickerActionRequest>) => {
|
||||
if (this.messaging.hasCapability(MatrixCapabilities.StickerSending)) {
|
||||
// Acknowledge first
|
||||
ev.preventDefault();
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
|
||||
// Send the sticker
|
||||
defaultDispatcher.dispatch({
|
||||
action: 'm.sticker',
|
||||
data: ev.detail.data,
|
||||
widgetId: this.mockWidget.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (WidgetType.STICKERPICKER.matches(this.mockWidget.type)) {
|
||||
this.messaging.on(`action:${ElementWidgetActions.OpenIntegrationManager}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
// Acknowledge first
|
||||
@ -394,23 +410,6 @@ export class StopGapWidget extends EventEmitter {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: Replace this event listener with appropriate driver functionality once the API
|
||||
// establishes a sane way to send events back and forth.
|
||||
this.messaging.on(`action:${WidgetApiFromWidgetAction.SendSticker}`,
|
||||
(ev: CustomEvent<IStickerActionRequest>) => {
|
||||
// Acknowledge first
|
||||
ev.preventDefault();
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
|
||||
// Send the sticker
|
||||
defaultDispatcher.dispatch({
|
||||
action: 'm.sticker',
|
||||
data: ev.detail.data,
|
||||
widgetId: this.mockWidget.id,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user