Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 107fa98

Browse files
committed
Support arbitrary widgets sticking to the screen + sending stickers
Following #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 element-hq/element-web#15001
1 parent 5f47077 commit 107fa98

File tree

6 files changed

+46
-59
lines changed

6 files changed

+46
-59
lines changed

src/components/views/elements/AppTile.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import PropTypes from 'prop-types';
2323
import {MatrixClientPeg} from '../../../MatrixClientPeg';
2424
import AccessibleButton from './AccessibleButton';
2525
import { _t } from '../../../languageHandler';
26-
import * as sdk from '../../../index';
2726
import AppPermission from './AppPermission';
2827
import AppWarning from './AppWarning';
2928
import Spinner from './Spinner';
@@ -375,19 +374,18 @@ export default class AppTile extends React.Component {
375374
/>
376375
</div>
377376
);
378-
// if the widget would be allowed to remain on screen, we must put it in
379-
// a PersistedElement from the get-go, otherwise the iframe will be
380-
// re-mounted later when we do.
381-
if (this.props.whitelistCapabilities.includes('m.always_on_screen')) {
382-
const PersistedElement = sdk.getComponent("elements.PersistedElement");
383-
// Also wrap the PersistedElement in a div to fix the height, otherwise
384-
// AppTile's border is in the wrong place
385-
appTileBody = <div className="mx_AppTile_persistedWrapper">
386-
<PersistedElement persistKey={this._persistKey}>
387-
{appTileBody}
388-
</PersistedElement>
389-
</div>;
390-
}
377+
378+
// all widgets can theoretically be allowed to remain on screen, so we wrap
379+
// them all in a PersistedElement from the get-go. If we wait, the iframe will
380+
// be re-mounted later, which means the widget has to start over, which is bad.
381+
382+
// Also wrap the PersistedElement in a div to fix the height, otherwise
383+
// AppTile's border is in the wrong place
384+
appTileBody = <div className="mx_AppTile_persistedWrapper">
385+
<PersistedElement persistKey={this._persistKey}>
386+
{appTileBody}
387+
</PersistedElement>
388+
</div>;
391389
}
392390
}
393391

@@ -474,10 +472,6 @@ AppTile.propTypes = {
474472
handleMinimisePointerEvents: PropTypes.bool,
475473
// Optionally hide the popout widget icon
476474
showPopout: PropTypes.bool,
477-
// Widget capabilities to allow by default (without user confirmation)
478-
// NOTE -- Use with caution. This is intended to aid better integration / UX
479-
// basic widget capabilities, e.g. injecting sticker message events.
480-
whitelistCapabilities: PropTypes.array,
481475
// Is this an instance of a user widget
482476
userWidget: PropTypes.bool,
483477
};
@@ -488,7 +482,6 @@ AppTile.defaultProps = {
488482
showTitle: true,
489483
showPopout: true,
490484
handleMinimisePointerEvents: false,
491-
whitelistCapabilities: [],
492485
userWidget: false,
493486
miniMode: false,
494487
};

src/components/views/elements/PersistentApp.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export default class PersistentApp extends React.Component {
7171
appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(),
7272
persistentWidgetInRoomId, appEvent.getId(),
7373
);
74-
const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, persistentWidgetInRoomId);
7574
const AppTile = sdk.getComponent('elements.AppTile');
7675
return <AppTile
7776
key={app.id}
@@ -82,7 +81,6 @@ export default class PersistentApp extends React.Component {
8281
creatorUserId={app.creatorUserId}
8382
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
8483
waitForIframeLoad={app.waitForIframeLoad}
85-
whitelistCapabilities={capWhitelist}
8684
miniMode={true}
8785
showMenubar={false}
8886
/>;

src/components/views/right_panel/WidgetCard.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
103103
creatorUserId={app.creatorUserId}
104104
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
105105
waitForIframeLoad={app.waitForIframeLoad}
106-
whitelistCapabilities={WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, room.roomId)}
107106
/>
108107
</BaseCard>;
109108
};

src/components/views/rooms/AppsDrawer.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ export default class AppsDrawer extends React.Component {
221221
creatorUserId={app.creatorUserId}
222222
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
223223
waitForIframeLoad={app.waitForIframeLoad}
224-
whitelistCapabilities={capWhitelist}
225224
/>);
226225
});
227226

src/components/views/rooms/Stickerpicker.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ export default class Stickerpicker extends React.Component {
280280
showPopout={false}
281281
onMinimiseClick={this._onHideStickersClick}
282282
handleMinimisePointerEvents={true}
283-
whitelistCapabilities={['m.sticker', 'visibility']}
284283
userWidget={true}
285284
/>
286285
</PersistedElement>

src/stores/widgets/StopGapWidget.ts

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ interface IAppTileProps {
7575

7676
// TODO: Don't use this because it's wrong
7777
class ElementWidget extends Widget {
78-
constructor(w) {
79-
super(w);
78+
constructor(private rawDefinition: IWidget) {
79+
super(rawDefinition);
8080
}
8181

8282
public get templateUrl(): string {
@@ -137,12 +137,7 @@ class ElementWidget extends Widget {
137137

138138
public getCompleteUrl(params: ITemplateParams, asPopout=false): string {
139139
return runTemplate(asPopout ? this.popoutTemplateUrl : this.templateUrl, {
140-
// we need to supply a whole widget to the template, but don't have
141-
// easy access to the definition the superclass is using, so be sad
142-
// and gutwrench it.
143-
// This isn't a problem when the widget architecture is fixed and this
144-
// subclass gets deleted.
145-
...super['definition'], // XXX: Private member access
140+
...this.rawDefinition,
146141
data: this.rawData,
147142
}, params);
148143
}
@@ -351,18 +346,39 @@ export class StopGapWidget extends EventEmitter {
351346
MatrixClientPeg.get().on('event', this.onEvent);
352347
MatrixClientPeg.get().on('Event.decrypted', this.onEventDecrypted);
353348

354-
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
355-
this.messaging.on("action:set_always_on_screen",
356-
(ev: CustomEvent<IStickyActionRequest>) => {
357-
if (this.messaging.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
349+
this.messaging.on(`action:${WidgetApiFromWidgetAction.UpdateAlwaysOnScreen}`,
350+
(ev: CustomEvent<IStickyActionRequest>) => {
351+
if (this.messaging.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
352+
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
358353
CountlyAnalytics.instance.trackJoinCall(this.appTileProps.room.roomId, true, true);
359-
ActiveWidgetStore.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value);
360-
ev.preventDefault();
361-
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); // ack
362354
}
363-
},
364-
);
365-
} else if (WidgetType.STICKERPICKER.matches(this.mockWidget.type)) {
355+
ActiveWidgetStore.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value);
356+
ev.preventDefault();
357+
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); // ack
358+
}
359+
},
360+
);
361+
362+
// TODO: Replace this event listener with appropriate driver functionality once the API
363+
// establishes a sane way to send events back and forth.
364+
this.messaging.on(`action:${WidgetApiFromWidgetAction.SendSticker}`,
365+
(ev: CustomEvent<IStickerActionRequest>) => {
366+
if (this.messaging.hasCapability(MatrixCapabilities.StickerSending)) {
367+
// Acknowledge first
368+
ev.preventDefault();
369+
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
370+
371+
// Send the sticker
372+
defaultDispatcher.dispatch({
373+
action: 'm.sticker',
374+
data: ev.detail.data,
375+
widgetId: this.mockWidget.id,
376+
});
377+
}
378+
},
379+
);
380+
381+
if (WidgetType.STICKERPICKER.matches(this.mockWidget.type)) {
366382
this.messaging.on(`action:${ElementWidgetActions.OpenIntegrationManager}`,
367383
(ev: CustomEvent<IWidgetApiRequest>) => {
368384
// Acknowledge first
@@ -394,23 +410,6 @@ export class StopGapWidget extends EventEmitter {
394410
}
395411
},
396412
);
397-
398-
// TODO: Replace this event listener with appropriate driver functionality once the API
399-
// establishes a sane way to send events back and forth.
400-
this.messaging.on(`action:${WidgetApiFromWidgetAction.SendSticker}`,
401-
(ev: CustomEvent<IStickerActionRequest>) => {
402-
// Acknowledge first
403-
ev.preventDefault();
404-
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
405-
406-
// Send the sticker
407-
defaultDispatcher.dispatch({
408-
action: 'm.sticker',
409-
data: ev.detail.data,
410-
widgetId: this.mockWidget.id,
411-
});
412-
},
413-
);
414413
}
415414
}
416415

0 commit comments

Comments
 (0)