mirror of https://github.com/vector-im/riot-web
Merge branch 'develop' into expand-codeblock
commit
8a4af2f348
60
CHANGELOG.md
60
CHANGELOG.md
|
@ -1,3 +1,63 @@
|
|||
Changes in [3.12.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.12.0) (2021-01-18)
|
||||
=====================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.12.0-rc.1...v3.12.0)
|
||||
|
||||
* Upgrade to JS SDK 9.5.0
|
||||
* Fix incoming call box on dark theme
|
||||
[\#5543](https://github.com/matrix-org/matrix-react-sdk/pull/5543)
|
||||
|
||||
Changes in [3.12.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.12.0-rc.1) (2021-01-13)
|
||||
===============================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.11.1...v3.12.0-rc.1)
|
||||
|
||||
* Upgrade to JS SDK 9.5.0-rc.1
|
||||
* Fix soft crash on soft logout page
|
||||
[\#5539](https://github.com/matrix-org/matrix-react-sdk/pull/5539)
|
||||
* Translations update from Weblate
|
||||
[\#5538](https://github.com/matrix-org/matrix-react-sdk/pull/5538)
|
||||
* Run TypeScript tests
|
||||
[\#5537](https://github.com/matrix-org/matrix-react-sdk/pull/5537)
|
||||
* Add a basic widget explorer to devtools (per-room)
|
||||
[\#5528](https://github.com/matrix-org/matrix-react-sdk/pull/5528)
|
||||
* Add <input type="password"> to security key field
|
||||
[\#5534](https://github.com/matrix-org/matrix-react-sdk/pull/5534)
|
||||
* Fix avatar upload prompt/tooltip floating wrong and permissions
|
||||
[\#5526](https://github.com/matrix-org/matrix-react-sdk/pull/5526)
|
||||
* Add a dialpad UI for PSTN lookup
|
||||
[\#5523](https://github.com/matrix-org/matrix-react-sdk/pull/5523)
|
||||
* Basic call transfer initiation support
|
||||
[\#5494](https://github.com/matrix-org/matrix-react-sdk/pull/5494)
|
||||
* Fix #15988
|
||||
[\#5524](https://github.com/matrix-org/matrix-react-sdk/pull/5524)
|
||||
* Bump node-notifier from 8.0.0 to 8.0.1
|
||||
[\#5520](https://github.com/matrix-org/matrix-react-sdk/pull/5520)
|
||||
* Use TypeScript source for development, swap to build during release
|
||||
[\#5503](https://github.com/matrix-org/matrix-react-sdk/pull/5503)
|
||||
* Look for emoji in the body that will be displayed
|
||||
[\#5517](https://github.com/matrix-org/matrix-react-sdk/pull/5517)
|
||||
* Bump ini from 1.3.5 to 1.3.7
|
||||
[\#5486](https://github.com/matrix-org/matrix-react-sdk/pull/5486)
|
||||
* Recognise `*.element.io` links as Element permalinks
|
||||
[\#5514](https://github.com/matrix-org/matrix-react-sdk/pull/5514)
|
||||
* Fixes for call UI
|
||||
[\#5509](https://github.com/matrix-org/matrix-react-sdk/pull/5509)
|
||||
* Add a snowfall chat effect (with /snowfall command)
|
||||
[\#5511](https://github.com/matrix-org/matrix-react-sdk/pull/5511)
|
||||
* fireworks effect
|
||||
[\#5507](https://github.com/matrix-org/matrix-react-sdk/pull/5507)
|
||||
* Don't play call end sound for calls that never started
|
||||
[\#5506](https://github.com/matrix-org/matrix-react-sdk/pull/5506)
|
||||
* Add /tableflip slash command
|
||||
[\#5485](https://github.com/matrix-org/matrix-react-sdk/pull/5485)
|
||||
* Import from src in IncomingCallBox.tsx
|
||||
[\#5504](https://github.com/matrix-org/matrix-react-sdk/pull/5504)
|
||||
* Social Login support both https and mxc icons
|
||||
[\#5499](https://github.com/matrix-org/matrix-react-sdk/pull/5499)
|
||||
* Fix padding in confirmation email registration prompt
|
||||
[\#5501](https://github.com/matrix-org/matrix-react-sdk/pull/5501)
|
||||
* Fix room list help prompt alignment
|
||||
[\#5500](https://github.com/matrix-org/matrix-react-sdk/pull/5500)
|
||||
|
||||
Changes in [3.11.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.11.1) (2020-12-21)
|
||||
=====================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.11.0...v3.11.1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "matrix-react-sdk",
|
||||
"version": "3.11.1",
|
||||
"version": "3.12.0",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
|
@ -82,7 +82,7 @@
|
|||
"linkifyjs": "^2.1.9",
|
||||
"lodash": "^4.17.19",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-widget-api": "^0.1.0-beta.10",
|
||||
"matrix-widget-api": "0.1.0-beta.11",
|
||||
"minimist": "^1.2.5",
|
||||
"pako": "^1.0.11",
|
||||
"parse5": "^5.1.1",
|
||||
|
|
|
@ -89,24 +89,18 @@ limitations under the License.
|
|||
}
|
||||
}
|
||||
|
||||
.mx_showMore {
|
||||
display: block;
|
||||
text-align: left;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
color: $muted-fg-color;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.metadata.visible {
|
||||
overflow-y: visible;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
protected readonly _matrixClient: MatrixClient;
|
||||
protected readonly _roomView: React.RefObject<any>;
|
||||
protected readonly _resizeContainer: React.RefObject<ResizeHandle>;
|
||||
protected readonly _compactLayoutWatcherRef: string;
|
||||
protected compactLayoutWatcherRef: string;
|
||||
protected resizer: Resizer;
|
||||
|
||||
constructor(props, context) {
|
||||
|
@ -157,18 +157,6 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
|
||||
CallMediaHandler.loadDevices();
|
||||
|
||||
document.addEventListener('keydown', this._onNativeKeyDown, false);
|
||||
|
||||
this._updateServerNoticeEvents();
|
||||
|
||||
this._matrixClient.on("accountData", this.onAccountData);
|
||||
this._matrixClient.on("sync", this.onSync);
|
||||
this._matrixClient.on("RoomState.events", this.onRoomStateEvents);
|
||||
|
||||
this._compactLayoutWatcherRef = SettingsStore.watchSetting(
|
||||
"useCompactLayout", null, this.onCompactLayoutChanged,
|
||||
);
|
||||
|
||||
fixupColorFonts();
|
||||
|
||||
this._roomView = React.createRef();
|
||||
|
@ -176,6 +164,24 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this._onNativeKeyDown, false);
|
||||
|
||||
this._updateServerNoticeEvents();
|
||||
|
||||
this._matrixClient.on("accountData", this.onAccountData);
|
||||
this._matrixClient.on("sync", this.onSync);
|
||||
// Call `onSync` with the current state as well
|
||||
this.onSync(
|
||||
this._matrixClient.getSyncState(),
|
||||
null,
|
||||
this._matrixClient.getSyncStateData(),
|
||||
);
|
||||
this._matrixClient.on("RoomState.events", this.onRoomStateEvents);
|
||||
|
||||
this.compactLayoutWatcherRef = SettingsStore.watchSetting(
|
||||
"useCompactLayout", null, this.onCompactLayoutChanged,
|
||||
);
|
||||
|
||||
this.resizer = this._createResizer();
|
||||
this.resizer.attach();
|
||||
this._loadResizerPreferences();
|
||||
|
@ -186,7 +192,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
this._matrixClient.removeListener("accountData", this.onAccountData);
|
||||
this._matrixClient.removeListener("sync", this.onSync);
|
||||
this._matrixClient.removeListener("RoomState.events", this.onRoomStateEvents);
|
||||
SettingsStore.unwatchSetting(this._compactLayoutWatcherRef);
|
||||
SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
|
||||
this.resizer.detach();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import {WidgetMessagingStore} from "../../../stores/widgets/WidgetMessagingStore
|
|||
import RoomContext from "../../../contexts/RoomContext";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {SettingLevel} from "../../../settings/SettingLevel";
|
||||
import Modal from "../../../Modal";
|
||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||
import {WidgetType} from "../../../widgets/WidgetType";
|
||||
|
@ -127,7 +126,8 @@ const WidgetContextMenu: React.FC<IProps> = ({
|
|||
console.info("Revoking permission for widget to load: " + app.eventId);
|
||||
const current = SettingsStore.getValue("allowedWidgets", roomId);
|
||||
current[app.eventId] = false;
|
||||
SettingsStore.setValue("allowedWidgets", roomId, SettingLevel.ROOM_ACCOUNT, current).catch(err => {
|
||||
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
|
||||
SettingsStore.setValue("allowedWidgets", roomId, level, current).catch(err => {
|
||||
console.error(err);
|
||||
// We don't really need to do anything about this - the user will just hit the button again.
|
||||
});
|
||||
|
|
|
@ -33,7 +33,6 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||
import {aboveLeftOf, ContextMenuButton} from "../../structures/ContextMenu";
|
||||
import PersistedElement, {getPersistKey} from "./PersistedElement";
|
||||
import {WidgetType} from "../../../widgets/WidgetType";
|
||||
import {SettingLevel} from "../../../settings/SettingLevel";
|
||||
import {StopGapWidget} from "../../../stores/widgets/StopGapWidget";
|
||||
import {ElementWidgetActions} from "../../../stores/widgets/ElementWidgetActions";
|
||||
import {MatrixCapabilities} from "matrix-widget-api";
|
||||
|
@ -240,7 +239,8 @@ export default class AppTile extends React.Component {
|
|||
console.info("Granting permission for widget to load: " + this.props.app.eventId);
|
||||
const current = SettingsStore.getValue("allowedWidgets", roomId);
|
||||
current[this.props.app.eventId] = true;
|
||||
SettingsStore.setValue("allowedWidgets", roomId, SettingLevel.ROOM_ACCOUNT, current).then(() => {
|
||||
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
|
||||
SettingsStore.setValue("allowedWidgets", roomId, level, current).then(() => {
|
||||
this.setState({hasPermissionToLoad: true});
|
||||
|
||||
// Fetch a token for the integration manager, now that we're allowed to
|
||||
|
|
|
@ -412,7 +412,10 @@ export default class TextualBody extends React.Component {
|
|||
ref: this._content,
|
||||
});
|
||||
if (this.props.replacingEventId) {
|
||||
body = [body, this._renderEditedMarker()];
|
||||
body = <>
|
||||
{body}
|
||||
{this._renderEditedMarker()}
|
||||
</>;
|
||||
}
|
||||
|
||||
if (this.props.highlightLink) {
|
||||
|
|
|
@ -29,7 +29,7 @@ import ActiveRoomObserver from "../../../ActiveRoomObserver";
|
|||
import { _t } from "../../../languageHandler";
|
||||
import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextMenu";
|
||||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
|
||||
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||
import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
|
@ -51,7 +51,6 @@ import IconizedContextMenu, {
|
|||
IconizedContextMenuRadio,
|
||||
} from "../context_menus/IconizedContextMenu";
|
||||
import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
|
||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -99,12 +98,18 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
|
||||
ActiveRoomObserver.addListener(this.props.room.roomId, this.onActiveRoomUpdate);
|
||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||
MessagePreviewStore.instance.on(ROOM_PREVIEW_CHANGED, this.onRoomPreviewChanged);
|
||||
MessagePreviewStore.instance.on(
|
||||
MessagePreviewStore.getPreviewChangedEventName(this.props.room),
|
||||
this.onRoomPreviewChanged,
|
||||
);
|
||||
this.notificationState = RoomNotificationStateStore.instance.getRoomState(this.props.room);
|
||||
this.notificationState.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
|
||||
this.roomProps = EchoChamber.forRoom(this.props.room);
|
||||
this.roomProps.on(PROPERTY_UPDATED, this.onRoomPropertyUpdate);
|
||||
CommunityPrototypeStore.instance.on(UPDATE_EVENT, this.onCommunityUpdate);
|
||||
CommunityPrototypeStore.instance.on(
|
||||
CommunityPrototypeStore.getUpdateEventName(this.props.room.roomId),
|
||||
this.onCommunityUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
private onNotificationUpdate = () => {
|
||||
|
@ -128,6 +133,24 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
if (prevProps.showMessagePreview !== this.props.showMessagePreview && this.showMessagePreview) {
|
||||
this.setState({messagePreview: this.generatePreview()});
|
||||
}
|
||||
if (prevProps.room?.roomId !== this.props.room?.roomId) {
|
||||
MessagePreviewStore.instance.off(
|
||||
MessagePreviewStore.getPreviewChangedEventName(prevProps.room),
|
||||
this.onRoomPreviewChanged,
|
||||
);
|
||||
MessagePreviewStore.instance.on(
|
||||
MessagePreviewStore.getPreviewChangedEventName(this.props.room),
|
||||
this.onRoomPreviewChanged,
|
||||
);
|
||||
CommunityPrototypeStore.instance.off(
|
||||
CommunityPrototypeStore.getUpdateEventName(prevProps.room?.roomId),
|
||||
this.onCommunityUpdate,
|
||||
);
|
||||
CommunityPrototypeStore.instance.on(
|
||||
CommunityPrototypeStore.getUpdateEventName(this.props.room?.roomId),
|
||||
this.onCommunityUpdate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
|
@ -140,11 +163,17 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
public componentWillUnmount() {
|
||||
if (this.props.room) {
|
||||
ActiveRoomObserver.removeListener(this.props.room.roomId, this.onActiveRoomUpdate);
|
||||
MessagePreviewStore.instance.off(
|
||||
MessagePreviewStore.getPreviewChangedEventName(this.props.room),
|
||||
this.onRoomPreviewChanged,
|
||||
);
|
||||
CommunityPrototypeStore.instance.off(
|
||||
CommunityPrototypeStore.getUpdateEventName(this.props.room.roomId),
|
||||
this.onCommunityUpdate,
|
||||
);
|
||||
}
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
MessagePreviewStore.instance.off(ROOM_PREVIEW_CHANGED, this.onRoomPreviewChanged);
|
||||
this.notificationState.off(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
|
||||
CommunityPrototypeStore.instance.off(UPDATE_EVENT, this.onCommunityUpdate);
|
||||
}
|
||||
|
||||
private onAction = (payload: ActionPayload) => {
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
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 React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
||||
import {_t} from "../../../languageHandler";
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import Pill from "../elements/Pill";
|
||||
import {makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import BaseAvatar from "../avatars/BaseAvatar";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
@replaceableComponent("views.settings.BridgeTile")
|
||||
export default class BridgeTile extends React.PureComponent {
|
||||
static propTypes = {
|
||||
ev: PropTypes.object.isRequired,
|
||||
room: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
visible: false,
|
||||
}
|
||||
|
||||
_toggleVisible() {
|
||||
this.setState({
|
||||
visible: !this.state.visible,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const content = this.props.ev.getContent();
|
||||
const { channel, network, protocol } = content;
|
||||
const protocolName = protocol.displayname || protocol.id;
|
||||
const channelName = channel.displayname || channel.id;
|
||||
const networkName = network ? network.displayname || network.id : protocolName;
|
||||
|
||||
let creator = null;
|
||||
if (content.creator) {
|
||||
creator = _t("This bridge was provisioned by <user />.", {}, {
|
||||
user: <Pill
|
||||
type={Pill.TYPE_USER_MENTION}
|
||||
room={this.props.room}
|
||||
url={makeUserPermalink(content.creator)}
|
||||
shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")}
|
||||
/>,
|
||||
});
|
||||
}
|
||||
|
||||
const bot = _t("This bridge is managed by <user />.", {}, {
|
||||
user: <Pill
|
||||
type={Pill.TYPE_USER_MENTION}
|
||||
room={this.props.room}
|
||||
url={makeUserPermalink(this.props.ev.getSender())}
|
||||
shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")}
|
||||
/>,
|
||||
});
|
||||
|
||||
let networkIcon;
|
||||
|
||||
if (protocol.avatar) {
|
||||
const avatarUrl = getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
protocol.avatar, 64, 64, "crop",
|
||||
);
|
||||
|
||||
networkIcon = <BaseAvatar className="protocol-icon"
|
||||
width={48}
|
||||
height={48}
|
||||
resizeMethod='crop'
|
||||
name={ protocolName }
|
||||
idName={ protocolName }
|
||||
url={ avatarUrl }
|
||||
/>;
|
||||
} else {
|
||||
networkIcon = <div class="noProtocolIcon"></div>;
|
||||
}
|
||||
|
||||
const id = this.props.ev.getId();
|
||||
const metadataClassname = "metadata" + (this.state.visible ? " visible" : "");
|
||||
return (<li key={id}>
|
||||
<div className="column-icon">
|
||||
{networkIcon}
|
||||
</div>
|
||||
<div className="column-data">
|
||||
<h3>{protocolName}</h3>
|
||||
<p className="workspace-channel-details">
|
||||
<span>{_t("Workspace: %(networkName)s", {networkName})}</span>
|
||||
<span className="channel">{_t("Channel: %(channelName)s", {channelName})}</span>
|
||||
</p>
|
||||
<p className={metadataClassname}>
|
||||
{creator} {bot}
|
||||
</p>
|
||||
<AccessibleButton className="mx_showMore" kind="secondary" onClick={this._toggleVisible.bind(this)}>
|
||||
{ this.state.visible ? _t("Show less") : _t("Show more") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</li>);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
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 React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
||||
import {_t} from "../../../languageHandler";
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import Pill from "../elements/Pill";
|
||||
import {makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import BaseAvatar from "../avatars/BaseAvatar";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { isUrlPermitted } from '../../../HtmlUtils';
|
||||
|
||||
interface IProps {
|
||||
ev: MatrixEvent;
|
||||
room: Room;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should match https://github.com/matrix-org/matrix-doc/blob/hs/msc-bridge-inf/proposals/2346-bridge-info-state-event.md#mbridge
|
||||
*/
|
||||
interface IBridgeStateEvent {
|
||||
bridgebot: string;
|
||||
creator?: string;
|
||||
protocol: {
|
||||
id: string;
|
||||
displayname?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
avatar_url?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
external_url?: string;
|
||||
};
|
||||
network?: {
|
||||
id: string;
|
||||
displayname?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
avatar_url?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
external_url?: string;
|
||||
};
|
||||
channel: {
|
||||
id: string;
|
||||
displayname?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
avatar_url?: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
external_url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default class BridgeTile extends React.PureComponent<IProps> {
|
||||
static propTypes = {
|
||||
ev: PropTypes.object.isRequired,
|
||||
room: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const content: IBridgeStateEvent = this.props.ev.getContent();
|
||||
// Validate
|
||||
if (!content.channel?.id || !content.protocol?.id) {
|
||||
console.warn(`Bridge info event ${this.props.ev.getId()} has missing content. Tile will not render`);
|
||||
return null;
|
||||
}
|
||||
if (!content.bridgebot) {
|
||||
// Bridgebot was not required previously, so in order to not break rooms we are allowing
|
||||
// the sender to be used in place. When the proposal is merged, this should be removed.
|
||||
console.warn(`Bridge info event ${this.props.ev.getId()} does not provide a 'bridgebot' key which`
|
||||
+ "is deprecated behaviour. Using sender for now.");
|
||||
content.bridgebot = this.props.ev.getSender();
|
||||
}
|
||||
const { channel, network, protocol } = content;
|
||||
const protocolName = protocol.displayname || protocol.id;
|
||||
const channelName = channel.displayname || channel.id;
|
||||
|
||||
let creator = null;
|
||||
if (content.creator) {
|
||||
creator = <li>{_t("This bridge was provisioned by <user />.", {}, {
|
||||
user: () => <Pill
|
||||
type={Pill.TYPE_USER_MENTION}
|
||||
room={this.props.room}
|
||||
url={makeUserPermalink(content.creator)}
|
||||
shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")}
|
||||
/>,
|
||||
})}</li>;
|
||||
}
|
||||
|
||||
const bot = <li>{_t("This bridge is managed by <user />.", {}, {
|
||||
user: () => <Pill
|
||||
type={Pill.TYPE_USER_MENTION}
|
||||
room={this.props.room}
|
||||
url={makeUserPermalink(content.bridgebot)}
|
||||
shouldShowPillAvatar={SettingsStore.getValue("Pill.shouldShowPillAvatar")}
|
||||
/>,
|
||||
})}</li>;
|
||||
|
||||
let networkIcon;
|
||||
|
||||
if (protocol.avatar_url) {
|
||||
const avatarUrl = getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
protocol.avatar_url, 64, 64, "crop",
|
||||
);
|
||||
|
||||
networkIcon = <BaseAvatar className="protocol-icon"
|
||||
width={48}
|
||||
height={48}
|
||||
resizeMethod='crop'
|
||||
name={ protocolName }
|
||||
idName={ protocolName }
|
||||
url={ avatarUrl }
|
||||
/>;
|
||||
} else {
|
||||
networkIcon = <div className="noProtocolIcon"></div>;
|
||||
}
|
||||
let networkItem = null;
|
||||
if (network) {
|
||||
const networkName = network.displayname || network.id;
|
||||
let networkLink = <span>{networkName}</span>;
|
||||
if (typeof network.external_url === "string" && isUrlPermitted(network.external_url)) {
|
||||
networkLink = <a href={network.external_url} target="_blank" rel="noreferrer noopener">{networkName}</a>
|
||||
}
|
||||
networkItem = _t("Workspace: <networkLink/>", {}, {
|
||||
networkLink: () => networkLink,
|
||||
});
|
||||
}
|
||||
|
||||
let channelLink = <span>{channelName}</span>;
|
||||
if (typeof channel.external_url === "string" && isUrlPermitted(channel.external_url)) {
|
||||
channelLink = <a href={channel.external_url} target="_blank" rel="noreferrer noopener">{channelName}</a>
|
||||
}
|
||||
|
||||
const id = this.props.ev.getId();
|
||||
return (<li key={id}>
|
||||
<div className="column-icon">
|
||||
{networkIcon}
|
||||
</div>
|
||||
<div className="column-data">
|
||||
<h3>{protocolName}</h3>
|
||||
<p className="workspace-channel-details">
|
||||
{networkItem}
|
||||
<span className="channel">{_t("Channel: <channelLink/>", {}, {
|
||||
channelLink: () => channelLink,
|
||||
})}</span>
|
||||
</p>
|
||||
<ul className="metadata">
|
||||
{creator} {bot}
|
||||
</ul>
|
||||
</div>
|
||||
</li>);
|
||||
}
|
||||
}
|
|
@ -584,6 +584,7 @@
|
|||
"Send stickers into this room": "Send stickers into this room",
|
||||
"Send stickers into your active room": "Send stickers into your active room",
|
||||
"Change which room you're viewing": "Change which room you're viewing",
|
||||
"Change which room, message, or user you're viewing": "Change which room, message, or user you're viewing",
|
||||
"Change the topic of this room": "Change the topic of this room",
|
||||
"See when the topic changes in this room": "See when the topic changes in this room",
|
||||
"Change the topic of your active room": "Change the topic of your active room",
|
||||
|
@ -965,10 +966,8 @@
|
|||
"Upload": "Upload",
|
||||
"This bridge was provisioned by <user />.": "This bridge was provisioned by <user />.",
|
||||
"This bridge is managed by <user />.": "This bridge is managed by <user />.",
|
||||
"Workspace: %(networkName)s": "Workspace: %(networkName)s",
|
||||
"Channel: %(channelName)s": "Channel: %(channelName)s",
|
||||
"Show less": "Show less",
|
||||
"Show more": "Show more",
|
||||
"Workspace: <networkLink/>": "Workspace: <networkLink/>",
|
||||
"Channel: <channelLink/>": "Channel: <channelLink/>",
|
||||
"Failed to upload profile picture!": "Failed to upload profile picture!",
|
||||
"Upload new:": "Upload new:",
|
||||
"No display name": "No display name",
|
||||
|
@ -1532,6 +1531,7 @@
|
|||
"Jump to first invite.": "Jump to first invite.",
|
||||
"Show %(count)s more|other": "Show %(count)s more",
|
||||
"Show %(count)s more|one": "Show %(count)s more",
|
||||
"Show less": "Show less",
|
||||
"Use default": "Use default",
|
||||
"All messages": "All messages",
|
||||
"Mentions & Keywords": "Mentions & Keywords",
|
||||
|
@ -1594,6 +1594,7 @@
|
|||
"New published address (e.g. #alias:server)": "New published address (e.g. #alias:server)",
|
||||
"Local Addresses": "Local Addresses",
|
||||
"Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)",
|
||||
"Show more": "Show more",
|
||||
"Error updating flair": "Error updating flair",
|
||||
"There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.": "There was an error updating the flair for this room. The server may not allow it or a temporary error occurred.",
|
||||
"Invalid community ID": "Invalid community ID",
|
||||
|
|
|
@ -426,7 +426,8 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
default: true,
|
||||
},
|
||||
"allowedWidgets": {
|
||||
supportedLevels: [SettingLevel.ROOM_ACCOUNT],
|
||||
supportedLevels: [SettingLevel.ROOM_ACCOUNT, SettingLevel.ROOM_DEVICE],
|
||||
supportedLevelsAreOrdered: true,
|
||||
default: {}, // none allowed
|
||||
},
|
||||
"analyticsOptIn": {
|
||||
|
|
|
@ -467,6 +467,32 @@ export default class SettingsStore {
|
|||
return LEVEL_HANDLERS[level].isSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the first supported level out of all the levels that can be used for a
|
||||
* specific setting.
|
||||
* @param {string} settingName The setting name.
|
||||
* @return {SettingLevel}
|
||||
*/
|
||||
public static firstSupportedLevel(settingName: string): SettingLevel {
|
||||
// Verify that the setting is actually a setting
|
||||
const setting = SETTINGS[settingName];
|
||||
if (!setting) {
|
||||
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
||||
}
|
||||
|
||||
const levelOrder = (setting.supportedLevelsAreOrdered ? setting.supportedLevels : LEVEL_ORDER);
|
||||
if (!levelOrder.includes(SettingLevel.DEFAULT)) levelOrder.push(SettingLevel.DEFAULT); // always include default
|
||||
|
||||
const handlers = SettingsStore.getHandlers(settingName);
|
||||
|
||||
for (const level of levelOrder) {
|
||||
const handler = handlers[level];
|
||||
if (!handler) continue;
|
||||
return level;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging function for reading explicit setting values without going through the
|
||||
* complicated/biased functions in the SettingsStore. This will print information to
|
||||
|
|
|
@ -169,7 +169,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
|
|||
|
||||
public isSupported(): boolean {
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli !== undefined && cli !== null;
|
||||
return cli !== undefined && cli !== null && !cli.isGuest();
|
||||
}
|
||||
|
||||
private getSettings(eventType = "im.vector.web.settings"): any { // TODO: [TS] Types on return
|
||||
|
|
|
@ -129,7 +129,7 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin
|
|||
|
||||
public isSupported(): boolean {
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli !== undefined && cli !== null;
|
||||
return cli !== undefined && cli !== null && !cli.isGuest();
|
||||
}
|
||||
|
||||
private getSettings(roomId: string, eventType = "im.vector.web.settings"): any { // TODO: [TS] Type return
|
||||
|
|
|
@ -48,6 +48,10 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
|
|||
return CommunityPrototypeStore.internalInstance;
|
||||
}
|
||||
|
||||
public static getUpdateEventName(roomId: string): string {
|
||||
return `${UPDATE_EVENT}:${roomId}`;
|
||||
}
|
||||
|
||||
public getSelectedCommunityId(): string {
|
||||
if (SettingsStore.getValue("feature_communities_v2_prototypes")) {
|
||||
return GroupFilterOrderStore.getSelectedTags()[0];
|
||||
|
@ -134,7 +138,8 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
} else if (payload.action === "MatrixActions.accountData") {
|
||||
if (payload.event_type.startsWith("im.vector.group_info.")) {
|
||||
this.emit(UPDATE_EVENT, payload.event_type.substring("im.vector.group_info.".length));
|
||||
const roomId = payload.event_type.substring("im.vector.group_info.".length);
|
||||
this.emit(CommunityPrototypeStore.getUpdateEventName(roomId), roomId);
|
||||
}
|
||||
} else if (payload.action === "select_tag") {
|
||||
// Automatically select the general chat when switching communities
|
||||
|
@ -167,7 +172,7 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
|
|||
if (getEffectiveMembership(myMember.membership) === EffectiveMembership.Invite) {
|
||||
// Fake an update for anything that might have started listening before the invite
|
||||
// data was available (eg: RoomPreviewBar after a refresh)
|
||||
this.emit(UPDATE_EVENT, room.roomId);
|
||||
this.emit(CommunityPrototypeStore.getUpdateEventName(room.roomId), room.roomId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import { UPDATE_EVENT } from "../AsyncStore";
|
|||
|
||||
// Emitted event for when a room's preview has changed. First argument will the room for which
|
||||
// the change happened.
|
||||
export const ROOM_PREVIEW_CHANGED = "room_preview_changed";
|
||||
const ROOM_PREVIEW_CHANGED = "room_preview_changed";
|
||||
|
||||
const PREVIEWS = {
|
||||
'm.room.message': {
|
||||
|
@ -84,6 +84,10 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
|||
return MessagePreviewStore.internalInstance;
|
||||
}
|
||||
|
||||
public static getPreviewChangedEventName(room: Room): string {
|
||||
return `${ROOM_PREVIEW_CHANGED}:${room?.roomId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pre-translated preview for a given room
|
||||
* @param room The room to get the preview for.
|
||||
|
@ -150,7 +154,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
|||
// We've muted the underlying Map, so just emit that we've changed.
|
||||
this.previews.set(room.roomId, map);
|
||||
this.emit(UPDATE_EVENT, this);
|
||||
this.emit(ROOM_PREVIEW_CHANGED, room);
|
||||
this.emit(MessagePreviewStore.getPreviewChangedEventName(room), room);
|
||||
}
|
||||
return; // we're done
|
||||
}
|
||||
|
@ -158,7 +162,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
|||
// At this point, we didn't generate a preview so clear it
|
||||
this.previews.set(room.roomId, new Map<TagID|TAG_ANY, string|null>());
|
||||
this.emit(UPDATE_EVENT, this);
|
||||
this.emit(ROOM_PREVIEW_CHANGED, room);
|
||||
this.emit(MessagePreviewStore.getPreviewChangedEventName(room), room);
|
||||
}
|
||||
|
||||
protected async onAction(payload: ActionPayload) {
|
||||
|
|
|
@ -20,9 +20,16 @@ export enum ElementWidgetActions {
|
|||
ClientReady = "im.vector.ready",
|
||||
HangupCall = "im.vector.hangup",
|
||||
OpenIntegrationManager = "integration_manager_open",
|
||||
|
||||
/**
|
||||
* @deprecated Use MSC2931 instead
|
||||
*/
|
||||
ViewRoom = "io.element.view_room",
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use MSC2931 instead
|
||||
*/
|
||||
export interface IViewRoomApiRequest extends IWidgetApiRequest {
|
||||
data: {
|
||||
room_id: string; // eslint-disable-line camelcase
|
||||
|
|
|
@ -15,5 +15,8 @@
|
|||
*/
|
||||
|
||||
export enum ElementWidgetCapabilities {
|
||||
/**
|
||||
* @deprecated Use MSC2931 instead.
|
||||
*/
|
||||
CanChangeViewedRoom = "io.element.view_room",
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
|
|||
import { CHAT_EFFECTS } from "../../effects";
|
||||
import { containsEmoji } from "../../effects/utils";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import {tryTransformPermalinkToLocalHref} from "../../utils/permalinks/Permalinks";
|
||||
|
||||
// TODO: Purge this from the universe
|
||||
|
||||
|
@ -171,4 +172,12 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async navigate(uri: string): Promise<void> {
|
||||
const localUri = tryTransformPermalinkToLocalHref(uri);
|
||||
if (!localUri || localUri === uri) { // parse failure can lead to an unmodified URL
|
||||
throw new Error("Failed to transform URI");
|
||||
}
|
||||
window.location.hash = localUri; // it'll just be a fragment
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,9 @@ export class CapabilityText {
|
|||
[ElementWidgetCapabilities.CanChangeViewedRoom]: {
|
||||
[GENERIC_WIDGET_KIND]: _td("Change which room you're viewing"),
|
||||
},
|
||||
[MatrixCapabilities.MSC2931Navigate]: {
|
||||
[GENERIC_WIDGET_KIND]: _td("Change which room, message, or user you're viewing"),
|
||||
},
|
||||
};
|
||||
|
||||
private static stateSendRecvCaps: ISendRecvStaticCapText = {
|
||||
|
|
|
@ -222,6 +222,7 @@ describe("<TextualBody />", () => {
|
|||
getRoom: () => mkStubRoom("room_id"),
|
||||
getAccountData: () => undefined,
|
||||
getUrlPreview: (url) => new Promise(() => {}),
|
||||
isGuest: () => false,
|
||||
};
|
||||
|
||||
const ev = mkEvent({
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -1809,6 +1809,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
|
||||
|
||||
"@types/events@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||
|
||||
"@types/fbemitter@*":
|
||||
version "2.0.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c"
|
||||
|
@ -6554,8 +6559,8 @@ mathml-tag-names@^2.0.1:
|
|||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||
|
||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
||||
version "9.4.1"
|
||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/1717fcf499b943517213f2a81c41ffec0b50748e"
|
||||
version "9.5.0"
|
||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/214a9df3823e602400b24d9a81cfc7b7df0a863b"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
another-json "^0.2.0"
|
||||
|
@ -6580,11 +6585,12 @@ matrix-react-test-utils@^0.2.2:
|
|||
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853"
|
||||
integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ==
|
||||
|
||||
matrix-widget-api@^0.1.0-beta.10:
|
||||
version "0.1.0-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.10.tgz#2e4d658d90ff3152c5567089b4ddd21fb44ec1dd"
|
||||
integrity sha512-yX2UURjM1zVp7snPiOFcH9+FDBdHfAdt5HEAyDUHGJ7w/F2zOtcK/y0dMlZ1+XhxY7Wv0IBZH0US8X/ioJRX1A==
|
||||
matrix-widget-api@0.1.0-beta.11:
|
||||
version "0.1.0-beta.11"
|
||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.11.tgz#3658e244bf82eebea36e64475ebfce86b778b476"
|
||||
integrity sha512-RxIghopRKTQdmYMJzZg/QR+wcPcKe9S1pZZ31zN/M1LKsvTLThBYdMcP1idKh7MkhpO9eCdCVjJOMJTrqxNzbQ==
|
||||
dependencies:
|
||||
"@types/events" "^3.0.0"
|
||||
events "^3.2.0"
|
||||
|
||||
mdast-util-compact@^1.0.0:
|
||||
|
|
Loading…
Reference in New Issue