Merge branch 'develop' into travis/room-list/unread-2
This commit is contained in:
commit
894357f7f6
16 changed files with 99 additions and 35 deletions
|
@ -304,18 +304,18 @@ limitations under the License.
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.mx_RoomSublist2_badgeContainer {
|
.mx_RoomSublist2_badgeContainer {
|
||||||
order: 1;
|
order: 0;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSublist2_headerText {
|
.mx_RoomSublist2_stickable {
|
||||||
order: 2;
|
order: 1;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSublist2_auxButton {
|
.mx_RoomSublist2_auxButton {
|
||||||
order: 4;
|
order: 2;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
width: 32px !important; // !important to override hover styles
|
width: 32px !important; // !important to override hover styles
|
||||||
height: 32px !important; // !important to override hover styles
|
height: 32px !important; // !important to override hover styles
|
||||||
|
|
|
@ -25,6 +25,7 @@ import RoomViewStore from "./stores/RoomViewStore";
|
||||||
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import {Capability} from "./widgets/WidgetApi";
|
import {Capability} from "./widgets/WidgetApi";
|
||||||
|
import {objectClone} from "./utils/objects";
|
||||||
|
|
||||||
const WIDGET_API_VERSION = '0.0.2'; // Current API version
|
const WIDGET_API_VERSION = '0.0.2'; // Current API version
|
||||||
const SUPPORTED_WIDGET_API_VERSIONS = [
|
const SUPPORTED_WIDGET_API_VERSIONS = [
|
||||||
|
@ -247,7 +248,7 @@ export default class FromWidgetPostMessageApi {
|
||||||
* @param {Object} res Response data
|
* @param {Object} res Response data
|
||||||
*/
|
*/
|
||||||
sendResponse(event, res) {
|
sendResponse(event, res) {
|
||||||
const data = JSON.parse(JSON.stringify(event.data));
|
const data = objectClone(event.data);
|
||||||
data.response = res;
|
data.response = res;
|
||||||
event.source.postMessage(data, event.origin);
|
event.source.postMessage(data, event.origin);
|
||||||
}
|
}
|
||||||
|
@ -260,7 +261,7 @@ export default class FromWidgetPostMessageApi {
|
||||||
*/
|
*/
|
||||||
sendError(event, msg, nestedError) {
|
sendError(event, msg, nestedError) {
|
||||||
console.error('Action:' + event.data.action + ' failed with message: ' + msg);
|
console.error('Action:' + event.data.action + ' failed with message: ' + msg);
|
||||||
const data = JSON.parse(JSON.stringify(event.data));
|
const data = objectClone(event.data);
|
||||||
data.response = {
|
data.response = {
|
||||||
error: {
|
error: {
|
||||||
message: msg,
|
message: msg,
|
||||||
|
|
|
@ -244,16 +244,17 @@ import RoomViewStore from './stores/RoomViewStore';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
import {WidgetType} from "./widgets/WidgetType";
|
import {WidgetType} from "./widgets/WidgetType";
|
||||||
|
import {objectClone} from "./utils/objects";
|
||||||
|
|
||||||
function sendResponse(event, res) {
|
function sendResponse(event, res) {
|
||||||
const data = JSON.parse(JSON.stringify(event.data));
|
const data = objectClone(event.data);
|
||||||
data.response = res;
|
data.response = res;
|
||||||
event.source.postMessage(data, event.origin);
|
event.source.postMessage(data, event.origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendError(event, msg, nestedError) {
|
function sendError(event, msg, nestedError) {
|
||||||
console.error("Action:" + event.data.action + " failed with message: " + msg);
|
console.error("Action:" + event.data.action + " failed with message: " + msg);
|
||||||
const data = JSON.parse(JSON.stringify(event.data));
|
const data = objectClone(event.data);
|
||||||
data.response = {
|
data.response = {
|
||||||
error: {
|
error: {
|
||||||
message: msg,
|
message: msg,
|
||||||
|
|
|
@ -19,7 +19,6 @@ import TagPanel from "./TagPanel";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import dis from "../../dispatcher/dispatcher";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
import { _t } from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
import SearchBox from "./SearchBox";
|
|
||||||
import RoomList2 from "../views/rooms/RoomList2";
|
import RoomList2 from "../views/rooms/RoomList2";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
|
@ -30,6 +29,8 @@ import AccessibleButton from "../views/elements/AccessibleButton";
|
||||||
import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2";
|
import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2";
|
||||||
import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore";
|
import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore";
|
||||||
import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
||||||
|
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||||
|
import { createRef } from "react";
|
||||||
|
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
* CAUTION *
|
* CAUTION *
|
||||||
|
@ -41,6 +42,7 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
|
resizeNotifier: ResizeNotifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
@ -49,6 +51,8 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LeftPanel2 extends React.Component<IProps, IState> {
|
export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
|
private listContainerRef: React.RefObject<HTMLDivElement> = createRef();
|
||||||
|
|
||||||
// TODO: Properly support TagPanel
|
// TODO: Properly support TagPanel
|
||||||
// TODO: Properly support searching/filtering
|
// TODO: Properly support searching/filtering
|
||||||
// TODO: Properly support breadcrumbs
|
// TODO: Properly support breadcrumbs
|
||||||
|
@ -65,10 +69,15 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
|
|
||||||
|
// We watch the middle panel because we don't actually get resized, the middle panel does.
|
||||||
|
// We listen to the noisy channel to avoid choppy reaction times.
|
||||||
|
this.props.resizeNotifier.on("middlePanelResizedNoisy", this.onResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
|
this.props.resizeNotifier.off("middlePanelResizedNoisy", this.onResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearch = (term: string): void => {
|
private onSearch = (term: string): void => {
|
||||||
|
@ -86,9 +95,7 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Apply this on resize, init, etc for reliability
|
private handleStickyHeaders(list: HTMLDivElement) {
|
||||||
private onScroll = (ev: React.MouseEvent<HTMLDivElement>) => {
|
|
||||||
const list = ev.target as HTMLDivElement;
|
|
||||||
const rlRect = list.getBoundingClientRect();
|
const rlRect = list.getBoundingClientRect();
|
||||||
const bottom = rlRect.bottom;
|
const bottom = rlRect.bottom;
|
||||||
const top = rlRect.top;
|
const top = rlRect.top;
|
||||||
|
@ -123,6 +130,18 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
header.style.top = `unset`;
|
header.style.top = `unset`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Apply this on resize, init, etc for reliability
|
||||||
|
private onScroll = (ev: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
const list = ev.target as HTMLDivElement;
|
||||||
|
this.handleStickyHeaders(list);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onResize = () => {
|
||||||
|
console.log("Resize width");
|
||||||
|
if (!this.listContainerRef.current) return; // ignore: no headers to sticky
|
||||||
|
this.handleStickyHeaders(this.listContainerRef.current);
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderHeader(): React.ReactNode {
|
private renderHeader(): React.ReactNode {
|
||||||
|
@ -230,9 +249,11 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
||||||
<aside className="mx_LeftPanel2_roomListContainer">
|
<aside className="mx_LeftPanel2_roomListContainer">
|
||||||
{this.renderHeader()}
|
{this.renderHeader()}
|
||||||
{this.renderSearchExplore()}
|
{this.renderSearchExplore()}
|
||||||
<div className="mx_LeftPanel2_actualRoomListContainer" onScroll={this.onScroll}>
|
<div
|
||||||
{roomList}
|
className="mx_LeftPanel2_actualRoomListContainer"
|
||||||
</div>
|
onScroll={this.onScroll}
|
||||||
|
ref={this.listContainerRef}
|
||||||
|
>{roomList}</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -677,7 +677,10 @@ class LoggedInView extends React.PureComponent<IProps, IState> {
|
||||||
if (SettingsStore.isFeatureEnabled("feature_new_room_list")) {
|
if (SettingsStore.isFeatureEnabled("feature_new_room_list")) {
|
||||||
// TODO: Supply props like collapsed and disabled to LeftPanel2
|
// TODO: Supply props like collapsed and disabled to LeftPanel2
|
||||||
leftPanel = (
|
leftPanel = (
|
||||||
<LeftPanel2 isMinimized={this.props.collapseLhs || false} />
|
<LeftPanel2
|
||||||
|
isMinimized={this.props.collapseLhs || false}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ export default class UserMenuButton extends React.Component<IProps, IState> {
|
||||||
SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
||||||
|
|
||||||
const newTheme = this.state.isDarkTheme ? "light" : "dark";
|
const newTheme = this.state.isDarkTheme ? "light" : "dark";
|
||||||
SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme);
|
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSettingsOpen = (ev: ButtonEvent, tabId: string) => {
|
private onSettingsOpen = (ev: ButtonEvent, tabId: string) => {
|
||||||
|
|
|
@ -291,7 +291,18 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
'mx_RoomSublist2_headerContainer_withAux': !!addRoomButton,
|
'mx_RoomSublist2_headerContainer_withAux': !!addRoomButton,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const badgeContainer = (
|
||||||
|
<div className="mx_RoomSublist2_badgeContainer">
|
||||||
|
{badge}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: a11y (see old component)
|
// TODO: a11y (see old component)
|
||||||
|
// Note: the addRoomButton conditionally gets moved around
|
||||||
|
// the DOM depending on whether or not the list is minimized.
|
||||||
|
// If we're minimized, we want it below the header so it
|
||||||
|
// doesn't become sticky.
|
||||||
|
// The same applies to the notification badge.
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<div className='mx_RoomSublist2_stickable'>
|
<div className='mx_RoomSublist2_stickable'>
|
||||||
|
@ -307,11 +318,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
||||||
<span>{this.props.label}</span>
|
<span>{this.props.label}</span>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
{this.renderMenu()}
|
{this.renderMenu()}
|
||||||
{addRoomButton}
|
{this.props.isMinimized ? null : addRoomButton}
|
||||||
<div className="mx_RoomSublist2_badgeContainer">
|
{this.props.isMinimized ? null : badgeContainer}
|
||||||
{badge}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{this.props.isMinimized ? badgeContainer : null}
|
||||||
|
{this.props.isMinimized ? addRoomButton : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {_t, pickBestLanguage} from "../../../languageHandler";
|
import {_t, pickBestLanguage} from "../../../languageHandler";
|
||||||
import * as sdk from "../../..";
|
import * as sdk from "../../..";
|
||||||
|
import {objectClone} from "../../../utils/objects";
|
||||||
|
|
||||||
export default class InlineTermsAgreement extends React.Component {
|
export default class InlineTermsAgreement extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -56,7 +57,7 @@ export default class InlineTermsAgreement extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_togglePolicy = (index) => {
|
_togglePolicy = (index) => {
|
||||||
const policies = JSON.parse(JSON.stringify(this.state.policies)); // deep & cheap clone
|
const policies = objectClone(this.state.policies);
|
||||||
policies[index].checked = !policies[index].checked;
|
policies[index].checked = !policies[index].checked;
|
||||||
this.setState({policies});
|
this.setState({policies});
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
|
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
|
||||||
import {SettingLevel} from "../SettingsStore";
|
import {SettingLevel} from "../SettingsStore";
|
||||||
import {objectKeyChanges} from "../../utils/objects";
|
import {objectClone, objectKeyChanges} from "../../utils/objects";
|
||||||
|
|
||||||
const BREADCRUMBS_LEGACY_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms";
|
const BREADCRUMBS_LEGACY_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms";
|
||||||
const BREADCRUMBS_EVENT_TYPE = "im.vector.setting.breadcrumbs";
|
const BREADCRUMBS_EVENT_TYPE = "im.vector.setting.breadcrumbs";
|
||||||
|
@ -162,7 +162,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
|
||||||
|
|
||||||
const event = cli.getAccountData(eventType);
|
const event = cli.getAccountData(eventType);
|
||||||
if (!event || !event.getContent()) return null;
|
if (!event || !event.getContent()) return null;
|
||||||
return JSON.parse(JSON.stringify(event.getContent())); // clone to prevent mutation
|
return objectClone(event.getContent()); // clone to prevent mutation
|
||||||
}
|
}
|
||||||
|
|
||||||
_notifyBreadcrumbsUpdate(event) {
|
_notifyBreadcrumbsUpdate(event) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
|
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
|
||||||
import {SettingLevel} from "../SettingsStore";
|
import {SettingLevel} from "../SettingsStore";
|
||||||
import {objectKeyChanges} from "../../utils/objects";
|
import {objectClone, objectKeyChanges} from "../../utils/objects";
|
||||||
|
|
||||||
const ALLOWED_WIDGETS_EVENT_TYPE = "im.vector.setting.allowed_widgets";
|
const ALLOWED_WIDGETS_EVENT_TYPE = "im.vector.setting.allowed_widgets";
|
||||||
|
|
||||||
|
@ -137,6 +137,6 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin
|
||||||
|
|
||||||
const event = room.getAccountData(eventType);
|
const event = room.getAccountData(eventType);
|
||||||
if (!event || !event.getContent()) return null;
|
if (!event || !event.getContent()) return null;
|
||||||
return event.getContent();
|
return objectClone(event.getContent()); // clone to prevent mutation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
|
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
|
||||||
import {SettingLevel} from "../SettingsStore";
|
import {SettingLevel} from "../SettingsStore";
|
||||||
import {objectKeyChanges} from "../../utils/objects";
|
import {objectClone, objectKeyChanges} from "../../utils/objects";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets and sets settings at the "room" level.
|
* Gets and sets settings at the "room" level.
|
||||||
|
@ -117,6 +117,6 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl
|
||||||
|
|
||||||
const event = room.currentState.getStateEvents(eventType, "");
|
const event = room.currentState.getStateEvents(eventType, "");
|
||||||
if (!event || !event.getContent()) return null;
|
if (!event || !event.getContent()) return null;
|
||||||
return event.getContent();
|
return objectClone(event.getContent()); // clone to prevent mutation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires when the middle panel has been resized.
|
* Fires when the middle panel has been resized (throttled).
|
||||||
* @event module:utils~ResizeNotifier#"middlePanelResized"
|
* @event module:utils~ResizeNotifier#"middlePanelResized"
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Fires when the middle panel has been resized by a pixel.
|
||||||
|
* @event module:utils~ResizeNotifier#"middlePanelResizedNoisy"
|
||||||
|
*/
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { throttle } from "lodash";
|
import { throttle } from "lodash";
|
||||||
|
|
||||||
|
@ -29,15 +33,24 @@ export default class ResizeNotifier extends EventEmitter {
|
||||||
this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
|
this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_noisyMiddlePanel() {
|
||||||
|
this.emit("middlePanelResizedNoisy");
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateMiddlePanel() {
|
||||||
|
this._throttledMiddlePanel();
|
||||||
|
this._noisyMiddlePanel();
|
||||||
|
}
|
||||||
|
|
||||||
// can be called in quick succession
|
// can be called in quick succession
|
||||||
notifyLeftHandleResized() {
|
notifyLeftHandleResized() {
|
||||||
// don't emit event for own region
|
// don't emit event for own region
|
||||||
this._throttledMiddlePanel();
|
this._updateMiddlePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
// can be called in quick succession
|
// can be called in quick succession
|
||||||
notifyRightHandleResized() {
|
notifyRightHandleResized() {
|
||||||
this._throttledMiddlePanel();
|
this._updateMiddlePanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
// can be called in quick succession
|
// can be called in quick succession
|
||||||
|
@ -48,7 +61,7 @@ export default class ResizeNotifier extends EventEmitter {
|
||||||
// taller than the available space
|
// taller than the available space
|
||||||
this.emit("leftPanelResized");
|
this.emit("leftPanelResized");
|
||||||
|
|
||||||
this._throttledMiddlePanel();
|
this._updateMiddlePanel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {IntegrationManagers} from "../integrations/IntegrationManagers";
|
||||||
import {Capability} from "../widgets/WidgetApi";
|
import {Capability} from "../widgets/WidgetApi";
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
import {WidgetType} from "../widgets/WidgetType";
|
import {WidgetType} from "../widgets/WidgetType";
|
||||||
|
import {objectClone} from "./objects";
|
||||||
|
|
||||||
export default class WidgetUtils {
|
export default class WidgetUtils {
|
||||||
/* Returns true if user is able to send state events to modify widgets in this room
|
/* Returns true if user is able to send state events to modify widgets in this room
|
||||||
|
@ -222,7 +223,7 @@ export default class WidgetUtils {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
// Get the current widgets and clone them before we modify them, otherwise
|
// Get the current widgets and clone them before we modify them, otherwise
|
||||||
// we'll modify the content of the old event.
|
// we'll modify the content of the old event.
|
||||||
const userWidgets = JSON.parse(JSON.stringify(WidgetUtils.getUserWidgets()));
|
const userWidgets = objectClone(WidgetUtils.getUserWidgets());
|
||||||
|
|
||||||
// Delete existing widget with ID
|
// Delete existing widget with ID
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -47,3 +47,14 @@ export function objectKeyChanges(a: any, b: any): string[] {
|
||||||
const diff = objectDiff(a, b);
|
const diff = objectDiff(a, b);
|
||||||
return arrayMerge(diff.removed, diff.added, diff.changed);
|
return arrayMerge(diff.removed, diff.added, diff.changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones an object by running it through JSON parsing. Note that this
|
||||||
|
* will destroy any complicated object types which do not translate to
|
||||||
|
* JSON.
|
||||||
|
* @param obj The object to clone.
|
||||||
|
* @returns The cloned object
|
||||||
|
*/
|
||||||
|
export function objectClone(obj: any): any {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ limitations under the License.
|
||||||
|
|
||||||
// converts a pixel value to rem.
|
// converts a pixel value to rem.
|
||||||
export function toRem(pixelValue: number): string {
|
export function toRem(pixelValue: number): string {
|
||||||
return pixelValue / 15 + "rem";
|
return pixelValue / 10 + "rem";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toPx(pixelValue: number): string {
|
export function toPx(pixelValue: number): string {
|
||||||
|
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
import { objectClone } from "../utils/objects";
|
||||||
|
|
||||||
export enum Capability {
|
export enum Capability {
|
||||||
Screenshot = "m.capability.screenshot",
|
Screenshot = "m.capability.screenshot",
|
||||||
|
@ -140,7 +141,7 @@ export class WidgetApi extends EventEmitter {
|
||||||
private replyToRequest(payload: ToWidgetRequest, reply: any) {
|
private replyToRequest(payload: ToWidgetRequest, reply: any) {
|
||||||
if (!window.parent) return;
|
if (!window.parent) return;
|
||||||
|
|
||||||
const request = JSON.parse(JSON.stringify(payload));
|
const request = objectClone(payload);
|
||||||
request.response = reply;
|
request.response = reply;
|
||||||
|
|
||||||
window.parent.postMessage(request, this.origin);
|
window.parent.postMessage(request, this.origin);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue