Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/feat/modal-widgets
This commit is contained in:
commit
0004dd4475
88 changed files with 2241 additions and 1305 deletions
|
@ -55,7 +55,7 @@ class WidgetEchoStore extends EventEmitter {
|
|||
const widgetId = w.getStateKey();
|
||||
// If there's no echo, or the echo still has a widget present, show the *old* widget
|
||||
// we don't include widgets that have changed for the same reason we don't include new ones,
|
||||
// ie. we'd need to fake matrix events to do so and therte's currently no need.
|
||||
// ie. we'd need to fake matrix events to do so and there's currently no need.
|
||||
if (!roomEchoState[widgetId] || Object.keys(roomEchoState[widgetId]).length !== 0) {
|
||||
echoedWidgets.push(w);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@ limitations under the License.
|
|||
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { IWidget } from "matrix-widget-api";
|
||||
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import WidgetEchoStore from "../stores/WidgetEchoStore";
|
||||
import RoomViewStore from "../stores/RoomViewStore";
|
||||
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
||||
import WidgetUtils from "../utils/WidgetUtils";
|
||||
import {SettingLevel} from "../settings/SettingLevel";
|
||||
|
@ -30,13 +32,9 @@ import {UPDATE_EVENT} from "./AsyncStore";
|
|||
|
||||
interface IState {}
|
||||
|
||||
export interface IApp {
|
||||
id: string;
|
||||
type: string;
|
||||
export interface IApp extends IWidget {
|
||||
roomId: string;
|
||||
eventId: string;
|
||||
creatorUserId: string;
|
||||
waitForIframeLoad?: boolean;
|
||||
// eslint-disable-next-line camelcase
|
||||
avatar_url: string; // MSC2765 https://github.com/matrix-org/matrix-doc/pull/2765
|
||||
}
|
||||
|
@ -46,6 +44,8 @@ interface IRoomWidgets {
|
|||
pinned: Record<string, boolean>;
|
||||
}
|
||||
|
||||
export const MAX_PINNED = 3;
|
||||
|
||||
// TODO consolidate WidgetEchoStore into this
|
||||
// TODO consolidate ActiveWidgetStore into this
|
||||
export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
||||
|
@ -68,7 +68,7 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
private initRoom(roomId: string) {
|
||||
if (!this.roomMap.has(roomId)) {
|
||||
this.roomMap.set(roomId, {
|
||||
pinned: {},
|
||||
pinned: {}, // ordered
|
||||
widgets: [],
|
||||
});
|
||||
}
|
||||
|
@ -122,6 +122,15 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
if (!room) return;
|
||||
const roomInfo = this.roomMap.get(room.roomId);
|
||||
roomInfo.widgets = [];
|
||||
|
||||
// first clean out old widgets from the map which originate from this room
|
||||
// otherwise we are out of sync with the rest of the app with stale widget events during removal
|
||||
Array.from(this.widgetMap.values()).forEach(app => {
|
||||
if (app.roomId === room.roomId) {
|
||||
this.widgetMap.delete(app.id);
|
||||
}
|
||||
});
|
||||
|
||||
this.generateApps(room).forEach(app => {
|
||||
this.widgetMap.set(app.id, app);
|
||||
roomInfo.widgets.push(app);
|
||||
|
@ -156,27 +165,34 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
|
||||
public isPinned(widgetId: string) {
|
||||
const roomId = this.getRoomId(widgetId);
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
|
||||
let pinned = roomInfo && roomInfo.pinned[widgetId];
|
||||
// Jitsi widgets should be pinned by default
|
||||
const widget = this.widgetMap.get(widgetId);
|
||||
if (pinned === undefined && WidgetType.JITSI.matches(widget?.type)) pinned = true;
|
||||
return pinned;
|
||||
return !!this.getPinnedApps(roomId).find(w => w.id === widgetId);
|
||||
}
|
||||
|
||||
public canPin(widgetId: string) {
|
||||
// only allow pinning up to a max of two as we do not yet have grid splits
|
||||
// the only case it will go to three is if you have two and then a Jitsi gets added
|
||||
const roomId = this.getRoomId(widgetId);
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
return roomInfo && Object.keys(roomInfo.pinned).filter(k => {
|
||||
return roomInfo.pinned[k] && roomInfo.widgets.some(app => app.id === k);
|
||||
}).length < 2;
|
||||
return this.getPinnedApps(roomId).length < MAX_PINNED;
|
||||
}
|
||||
|
||||
public pinWidget(widgetId: string) {
|
||||
const roomId = this.getRoomId(widgetId);
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
if (!roomInfo) return;
|
||||
|
||||
// When pinning, first confirm all the widgets (Jitsi) which were autopinned so that the order is correct
|
||||
const autoPinned = this.getPinnedApps(roomId).filter(app => !roomInfo.pinned[app.id]);
|
||||
autoPinned.forEach(app => {
|
||||
this.setPinned(app.id, true);
|
||||
});
|
||||
|
||||
this.setPinned(widgetId, true);
|
||||
|
||||
// Show the apps drawer upon the user pinning a widget
|
||||
if (RoomViewStore.getRoomId() === this.getRoomId(widgetId)) {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "appsDrawer",
|
||||
show: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public unpinWidget(widgetId: string) {
|
||||
|
@ -187,6 +203,10 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
const roomId = this.getRoomId(widgetId);
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
if (!roomInfo) return;
|
||||
if (roomInfo.pinned[widgetId] === false && value) {
|
||||
// delete this before write to maintain the correct object insertion order
|
||||
delete roomInfo.pinned[widgetId];
|
||||
}
|
||||
roomInfo.pinned[widgetId] = value;
|
||||
|
||||
// Clean up the pinned record
|
||||
|
@ -201,13 +221,61 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
this.emit(UPDATE_EVENT);
|
||||
}
|
||||
|
||||
public getApps(room: Room, pinned?: boolean): IApp[] {
|
||||
const roomInfo = this.getRoom(room.roomId);
|
||||
if (!roomInfo) return [];
|
||||
if (pinned) {
|
||||
return roomInfo.widgets.filter(app => this.isPinned(app.id));
|
||||
public movePinnedWidget(widgetId: string, delta: 1 | -1) {
|
||||
// TODO simplify this by changing the storage medium of pinned to an array once the Jitsi default-on goes away
|
||||
const roomId = this.getRoomId(widgetId);
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
if (!roomInfo || roomInfo.pinned[widgetId] === false) return;
|
||||
|
||||
const pinnedApps = this.getPinnedApps(roomId).map(app => app.id);
|
||||
const i = pinnedApps.findIndex(id => id === widgetId);
|
||||
|
||||
if (delta > 0) {
|
||||
pinnedApps.splice(i, 2, pinnedApps[i + 1], pinnedApps[i]);
|
||||
} else {
|
||||
pinnedApps.splice(i - 1, 2, pinnedApps[i], pinnedApps[i - 1]);
|
||||
}
|
||||
return roomInfo.widgets;
|
||||
|
||||
const reorderedPinned: IRoomWidgets["pinned"] = {};
|
||||
pinnedApps.forEach(id => {
|
||||
reorderedPinned[id] = true;
|
||||
});
|
||||
Object.keys(roomInfo.pinned).forEach(id => {
|
||||
if (reorderedPinned[id] === undefined) {
|
||||
reorderedPinned[id] = roomInfo.pinned[id];
|
||||
}
|
||||
});
|
||||
roomInfo.pinned = reorderedPinned;
|
||||
|
||||
SettingsStore.setValue("Widgets.pinned", roomId, SettingLevel.ROOM_ACCOUNT, roomInfo.pinned);
|
||||
this.emit(roomId);
|
||||
this.emit(UPDATE_EVENT);
|
||||
}
|
||||
|
||||
public getPinnedApps(roomId: string): IApp[] {
|
||||
// returns the apps in the order they were pinned with, up to the maximum
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
if (!roomInfo) return [];
|
||||
|
||||
// Show Jitsi widgets even if the user already had the maximum pinned, instead of their latest pinned,
|
||||
// except if the user already explicitly unpinned the Jitsi widget
|
||||
const priorityWidget = roomInfo.widgets.find(widget => {
|
||||
return roomInfo.pinned[widget.id] === undefined && WidgetType.JITSI.matches(widget.type);
|
||||
});
|
||||
|
||||
const order = Object.keys(roomInfo.pinned).filter(k => roomInfo.pinned[k]);
|
||||
let apps = order.map(wId => this.widgetMap.get(wId)).filter(Boolean);
|
||||
apps = apps.slice(0, priorityWidget ? MAX_PINNED - 1 : MAX_PINNED);
|
||||
if (priorityWidget) {
|
||||
apps.push(priorityWidget);
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
public getApps(roomId: string): IApp[] {
|
||||
const roomInfo = this.getRoom(roomId);
|
||||
return roomInfo?.widgets || [];
|
||||
}
|
||||
|
||||
public doesRoomHaveConference(room: Room): boolean {
|
||||
|
|
|
@ -76,7 +76,7 @@ class ElementWidget extends Widget {
|
|||
if (WidgetType.JITSI.matches(this.type)) {
|
||||
return WidgetUtils.getLocalJitsiWrapperUrl({
|
||||
forLocalRender: true,
|
||||
auth: super.rawData?.auth, // this.rawData can call templateUrl, do this to prevent looping
|
||||
auth: super.rawData?.auth as string, // this.rawData can call templateUrl, do this to prevent looping
|
||||
});
|
||||
}
|
||||
return super.templateUrl;
|
||||
|
@ -86,7 +86,7 @@ class ElementWidget extends Widget {
|
|||
if (WidgetType.JITSI.matches(this.type)) {
|
||||
return WidgetUtils.getLocalJitsiWrapperUrl({
|
||||
forLocalRender: false, // The only important difference between this and templateUrl()
|
||||
auth: super.rawData?.auth,
|
||||
auth: super.rawData?.auth as string,
|
||||
});
|
||||
}
|
||||
return this.templateUrl; // use this instead of super to ensure we get appropriate templating
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue