diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5aac4e2974..151888a17e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,86 @@
+Changes in [3.10.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.10.0) (2020-12-07)
+=====================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.10.0-rc.1...v3.10.0)
+
+ * Upgrade to JS SDK 9.3.0
+
+Changes in [3.10.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.10.0-rc.1) (2020-12-02)
+===============================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.9.0...v3.10.0-rc.1)
+
+ * Upgrade to JS SDK 9.3.0-rc.1
+ * Translations update from Weblate
+ [\#5461](https://github.com/matrix-org/matrix-react-sdk/pull/5461)
+ * Fix VoIP call plinth on dark theme
+ [\#5460](https://github.com/matrix-org/matrix-react-sdk/pull/5460)
+ * Add sanity checking around widget pinning
+ [\#5459](https://github.com/matrix-org/matrix-react-sdk/pull/5459)
+ * Update i18n for Appearance User Settings
+ [\#5457](https://github.com/matrix-org/matrix-react-sdk/pull/5457)
+ * Only show 'answered elsewhere' if we tried to answer too
+ [\#5455](https://github.com/matrix-org/matrix-react-sdk/pull/5455)
+ * Fixed Avatar for 3PID invites
+ [\#5442](https://github.com/matrix-org/matrix-react-sdk/pull/5442)
+ * Slightly better error if we can't capture user media
+ [\#5449](https://github.com/matrix-org/matrix-react-sdk/pull/5449)
+ * Make it possible in-code to hide rooms from the room list
+ [\#5445](https://github.com/matrix-org/matrix-react-sdk/pull/5445)
+ * Fix the stickerpicker
+ [\#5447](https://github.com/matrix-org/matrix-react-sdk/pull/5447)
+ * Add live password validation to change password dialog
+ [\#5436](https://github.com/matrix-org/matrix-react-sdk/pull/5436)
+ * LaTeX rendering in element-web using KaTeX
+ [\#5244](https://github.com/matrix-org/matrix-react-sdk/pull/5244)
+ * Add lifecycle customisation point after logout
+ [\#5448](https://github.com/matrix-org/matrix-react-sdk/pull/5448)
+ * Simplify UserMenu for Guests as they can't use most of the options
+ [\#5421](https://github.com/matrix-org/matrix-react-sdk/pull/5421)
+ * Fix known issues with modal widgets
+ [\#5444](https://github.com/matrix-org/matrix-react-sdk/pull/5444)
+ * Fix existing widgets not having approved capabilities for their function
+ [\#5443](https://github.com/matrix-org/matrix-react-sdk/pull/5443)
+ * Use the WidgetDriver to run OIDC requests
+ [\#5440](https://github.com/matrix-org/matrix-react-sdk/pull/5440)
+ * Add a customisation point for widget permissions and fix amnesia issues
+ [\#5439](https://github.com/matrix-org/matrix-react-sdk/pull/5439)
+ * Fix Widget event notification text including spurious space
+ [\#5441](https://github.com/matrix-org/matrix-react-sdk/pull/5441)
+ * Move call listener out of MatrixChat
+ [\#5438](https://github.com/matrix-org/matrix-react-sdk/pull/5438)
+ * New Look in-Call View
+ [\#5432](https://github.com/matrix-org/matrix-react-sdk/pull/5432)
+ * Support arbitrary widgets sticking to the screen + sending stickers
+ [\#5435](https://github.com/matrix-org/matrix-react-sdk/pull/5435)
+ * Auth typescripting and validation tweaks
+ [\#5433](https://github.com/matrix-org/matrix-react-sdk/pull/5433)
+ * Add new widget API actions for changing rooms and sending/receiving events
+ [\#5385](https://github.com/matrix-org/matrix-react-sdk/pull/5385)
+ * Revert room header click behaviour to opening room settings
+ [\#5434](https://github.com/matrix-org/matrix-react-sdk/pull/5434)
+ * Add option to send/edit a message with Ctrl + Enter / Command + Enter
+ [\#5160](https://github.com/matrix-org/matrix-react-sdk/pull/5160)
+ * Add Analytics instrumentation to the Homepage
+ [\#5409](https://github.com/matrix-org/matrix-react-sdk/pull/5409)
+ * Fix encrypted video playback in Chrome-based browsers
+ [\#5430](https://github.com/matrix-org/matrix-react-sdk/pull/5430)
+ * Add border-radius for video
+ [\#5333](https://github.com/matrix-org/matrix-react-sdk/pull/5333)
+ * Push name to the end, near text, in IRC layout
+ [\#5166](https://github.com/matrix-org/matrix-react-sdk/pull/5166)
+ * Disable notifications for the room you have recently been active in
+ [\#5325](https://github.com/matrix-org/matrix-react-sdk/pull/5325)
+ * Search through the list of unfiltered rooms rather than the rooms in the
+ state which are already filtered by the search text
+ [\#5331](https://github.com/matrix-org/matrix-react-sdk/pull/5331)
+ * Lighten blockquote colour in dark mode
+ [\#5353](https://github.com/matrix-org/matrix-react-sdk/pull/5353)
+ * Specify community description img must be mxc urls
+ [\#5364](https://github.com/matrix-org/matrix-react-sdk/pull/5364)
+ * Add keyboard shortcut to close the current conversation
+ [\#5253](https://github.com/matrix-org/matrix-react-sdk/pull/5253)
+ * Redirect user home from auth screens if they are already logged in
+ [\#5423](https://github.com/matrix-org/matrix-react-sdk/pull/5423)
+
Changes in [3.9.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.9.0) (2020-11-23)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.9.0-rc.1...v3.9.0)
diff --git a/package.json b/package.json
index 1e778f9875..b328823b24 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
- "version": "3.9.0",
+ "version": "3.10.0",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@@ -58,6 +58,7 @@
"blueimp-canvas-to-blob": "^3.27.0",
"browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3",
+ "cheerio": "^1.0.0-rc.3",
"classnames": "^2.2.6",
"commonmark": "^0.29.1",
"counterpart": "^0.18.6",
@@ -77,7 +78,6 @@
"html-entities": "^1.3.1",
"is-ip": "^2.0.0",
"katex": "^0.12.0",
- "cheerio": "^1.0.0-rc.3",
"linkifyjs": "^2.1.9",
"lodash": "^4.17.19",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
@@ -159,6 +159,7 @@
"lolex": "^5.1.2",
"matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2",
+ "olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"react-test-renderer": "^16.13.1",
"rimraf": "^2.7.1",
"stylelint": "^9.10.1",
diff --git a/res/css/_common.scss b/res/css/_common.scss
index 7ab88d6f02..87336a1c03 100644
--- a/res/css/_common.scss
+++ b/res/css/_common.scss
@@ -170,7 +170,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
border: 1px solid rgba($primary-fg-color, .1);
// these things should probably not be defined globally
margin: 9px;
- flex: 0 0 auto;
}
.mx_textinput {
diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss
index a8cb7d7eee..9c98ca3a1c 100644
--- a/res/css/structures/auth/_Login.scss
+++ b/res/css/structures/auth/_Login.scss
@@ -81,6 +81,7 @@ limitations under the License.
}
.mx_Login_underlinedServerName {
+ width: max-content;
border-bottom: 1px dashed $accent-color;
}
diff --git a/res/css/views/rooms/_MemberList.scss b/res/css/views/rooms/_MemberList.scss
index f00907aeef..9753d3afb5 100644
--- a/res/css/views/rooms/_MemberList.scss
+++ b/res/css/views/rooms/_MemberList.scss
@@ -46,6 +46,11 @@ limitations under the License.
}
}
+.mx_GroupMemberList_query,
+.mx_GroupRoomList_query {
+ flex: 0 0 auto;
+}
+
.mx_MemberList_chevron {
position: absolute;
right: 35px;
@@ -59,10 +64,8 @@ limitations under the License.
flex: 1 1 0px;
}
-.mx_MemberList_query,
-.mx_GroupMemberList_query,
-.mx_GroupRoomList_query {
- flex: 1 1 0;
+.mx_MemberList_query {
+ height: 16px;
// stricter rule to override the one in _common.scss
&[type="text"] {
@@ -70,10 +73,6 @@ limitations under the License.
}
}
-.mx_MemberList_query {
- height: 16px;
-}
-
.mx_MemberList_wrapper {
padding: 10px;
}
diff --git a/res/css/views/voip/_CallView.scss b/res/css/views/voip/_CallView.scss
index 5447f32f5d..236880a531 100644
--- a/res/css/views/voip/_CallView.scss
+++ b/res/css/views/voip/_CallView.scss
@@ -20,6 +20,7 @@ limitations under the License.
background-color: $voipcall-plinth-color;
padding-left: 8px;
padding-right: 8px;
+ margin: 5px 5px 5px 18px;
// XXX: CallContainer sets pointer-events: none - should probably be set back in a better place
pointer-events: initial;
}
@@ -135,9 +136,9 @@ limitations under the License.
padding-top: 20px;
padding-bottom: 15px;
color: $accent-fg-color;
- font-weight: bold;
.mx_AccessibleButton_hasKind {
padding: 0px;
+ font-weight: bold;
}
}
@@ -219,6 +220,7 @@ limitations under the License.
}
.mx_CallView_header_callType {
+ font-size: 1.2rem;
font-weight: bold;
vertical-align: middle;
}
diff --git a/scripts/ci/riot-unit-tests.sh b/scripts/ci/riot-unit-tests.sh
deleted file mode 120000
index 199dfb58fd..0000000000
--- a/scripts/ci/riot-unit-tests.sh
+++ /dev/null
@@ -1 +0,0 @@
-app-tests.sh
\ No newline at end of file
diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx
index f443e59090..41dc031b06 100644
--- a/src/CallHandler.tsx
+++ b/src/CallHandler.tsx
@@ -416,14 +416,14 @@ export default class CallHandler {
title = _t("Unable to access microphone");
description =
{_t(
- "Call failed because no microphone could not be accessed. " +
+ "Call failed because microphone could not be accessed. " +
"Check that a microphone is plugged in and set up correctly.",
)}
;
} else if (call.type === CallType.Video) {
title = _t("Unable to access webcam / microphone");
description =
- {_t("Call failed because no webcam or microphone could not be accessed. Check that:")}
+ {_t("Call failed because webcam or microphone could not be accessed. Check that:")}
- {_t("A microphone and webcam are plugged in and set up correctly")}
- {_t("Permission is granted to use the webcam")}
diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index 9af5ebcbfb..e2ae875ac3 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -46,6 +46,7 @@ import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from
import SdkConfig from "./SdkConfig";
import SettingsStore from "./settings/SettingsStore";
import {UIFeature} from "./settings/UIFeature";
+import {CHAT_EFFECTS} from "./effects"
import CallHandler from "./CallHandler";
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
@@ -78,6 +79,7 @@ export const CommandCategories = {
"actions": _td("Actions"),
"admin": _td("Admin"),
"advanced": _td("Advanced"),
+ "effects": _td("Effects"),
"other": _td("Other"),
};
@@ -1094,6 +1096,30 @@ export const Commands = [
category: CommandCategories.messages,
hideCompletionAfterSpace: true,
}),
+
+ ...CHAT_EFFECTS.map((effect) => {
+ return new Command({
+ command: effect.command,
+ description: effect.description(),
+ args: '',
+ runFn: function(roomId, args) {
+ return success((async () => {
+ if (!args) {
+ args = effect.fallbackMessage();
+ MatrixClientPeg.get().sendEmoteMessage(roomId, args);
+ } else {
+ const content = {
+ msgtype: effect.msgType,
+ body: args,
+ };
+ MatrixClientPeg.get().sendMessage(roomId, content);
+ }
+ dis.dispatch({action: `effects.${effect.command}`});
+ })());
+ },
+ category: CommandCategories.effects,
+ })
+ }),
];
// build a map from names and aliases to the Command objects.
diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx
index adcb401ec1..0ee847fbc9 100644
--- a/src/components/structures/RoomView.tsx
+++ b/src/components/structures/RoomView.tsx
@@ -69,11 +69,15 @@ import AuxPanel from "../views/rooms/AuxPanel";
import RoomHeader from "../views/rooms/RoomHeader";
import {XOR} from "../../@types/common";
import { IThreepidInvite } from "../../stores/ThreepidInviteStore";
+import EffectsOverlay from "../views/elements/EffectsOverlay";
+import {containsEmoji} from '../../effects/utils';
+import {CHAT_EFFECTS} from '../../effects';
import { CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import WidgetStore from "../../stores/WidgetStore";
import {UPDATE_EVENT} from "../../stores/AsyncStore";
import Notifier from "../../Notifier";
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
+import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
const DEBUG = false;
let debuglog = function(msg: string) {};
@@ -248,6 +252,8 @@ export default class RoomView extends React.Component {
this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
this.context.on("userTrustStatusChanged", this.onUserVerificationChanged);
this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
+ this.context.on("Event.decrypted", this.onEventDecrypted);
+ this.context.on("event", this.onEvent);
// Start listening for RoomViewStore updates
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate);
@@ -581,6 +587,8 @@ export default class RoomView extends React.Component {
this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged);
+ this.context.removeListener("Event.decrypted", this.onEventDecrypted);
+ this.context.removeListener("event", this.onEvent);
}
window.removeEventListener('beforeunload', this.onPageUnload);
@@ -781,6 +789,30 @@ export default class RoomView extends React.Component {
}
};
+ private onEventDecrypted = (ev) => {
+ if (ev.isDecryptionFailure()) return;
+ this.handleEffects(ev);
+ };
+
+ private onEvent = (ev) => {
+ if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
+ this.handleEffects(ev);
+ };
+
+ private handleEffects = (ev) => {
+ if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
+ if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
+
+ const notifState = RoomNotificationStateStore.instance.getRoomState(this.state.room);
+ if (!notifState.isUnread) return;
+
+ CHAT_EFFECTS.forEach(effect => {
+ if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
+ dis.dispatch({action: `effects.${effect.command}`});
+ }
+ });
+ };
+
private onRoomName = (room: Room) => {
if (this.state.room && room.roomId == this.state.room.roomId) {
this.forceUpdate();
@@ -1946,9 +1978,14 @@ export default class RoomView extends React.Component {
mx_RoomView_inCall: Boolean(activeCall),
});
+ const showChatEffects = SettingsStore.getValue('showChatEffects');
+
return (
+ {showChatEffects && this.roomView.current &&
+
+ }
{
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
- onFinished: async (proceed) => {
+ onFinished: async (proceed, reason) => {
if (!proceed) return;
const cli = MatrixClientPeg.get();
@@ -157,6 +157,8 @@ export default class MessageContextMenu extends React.Component {
await cli.redactEvent(
this.props.mxEvent.getRoomId(),
this.props.mxEvent.getId(),
+ undefined,
+ reason ? { reason } : {},
);
} catch (e) {
const code = e.errcode || e.statusCode;
diff --git a/src/components/views/dialogs/ConfirmRedactDialog.js b/src/components/views/dialogs/ConfirmRedactDialog.js
index 3106df1d5b..2216f9a93a 100644
--- a/src/components/views/dialogs/ConfirmRedactDialog.js
+++ b/src/components/views/dialogs/ConfirmRedactDialog.js
@@ -23,15 +23,17 @@ import { _t } from '../../../languageHandler';
*/
export default class ConfirmRedactDialog extends React.Component {
render() {
- const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
+ const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog');
return (
-
-
+
);
}
}
diff --git a/src/components/views/dialogs/ServerPickerDialog.tsx b/src/components/views/dialogs/ServerPickerDialog.tsx
index 9eb819c98e..65d53f0870 100644
--- a/src/components/views/dialogs/ServerPickerDialog.tsx
+++ b/src/components/views/dialogs/ServerPickerDialog.tsx
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React, {createRef} from 'react';
+import React, {createRef} from "react";
+import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import BaseDialog from './BaseDialog';
@@ -47,9 +48,20 @@ export default class ServerPickerDialog extends React.PureComponent({
- deriveData: async ({ value: hsUrl }) => {
- // Always try and use the defaults first
- const defaultConfig: ValidatedServerConfig = SdkConfig.get()["validated_server_config"];
- if (defaultConfig.hsUrl === hsUrl) return {};
+ deriveData: async ({ value }) => {
+ let hsUrl = value.trim(); // trim to account for random whitespace
+
+ // if the URL has no protocol, try validate it as a serverName via well-known
+ if (!hsUrl.includes("://")) {
+ try {
+ const discoveryResult = await AutoDiscovery.findClientConfig(hsUrl);
+ this.validatedConf = AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(hsUrl, discoveryResult);
+ return {}; // we have a validated config, we don't need to try the other paths
+ } catch (e) {
+ console.error(`Attempted ${hsUrl} as a server_name but it failed`, e);
+ }
+ }
+
+ // if we got to this stage then either the well-known failed or the URL had a protocol specified,
+ // so validate statically only. If the URL has no protocol, default to https.
+ if (!hsUrl.includes("://")) {
+ hsUrl = "https://" + hsUrl;
+ }
try {
this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl);
@@ -81,17 +108,22 @@ export default class ServerPickerDialog extends React.PureComponent
;
- const memberCount = useMemberCount(room);
+ const memberCount = useRoomMemberCount(room);
return
diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx
index be4ecaffb3..9be3d6be18 100644
--- a/src/components/views/rooms/NewRoomIntro.tsx
+++ b/src/components/views/rooms/NewRoomIntro.tsx
@@ -60,8 +60,9 @@ const NewRoomIntro = () => {
{ caption && { caption }
}
;
} else {
+ const inRoom = room && room.getMyMembership() === "join";
const topic = room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
- const canAddTopic = room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getUserId());
+ const canAddTopic = inRoom && room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getUserId());
const onTopicClick = () => {
dis.dispatch({
@@ -99,9 +100,25 @@ const NewRoomIntro = () => {
});
}
- const onInviteClick = () => {
- dis.dispatch({ action: "view_invite", roomId });
- };
+ let canInvite = inRoom;
+ const powerLevels = room.currentState.getStateEvents(EventType.RoomPowerLevels, "")?.getContent();
+ const me = room.getMember(cli.getUserId());
+ if (powerLevels && me && powerLevels.invite > me.powerLevel) {
+ canInvite = false;
+ }
+
+ let buttons;
+ if (canInvite) {
+ const onInviteClick = () => {
+ dis.dispatch({ action: "view_invite", roomId });
+ };
+
+ buttons =
+
+ {_t("Invite to this room")}
+
+
+ }
const avatarUrl = room.currentState.getStateEvents(EventType.RoomAvatar, "")?.getContent()?.url;
body =
@@ -119,11 +136,7 @@ const NewRoomIntro = () => {
roomName: () => { room.name },
})}
{topicText}
-
-
- {_t("Invite to this room")}
-
-
+ { buttons }
;
}
diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js
index e88060304a..8171da7eca 100644
--- a/src/components/views/rooms/SendMessageComposer.js
+++ b/src/components/views/rooms/SendMessageComposer.js
@@ -42,6 +42,8 @@ import {Key, isOnlyCtrlOrCmdKeyEvent} from "../../../Keyboard";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import RateLimitedFunc from '../../../ratelimitedfunc';
import {Action} from "../../../dispatcher/actions";
+import {containsEmoji} from "../../../effects/utils";
+import {CHAT_EFFECTS} from '../../../effects';
import SettingsStore from "../../../settings/SettingsStore";
import CountlyAnalytics from "../../../CountlyAnalytics";
@@ -326,6 +328,11 @@ export default class SendMessageComposer extends React.Component {
});
}
dis.dispatch({action: "message_sent"});
+ CHAT_EFFECTS.forEach((effect) => {
+ if (containsEmoji(content, effect.emojis)) {
+ dis.dispatch({action: `effects.${effect.command}`});
+ }
+ });
CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, false, !!replyToEvent, content);
}
diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
index d6a4921f1a..4d8493401e 100644
--- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
+++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js
@@ -50,6 +50,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
'showAvatarChanges',
'showDisplaynameChanges',
'showImages',
+ 'showChatEffects',
'Pill.shouldShowPillAvatar',
];
diff --git a/src/effects/ICanvasEffect.ts b/src/effects/ICanvasEffect.ts
new file mode 100644
index 0000000000..9bf3e9293d
--- /dev/null
+++ b/src/effects/ICanvasEffect.ts
@@ -0,0 +1,47 @@
+/*
+ Copyright 2020 Nurjin Jafar
+ Copyright 2020 Nordeck IT + Consulting GmbH.
+
+ 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.
+ */
+/**
+ * Defines the constructor of a canvas based room effect
+ */
+export interface ICanvasEffectConstructable {
+ /**
+ * @param {{[key:string]:any}} options? Optional animation options
+ * @returns ICanvasEffect Returns a new instance of the canvas effect
+ */
+ new(options?: { [key: string]: any }): ICanvasEffect;
+}
+
+/**
+ * Defines the interface of a canvas based room effect
+ */
+export default interface ICanvasEffect {
+ /**
+ * @param {HTMLCanvasElement} canvas The canvas instance as the render target of the animation
+ * @param {number} timeout? A timeout that defines the runtime of the animation (defaults to false)
+ */
+ start: (canvas: HTMLCanvasElement, timeout?: number) => Promise;
+
+ /**
+ * Stops the current animation
+ */
+ stop: () => Promise;
+
+ /**
+ * Returns a value that defines if the animation is currently running
+ */
+ isRunning: boolean;
+}
diff --git a/src/effects/confetti/index.ts b/src/effects/confetti/index.ts
new file mode 100644
index 0000000000..8c4b2d2616
--- /dev/null
+++ b/src/effects/confetti/index.ts
@@ -0,0 +1,191 @@
+/*
+ Copyright 2020 Nurjin Jafar
+ Copyright 2020 Nordeck IT + Consulting GmbH.
+
+ 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 ICanvasEffect from '../ICanvasEffect';
+
+export type ConfettiOptions = {
+ /**
+ * max confetti count
+ */
+ maxCount: number,
+ /**
+ * particle animation speed
+ */
+ speed: number,
+ /**
+ * the confetti animation frame interval in milliseconds
+ */
+ frameInterval: number,
+ /**
+ * the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
+ */
+ alpha: number,
+ /**
+ * use gradient instead of solid particle color
+ */
+ gradient: boolean,
+}
+
+type ConfettiParticle = {
+ color: string,
+ color2: string,
+ x: number,
+ y: number,
+ diameter: number,
+ tilt: number,
+ tiltAngleIncrement: number,
+ tiltAngle: number,
+}
+
+export const DefaultOptions: ConfettiOptions = {
+ maxCount: 150,
+ speed: 3,
+ frameInterval: 15,
+ alpha: 1.0,
+ gradient: false,
+};
+
+export default class Confetti implements ICanvasEffect {
+ private readonly options: ConfettiOptions;
+
+ constructor(options: { [key: string]: any }) {
+ this.options = {...DefaultOptions, ...options};
+ }
+
+ private context: CanvasRenderingContext2D | null = null;
+ private supportsAnimationFrame = window.requestAnimationFrame;
+ private colors = ['rgba(30,144,255,', 'rgba(107,142,35,', 'rgba(255,215,0,',
+ 'rgba(255,192,203,', 'rgba(106,90,205,', 'rgba(173,216,230,',
+ 'rgba(238,130,238,', 'rgba(152,251,152,', 'rgba(70,130,180,',
+ 'rgba(244,164,96,', 'rgba(210,105,30,', 'rgba(220,20,60,'];
+
+ private lastFrameTime = Date.now();
+ private particles: Array = [];
+ private waveAngle = 0;
+
+ public isRunning: boolean;
+
+ public start = async (canvas: HTMLCanvasElement, timeout = 3000) => {
+ if (!canvas) {
+ return;
+ }
+ this.context = canvas.getContext('2d');
+ this.particles = [];
+ const count = this.options.maxCount;
+ while (this.particles.length < count) {
+ this.particles.push(this.resetParticle({} as ConfettiParticle, canvas.width, canvas.height));
+ }
+ this.isRunning = true;
+ this.runAnimation();
+ if (timeout) {
+ window.setTimeout(this.stop, timeout);
+ }
+ }
+
+ public stop = async () => {
+ this.isRunning = false;
+ }
+
+ private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => {
+ particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')');
+ if (this.options.gradient) {
+ particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')');
+ } else {
+ particle.color2 = particle.color;
+ }
+ particle.x = Math.random() * width;
+ particle.y = Math.random() * -height;
+ particle.diameter = Math.random() * 10 + 5;
+ particle.tilt = Math.random() * -10;
+ particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
+ particle.tiltAngle = Math.random() * Math.PI;
+ return particle;
+ }
+
+ private runAnimation = (): void => {
+ if (!this.context || !this.context.canvas) {
+ return;
+ }
+ if (this.particles.length === 0) {
+ this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
+ } else {
+ const now = Date.now();
+ const delta = now - this.lastFrameTime;
+ if (!this.supportsAnimationFrame || delta > this.options.frameInterval) {
+ this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
+ this.updateParticles();
+ this.drawParticles(this.context);
+ this.lastFrameTime = now - (delta % this.options.frameInterval);
+ }
+ requestAnimationFrame(this.runAnimation);
+ }
+ }
+
+
+ private drawParticles = (context: CanvasRenderingContext2D): void => {
+ if (!this.context || !this.context.canvas) {
+ return;
+ }
+ let x; let x2; let y2;
+ for (const particle of this.particles) {
+ this.context.beginPath();
+ context.lineWidth = particle.diameter;
+ x2 = particle.x + particle.tilt;
+ x = x2 + particle.diameter / 2;
+ y2 = particle.y + particle.tilt + particle.diameter / 2;
+ if (this.options.gradient) {
+ const gradient = context.createLinearGradient(x, particle.y, x2, y2);
+ gradient.addColorStop(0, particle.color);
+ gradient.addColorStop(1.0, particle.color2);
+ context.strokeStyle = gradient;
+ } else {
+ context.strokeStyle = particle.color;
+ }
+ context.moveTo(x, particle.y);
+ context.lineTo(x2, y2);
+ context.stroke();
+ }
+ }
+
+ private updateParticles = () => {
+ if (!this.context || !this.context.canvas) {
+ return;
+ }
+ const width = this.context.canvas.width;
+ const height = this.context.canvas.height;
+ let particle: ConfettiParticle;
+ this.waveAngle += 0.01;
+ for (let i = 0; i < this.particles.length; i++) {
+ particle = this.particles[i];
+ if (!this.isRunning && particle.y < -15) {
+ particle.y = height + 100;
+ } else {
+ particle.tiltAngle += particle.tiltAngleIncrement;
+ particle.x += Math.sin(this.waveAngle) - 0.5;
+ particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.options.speed) * 0.5;
+ particle.tilt = Math.sin(particle.tiltAngle) * 15;
+ }
+ if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
+ if (this.isRunning && this.particles.length <= this.options.maxCount) {
+ this.resetParticle(particle, width, height);
+ } else {
+ this.particles.splice(i, 1);
+ i--;
+ }
+ }
+ }
+ }
+}
diff --git a/src/effects/index.ts b/src/effects/index.ts
new file mode 100644
index 0000000000..16a0851070
--- /dev/null
+++ b/src/effects/index.ts
@@ -0,0 +1,89 @@
+/*
+ Copyright 2020 Nurjin Jafar
+ Copyright 2020 Nordeck IT + Consulting GmbH.
+
+ 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 { _t, _td } from "../languageHandler";
+
+export type Effect = {
+ /**
+ * one or more emojis that will trigger this effect
+ */
+ emojis: Array;
+ /**
+ * the matrix message type that will trigger this effect
+ */
+ msgType: string;
+ /**
+ * the room command to trigger this effect
+ */
+ command: string;
+ /**
+ * a function that returns the translated description of the effect
+ */
+ description: () => string;
+ /**
+ * a function that returns the translated fallback message. this message will be shown if the user did not provide a custom message
+ */
+ fallbackMessage: () => string;
+ /**
+ * animation options
+ */
+ options: TOptions;
+}
+
+type ConfettiOptions = {
+ /**
+ * max confetti count
+ */
+ maxCount: number,
+ /**
+ * particle animation speed
+ */
+ speed: number,
+ /**
+ * the confetti animation frame interval in milliseconds
+ */
+ frameInterval: number,
+ /**
+ * the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
+ */
+ alpha: number,
+ /**
+ * use gradient instead of solid particle color
+ */
+ gradient: boolean,
+}
+
+/**
+ * This configuration defines room effects that can be triggered by custom message types and emojis
+ */
+export const CHAT_EFFECTS: Array> = [
+ {
+ emojis: ['🎊', '🎉'],
+ msgType: 'nic.custom.confetti',
+ command: 'confetti',
+ description: () => _td("Sends the given message with confetti"),
+ fallbackMessage: () => _t("sends confetti") + " 🎉",
+ options: {
+ maxCount: 150,
+ speed: 3,
+ frameInterval: 15,
+ alpha: 1.0,
+ gradient: false,
+ },
+ } as Effect,
+];
+
+
diff --git a/src/effects/utils.ts b/src/effects/utils.ts
new file mode 100644
index 0000000000..c2b499b154
--- /dev/null
+++ b/src/effects/utils.ts
@@ -0,0 +1,24 @@
+/*
+ Copyright 2020 Nurjin Jafar
+ Copyright 2020 Nordeck IT + Consulting GmbH.
+
+ 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.
+ */
+/**
+ * Checks a message if it contains one of the provided emojis
+ * @param {Object} content The message
+ * @param {Array} emojis The list of emojis to check for
+ */
+export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array): boolean => {
+ return emojis.some((emoji) => content.body && content.body.includes(emoji));
+}
diff --git a/src/hooks/useRoomMembers.ts b/src/hooks/useRoomMembers.ts
new file mode 100644
index 0000000000..e25436045e
--- /dev/null
+++ b/src/hooks/useRoomMembers.ts
@@ -0,0 +1,40 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+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 {useState} from "react";
+import {Room} from "matrix-js-sdk/src/models/room";
+import {RoomMember} from "matrix-js-sdk/src/models/room-member";
+
+import {useEventEmitter} from "./useEventEmitter";
+import {throttle} from "lodash";
+
+// Hook to simplify watching Matrix Room joined members
+export const useRoomMembers = (room: Room, throttleWait = 250) => {
+ const [members, setMembers] = useState(room.getJoinedMembers());
+ useEventEmitter(room.currentState, "RoomState.members", throttle(() => {
+ setMembers(room.getJoinedMembers());
+ }, throttleWait, {leading: true, trailing: true}));
+ return members;
+};
+
+// Hook to simplify watching Matrix Room joined member count
+export const useRoomMemberCount = (room: Room, throttleWait = 250) => {
+ const [count, setCount] = useState(room.getJoinedMemberCount());
+ useEventEmitter(room.currentState, "RoomState.members", throttle(() => {
+ setCount(room.getJoinedMemberCount());
+ }, throttleWait, {leading: true, trailing: true}));
+ return count;
+};
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index fd01ac63a9..96f8917d60 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -47,9 +47,9 @@
"Try using turn.matrix.org": "Try using turn.matrix.org",
"OK": "OK",
"Unable to access microphone": "Unable to access microphone",
- "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.",
+ "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.",
"Unable to access webcam / microphone": "Unable to access webcam / microphone",
- "Call failed because no webcam or microphone could not be accessed. Check that:": "Call failed because no webcam or microphone could not be accessed. Check that:",
+ "Call failed because webcam or microphone could not be accessed. Check that:": "Call failed because webcam or microphone could not be accessed. Check that:",
"A microphone and webcam are plugged in and set up correctly": "A microphone and webcam are plugged in and set up correctly",
"Permission is granted to use the webcam": "Permission is granted to use the webcam",
"No other application is using the webcam": "No other application is using the webcam",
@@ -406,6 +406,7 @@
"Messages": "Messages",
"Actions": "Actions",
"Advanced": "Advanced",
+ "Effects": "Effects",
"Other": "Other",
"Command error": "Command error",
"Usage": "Usage",
@@ -826,6 +827,7 @@
"Manually verify all remote sessions": "Manually verify all remote sessions",
"IRC display name width": "IRC display name width",
"Enable experimental, compact IRC style layout": "Enable experimental, compact IRC style layout",
+ "Show chat effects": "Show chat effects",
"Collecting app version information": "Collecting app version information",
"Collecting logs": "Collecting logs",
"Uploading logs": "Uploading logs",
@@ -844,6 +846,8 @@
"When rooms are upgraded": "When rooms are upgraded",
"My Ban List": "My Ban List",
"This is your list of users/servers you have blocked - don't leave the room!": "This is your list of users/servers you have blocked - don't leave the room!",
+ "Sends the given message with confetti": "Sends the given message with confetti",
+ "sends confetti": "sends confetti",
"You held the call Resume": "You held the call Resume",
"%(peerName)s held the call": "%(peerName)s held the call",
"Video Call": "Video Call",
@@ -1965,6 +1969,7 @@
"Removing…": "Removing…",
"Confirm Removal": "Confirm Removal",
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.",
+ "Reason (optional)": "Reason (optional)",
"Clear all data in this session?": "Clear all data in this session?",
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.",
"Clear all data": "Clear all data",
@@ -2158,11 +2163,12 @@
"A connection error occurred while trying to contact the server.": "A connection error occurred while trying to contact the server.",
"The server is not configured to indicate what the problem is (CORS).": "The server is not configured to indicate what the problem is (CORS).",
"Recent changes that have not yet been received": "Recent changes that have not yet been received",
- "Unable to validate homeserver/identity server": "Unable to validate homeserver/identity server",
+ "Unable to validate homeserver": "Unable to validate homeserver",
+ "Invalid URL": "Invalid URL",
"Specify a homeserver": "Specify a homeserver",
"Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.": "Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.",
"Sign into your homeserver": "Sign into your homeserver",
- "We call the places you where you can host your account ‘homeservers’.": "We call the places you where you can host your account ‘homeservers’.",
+ "We call the places where you can host your account ‘homeservers’.": "We call the places where you can host your account ‘homeservers’.",
"Other homeserver": "Other homeserver",
"Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.",
"Learn more": "Learn more",
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index 31e133be72..6bec31a1cb 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -634,6 +634,11 @@ export const SETTINGS: {[setting: string]: ISetting} = {
displayName: _td("Enable experimental, compact IRC style layout"),
default: false,
},
+ "showChatEffects": {
+ supportedLevels: LEVELS_ACCOUNT_SETTINGS,
+ displayName: _td("Show chat effects"),
+ default: true,
+ },
"Widgets.pinned": {
supportedLevels: LEVELS_ROOM_OR_ACCOUNT,
default: {},
diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts
index 7c05e4b500..82f169f498 100644
--- a/src/settings/SettingsStore.ts
+++ b/src/settings/SettingsStore.ts
@@ -42,7 +42,7 @@ for (const key of Object.keys(SETTINGS)) {
if (SETTINGS[key].invertedSettingName) {
// Invert now so that the rest of the system will invert it back
// to what was intended.
- invertedDefaultSettings[key] = !SETTINGS[key].default;
+ invertedDefaultSettings[SETTINGS[key].invertedSettingName] = !SETTINGS[key].default;
}
}
diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js
index 94aa0c3544..18b6451d3e 100644
--- a/src/utils/AutoDiscoveryUtils.js
+++ b/src/utils/AutoDiscoveryUtils.js
@@ -34,6 +34,8 @@ export class ValidatedServerConfig {
isUrl: string;
isDefault: boolean;
+ // when the server config is based on static URLs the hsName is not resolvable and things may wish to use hsUrl
+ isNameResolvable: boolean;
warning: string;
}
@@ -161,7 +163,7 @@ export default class AutoDiscoveryUtils {
const url = new URL(homeserverUrl);
const serverName = url.hostname;
- return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result, syntaxOnly);
+ return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result, syntaxOnly, true);
}
/**
@@ -179,12 +181,12 @@ export default class AutoDiscoveryUtils {
* input.
* @param {string} serverName The domain name the AutoDiscovery result is for.
* @param {*} discoveryResult The AutoDiscovery result.
- * @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
- * not be raised.
+ * @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will not be raised.
+ * @param {boolean} isSynthetic If true, then the discoveryResult was synthesised locally.
* @returns {Promise} Resolves to the validated configuration.
*/
static buildValidatedConfigFromDiscovery(
- serverName: string, discoveryResult, syntaxOnly=false): ValidatedServerConfig {
+ serverName: string, discoveryResult, syntaxOnly=false, isSynthetic=false): ValidatedServerConfig {
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
@@ -252,6 +254,7 @@ export default class AutoDiscoveryUtils {
isUrl: preferredIdentityUrl,
isDefault: false,
warning: hsResult.error,
+ isNameResolvable: !isSynthetic,
});
}
}
diff --git a/src/utils/permalinks/Permalinks.js b/src/utils/permalinks/Permalinks.js
index 0f54bcce05..39c5776852 100644
--- a/src/utils/permalinks/Permalinks.js
+++ b/src/utils/permalinks/Permalinks.js
@@ -129,6 +129,17 @@ export class RoomPermalinkCreator {
return getPermalinkConstructor().forEvent(this._roomId, eventId, this._serverCandidates);
}
+ forShareableRoom() {
+ if (this._room) {
+ // Prefer to use canonical alias for permalink if possible
+ const alias = this._room.getCanonicalAlias();
+ if (alias) {
+ return getPermalinkConstructor().forRoom(alias, this._serverCandidates);
+ }
+ }
+ return getPermalinkConstructor().forRoom(this._roomId, this._serverCandidates);
+ }
+
forRoom() {
return getPermalinkConstructor().forRoom(this._roomId, this._serverCandidates);
}
diff --git a/test/utils/permalinks/Permalinks-test.js b/test/utils/permalinks/Permalinks-test.js
index 72cd66cb69..0bd4466d97 100644
--- a/test/utils/permalinks/Permalinks-test.js
+++ b/test/utils/permalinks/Permalinks-test.js
@@ -34,6 +34,7 @@ function mockRoom(roomId, members, serverACL) {
return {
roomId,
+ getCanonicalAlias: () => roomId,
getJoinedMembers: () => members,
getMember: (userId) => members.find(m => m.userId === userId),
currentState: {
diff --git a/yarn.lock b/yarn.lock
index c06494d319..1a6a0b0fb3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1256,10 +1256,10 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.11.2":
- version "7.11.2"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
- integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
+"@babel/runtime@^7.12.5":
+ version "7.12.5"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
+ integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
dependencies:
regenerator-runtime "^0.13.4"
@@ -4842,9 +4842,9 @@ has@^1.0.1, has@^1.0.3:
function-bind "^1.1.1"
highlight.js@^10.1.2:
- version "10.1.2"
- resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c"
- integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA==
+ version "10.4.1"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0"
+ integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==
hoist-non-react-statics@^3.3.0:
version "3.3.2"
@@ -6390,10 +6390,10 @@ log-symbols@^2.0.0, log-symbols@^2.2.0:
dependencies:
chalk "^2.0.1"
-loglevel@^1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
- integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==
+loglevel@^1.7.1:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
+ integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
lolex@^5.0.0, lolex@^5.1.2:
version "5.1.2"
@@ -6513,15 +6513,15 @@ mathml-tag-names@^2.0.1:
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
- version "9.2.0"
- resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6661bde6088e6e43f31198e8532432e162aef33c"
+ version "9.3.0"
+ resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/ff6612f9d0aa1a7c08b65a0b41c5ab997506016f"
dependencies:
- "@babel/runtime" "^7.11.2"
+ "@babel/runtime" "^7.12.5"
another-json "^0.2.0"
browser-request "^0.3.3"
bs58 "^4.0.1"
content-type "^1.0.4"
- loglevel "^1.7.0"
+ loglevel "^1.7.1"
qs "^6.9.4"
request "^2.88.2"
unhomoglyph "^1.0.6"
@@ -6994,6 +6994,10 @@ object.values@^1.1.1:
function-bind "^1.1.1"
has "^1.0.3"
+"olm@https://packages.matrix.org/npm/olm/olm-3.2.1.tgz":
+ version "3.2.1"
+ resolved "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz#d623d76f99c3518dde68be8c86618d68bc7b004a"
+
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"