Fixes https://github.com/vector-im/riot-web/issues/9354 https://github.com/matrix-org/matrix-react-sdk/pull/2801 introduced a change which tried to make sure that when the widget URL was changed that the picker would be re-mounted, however it accidentally introduced a regression. While it effectively did the task it wanted to, it failed to keep the previously-mounted sticker picker alive. This is because the Stickerpicker component is remounted when opened, and the _updateWidget function is called. This results in this.state not having the "current" widget, meaning a URL change is always detected when the component is remounted (room changes, open sticker picker). Instead of remounting always, we'll instead track which sticker picker widget is being used out of band. This therefore means that whenever the Stickerpicker component is mounted it doesn't create a whole new widget, and the existing (background) picker can be used. This also fixes the loading screen that people would see when opening the sticker picker after switching rooms, something which the persistent widget stuff is supposed to solve.
170 lines
5.0 KiB
JavaScript
170 lines
5.0 KiB
JavaScript
/*
|
|
Copyright 2018 New Vector Ltd
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
import EventEmitter from 'events';
|
|
|
|
import MatrixClientPeg from '../MatrixClientPeg';
|
|
|
|
/**
|
|
* Stores information about the widgets active in the app right now:
|
|
* * What widget is set to remain always-on-screen, if any
|
|
* Only one widget may be 'always on screen' at any one time.
|
|
* * Negotiated capabilities for active apps
|
|
* * Which stickerpicker the app is using, if any
|
|
*/
|
|
class ActiveWidgetStore extends EventEmitter {
|
|
constructor() {
|
|
super();
|
|
this._persistentWidgetId = null;
|
|
|
|
// A list of negotiated capabilities for each widget, by ID
|
|
// {
|
|
// widgetId: [caps...],
|
|
// }
|
|
this._capsByWidgetId = {};
|
|
|
|
// A WidgetMessaging instance for each widget ID
|
|
this._widgetMessagingByWidgetId = {};
|
|
|
|
// What room ID each widget is associated with (if it's a room widget)
|
|
this._roomIdByWidgetId = {};
|
|
|
|
// The sticker picker widget definition the app is currently using, if any
|
|
this._stickerPickerWidget = null;
|
|
|
|
this.onRoomStateEvents = this.onRoomStateEvents.bind(this);
|
|
|
|
this.dispatcherRef = null;
|
|
}
|
|
|
|
start() {
|
|
MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents);
|
|
}
|
|
|
|
stop() {
|
|
if (MatrixClientPeg.get()) {
|
|
MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents);
|
|
}
|
|
this._capsByWidgetId = {};
|
|
this._widgetMessagingByWidgetId = {};
|
|
this._roomIdByWidgetId = {};
|
|
}
|
|
|
|
onRoomStateEvents(ev, state) {
|
|
// XXX: This listens for state events in order to remove the active widget.
|
|
// Everything else relies on views listening for events and calling setters
|
|
// on this class which is terrible. This store should just listen for events
|
|
// and keep itself up to date.
|
|
if (ev.getType() !== 'im.vector.modular.widgets') return;
|
|
|
|
if (ev.getStateKey() === this._persistentWidgetId) {
|
|
this.destroyPersistentWidget();
|
|
}
|
|
}
|
|
|
|
destroyPersistentWidget() {
|
|
const toDeleteId = this._persistentWidgetId;
|
|
|
|
this.setWidgetPersistence(toDeleteId, false);
|
|
this.delWidgetMessaging(toDeleteId);
|
|
this.delWidgetCapabilities(toDeleteId);
|
|
this.delRoomId(toDeleteId);
|
|
}
|
|
|
|
setWidgetPersistence(widgetId, val) {
|
|
if (this._persistentWidgetId === widgetId && !val) {
|
|
this._persistentWidgetId = null;
|
|
} else if (this._persistentWidgetId !== widgetId && val) {
|
|
this._persistentWidgetId = widgetId;
|
|
}
|
|
this.emit('update');
|
|
}
|
|
|
|
getWidgetPersistence(widgetId) {
|
|
return this._persistentWidgetId === widgetId;
|
|
}
|
|
|
|
getPersistentWidgetId() {
|
|
return this._persistentWidgetId;
|
|
}
|
|
|
|
setWidgetCapabilities(widgetId, caps) {
|
|
this._capsByWidgetId[widgetId] = caps;
|
|
this.emit('update');
|
|
}
|
|
|
|
widgetHasCapability(widgetId, cap) {
|
|
return this._capsByWidgetId[widgetId] && this._capsByWidgetId[widgetId].includes(cap);
|
|
}
|
|
|
|
delWidgetCapabilities(widgetId) {
|
|
delete this._capsByWidgetId[widgetId];
|
|
this.emit('update');
|
|
}
|
|
|
|
setWidgetMessaging(widgetId, wm) {
|
|
this._widgetMessagingByWidgetId[widgetId] = wm;
|
|
this.emit('update');
|
|
}
|
|
|
|
getWidgetMessaging(widgetId) {
|
|
return this._widgetMessagingByWidgetId[widgetId];
|
|
}
|
|
|
|
delWidgetMessaging(widgetId) {
|
|
if (this._widgetMessagingByWidgetId[widgetId]) {
|
|
try {
|
|
this._widgetMessagingByWidgetId[widgetId].stop();
|
|
} catch (e) {
|
|
console.error('Failed to stop listening for widgetMessaging events', e.message);
|
|
}
|
|
delete this._widgetMessagingByWidgetId[widgetId];
|
|
this.emit('update');
|
|
}
|
|
}
|
|
|
|
getRoomId(widgetId) {
|
|
return this._roomIdByWidgetId[widgetId];
|
|
}
|
|
|
|
setRoomId(widgetId, roomId) {
|
|
this._roomIdByWidgetId[widgetId] = roomId;
|
|
this.emit('update');
|
|
}
|
|
|
|
delRoomId(widgetId) {
|
|
delete this._roomIdByWidgetId[widgetId];
|
|
this.emit('update');
|
|
}
|
|
|
|
getStickerPickerWidget() {
|
|
return this._stickerPickerWidget;
|
|
}
|
|
|
|
setStickerPickerWidget(widget) {
|
|
this._stickerPickerWidget = widget;
|
|
}
|
|
|
|
delStickerPickerWidget() {
|
|
this._stickerPickerWidget = null;
|
|
}
|
|
}
|
|
|
|
if (global.singletonActiveWidgetStore === undefined) {
|
|
global.singletonActiveWidgetStore = new ActiveWidgetStore();
|
|
}
|
|
export default global.singletonActiveWidgetStore;
|