From 702a8ff4a947adba2f859f9e063655d8d3652b44 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 8 Dec 2019 01:01:19 +0000 Subject: [PATCH 001/101] Change ref handling in TextualBody to prevent it parsing generated nodes Remove unused paths Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/HtmlUtils.js | 5 ++-- .../views/context_menus/MessageContextMenu.js | 2 +- src/components/views/messages/TextualBody.js | 29 ++++++++++--------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 2b7384a5aa..9cf3994ff4 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -394,6 +394,7 @@ class TextHighlighter extends BaseHighlighter { * opts.stripReplyFallback: optional argument specifying the event is a reply and so fallback needs removing * opts.returnString: return an HTML string rather than JSX elements * opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer + * opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString) */ export function bodyToHtml(content, highlights, opts={}) { const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body; @@ -476,8 +477,8 @@ export function bodyToHtml(content, highlights, opts={}) { }); return isDisplayedWithHtml ? - : - { strippedBody }; + : + { strippedBody }; } /** diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index efbfc4322f..2084a67cdc 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -422,7 +422,7 @@ module.exports = createReactClass({ ); - if (this.props.eventTileOps && this.props.eventTileOps.getInnerText) { + if (this.props.eventTileOps) { // this event is rendered using TextuaLBody quoteButton = ( { _t('Quote') } diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 2680c13512..fdfa351df3 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; @@ -86,6 +86,10 @@ module.exports = createReactClass({ return successful; }, + componentWillMount: function() { + this._content = createRef(); + }, + componentDidMount: function() { this._unmounted = false; if (!this.props.editState) { @@ -94,13 +98,13 @@ module.exports = createReactClass({ }, _applyFormatting() { - this.activateSpoilers(this.refs.content.children); + this.activateSpoilers([this._content.current]); // pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer // are still sent as plaintext URLs. If these are ever pillified in the composer, // we should be pillify them here by doing the linkifying BEFORE the pillifying. - pillifyLinks(this.refs.content.children, this.props.mxEvent); - HtmlUtils.linkifyElement(this.refs.content); + pillifyLinks([this._content.current], this.props.mxEvent); + HtmlUtils.linkifyElement(this._content.current); this.calculateUrlPreview(); if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") { @@ -163,7 +167,8 @@ module.exports = createReactClass({ //console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview); if (this.props.showUrlPreview) { - let links = this.findLinks(this.refs.content.children); + // pass only the first child which is the event tile otherwise this recurses on edited events + let links = this.findLinks([this._content.current]); if (links.length) { // de-dup the links (but preserve ordering) const seen = new Set(); @@ -325,10 +330,6 @@ module.exports = createReactClass({ global.localStorage.removeItem("hide_preview_" + this.props.mxEvent.getId()); } }, - - getInnerText: () => { - return this.refs.content.innerText; - }, }; }, @@ -424,6 +425,7 @@ module.exports = createReactClass({ disableBigEmoji: content.msgtype === "m.emote" || !SettingsStore.getValue('TextualBody.enableBigEmoji'), // Part of Replies fallback support stripReplyFallback: stripReply, + ref: this._content, }); if (this.props.replacingEventId) { body = [body, this._renderEditedMarker()]; @@ -450,15 +452,14 @@ module.exports = createReactClass({ switch (content.msgtype) { case "m.emote": - const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); return ( - + - { name } + { mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender() }   { body } @@ -467,14 +468,14 @@ module.exports = createReactClass({ ); case "m.notice": return ( - + { body } { widgets } ); default: // including "m.text" return ( - + { body } { widgets } From 0d537ecbb3e9cb59084e468fec0499b8ee6afe7a Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 2 Dec 2019 17:27:12 +0000 Subject: [PATCH 002/101] Add bridge info tab --- .../settings/tabs/room/BridgeSettingsTab.js | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/components/views/settings/tabs/room/BridgeSettingsTab.js diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js new file mode 100644 index 0000000000..732d7b2947 --- /dev/null +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -0,0 +1,128 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import {_t} from "../../../../../languageHandler"; +import MatrixClientPeg from "../../../../../MatrixClientPeg"; +import Pill from "../../../elements/Pill"; +import {makeUserPermalink} from "../../../../../utils/permalinks/Permalinks"; + +const BRIDGE_EVENT_TYPES = [ + "uk.half-shot.bridge", + // m.bridge +]; + +export default class BridgeSettingsTab extends React.Component { + static propTypes = { + roomId: PropTypes.string.isRequired, + }; + + constructor() { + super(); + + this.state = { + }; + } + + componentWillMount() { + + } + + _renderBridgeCard(event, room) { + const content = event.getContent(); + if (!content || !content.channel || !content.protocol) { + return null; + } + const protocolName = content.protocol.displayname || content.protocol.id; + const channelName = content.channel.displayname || content.channel.id; + const networkName = content.network ? " on " + (content.network.displayname || content.network.id) : ""; + let status = null; + if (content.status === "active") { + status = (

Status: Active

); + } else if (content.status === "disabled") { + status = (

Status: Disabled

); + } + + let creator = null; + if (content.creator) { + creator = (

+ This bridge was provisioned by +

); + } + + const bot = (

+ The bridge is managed by the bot user.

+ ); + + const chanAndNetworkInfo = ( +

Bridged into {channelName}{networkName}, on {protocolName}

+ ); + + return (
  • +
    +

    {channelName}{networkName} ({protocolName})

    +
    + {status} + {creator} + {bot} + {chanAndNetworkInfo} +
    +
    +
  • ); + } + + static getBridgeStateEvents(roomId) { + const client = MatrixClientPeg.get(); + const roomState = (client.getRoom(roomId)).currentState; + + const bridgeEvents = Array.concat(...BRIDGE_EVENT_TYPES.map((typeName) => + Object.values(roomState.events[typeName] || {}), + )); + + return bridgeEvents; + } + + render() { + // This settings tab will only be invoked if the following function returns more + // than 0 events, so no validation is needed at this stage. + const bridgeEvents = BridgeSettingsTab.getBridgeStateEvents(this.props.roomId); + const client = MatrixClientPeg.get(); + const room = client.getRoom(this.props.roomId); + + return ( +
    +
    {_t("Bridge Info")}
    +
    +

    Below is a list of bridges connected to this room.

    +
      + { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) } +
    +
    +
    + ); + } +} From 7c35b16f4d272b048e23ecd969e0f7aea5e44983 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 2 Dec 2019 17:27:23 +0000 Subject: [PATCH 003/101] Add bridge tab button --- src/components/views/dialogs/RoomSettingsDialog.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 740dc4d2c2..b262a1f078 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -24,6 +24,7 @@ import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab"; import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab"; import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab"; import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; +import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; import dis from "../../../dispatcher"; @@ -52,6 +53,7 @@ export default class RoomSettingsDialog extends React.Component { _getTabs() { const tabs = []; + const shouldShowBridgeIcon = BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; tabs.push(new Tab( _td("General"), @@ -73,6 +75,15 @@ export default class RoomSettingsDialog extends React.Component { "mx_RoomSettingsDialog_rolesIcon", , )); + + if (shouldShowBridgeIcon) { + tabs.push(new Tab( + _td("Bridge Info"), + "mx_RoomSettingsDialog_bridgesIcon", + , + )); + } + tabs.push(new Tab( _td("Advanced"), "mx_RoomSettingsDialog_warningIcon", From 626ab17ed38e9c8c11dd9accb70d38be7439f1ac Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 2 Dec 2019 17:27:31 +0000 Subject: [PATCH 004/101] Styling --- .../views/dialogs/_RoomSettingsDialog.scss | 17 +++++++ res/img/feather-customised/bridge.svg | 50 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 res/img/feather-customised/bridge.svg diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 723eb237ad..8e648e8881 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -29,6 +29,11 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/users-sm.svg'); } +.mx_RoomSettingsDialog_bridgesIcon::before { + // This icon is pants, please improve :) + mask-image: url('$(res)/img/feather-customised/bridge.svg'); +} + .mx_RoomSettingsDialog_warningIcon::before { mask-image: url('$(res)/img/feather-customised/warning-triangle.svg'); } @@ -42,3 +47,15 @@ limitations under the License. padding-left: 40px; padding-right: 80px; } + + +.mx_RoomSettingsDialog_BridgeList { + padding: 0; +} + +.mx_RoomSettingsDialog_BridgeList li { + list-style-type: none; + padding: 0; + margin: 0; + border-bottom: 1px solid $panel-divider-color; +} \ No newline at end of file diff --git a/res/img/feather-customised/bridge.svg b/res/img/feather-customised/bridge.svg new file mode 100644 index 0000000000..f8f3468155 --- /dev/null +++ b/res/img/feather-customised/bridge.svg @@ -0,0 +1,50 @@ + + + + + + + image/svg+xml + + + + + + + + + + + From 2bc6e2e3326250fb55ebf3b4662fd42602db1ecf Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 2 Dec 2019 17:27:38 +0000 Subject: [PATCH 005/101] Add the one string I bothered to i18n --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 182c761c5f..74ac452bb1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -725,6 +725,7 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", + "Bridge Info": "Bridge Info", "Room Addresses": "Room Addresses", "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "URL Previews": "URL Previews", From 9f2ccdf913dce10170a7d2465a2f97ac0936d77e Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Wed, 4 Dec 2019 15:02:36 +0000 Subject: [PATCH 006/101] Add support for displaying avatars and links in bridge info --- .../settings/tabs/room/BridgeSettingsTab.js | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 732d7b2947..459f11277c 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -20,6 +20,8 @@ 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 { ContentRepo } from "matrix-js-sdk"; const BRIDGE_EVENT_TYPES = [ "uk.half-shot.bridge", @@ -47,9 +49,10 @@ export default class BridgeSettingsTab extends React.Component { if (!content || !content.channel || !content.protocol) { return null; } + const { channel, network } = content; const protocolName = content.protocol.displayname || content.protocol.id; - const channelName = content.channel.displayname || content.channel.id; - const networkName = content.network ? " on " + (content.network.displayname || content.network.id) : ""; + const channelName = channel.displayname || channel.id; + const networkName = network ? network.displayname || network.id : ""; let status = null; if (content.status === "active") { status = (

    Status: Active

    ); @@ -78,13 +81,41 @@ export default class BridgeSettingsTab extends React.Component { /> bot user.

    ); + const channelLink = channel.external_url ? ({channelName}) : channelName; + const networkLink = network && network.external_url ? ({networkName}) + : networkName; + const chanAndNetworkInfo = ( -

    Bridged into {channelName}{networkName}, on {protocolName}

    +

    Bridged into {channelLink} {networkLink}, on {protocolName}

    ); + let networkIcon = null; + if (networkName && network.avatar) { + const avatarUrl = ContentRepo.getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + network.avatar, 32, 32, "crop", + ); + networkIcon = ; + } + + let channelIcon = null; + if (channel.avatar) { + const avatarUrl = ContentRepo.getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + channel.avatar, 32, 32, "crop", + ); + console.log(channel.avatar); + channelIcon = ; + } + return (
  • -

    {channelName}{networkName} ({protocolName})

    +

    {channelIcon} {channelName} {networkName ? ` on ${networkName}` : ""} {networkIcon}

    +

    Connected via {protocolName}

    {status} {creator} From ce21ce8bbea93c9a2b48b0c0c6f341f4360d375b Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Thu, 5 Dec 2019 00:28:29 +0000 Subject: [PATCH 007/101] Lint --- .../views/settings/tabs/room/BridgeSettingsTab.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 459f11277c..031e2651c3 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -80,10 +80,15 @@ export default class BridgeSettingsTab extends React.Component { shouldShowPillAvatar={true} /> bot user.

    ); + let channelLink = channelName; + if (channel.external_url) { + channelLink = {channelName}; + } - const channelLink = channel.external_url ? ({channelName}) : channelName; - const networkLink = network && network.external_url ? ({networkName}) - : networkName; + let networkLink = networkName; + if (network && network.external_url) { + networkLink = {networkName}; + } const chanAndNetworkInfo = (

    Bridged into {channelLink} {networkLink}, on {protocolName}

    From d9943754f7c936fae5643ae6c16205d99467d9f9 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Dec 2019 13:28:16 +0000 Subject: [PATCH 008/101] Remove `status` as it's no longer part of the MSC --- .../views/settings/tabs/room/BridgeSettingsTab.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 031e2651c3..a165a1db44 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -53,12 +53,6 @@ export default class BridgeSettingsTab extends React.Component { const protocolName = content.protocol.displayname || content.protocol.id; const channelName = channel.displayname || channel.id; const networkName = network ? network.displayname || network.id : ""; - let status = null; - if (content.status === "active") { - status = (

    Status: Active

    ); - } else if (content.status === "disabled") { - status = (

    Status: Disabled

    ); - } let creator = null; if (content.creator) { @@ -122,7 +116,6 @@ export default class BridgeSettingsTab extends React.Component {

    {channelIcon} {channelName} {networkName ? ` on ${networkName}` : ""} {networkIcon}

    Connected via {protocolName}

    - {status} {creator} {bot} {chanAndNetworkInfo} From 7ee5f7ba38e25cf5774e03008ba375c7d5e3f791 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Dec 2019 13:28:43 +0000 Subject: [PATCH 009/101] Add feature flag --- src/components/views/dialogs/RoomSettingsDialog.js | 4 +++- src/i18n/strings/en_EN.json | 3 ++- src/settings/Settings.js | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index b262a1f078..2952439076 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -28,6 +28,7 @@ import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; import dis from "../../../dispatcher"; +import SettingsStore from "../settings/SettingsStore"; export default class RoomSettingsDialog extends React.Component { static propTypes = { @@ -53,7 +54,8 @@ export default class RoomSettingsDialog extends React.Component { _getTabs() { const tabs = []; - const shouldShowBridgeIcon = BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; + const featureFlag = SettingsStore.isFeatureEnabled("feature_bridge_state"); + const shouldShowBridgeIcon = featureFlag && BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; tabs.push(new Tab( _td("General"), diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 74ac452bb1..14ba96fa4a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1932,5 +1932,6 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", + "Show info about bridges in room settings": "Show info about bridges in room settings" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index b02ab82400..94cc5b587d 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -154,6 +154,12 @@ export const SETTINGS = { displayName: _td("Enable local event indexing and E2EE search (requires restart)"), default: false, }, + "feature_bridge_state": { + isFeature: true, + supportedLevels: LEVELS_FEATURE, + displayName: _td("Show info about bridges in room settings"), + default: false, + }, "useCiderComposer": { displayName: _td("Use the new, faster, composer for writing messages"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 6225e402ccdf7781cc6062c339265d63da455fc9 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Dec 2019 13:54:21 +0000 Subject: [PATCH 010/101] i18n'ed all over the plaace --- .../views/dialogs/RoomSettingsDialog.js | 2 +- .../settings/tabs/room/BridgeSettingsTab.js | 58 ++++++++++++------- src/i18n/strings/en_EN.json | 10 +++- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 2952439076..9ac2b17f23 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -28,7 +28,7 @@ import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; import dis from "../../../dispatcher"; -import SettingsStore from "../settings/SettingsStore"; +import SettingsStore from "../../../settings/SettingsStore"; export default class RoomSettingsDialog extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index a165a1db44..82382e7828 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -52,28 +52,31 @@ export default class BridgeSettingsTab extends React.Component { const { channel, network } = content; const protocolName = content.protocol.displayname || content.protocol.id; const channelName = channel.displayname || channel.id; - const networkName = network ? network.displayname || network.id : ""; + const networkName = network ? network.displayname || network.id : protocolName; let creator = null; if (content.creator) { - creator = (

    - This bridge was provisioned by -

    ); + const pill = ; + creator = (

    { + _t("This bridge was provisioned by %(pill)s", { + pill, + }) + }

    ); } - const bot = (

    - The bridge is managed by the bot user.

    - ); + const bot = (

    {_t("This bridge is managed by the %(pill)s bot user.", { + pill: , + })}

    ); let channelLink = channelName; if (channel.external_url) { channelLink = {channelName}; @@ -85,7 +88,11 @@ export default class BridgeSettingsTab extends React.Component { } const chanAndNetworkInfo = ( -

    Bridged into {channelLink} {networkLink}, on {protocolName}

    + (_t("Bridged into %(channelLink)s %(networkLink)s, on %(protocolName)s", { + channelLink, + networkLink, + protocolName, + })) ); let networkIcon = null; @@ -111,14 +118,21 @@ export default class BridgeSettingsTab extends React.Component { url={ avatarUrl } />; } + const heading = _t("Connected to %(channelIcon)s %(channelName)s on %(networkIcon)s %(networkName)s", { + channelIcon, + channelName, + networkName, + networkIcon, + }); + return (
  • -

    {channelIcon} {channelName} {networkName ? ` on ${networkName}` : ""} {networkIcon}

    -

    Connected via {protocolName}

    +

    {heading}

    +

    {_t("Connected via %(protocolName)s", { protocolName })}

    {creator} {bot} - {chanAndNetworkInfo} +

    {chanAndNetworkInfo}

  • ); @@ -146,7 +160,7 @@ export default class BridgeSettingsTab extends React.Component {
    {_t("Bridge Info")}
    -

    Below is a list of bridges connected to this room.

    +

    { _t("Below is a list of bridges connected to this room.") }

      { bridgeEvents.map((event) => this._renderBridgeCard(event, room)) }
    diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 14ba96fa4a..b76310ec27 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -342,6 +342,7 @@ "Send verification requests in direct message, including a new verification UX in the member panel.": "Send verification requests in direct message, including a new verification UX in the member panel.", "Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)", "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)", + "Show info about bridges in room settings": "Show info about bridges in room settings", "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", @@ -725,7 +726,13 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", + "This bridge was provisioned by %(pill)s": "This bridge was provisioned by %(pill)s", + "This bridge is managed by the %(pill)s bot user.": "This bridge is managed by the %(pill)s bot user.", + "Bridged into %(channelLink)s %(networkLink)s, on %(protocolName)s": "Bridged into %(channelLink)s %(networkLink)s, on %(protocolName)s", + "Connected to %(channelIcon)s %(channelName)s on %(networkIcon)s %(networkName)s": "Connected to %(channelIcon)s %(channelName)s on %(networkIcon)s %(networkName)s", + "Connected via %(protocolName)s": "Connected via %(protocolName)s", "Bridge Info": "Bridge Info", + "Below is a list of bridges connected to this room.": "Below is a list of bridges connected to this room.", "Room Addresses": "Room Addresses", "Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?", "URL Previews": "URL Previews", @@ -1932,6 +1939,5 @@ "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", "Failed to set direct chat tag": "Failed to set direct chat tag", "Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room", - "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room", - "Show info about bridges in room settings": "Show info about bridges in room settings" + "Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room" } From eb5ebaf7547ddcc16e5500081b135862aab33b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Mon, 16 Dec 2019 12:19:22 +0000 Subject: [PATCH 011/101] Translated using Weblate (French) Currently translated at 100.0% (2001 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index f4e2a86e77..96f2337229 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2008,5 +2008,12 @@ "Hide verified sessions": "Masquer les sessions vérifiées", "%(count)s verified sessions|other": "%(count)s sessions vérifiées", "%(count)s verified sessions|one": "1 session vérifiée", - "Close preview": "Fermer l’aperçu" + "Close preview": "Fermer l’aperçu", + "This user has not verified all of their devices.": "Cet utilisateur n'a pas vérifié tous ses appareils.", + "You have not verified this user. This user has verified all of their devices.": "Vous n'avez pas vérifié cet utilisateur. Cet utilisateur a vérifié tous ses appareils.", + "You have verified this user. This user has verified all of their devices.": "Vous avez vérifié cet utilisateur. Cet utilisateur a vérifié tous ses appareils.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Certains utilisateurs dans ce salon chiffré n’ont pas été vérifiés par vous ou n’ont pas vérifié leurs propres appareils.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Tous les utilisateurs de ce salon chiffré ont été vérifiés par vous et ont vérifié leurs propres appareils.", + "Language Dropdown": "Sélection de la langue", + "Country Dropdown": "Sélection du pays" } From 284a1fdde797e55e2a465a81a270a55a385d7aec Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Tue, 17 Dec 2019 03:40:47 +0000 Subject: [PATCH 012/101] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1998 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 829af63542..cea6d8d70d 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2008,5 +2008,12 @@ "Secure your encrypted messages with a passphrase": "使用密碼保護您的加密訊息", "Storing secrets...": "正在儲存秘密……", "Unable to set up secret storage": "無法設定秘密儲存空間", - "Close preview": "關閉預覽" + "Close preview": "關閉預覽", + "This user has not verified all of their devices.": "這個使用者尚未驗證所有裝置。", + "You have not verified this user. This user has verified all of their devices.": "您尚未驗證此使用者。此使用者已驗證所有裝置。", + "You have verified this user. This user has verified all of their devices.": "您已驗證此使用者。此使用者已驗證所有裝置。", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "在此已加密的聊天室中的某些使用者尚未被您驗證,或是他們尚未驗證他們的裝置。", + "All users in this encrypted room are verified by you and they have verified their own devices.": "在此已加密的聊天室中的所有使用者已被您驗證,他們也已驗證所有裝置。", + "Language Dropdown": "語言下拉式選單", + "Country Dropdown": "國家下拉式選單" } From a7a7a9cb2349d58d252624e48c31dc91d885fdd5 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 17 Dec 2019 05:48:28 +0000 Subject: [PATCH 013/101] Translated using Weblate (Hungarian) Currently translated at 100.0% (1998 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 0b094d86af..0faf20937b 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2009,5 +2009,12 @@ "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "A jelmondat megadásával hozzáférhetsz a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz.", "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "A visszaállítási kulcs megadásával hozzáférhetsz a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz.", "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Biztonsági tárolót fogunk használni, hogy tárolhassuk a kereszt-aláíráshoz tartozó azonosítót titkosított formában, amivel más eszközöket és üzenet kulcsokat lehet ellenőrizni. Védd a titkosított üzenetekhez való hozzáférést jelmondattal és azt tartsd titokban.", - "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "A biztonsági tároló beállítása nélkül ha kijelentkezel és egy másik eszközön lépsz be nem fogsz hozzáférni a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz." + "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "A biztonsági tároló beállítása nélkül ha kijelentkezel és egy másik eszközön lépsz be nem fogsz hozzáférni a titkosított üzeneteidhez és a kereszt-aláíráshoz tartozó azonosítódhoz amivel más eszközöket ellenőrizhetsz.", + "This user has not verified all of their devices.": "Ez a felhasználó még ellenőrizte az összes eszközét.", + "You have not verified this user. This user has verified all of their devices.": "Ezt a felhasználót még nem ellenőrizted. Minden eszközét ellenőrizte ez a felhasználó.", + "You have verified this user. This user has verified all of their devices.": "Ezt a felhasználót ellenőrizted. Minden eszközét ellenőrizte ez a felhasználó.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Néhány felhasználót még nem ellenőriztél ebben a titkosított szobában vagy még ők nem ellenőrizték az összes eszközüket.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Minden felhasználót ellenőriztél ebben a titkosított szobában és ők ellenőrizték az összes eszközüket.", + "Language Dropdown": "Nyelvválasztó lenyíló menü", + "Country Dropdown": "Ország lenyíló menü" } From a5d5569af4e06d57c0d89abaf75fb2966ca6cceb Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 17 Dec 2019 21:40:17 +0000 Subject: [PATCH 014/101] Translated using Weblate (Japanese) Currently translated at 59.3% (1184 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ja/ --- src/i18n/strings/ja.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/ja.json b/src/i18n/strings/ja.json index 14a1b6d429..cce9a81188 100644 --- a/src/i18n/strings/ja.json +++ b/src/i18n/strings/ja.json @@ -446,7 +446,7 @@ "Key share requests are sent to your other devices automatically. If you rejected or dismissed the key share request on your other devices, click here to request the keys for this session again.": "共有キーリクエストは、他の端末に自動的に送信されます。 他の端末での共有キーリクエストを拒否または却下した場合は、ここをクリックしてこのセッションのキーを再度要求してください。", "If your other devices do not have the key for this message you will not be able to decrypt them.": "他の端末にこのメッセージのキーがない場合、それらの端末を復号化することはできません。", "Key request sent.": "キーリクエストが送信されました。", - "Re-request encryption keys from your other devices.": "他の端末から暗号化キーを再リクエストします。", + "Re-request encryption keys from your other devices.": "他の端末から暗号化キーを再リクエストします。", "Undecryptable": "解読不能", "Encrypted by an unverified device": "未検証の端末によって暗号化されました", "Unencrypted message": "暗号化されていないメッセージ", From 3f7ea3bc66ba47d171ff406ab79a8e5d126ef116 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Thu, 19 Dec 2019 07:39:57 +0000 Subject: [PATCH 015/101] Translated using Weblate (Finnish) Currently translated at 95.5% (1909 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 6f941b5d4a..381eff2e8e 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1078,7 +1078,7 @@ "This room is a continuation of another conversation.": "Tämä huone on jatkumo toisesta keskustelusta.", "Don't ask me again": "Älä kysy uudelleen", "Not now": "Ei nyt", - "The conversation continues here.": "Keskustelu jatkuu tässä.", + "The conversation continues here.": "Keskustelu jatkuu täällä.", "Share Link to User": "Jaa linkki käyttäjälle", "Muted Users": "Mykistetyt käyttäjät", "Timeline": "Aikajana", From 1010c0a27b22466e25aae9bb1cb34fbb3d097f01 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 19 Dec 2019 08:58:42 +0000 Subject: [PATCH 016/101] Translated using Weblate (Italian) Currently translated at 99.9% (1997 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 08be6d4c06..b18b6c9272 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2006,5 +2006,12 @@ "Secure your encrypted messages with a passphrase": "Proteggi i tuoi messaggi cifrati con una password", "Storing secrets...": "Memorizzo i segreti...", "Unable to set up secret storage": "Impossibile impostare un archivio segreto", - "Close preview": "Chiudi anteprima" + "Close preview": "Chiudi anteprima", + "This user has not verified all of their devices.": "Questo utente non ha verificato tutti i suoi dispositivi.", + "You have not verified this user. This user has verified all of their devices.": "Non hai verificato questo utente. Questo utente ha verificato tutti i suoi dispositivi.", + "You have verified this user. This user has verified all of their devices.": "Hai verificato questo utente. Questo utente ha verificato tutti i suoi dispositivi.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Non hai verificato alcuni utenti in questa stanza criptata o essi non hanno verificato i loro dispositivi.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Hai verificato tutti gli utenti in questa stanza criptata ed essi hanno verificato i loro dispositivi.", + "Language Dropdown": "Lingua a tendina", + "Country Dropdown": "Nazione a tendina" } From 00b66b487799b4095f213ebf81e0835baf1f4c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20V=C3=A1gner?= Date: Thu, 19 Dec 2019 10:03:51 +0000 Subject: [PATCH 017/101] Translated using Weblate (Slovak) Currently translated at 75.0% (1498 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 111 ++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 1782059252..21830a92e0 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -1187,70 +1187,70 @@ "Verify this user by confirming the following number appears on their screen.": "Overte tohoto používateľa tým, že zistíte, či sa na jeho obrazovke objaví nasledujúce číslo.", "Unable to find a supported verification method.": "Nie je možné nájsť podporovanú metódu overenia.", "For maximum security, we recommend you do this in person or use another trusted means of communication.": "Pre zachovanie maximálnej bezpečnosti odporúčame, aby ste toto vykonali osobne, alebo použili iný dôverihodný komunikačný kanál.", - "Dog": "Pes", - "Cat": "Mačka", - "Lion": "Lev", + "Dog": "Hlava psa", + "Cat": "Hlava mačky", + "Lion": "Hlava leva", "Horse": "Kôň", - "Unicorn": "Jednorožec", - "Pig": "Prasa", + "Unicorn": "Hlava jednorožca", + "Pig": "Hlava Prasaťa", "Elephant": "Slon", - "Rabbit": "Zajac", - "Panda": "Panda", + "Rabbit": "Hlava Zajaca", + "Panda": "Hlava Pandy", "Rooster": "Kohút", "Penguin": "Tučniak", "Turtle": "Korytnačka", "Fish": "Ryba", "Octopus": "Chobotnica", "Butterfly": "Motýľ", - "Flower": "Kvetina", - "Tree": "Strom", + "Flower": "Tulipán", + "Tree": "Listnatý strom", "Cactus": "Kaktus", - "Mushroom": "Hríb", + "Mushroom": "Huba", "Globe": "Zemeguľa", - "Moon": "Mesiac", + "Moon": "Polmesiac", "Cloud": "Oblak", "Fire": "Oheň", "Banana": "Banán", - "Apple": "Jablko", + "Apple": "Červené jablko", "Strawberry": "Jahoda", - "Corn": "Kukurica", + "Corn": "Kukuričný klas", "Pizza": "Pizza", - "Cake": "Koláč", - "Heart": "Srdce", - "Smiley": "Úsmev", + "Cake": "Narodeninová torta", + "Heart": "Červené srdce", + "Smiley": "Škeriaca sa tvár", "Robot": "Robot", - "Hat": "Klobúk", + "Hat": "Cylinder", "Glasses": "Okuliare", - "Spanner": "Skrutkovač", - "Santa": "Mikuláš", + "Spanner": "Francúzsky kľúč", + "Santa": "Santa Claus", "Thumbs up": "palec nahor", "Umbrella": "Dáždnik", "Hourglass": "Presýpacie hodiny", - "Clock": "Hodiny", - "Gift": "Darček", + "Clock": "Budík", + "Gift": "Zabalený darček", "Light bulb": "Žiarovka", - "Book": "Kniha", + "Book": "Zatvorená kniha", "Pencil": "Ceruzka", - "Paperclip": "Kancelárska sponka", + "Paperclip": "Sponka na papier", "Scissors": "Nožnice", - "Padlock": "Visiaci zámok", + "Padlock": "Zatvorená zámka", "Key": "Kľúč", "Hammer": "Kladivo", "Telephone": "Telefón", - "Flag": "Vlajka", - "Train": "Vlak", + "Flag": "Kockovaná zástava", + "Train": "Rušeň", "Bicycle": "Bicykel", "Aeroplane": "Lietadlo", "Rocket": "Raketa", "Trophy": "Trofej", - "Ball": "Lopta", + "Ball": "Futbal", "Guitar": "Gitara", "Trumpet": "Trúbka", - "Bell": "Zvonček", + "Bell": "Zvon", "Anchor": "Kotva", "Headphones": "Slúchadlá", - "Folder": "Priečinok", - "Pin": "Pin", + "Folder": "Fascikel", + "Pin": "Špendlík", "Your homeserver does not support device management.": "Váš domovský server nepodporuje správu zariadení.", "Yes": "Áno", "No": "Nie", @@ -1461,7 +1461,7 @@ "The server does not support the room version specified.": "Server nepodporuje zadanú verziu miestnosti.", "Name or Matrix ID": "Meno alebo Matrix ID", "Email, name or Matrix ID": "Email, meno alebo Matrix ID", - "Failed to start chat": "Nepodarilo sa spustiť konverzáciu", + "Failed to start chat": "Nepodarilo sa začať konverzáciu", "Messages": "Správy", "Actions": "Akcie", "Room upgrade confirmation": "Potvrdenie aktualizácii miestnosti", @@ -1514,5 +1514,52 @@ "Terms of service not accepted or the identity server is invalid.": "Neprijali ste Podmienky poskytovania služby alebo to nie je správny server.", "Identity server has no terms of service": "Server totožností nemá žiadne podmienky poskytovania služieb", "The identity server you have chosen does not have any terms of service.": "Zadaný server totožností nezverejňuje žiadne podmienky poskytovania služieb.", - "Only continue if you trust the owner of the server.": "Pokračujte len v prípade, že dôverujete prevádzkovateľovi servera." + "Only continue if you trust the owner of the server.": "Pokračujte len v prípade, že dôverujete prevádzkovateľovi servera.", + "Add Email Address": "Pridať emailovú adresu", + "Add Phone Number": "Pridať telefónne číslo", + "Send cross-signing keys to homeserver": "Poslať kľúče pre podpisovanie naprieč zariadeniami na domovský server", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Táto akcia si vyžaduje mať overenú emailovú adresu alebo telefónne číslo cez predvolený server totožností , ale server nezverejnil podmienky používania.", + "Trust": "Dôverovať", + "Custom (%(level)s)": "Vlastná (%(level)s)", + "Sends a message as plain text, without interpreting it as markdown": "Odošle správu vo formáte obyčajný text, bez prekladu markdown", + "You do not have the required permissions to use this command.": "Na použitie tohoto príkazu nemáte dostatočné povolenia.", + "Error upgrading room": "Chyba pri aktualizácii miestnosti", + "Double check that your server supports the room version chosen and try again.": "Uistite sa, že domovský server podporuje zvolenú verziu miestnosti a skúste znovu.", + "Changes the avatar of the current room": "Zmení obrázok miestnosti", + "Use an identity server": "Použiť server totožností", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Aby ste mohli používateľov pozývať zadaním emailovej adresy, je potrebné nastaviť adresu servera totožností. Klepnutím na tlačidlo pokračovať použijete predvolený server (%(defaultIdentityServerName)s) a zmeniť to môžete v nastaveniach.", + "Use an identity server to invite by email. Manage in Settings.": "Server totožností sa použije na pozývanie používateľov zadaním emailovej adresy. Spravujte v nastaveniach.", + "%(senderName)s placed a voice call.": "%(senderName)s uskutočnil telefonát.", + "%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s uskutočnil telefonát. (Nepodporované týmto prehliadačom)", + "%(senderName)s placed a video call.": "%(senderName)s uskutočnil video hovor.", + "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s uskutočnil video hovor. (Nepodporované týmto prehliadačom)", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s odstránil pravidlo zákazu vstúpiť používateľom zhodujúcich sa s %(glob)s", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s odstránil pravidlo zákaz vstúpiť do miestností zhodujúcich sa s %(glob)s", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s odstránil pravidlo zakázať vstúpiť z domovského servera zhodnými s %(glob)s", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s odstránil pravidlo zákazu vstupu zhodné s %(glob)s", + "%(senderName)s updated an invalid ban rule": "%(senderName)s aktualizoval neplatné pravidlo zákazu vstúpiť", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zákazu vstúpiť používateľom zhodujúcim sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť do miestností shodujúcich sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť z domovských serverov zhodujúcich sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť zhodujúce sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť používateľom zhodujúcim sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť do miestností zhodujúcich sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť z domovských serverov zhodujúcich sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s vytvoril pravidlo zakázať vstúpiť zhodujúce sa s %(glob)s, dôvod: %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť používateľom pôvodne zhodujúcich sa s %(oldGlob)s na používateľov zhodujúcich sa s %(newGlob)s, dôvod: %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť do miestností pôvodne zhodujúcich sa s %(oldGlob)s na miestnosti zhodujúce sa s %(newGlob)s, dôvod: %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s zmenil pravidlo zakázať vstúpiť z domovských serverov pôvodne sa zhodujúcich s %(oldGlob)s na servery zhodujúce sa s %(newGlob)s, dôvod: %(reason)s", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s aktualizoval pravidlo zakázať vstúpiť pôvodne sa zhodujúce s %(oldGlob)s na %(newGlob)s, dôvod: %(reason)s", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Multiple integration managers": "Viacej integračných serverov", + "Try out new ways to ignore people (experimental)": "Vyskúšajte si nový spôsob ignorovania používateľov (experiment)", + "Send verification requests in direct message, including a new verification UX in the member panel.": "Požiadavky na overenie používateľov posielať v priamych konverzáciách, zahŕňa tiež nové rozhranie overenia v zozname používateľov.", + "Enable cross-signing to verify per-user instead of per-device (in development)": "Povoliť podpisovanie naprieč zariadeniami, umožňuje overovanie používateľov namiesto ich zariadení (vo vývoji)", + "Enable local event indexing and E2EE search (requires restart)": "Povoliť lokálne indexovanie udalostí a vyhľadávanie v šifrovaných miestnostiach", + "Use the new, faster, composer for writing messages": "Používať nový, rýchly, editor pri písaní správ", + "Match system theme": "Prispôsobiť sa vzhľadu systému", + "Send read receipts for messages (requires compatible homeserver to disable)": "Odosielať potvrdenia o prečítaní správ (na zakázanie je vyžadovaný kompatibilný domovský server)", + "Show previews/thumbnails for images": "Zobrazovať ukážky/náhľady obrázkov", + "My Ban List": "Môj zoznam zakázať vstúpiť", + "Decline (%(counter)s)": "Zamietnuť (%(counter)s)" } From 709b2eed7c802789334769d176790bfe5c6d9705 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 19 Dec 2019 15:10:54 +0000 Subject: [PATCH 018/101] Add bunch of null-guards and similar to fix React Errors/complaints --- src/components/views/elements/LazyRenderList.js | 10 ++++++++-- src/components/views/elements/Pill.js | 2 +- src/components/views/elements/SettingsFlag.js | 5 +++-- src/components/views/elements/TagTile.js | 2 ++ src/components/views/groups/GroupPublicityToggle.js | 4 ++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/LazyRenderList.js b/src/components/views/elements/LazyRenderList.js index 0fc0ef6733..7572dced0b 100644 --- a/src/components/views/elements/LazyRenderList.js +++ b/src/components/views/elements/LazyRenderList.js @@ -56,14 +56,20 @@ class ItemRange { } export default class LazyRenderList extends React.Component { + constructor(props) { + super(props); + + this.state = {}; + } + static getDerivedStateFromProps(props, state) { const range = LazyRenderList.getVisibleRangeFromProps(props); const intersectRange = range.expand(props.overflowMargin); const renderRange = range.expand(props.overflowItems); - const listHasChangedSize = !!state && renderRange.totalSize() !== state.renderRange.totalSize(); + const listHasChangedSize = !!state.renderRange && renderRange.totalSize() !== state.renderRange.totalSize(); // only update render Range if the list has shrunk/grown and we need to adjust padding OR // if the new range + overflowMargin isn't contained by the old anymore - if (listHasChangedSize || !state || !state.renderRange.contains(intersectRange)) { + if (listHasChangedSize || !state.renderRange || !state.renderRange.contains(intersectRange)) { return {renderRange}; } return null; diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 12830488b1..b821276e92 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -127,7 +127,7 @@ const Pill = createReactClass({ } break; case Pill.TYPE_USER_MENTION: { - const localMember = nextProps.room.getMember(resourceId); + const localMember = nextProps.room ? nextProps.room.getMember(resourceId) : null; member = localMember; if (!localMember) { member = new RoomMember(null, resourceId); diff --git a/src/components/views/elements/SettingsFlag.js b/src/components/views/elements/SettingsFlag.js index a3a6d18d33..d62f3182fc 100644 --- a/src/components/views/elements/SettingsFlag.js +++ b/src/components/views/elements/SettingsFlag.js @@ -34,12 +34,13 @@ module.exports = createReactClass({ getInitialState: function() { return { - value: SettingsStore.getValueAt( + // convert to Boolean to protect against null-capable "tri-state" Settings e.g fallbackICEServerAllowed + value: Boolean(SettingsStore.getValueAt( this.props.level, this.props.name, this.props.roomId, this.props.isExplicit, - ), + )), }; }, diff --git a/src/components/views/elements/TagTile.js b/src/components/views/elements/TagTile.js index 767980f0a0..e31c1c524e 100644 --- a/src/components/views/elements/TagTile.js +++ b/src/components/views/elements/TagTile.js @@ -56,6 +56,8 @@ export default createReactClass({ hover: false, // The profile data of the group if this.props.tag is a group ID profile: null, + // Whether or not the context menu is open + menuDisplayed: false, }; }, diff --git a/src/components/views/groups/GroupPublicityToggle.js b/src/components/views/groups/GroupPublicityToggle.js index bacf54382a..176bd9f988 100644 --- a/src/components/views/groups/GroupPublicityToggle.js +++ b/src/components/views/groups/GroupPublicityToggle.js @@ -32,7 +32,7 @@ export default createReactClass({ return { busy: false, ready: false, - isGroupPublicised: null, + isGroupPublicised: false, // assume false as expects a boolean }; }, @@ -43,7 +43,7 @@ export default createReactClass({ _initGroupStore: function(groupId) { this._groupStoreToken = GroupStore.registerListener(groupId, () => { this.setState({ - isGroupPublicised: GroupStore.getGroupPublicity(groupId), + isGroupPublicised: Boolean(GroupStore.getGroupPublicity(groupId)), ready: GroupStore.isStateReady(groupId, GroupStore.STATE_KEY.Summary), }); }); From 372512b0c4b8fb5ffb6ee676ba3e6d117cfa1234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 19 Dec 2019 17:54:46 +0000 Subject: [PATCH 019/101] Translated using Weblate (French) Currently translated at 100.0% (1998 of 1998 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 96f2337229..f486048d66 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2015,5 +2015,6 @@ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Certains utilisateurs dans ce salon chiffré n’ont pas été vérifiés par vous ou n’ont pas vérifié leurs propres appareils.", "All users in this encrypted room are verified by you and they have verified their own devices.": "Tous les utilisateurs de ce salon chiffré ont été vérifiés par vous et ont vérifié leurs propres appareils.", "Language Dropdown": "Sélection de la langue", - "Country Dropdown": "Sélection du pays" + "Country Dropdown": "Sélection du pays", + "The message you are trying to send is too large.": "Le message que vous essayez d’envoyer est trop gros." } From 21dcf65858940dbf6b076b8303442bba1d0b4fee Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Fri, 20 Dec 2019 04:49:56 +0000 Subject: [PATCH 020/101] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2000 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index cea6d8d70d..73af96e7fc 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2015,5 +2015,8 @@ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "在此已加密的聊天室中的某些使用者尚未被您驗證,或是他們尚未驗證他們的裝置。", "All users in this encrypted room are verified by you and they have verified their own devices.": "在此已加密的聊天室中的所有使用者已被您驗證,他們也已驗證所有裝置。", "Language Dropdown": "語言下拉式選單", - "Country Dropdown": "國家下拉式選單" + "Country Dropdown": "國家下拉式選單", + "The message you are trying to send is too large.": "您正試圖傳送的訊息太大了。", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "秘密儲存空間將使用你既有的金鑰備份資訊來設定。您的秘密儲存空間密碼與復原金鑰會與您的金鑰備份相同", + "Migrate from Key Backup": "從金鑰備份導入" } From 0e7949d73eda08c9ac49ed727c855eb24a165e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 19 Dec 2019 23:06:27 +0000 Subject: [PATCH 021/101] Translated using Weblate (French) Currently translated at 100.0% (2000 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index f486048d66..fb233b40d9 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2016,5 +2016,7 @@ "All users in this encrypted room are verified by you and they have verified their own devices.": "Tous les utilisateurs de ce salon chiffré ont été vérifiés par vous et ont vérifié leurs propres appareils.", "Language Dropdown": "Sélection de la langue", "Country Dropdown": "Sélection du pays", - "The message you are trying to send is too large.": "Le message que vous essayez d’envoyer est trop gros." + "The message you are trying to send is too large.": "Le message que vous essayez d’envoyer est trop gros.", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Le coffre secret sera configuré en utilisant vos informations de sauvegarde de clés existantes. Votre phrase de passe et votre clé de récupération seront les mêmes que pour la sauvegarde de clés", + "Migrate from Key Backup": "Migrer depuis la sauvegarde de clés" } From fef19c510c36ae1346a772c025e175bf97928c7f Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 20 Dec 2019 12:54:30 +0000 Subject: [PATCH 022/101] Translated using Weblate (Hungarian) Currently translated at 100.0% (2000 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 0faf20937b..fd88250707 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2016,5 +2016,8 @@ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Néhány felhasználót még nem ellenőriztél ebben a titkosított szobában vagy még ők nem ellenőrizték az összes eszközüket.", "All users in this encrypted room are verified by you and they have verified their own devices.": "Minden felhasználót ellenőriztél ebben a titkosított szobában és ők ellenőrizték az összes eszközüket.", "Language Dropdown": "Nyelvválasztó lenyíló menü", - "Country Dropdown": "Ország lenyíló menü" + "Country Dropdown": "Ország lenyíló menü", + "The message you are trying to send is too large.": "Túl nagy képet próbálsz elküldeni.", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "A Biztonsági Tároló a már létező kulcs mentés adatai felhasználásával lesz létrehozva. A biztonsági tároló jelmondata és a visszaállítási kulcs meg fog egyezni a kulcs mentésénél használttal", + "Migrate from Key Backup": "Mozgatás a Kulcs Mentésből" } From 9a51b418463423a5a23282f9e1f024bf8b981cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20V=C3=A1gner?= Date: Fri, 20 Dec 2019 10:05:17 +0000 Subject: [PATCH 023/101] Translated using Weblate (Slovak) Currently translated at 77.8% (1556 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 65 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 21830a92e0..8befc3c18c 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -1561,5 +1561,68 @@ "Send read receipts for messages (requires compatible homeserver to disable)": "Odosielať potvrdenia o prečítaní správ (na zakázanie je vyžadovaný kompatibilný domovský server)", "Show previews/thumbnails for images": "Zobrazovať ukážky/náhľady obrázkov", "My Ban List": "Môj zoznam zakázať vstúpiť", - "Decline (%(counter)s)": "Zamietnuť (%(counter)s)" + "Decline (%(counter)s)": "Zamietnuť (%(counter)s)", + "The message you are trying to send is too large.": "Správa, ktorú sa usilujete odoslať, je príliš veľká.", + "This is your list of users/servers you have blocked - don't leave the room!": "Toto je zoznam používateľov / serverov, ktorých ste zablokovali - neopúšťajte miestnosť!", + "Upload": "Nahrať", + "Cross-signing and secret storage are enabled.": "Podpisovanie naprieč zariadeniami a bezpečné úložisko sú aktívne.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Na bezpečnom úložisku vo vašom účte máte uloženú totožnosť podpisovania naprieč zariadeniami, ale v tomto zariadení zatiaľ tejto totožnosti nedôverujete.", + "Cross-signing and secret storage are not yet set up.": "Podpisovanie naprieč zariadeniami a bezpečné úložisko zatiaľ nie sú nastavené.", + "Bootstrap cross-signing and secret storage": "Zaviesť podpisovanie naprieč zariadeniami a bezpečné úložisko", + "Cross-signing public keys:": "Verejné kľúče podpisovania naprieč zariadeniami:", + "on device": "na zariadení", + "not found": "nenájdené", + "Cross-signing private keys:": "Súkromné kľúče podpisovania naprieč zariadeniami:", + "in secret storage": "na bezpečnom úložisku", + "Secret storage public key:": "Verejný kľúč bezpečného úložiska:", + "in account data": "v údajoch účtu", + "Connecting to integration manager...": "Pripájanie k integračnému serveru…", + "Cannot connect to integration manager": "Nie je možné sa pripojiť k integračnému serveru", + "The integration manager is offline or it cannot reach your homeserver.": "Integračný server je offline, alebo nemôže pristupovať k domovskému serveru.", + "not stored": "neuložené", + "Backup has a valid signature from this user": "Záloha je podpísaná platným kľúčom od tohoto používateľa", + "Backup has a invalid signature from this user": "Záloha je podpísaná neplatným kľúčom od tohoto používateľa", + "Backup has a signature from unknown user with ID %(deviceId)s": "Podpis zálohy pochádza od neznámeho používateľa ID %(deviceId)s", + "Backup has a signature from unknown device with ID %(deviceId)s": "Podpis zálohy pochádza z neznámeho zariadenia ID %(deviceId)s", + "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Záloha kľúčov je uložená na bezpečnom úložisku, ale funkcia podpisovanie naprieč zariadeniami nie je na tomto zariadení aktívna. Ak chcete zmeniť stav zálohy kľúčov, zapnite podpisovanie naprieč zariadeniami v časti experimenty.", + "Backup key stored: ": "Záloha kľúčov uložená: ", + "Start using Key Backup with Secure Secret Storage": "Začnite používať zálohu kľúčov na bezpečnom úložisku", + "Clear notifications": "Vymazať oznámenia", + "Change identity server": "Zmeniť server totožností", + "Disconnect from the identity server and connect to instead?": "Naozaj si želáte odpojiť od servera totožností a pripojiť sa namiesto toho k serveru ?", + "Disconnect identity server": "Odpojiť server totožností", + "Disconnect from the identity server ?": "Naozaj sa chcete odpojiť od servera totožností ?", + "Disconnect": "Odpojiť", + "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.": "Pred odpojením zo servera totožností by ste mali z neho odstrániť vaše osobné údaje. Žiaľ, server momentálne nie je dostupný a nie je možné sa k nemu pripojiť.", + "You should:": "Mali by ste:", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)": "Skontrolovať rozšírenia inštalované vo webovom prehliadači, ktoré by mohli blokovať prístup k serveru totožností (napr. rozšírenie Privacy Badger)", + "contact the administrators of identity server ": "Kontaktovať správcu servera totožností ", + "wait and try again later": "Počkať a skúsiť znovu neskôr", + "Disconnect anyway": "Napriek tomu sa odpojiť", + "You are still sharing your personal data on the identity server .": "na servery máte stále uložené vaše osobné údaje.", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Odporúčame, aby ste ešte pred odpojením sa zo servera totožností odstránili vašu emailovú adresu a telefónne číslo.", + "Identity Server (%(server)s)": "Server totožností (%(server)s)", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Momentálne na vyhľadávanie kontaktov a na možnosť byť nájdení kontaktmi ktorých poznáte používate . Zmeniť server totožností môžete nižšie.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Ak nechcete na vyhľadávanie kontaktov a možnosť byť nájdení používať , zadajte adresu servera totožností nižšie.", + "Identity Server": "Server totožností", + "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Momentálne nepoužívate žiaden server totožností. Ak chcete vyhľadávať kontakty a zároveň umožniť ostatným vašim kontaktom, aby mohli nájsť vás, nastavte si server totožností nižšie.", + "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Ak sa odpojíte od servera totožností, vaše kontakty vás nebudú môcť nájsť a ani vy nebudete môcť pozývať používateľov zadaním emailovej adresy a telefónneho čísla.", + "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Používanie servera totožností je voliteľné. Ak sa rozhodnete, že nebudete používať server totožností, nebudú vás vaši známi môcť nájsť a ani vy nebudete môcť pozývať používateľov zadaním emailovej adresy alebo telefónneho čísla.", + "Do not use an identity server": "Nepoužívať server totožností", + "Enter a new identity server": "Zadať nový server totožností", + "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Použiť integračný server (%(serverName)s) na správu botov, widgetov a balíčkov s nálepkami.", + "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Použiť integračný server na správu botov, widgetov a balíčkov s nálepkami.", + "Manage integrations": "Spravovať integrácie", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integračné servery zhromažďujú údaje nastavení, môžu spravovať widgety, odosielať vo vašom mene pozvánky alebo meniť úroveň moci.", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Súhlas s podmienkami používania servera totožností (%(serverName)s), aby ste mohli byť nájdení zadaním emailovej adresy alebo telefónneho čísla.", + "Discovery": "Objaviť", + "Deactivate account": "Deaktivovať účet", + "Clear cache and reload": "Vymazať vyrovnávaciu pamäť a načítať znovu", + "Customise your experience with experimental labs features. Learn more.": "Prispôsobte si zážitok s používania aktivovaním experimentálnych vlastností. Zistiť viac.", + "Ignored/Blocked": "Ignorovaní / Blokovaní", + "Error adding ignored user/server": "Chyba pri pridávaní ignorovaného používateľa / servera", + "Something went wrong. Please try again or view your console for hints.": "Niečo sa nepodarilo. Prosím, skúste znovu neskôr alebo si prečítajte ďalšie usmernenia zobrazením konzoly.", + "Error subscribing to list": "Chyba pri prihlasovaní sa do zoznamu", + "Please verify the room ID or alias and try again.": "Prosím, overte platnosť ID miestnosti alebo alias a skúste znovu.\nPlease verify the room ID or alias and try again.", + "Error removing ignored user/server": "Chyba pri odstraňovaní ignorovaného používateľa / servera" } From 3dcef53b930833b1e945df7e50054479becafeb9 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 20 Dec 2019 21:13:53 +0000 Subject: [PATCH 024/101] Translated using Weblate (Turkish) Currently translated at 33.8% (677 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 92 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 2462b5789f..da177ae68c 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -589,5 +589,95 @@ "The platform you're on": "Bulunduğun platform", "The version of Riot.im": "Riot.im'in sürümü", "Your language of choice": "Seçtiginiz diliniz", - "Which officially provided instance you are using, if any": "" + "Which officially provided instance you are using, if any": "", + "Add Email Address": "E-posta Adresi Ekle", + "Add Phone Number": "Telefon Numarası Ekle", + "Your identity server's URL": "Kimlik sunucunuzun linki", + "e.g. %(exampleValue)s": "örn.%(exampleValue)s", + "Every page you use in the app": "uygulamadaki kullandığınız tüm sayfalar", + "e.g. ": "örn. ", + "Your User Agent": "Kullanıcı Ajanınız", + "Your device resolution": "Cihazınızın çözünürlüğü", + "Call Failed": "Arama Başarısız", + "Review Devices": "Cihazları Gözden Geçir", + "Call Anyway": "Yinede Ara", + "Answer Anyway": "Yinede Cevapla", + "Call": "Ara", + "Answer": "Cevap", + "Call failed due to misconfigured server": "Hatalı yapılandırılmış sunucu nedeniyle arama başarısız", + "Call in Progress": "Arama Yapılıyor", + "A call is already in progress!": "Zaten bir arama devam etmekte!", + "Permission Required": "İzin Gerekli", + "Replying With Files": "Dosyalarla Cevaplanıyor", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s%(monthName)s%(day)s%(fullYear)s", + "Invite new community members": "Yeni topluluk üyelerini davet et", + "Name or Matrix ID": "İsim yada Matrix ID", + "Invite to Community": "Topluluğa Davet", + "Add rooms to the community": "Topluluğa odalar ekle", + "Add to community": "Topluluğa ekle", + "Failed to invite the following users to %(groupId)s:": "%(groupId)s grubuna belirtilen kullanıcıların davet işlemi başarısız oldu:", + "Failed to invite users to community": "Kullanıcıların topluluğa daveti başarısız", + "Identity server has no terms of service": "Kimlik sunucusu hizmet kurallarına sahip değil", + "Only continue if you trust the owner of the server.": "Sadece sunucunun sahibine güveniyorsanız devam edin.", + "Trust": "Güven", + "Unable to load! Check your network connectivity and try again.": "Yüklenemiyor! Ağ bağlantınızı kontrol edin ve yeniden deneyin.", + "Registration Required": "Kayıt Zorunlu", + "You need to register to do this. Would you like to register now?": "Bunu yapabilmek için kayıt olmalısınız. Şimdi kayıt olmak ister misiniz?", + "Restricted": "Sınırlı", + "Email, name or Matrix ID": "E-posta, isim yada Matrix ID", + "Failed to start chat": "Sohbet başlatma başarısız", + "Failed to invite users to the room:": "Kullanıcıların odaya daveti başarısız oldu:", + "Missing roomId.": "roomId eksik.", + "You are not in this room.": "Bu odada değilsin.", + "You do not have permission to do that in this room.": "Bu odada bunu yapma yetkiniz yok.", + "Messages": "Mesajlar", + "Actions": "Eylemler", + "Other": "Diğer", + "Upgrades a room to a new version": "Odayı yeni bir sürüme yükseltir", + "You do not have the required permissions to use this command.": "Bu komutu kullanmak için gerekli izinlere sahip değilsin.", + "Error upgrading room": "Oda güncellenirken hata", + "Changes your avatar in all rooms": "Tüm odalardaki avatarlarını değiştirir", + "This room has no topic.": "Bu odanın başlığı yok.", + "Sets the room name": "Oda adını düzenler", + "Use an identity server": "Bir kimlik sunucusu kullan", + "Define the power level of a user": "Bir kullanıcının güç düzeyini tanımla", + "Opens the Developer Tools dialog": "Geliştirici Araçları kutucuğunu açar", + "%(senderDisplayName)s upgraded this room.": "Odayı güncelleyen %(senderDisplayName)s.", + "%(senderDisplayName)s made the room invite only.": "Odayı sadece davetle yapan %(senderDisplayName)s.", + "%(senderDisplayName)s has prevented guests from joining the room.": "Odaya misafirlerin girişini engelleyen %(senderDisplayName)s.", + "%(senderName)s removed the main address for this room.": "Bu oda için ana adresi silen %(senderName)s.", + "Light theme": "Açık tema", + "Dark theme": "Koyu tema", + "%(displayName)s is typing …": "%(displayName)s yazıyor…", + "%(names)s and %(count)s others are typing …|one": "%(names)s ve bir diğeri yazıyor…", + "%(names)s and %(lastPerson)s are typing …": "%(names)s ve %(lastPerson)s yazıyor…", + "Cannot reach homeserver": "Ana sunucuya erişilemiyor", + "Your Riot is misconfigured": "Rioutunuz hatalı yapılandırılmış", + "Cannot reach identity server": "Kimlik sunucu erişilemiyor", + "No homeserver URL provided": "Ana sunucu adresi belirtilmemiş", + "Unexpected error resolving homeserver configuration": "Ana sunucu yapılandırması çözümlenirken beklenmeyen hata", + "Unexpected error resolving identity server configuration": "Kimlik sunucu yapılandırması çözümlenirken beklenmeyen hata", + "The message you are trying to send is too large.": "Göndermeye çalıştığın mesaj çok büyük.", + "This homeserver has hit its Monthly Active User limit.": "Bu ana sunucu Aylık Aktif Kullanıcı limitine ulaştı.", + "Riot URL": "Riot Linki", + "Room ID": "Oda ID", + "More options": "Daha fazla seçenek", + "Join": "Katıl", + "Yes": "Evet", + "No": "Hayır", + "expand": "genişlet", + "Communities": "Topluluklar", + "Rotate Left": "Sola Döndür", + "Rotate Right": "Sağa Döndür", + "Rotate clockwise": "Saat yönünde döndür", + "%(nameList)s %(transitionList)s": "%(nameList)s%(transitionList)s", + "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s %(count)s kez katıldı", + "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s katıldı", + "%(oneUser)sjoined %(count)s times|other": "%(oneUser)s %(count)s kez katıldı", + "%(oneUser)sjoined %(count)s times|one": "%(oneUser)s katıldı", + "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s kullanıcı ayrıldı", + "%(oneUser)sleft %(count)s times|one": "%(oneUser)s ayrıldı", + "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s katıldı ve ayrıldı", + "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s katıldı ve ayrıldı", + "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s ayrıldı ve yeniden katıldı" } From e47ac5fbb530f87c39e3084e9f2fe3b030c04bee Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Sun, 22 Dec 2019 10:19:38 +0000 Subject: [PATCH 025/101] Translated using Weblate (Finnish) Currently translated at 97.0% (1940 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 381eff2e8e..bb63641848 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1926,5 +1926,37 @@ "Customise your experience with experimental labs features. Learn more.": "Muokkaa kokemustasi kokeellisilla laboratio-ominaisuuksia. Tutki vaihtoehtoja.", "Error upgrading room": "Virhe päivitettäessä huonetta", "Double check that your server supports the room version chosen and try again.": "Tarkista, että palvelimesi tukee valittua huoneversiota ja yritä uudelleen.", - "Invite joined members to the new room automatically": "Kutsu huoneen jäsenet liittymään uuteen huoneeseen automaattisesti" + "Invite joined members to the new room automatically": "Kutsu huoneen jäsenet liittymään uuteen huoneeseen automaattisesti", + "Send cross-signing keys to homeserver": "Lähetä ristivarmennuksen tarvitsemat avaimet kotipalvelimelle", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s poisti porttikiellon käyttäjiltä, jotka täsmäsivät sääntöön %(glob)s", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s poisti huoneita estävän säännön %(glob)s", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s poisti palvelimia estävän säännön %(glob)s", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s poisti estosäännön %(glob)s", + "%(senderName)s updated an invalid ban rule": "%(senderName)s muokkasi epäkelpoa estosääntöä", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s muokkasi käyttäjiä estävää sääntöä %(glob)s seuraavasta syystä: %(reason)s", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s muokkasi huoneita estävää sääntöä %(glob)s seuraavasta syystä: %(reason)s", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s muokkasi palvelimia estävää sääntöä %(glob)s seuraavasta syystä: %(reason)s", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s muokkasi estosääntöä %(glob)s seuraavasta syystä: %(reason)s", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s loi porttikiellonsäännön %(glob)s, syy: %(reason)s", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s loi huoneita estävän säännön %(glob)s, syy: %(reason)s", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s loi palvelimia estävän säännön %(glob)s, syy: %(reason)s", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s loi estosäännön %(glob)s, syy: %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti sääntöä, joka esti käyttäjiä säännöllä %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti sääntöä, joka esti huoneita säännöllä %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti sääntöä, joka esti palvelimia säännöllä %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s muutti estosääntöä muodosta %(oldGlob)s muotoon %(newGlob)s. Syy: %(reason)s", + "The message you are trying to send is too large.": "Lähettämäsi viesti on liian suuri.", + "Cross-signing and secret storage are enabled.": "Ristivarmennus ja salavarasto on käytössä.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "Tunnuksellasi on ristivarmennusidentiteetti salavarastossa, mutta tämä laite ei luota siihen.", + "Cross-signing and secret storage are not yet set up.": "Ristivarmennusta ja salavarastoa ei ole vielä otettu käyttöön.", + "Bootstrap cross-signing and secret storage": "Ota käyttöön ristivarmennus ja salavarasto", + "Cross-signing public keys:": "Ristivarmennuksen julkiset avaimet:", + "on device": "laitteella", + "not found": "ei löydetty", + "Cross-signing private keys:": "Ristivarmennuksen salaiset avaimet:", + "in secret storage": "salavarastossa", + "Secret storage public key:": "Salavaraston julkinen avain:", + "in account data": "tunnuksen tiedoissa", + "not stored": "ei tallennettu", + "Cross-signing": "Ristivarmennus" } From 4d61e0bbe7d1f1abb441a3d871c3f78e2c3a357b Mon Sep 17 00:00:00 2001 From: random Date: Mon, 23 Dec 2019 09:45:06 +0000 Subject: [PATCH 026/101] Translated using Weblate (Italian) Currently translated at 99.9% (1999 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index b18b6c9272..5d0c769e6e 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -271,9 +271,9 @@ "Blacklisted": "In lista nera", "device id: ": "ID dispositivo: ", "Disinvite": "Revoca invito", - "Kick": "Caccia fuori", + "Kick": "Butta fuori", "Disinvite this user?": "Revocare l'invito a questo utente?", - "Kick this user?": "Cacciare questo utente?", + "Kick this user?": "Buttare fuori questo utente?", "Failed to kick": "Espulsione fallita", "Unban": "Togli ban", "Ban": "Bandisci", @@ -2013,5 +2013,8 @@ "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Non hai verificato alcuni utenti in questa stanza criptata o essi non hanno verificato i loro dispositivi.", "All users in this encrypted room are verified by you and they have verified their own devices.": "Hai verificato tutti gli utenti in questa stanza criptata ed essi hanno verificato i loro dispositivi.", "Language Dropdown": "Lingua a tendina", - "Country Dropdown": "Nazione a tendina" + "Country Dropdown": "Nazione a tendina", + "The message you are trying to send is too large.": "Il messaggio che stai tentando di inviare è troppo grande.", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "L'archivio segreto verrà impostato usando i dettagli del tuo backup chiavi. La password dell'archivio segreto e la chiave di ripristino saranno le stesse del tuo backup chiavi", + "Migrate from Key Backup": "Migra dal backup chiavi" } From 131ecfe912736c82ae7ab52710aff1841faf8905 Mon Sep 17 00:00:00 2001 From: catborise Date: Sun, 22 Dec 2019 08:25:01 +0000 Subject: [PATCH 027/101] Translated using Weblate (Turkish) Currently translated at 36.8% (736 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 61 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index da177ae68c..647ca2f6a5 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -679,5 +679,64 @@ "%(oneUser)sleft %(count)s times|one": "%(oneUser)s ayrıldı", "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s katıldı ve ayrıldı", "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s katıldı ve ayrıldı", - "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s ayrıldı ve yeniden katıldı" + "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s ayrıldı ve yeniden katıldı", + "were invited %(count)s times|other": "%(count)s kez davet edildi", + "were invited %(count)s times|one": "davet edildi", + "was invited %(count)s times|other": "%(count)s kez davet edildi", + "was invited %(count)s times|one": "davet edildi", + "were kicked %(count)s times|other": "%(count)s kez atıldı", + "were kicked %(count)s times|one": "atıldı", + "was kicked %(count)s times|other": "%(count)s kez atıldı", + "was kicked %(count)s times|one": "atıldı", + "%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s isimlerini değiştrtiler", + "%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s ismini değiştirdi", + "Power level": "Güç düzeyi", + "e.g. my-room": "örn. odam", + "Some characters not allowed": "Bazı karakterlere izin verilmiyor", + "Matrix ID": "Matrix ID", + "Matrix Room ID": "Matrix Oda ID", + "email address": "e-posta adresi", + "That doesn't look like a valid email address": "Geçerli bir e-posta adresi gibi gözükmüyor", + "You have entered an invalid address.": "Geçersiz bir adres girdiniz.", + "Invite anyway and never warn me again": "Yinede davet et ve asla beni uyarma", + "Invite anyway": "Yinede davet et", + "Close dialog": "Kutucuğu kapat", + "Preparing to send logs": "Loglar gönderilmek için hazırlanıyor", + "Logs sent": "Loglar gönderiliyor", + "Thank you!": "Teşekkürler!", + "Failed to send logs: ": "Loglarıb gönderilmesi başarısız: ", + "GitHub issue": "GitHub sorunu", + "Notes": "Notlar", + "Removing…": "Siliniyor…", + "Clear all data on this device?": "Bu cihazdaki bütün verileri sil?", + "Clear all data": "Bütün verileri sil", + "Community IDs cannot be empty.": "Topluluk ID leri boş bırakılamaz.", + "Something went wrong whilst creating your community": "Topluluğunuz oluşturulurken bir şeyler yanlış gitti", + "Create Community": "Topluluk Oluştur", + "Community Name": "Topluluk Adı", + "Example": "Örnek", + "Community ID": "Topluluk ID", + "example": "örnek", + "Create": "Oluştur", + "Please enter a name for the room": "Lütfen oda için bir ad girin", + "This room is private, and can only be joined by invitation.": "Bu oda özel, sadece davet ile katılınabilir.", + "Create a private room": "Özel bir oda oluştur", + "Hide advanced": "Gelişmiş gizle", + "Show advanced": "Gelişmiş göster", + "Incompatible Database": "Uyumsuz Veritabanı", + "To continue, please enter your password:": "Devam etmek için lütfen şifrenizi giriniz:", + "Begin Verifying": "Doğrulamaya Başla", + "Use two-way text verification": "İki yönlü metin doğrulama kullan", + "Back": "Geri", + "You must specify an event type!": "Bir olay tipi seçmek zorundasınız!", + "Event sent!": "Olay gönderildi!", + "Event Type": "Olay Tipi", + "State Key": "Durum Anahtarı", + "Event Content": "Olay İçeriği", + "Send Account Data": "Hesap Verisi Gönder", + "Filter results": "Sonuçları filtrele", + "View Servers in Room": "Odadaki Sunucuları Gör", + "Toolbox": "Araç Kutusu", + "Developer Tools": "Geliştirici Araçları", + "Integrations are disabled": "Bütünleştirmeler kapatılmış" } From 178dbdca5b361d1aecd445492ef3ce5307807ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Mon, 23 Dec 2019 17:42:06 +0000 Subject: [PATCH 028/101] Translated using Weblate (French) Currently translated at 100.0% (2001 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index fb233b40d9..727d171ca4 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2018,5 +2018,6 @@ "Country Dropdown": "Sélection du pays", "The message you are trying to send is too large.": "Le message que vous essayez d’envoyer est trop gros.", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Le coffre secret sera configuré en utilisant vos informations de sauvegarde de clés existantes. Votre phrase de passe et votre clé de récupération seront les mêmes que pour la sauvegarde de clés", - "Migrate from Key Backup": "Migrer depuis la sauvegarde de clés" + "Migrate from Key Backup": "Migrer depuis la sauvegarde de clés", + "Help": "Aide" } From abb5ea2df8565b8ef5bce1cf39a404d428fc32c4 Mon Sep 17 00:00:00 2001 From: "ferhad.necef" Date: Wed, 25 Dec 2019 15:54:20 +0000 Subject: [PATCH 029/101] Translated using Weblate (Azerbaijani) Currently translated at 22.3% (447 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/az/ --- src/i18n/strings/az.json | 62 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/az.json b/src/i18n/strings/az.json index c53258a28f..e4316496ec 100644 --- a/src/i18n/strings/az.json +++ b/src/i18n/strings/az.json @@ -70,7 +70,7 @@ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(time)s", "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s", "Unable to enable Notifications": "Xəbərdarlıqları daxil qoşmağı bacarmadı", - "Default": "Default", + "Default": "Varsayılan olaraq", "Moderator": "Moderator", "Admin": "Administrator", "Start a chat": "Danışığa başlamaq", @@ -389,5 +389,63 @@ "Verified key": "Təsdiqlənmiş açar", "Sends the given message coloured as a rainbow": "Verilən mesajı göy qurşağı kimi rəngli göndərir", "Sends the given emote coloured as a rainbow": "Göndərilmiş emote rəngini göy qurşağı kimi göndərir", - "Unrecognised command:": "Tanınmayan əmr:" + "Unrecognised command:": "Tanınmayan əmr:", + "Add Email Address": "Emal ünvan əlavə etmək", + "Add Phone Number": "Telefon nömrəsi əlavə etmək", + "e.g. %(exampleValue)s": "e.g. %(exampleValue)s", + "Call failed due to misconfigured server": "Düzgün qurulmamış server səbəbindən zəng alınmadı", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Xahiş edirik, baş serverin administratoruna müraciət edin (%(homeserverDomain)s) ki zənglərin etibarlı işləməsi üçün dönüş serverini konfiqurasiya etsin.", + "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternativ olaraq, ümumi serveri turn.matrix.org istifadə etməyə cəhd edə bilərsiniz, lakin bu qədər etibarlı olmayacaq və IP ünvanınızı bu serverlə bölüşəcəkdir. Bunu Ayarlarda da idarə edə bilərsiniz.", + "Try using turn.matrix.org": "Turn.matrix.org istifadə edin", + "The file '%(fileName)s' failed to upload.": "'%(fileName)s' faylı yüklənə bilmədi.", + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "'%(fileName)s' faylı yükləmə üçün bu server ölçü həddini aşmışdır", + "Send cross-signing keys to homeserver": "Ev serveri üçün çarpaz imzalı açarları göndərin", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", + "Add rooms to the community": "Icmaya otaqlar əlavə edin", + "Failed to invite the following users to %(groupId)s:": "Aşağıdakı istifadəçiləri %(groupId)s - ə dəvət etmək alınmadı:", + "Failed to invite users to %(groupId)s": "İstifadəçiləri %(groupId)s - a dəvət etmək alınmadı", + "Failed to add the following rooms to %(groupId)s:": "Aşağıdakı otaqları %(groupId)s - a əlavə etmək alınmadı:", + "Identity server has no terms of service": "Şəxsiyyət serverinin xidmət şərtləri yoxdur", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Bu hərəkət e-poçt ünvanı və ya telefon nömrəsini təsdiqləmək üçün standart şəxsiyyət serverinə girməyi tələb edir, lakin serverdə heç bir xidmət şəraiti yoxdur.", + "Only continue if you trust the owner of the server.": "Yalnız server sahibinə etibar etsəniz davam edin.", + "Trust": "Etibar", + "Custom (%(level)s)": "Xüsusi (%(level)s)", + "Failed to start chat": "Söhbətə başlamaq olmur", + "Failed to invite the following users to the %(roomName)s room:": "Aşağıdakı istifadəçiləri %(roomName)s otağına dəvət etmək alınmadı:", + "Room %(roomId)s not visible": "Otaq %(roomId)s görünmür", + "Messages": "Mesajlar", + "Actions": "Tədbirlər", + "Other": "Digər", + "Sends a message as plain text, without interpreting it as markdown": "Bir mesajı qeyd kimi şərh etmədən, düz mətn şəklində göndərir", + "You do not have the required permissions to use this command.": "Bu komandadan (əmrdən) istifadə etmək üçün tələb olunan icazəniz yoxdur.", + "Error upgrading room": "Otaq yeniləmə xətası", + "Double check that your server supports the room version chosen and try again.": "Serverinizin seçilmiş otaq versiyasını dəstəklədiyini bir daha yoxlayın və yenidən cəhd edin.", + "Changes the avatar of the current room": "Cari otağın avatarını dəyişdirir", + "Use an identity server": "Şəxsiyyət serverindən istifadə edin", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "E-poçtla dəvət etmək üçün şəxsiyyət serverindən istifadə edin. Defolt şəxsiyyət serverini (%(defaultIdentityServerName)s) istifadə etməyə və ya Parametrlərdə idarə etməyə davam edin.", + "Use an identity server to invite by email. Manage in Settings.": "E-poçtla dəvət etmək üçün şəxsiyyət serverindən istifadə edin. Parametrlərdə idarə edin.", + "Please supply a https:// or http:// widget URL": "Zəhmət olmasa https:// və ya http:// widget URL təmin edin", + "Device already verified!": "Cihaz artıq təsdiqləndi!", + "WARNING: Device already verified, but keys do NOT MATCH!": "XƏBƏRDARLIQ: Cihaz artıq təsdiqləndi, lakin açarlar uyğun gəlmir!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "XƏBƏRDARLIQ: ƏSAS VERIFİKASİYA VERİLİR! %(userId)s və cihaz %(deviceId)s üçün imza açarı \"%(fprint)s\" ilə təmin olunmayan \"%(fingerprint)s\". Bu, ünsiyyətlərinizin tutulduğunu ifadə edə bilər!", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Təqdim etdiyiniz imza açarı %(userId)s cihazının %(deviceId)s cihazından aldığınız imza açarına uyğundur. Cihaz təsdiqlənmiş kimi qeyd edildi.", + "Forces the current outbound group session in an encrypted room to be discarded": "Şifrəli bir otaqda mövcud qrup sessiyasını ləğv etməyə məcbur edir", + "Displays list of commands with usages and descriptions": "İstifadə qaydaları və təsvirləri ilə komanda siyahısını göstərir", + "%(senderName)s requested a VoIP conference.": "%(senderName)s VoIP konfrans istədi.", + "%(oldDisplayName)s changed their display name to %(displayName)s.": "%(oldDisplayName)s göstərilən adlarını %(displayName)s olaraq dəyişdirdi.", + "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s öz adlarını %(displayName)s olaraq təyin etdilər.", + "%(senderName)s set a profile picture.": "%(senderName)s profil şəkli təyin etdi.", + "%(senderName)s made no change.": "%(senderName)s dəyişiklik etməyib.", + "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s otaq otağını sildi.", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s bu otağı təkmilləşdirdi.", + "%(senderDisplayName)s made the room public to whoever knows the link.": "%(senderDisplayName)s linki olanlara otağı açıq etdi.", + "%(senderDisplayName)s made the room invite only.": "%(senderDisplayName)s otağı yalnız dəvətlə açıq etdi.", + "%(senderDisplayName)s changed the join rule to %(rule)s": "%(senderDisplayName)s qoşulma qaydasını %(rule)s olaraq dəyişdirdi", + "%(senderDisplayName)s has allowed guests to join the room.": "%(senderDisplayName)s qonaq otağa qoşulmasına icazə verdi.", + "%(senderDisplayName)s has prevented guests from joining the room.": "%(senderDisplayName)s qonaqların otağa daxil olmasının qarşısını aldı.", + "%(senderDisplayName)s changed guest access to %(rule)s": "%(senderDisplayName)s %(rule)s-a qonaq girişi dəyişdirildi.", + "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "Bu otaqda %(qruplar)s üçün %(senderDisplayName)s aktiv oldu.", + "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "Bu otaqda %(groups)s üçün %(senderDisplayName)s aktiv oldu.", + "powered by Matrix": "Matrix tərəfindən təchiz edilmişdir", + "Custom Server Options": "Xüsusi Server Seçimləri" } From b4a569dfc7ea8653ab353a62092c2bff0ab65945 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Tue, 24 Dec 2019 06:39:24 +0000 Subject: [PATCH 030/101] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2001 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 73af96e7fc..e6ce4077e5 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2018,5 +2018,6 @@ "Country Dropdown": "國家下拉式選單", "The message you are trying to send is too large.": "您正試圖傳送的訊息太大了。", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "秘密儲存空間將使用你既有的金鑰備份資訊來設定。您的秘密儲存空間密碼與復原金鑰會與您的金鑰備份相同", - "Migrate from Key Backup": "從金鑰備份導入" + "Migrate from Key Backup": "從金鑰備份導入", + "Help": "說明" } From b8dbd32363b97f220f814195fda80cead34510bf Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Wed, 25 Dec 2019 08:11:50 +0000 Subject: [PATCH 031/101] Translated using Weblate (Finnish) Currently translated at 100.0% (2001 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 65 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index bb63641848..8881456d8c 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1343,7 +1343,7 @@ "Failed to decrypt %(failedCount)s sessions!": "%(failedCount)s istunnon purkaminen epäonnistui!", "Restored %(sessionCount)s session keys": "%(sessionCount)s istunnon avainta palautettu", "Enter Recovery Passphrase": "Syötä palautuksen salalause", - "Warning: you should only set up key backup from a trusted computer.": "Varoitus: sinun pitäisi ottaa avainvarmuuskopio käyttöön vain luotetulta tietokoneelta.", + "Warning: you should only set up key backup from a trusted computer.": "Varoitus: sinun pitäisi ottaa avainvarmuuskopio käyttöön vain luotetulla tietokoneella.", "Access your secure message history and set up secure messaging by entering your recovery passphrase.": "Pääse turvattuun viestihistoriaasi ja ota käyttöön turvallinen viestintä syöttämällä palautuksen salalauseesi.", "If you've forgotten your recovery passphrase you can use your recovery key or set up new recovery options": "Jos olet unohtanut palautuksen salalauseesi, voit käyttää palautusavaintasi tai ottaa käyttöön uuden palautustavan", "Access your secure message history and set up secure messaging by entering your recovery key.": "Pääse turvattuun viestihistoriaasi ja ota käyttöön turvallinen viestintä syöttämällä palautusavaimesi.", @@ -1958,5 +1958,66 @@ "Secret storage public key:": "Salavaraston julkinen avain:", "in account data": "tunnuksen tiedoissa", "not stored": "ei tallennettu", - "Cross-signing": "Ristivarmennus" + "Cross-signing": "Ristivarmennus", + "Backup has a valid signature from this user": "Varmuuskopiossa on kelvollinen allekirjoitus tältä käyttäjältä", + "Backup has a invalid signature from this user": "Varmuuskopiossa on epäkelpo allekirjoitus tältä käyttäjältä", + "Backup has a signature from unknown user with ID %(deviceId)s": "Varmuuskopiossa on tuntematon allekirjoitus käyttäjältä, jonka ID on %(deviceId)s", + "Backup has a signature from unknown device with ID %(deviceId)s": "Varmuuskopiossa on tuntematon allekirjoitus laitteelta, jonka ID on %(deviceId)s", + "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Vara-avain on tallennettu salavarastoon, mutta salavarasto ei ole käytössä tällä laitteella. Ota käyttöön ristivarmennus Laboratoriosta, jotta voi muokata avainvarmuuskopion tilaa.", + "Backup key stored: ": "Vara-avain on tallennettu: ", + "Start using Key Backup with Secure Secret Storage": "Aloita avainten varmuuskopiointi turvalliseen salavarastoon", + "This user has not verified all of their devices.": "Tämä käyttäjä ei ole varmentanut kaikkia laitteitaan.", + "You have not verified this user. This user has verified all of their devices.": "Et ole varmentanut tätä käyttäjää. Tämä käyttäjä on varmentanut kaikki laitteensa.", + "You have verified this user. This user has verified all of their devices.": "Olet varmentanut tämän käyttäjän. Tämä käyttäjä on varmentanut kaikki laitteensa.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Et ole varmentanut osaa tämän salausta käyttävän huoneen käyttäjistä tai he eivät ole varmentaneet omia laitteitaan.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Olet varmentanut kaikki käyttäjät tässä salausta käyttävässä huoneessa ja he ovat varmentaneet omat laitteensa.", + "This message cannot be decrypted": "Tätä viestiä ei voida avata luettavaksi", + "Unencrypted": "Suojaamaton", + "Close preview": "Sulje esikatselu", + " wants to chat": " haluaa keskustella", + "Start chatting": "Aloita keskustelu", + "Hide verified sessions": "Piilota varmennetut istunnot", + "%(count)s verified sessions|other": "%(count)s varmennettua istuntoa", + "%(count)s verified sessions|one": "1 varmennettu istunto", + "Reactions": "Reaktiot", + " reacted with %(content)s": " reagoi: %(content)s", + "Language Dropdown": "Kielipudotusvalikko", + "Automatically invite users": "Kutsu käyttäjät automaattisesti", + "Upgrade private room": "Päivitä yksityinen huone", + "Upgrade public room": "Päivitä julkinen huone", + "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Huoneen päivittäminen on monimutkainen toimenpide ja yleensä sitä suositellaan, kun huone on epävakaa bugien, puuttuvien ominaisuuksien tai tietoturvaongelmien takia.", + "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Tämä yleensä vaikuttaa siihen, miten huonetta käsitellään palvelimella. Jos sinulla on ongelmia Riottisi kanssa, ilmoita virheestä.", + "You'll upgrade this room from to .": "Olat päivittämässä tätä huonetta versiosta versioon .", + "Upgrade": "Päivitä", + "Enter secret storage passphrase": "Syötä salavaraston salalause", + "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Salavavaraston avaaminen epäonnistui. Varmista, että syötit oikean salalauseen.", + "Warning: You should only access secret storage from a trusted computer.": "Varoitus: sinun pitäisi käyttää salavarastoa vain luotetulta tietokoneelta.", + "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Käytä turvattua viestihistoriaasi ja ristivarmennuksen identiteettiäsi muiden laitteiden varmentamiseen syöttämällä salalauseesi.", + "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Jos olet unohtanut salalauseesi, voit käyttää palautusavaintasi tai asettaa uusia palautusvaihtoehtoja.", + "Enter secret storage recovery key": "Syötä salavaraston palautusavain", + "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Salavaraston käyttö epäonnistui. Varmista, että syötit oikean palautusavaimen.", + "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Käytä turvattua viestihistoriaasi ja ristivarmennuksen identiteettiäsi muiden laitteiden varmentamiseen syöttämällä palautusavaimesi.", + "If you've forgotten your recovery key you can .": "Jos olet unohtanut palautusavaimesi, voit .", + "Warning: You should only set up key backup from a trusted computer.": "Varoitus: sinun pitäisi ottaa avainten varmuuskopiointi käyttöön vain luotetulla tietokoneella.", + "If you've forgotten your recovery key you can ": "Jos olet unohtanut palautusavaimesi, voit ", + "Notification settings": "Ilmoitusasetukset", + "Help": "Ohje", + "User Status": "Käyttäjän tila", + "Country Dropdown": "Maapudotusvalikko", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Salavarasto otetaan käyttöön nykyisen avainten varmuuskopiointimenetelmäsi tiedoilla. Salavaraston salalause ja palautusavain tulee olemaan samat kuin ne olivat avainten varmuuskopioinnissasi", + "Warning: You should only set up secret storage from a trusted computer.": "Varoitus: sinun pitäisi ottaa salavarasto käyttöön vain luotetulla tietokoneella.", + "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Voimme vaihtoehtoisesti tallentaa salavarastoon salatun kopion ristivarmennuksen identiteetistäsi muiden laitteiden varmentamiseen ja lähettääksesi avaimia meidän palvelimelle. Suojaa pääsysi salattuihin viesteihisi pitämällä salalauseesi turvassa.", + "Set up with a recovery key": "Ota käyttöön palautusavaimella", + "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Turvaverkkona, voit käyttää sitä palauttamaan pääsysi salattuihin viesteihin, jos unohdat salalauseesi.", + "As a safety net, you can use it to restore your access to encrypted messages.": "Turvaverkkona, voit käyttää sitä palauttamaan pääsysi salattuihin viesteihisi.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Pidä palautusavaimesi jossain hyvin turvallisessa paikassa, kuten salasananhallintasovelluksessa (tai kassakaapissa).", + "Your recovery key has been copied to your clipboard, paste it to:": "Palautusavaimesi on kopioitu leikepöydällesi. Liitä se:", + "Your recovery key is in your Downloads folder.": "Palautusavaimesi on Lataukset-kansiossasi.", + "Your access to encrypted messages is now protected.": "Pääsysi salattuihin viesteihisi on nyt turvattu.", + "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Ottamatta käyttöön salavarastoa et voi palauttaa pääsyäsi salattuihin viesteihisi tai ristivarmennuksen identiteettiisi, jos kirjaudut ulos tai käytät toista laitetta.", + "Set up secret storage": "Ota salavarasto käyttöön", + "Migrate from Key Backup": "Siirrä tiedot vanhasta avainten varmuuskopiointijärjestelmästä", + "Secure your encrypted messages with a passphrase": "Turvaa salatut viestisi salalauseella", + "Storing secrets...": "Tallennetaan salaisuuksia...", + "Unable to set up secret storage": "Salavaraston käyttöönotto epäonnistui" } From 5e4f341cd870aa0b38098dba2fb29a0b69168567 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Wed, 25 Dec 2019 19:16:46 +0000 Subject: [PATCH 032/101] Translated using Weblate (Hungarian) Currently translated at 100.0% (2001 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index fd88250707..2f31f75407 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2019,5 +2019,6 @@ "Country Dropdown": "Ország lenyíló menü", "The message you are trying to send is too large.": "Túl nagy képet próbálsz elküldeni.", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "A Biztonsági Tároló a már létező kulcs mentés adatai felhasználásával lesz létrehozva. A biztonsági tároló jelmondata és a visszaállítási kulcs meg fog egyezni a kulcs mentésénél használttal", - "Migrate from Key Backup": "Mozgatás a Kulcs Mentésből" + "Migrate from Key Backup": "Mozgatás a Kulcs Mentésből", + "Help": "Segítség" } From a0e816d134f4f80db26b131b02ce9c70b76a3372 Mon Sep 17 00:00:00 2001 From: "ferhad.necef" Date: Wed, 25 Dec 2019 16:02:48 +0000 Subject: [PATCH 033/101] Translated using Weblate (Russian) Currently translated at 90.8% (1816 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index c71e4236b9..fc8b37140e 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -1830,5 +1830,7 @@ "My Ban List": "Мой список запрещенных", "Ignored/Blocked": "Игнорируемые/Заблокированные", "Error adding ignored user/server": "Ошибка добавления игнорируемого пользователя/сервера", - "Error subscribing to list": "Ошибка при подписке на список" + "Error subscribing to list": "Ошибка при подписке на список", + "Send cross-signing keys to homeserver": "Отправка ключей перекрестной подписи на домашний сервер", + "Error upgrading room": "Ошибка обновления комнаты" } From 7043453d3ebcd97ccfec519f224f9175326baf5a Mon Sep 17 00:00:00 2001 From: catborise Date: Tue, 24 Dec 2019 15:28:22 +0000 Subject: [PATCH 034/101] Translated using Weblate (Turkish) Currently translated at 44.7% (894 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 163 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 647ca2f6a5..85715846ac 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -738,5 +738,166 @@ "View Servers in Room": "Odadaki Sunucuları Gör", "Toolbox": "Araç Kutusu", "Developer Tools": "Geliştirici Araçları", - "Integrations are disabled": "Bütünleştirmeler kapatılmış" + "Integrations are disabled": "Bütünleştirmeler kapatılmış", + "Integrations not allowed": "Bütünleştirmelere izin verilmiyor", + "Loading device info...": "Cihaz bilgileri yükleniyor...", + "Incompatible local cache": "Yerel geçici bellek uyumsuz", + "Clear cache and resync": "Geçici belleği temizle ve yeniden eşle", + "Updating Riot": "Riot güncelleniyor", + "I don't want my encrypted messages": "Şifrelenmiş mesajlarımı istemiyorum", + "Manually export keys": "Elle dışa aktarılmış anahtarlar", + "You'll lose access to your encrypted messages": "Şifrelenmiş mesajlarınıza erişiminizi kaybedeceksiniz", + "Are you sure you want to sign out?": "Oturumdan çıkmak istediğinize emin misiniz?", + "Your homeserver doesn't seem to support this feature.": "Ana sunucunuz bu özelliği desteklemiyor gözüküyor.", + "Message edits": "Mesajları düzenle", + "Report bugs & give feedback": "Hataları raporla & geri bildirim yap", + "Please fill why you're reporting.": "Lütfen neden raporlama yaptığınızı belirtin.", + "Report Content to Your Homeserver Administrator": "Ana Sunucu Yöneticinize İçeriği Raporlayın", + "Send report": "Rapor gönder", + "Room Settings - %(roomName)s": "Oda Ayarları - %(roomName)s", + "Failed to upgrade room": "Oda güncelleme başarısız", + "The room upgrade could not be completed": "Oda güncelleme tamamlanamadı", + "Upgrade this room to version %(version)s": "Bu odayı %(version)s versiyonuna yükselt", + "Upgrade Room Version": "Oda Sürümünü Yükselt", + "Automatically invite users": "Otomatik olarak kullanıcıları davet et", + "Upgrade private room": "Özel oda güncelle", + "Upgrade": "Yükselt", + "Sign out and remove encryption keys?": "Oturumu kapat ve şifreleme anahtarlarını sil?", + "Clear Storage and Sign Out": "Depolamayı temizle ve Oturumu Kapat", + "Send Logs": "Logları Gönder", + "Refresh": "Yenile", + "Checking...": "Kontrol ediliyor...", + "To get started, please pick a username!": "Başlamak için lütfen bir kullanıcı adı seçin!", + "Share Room": "Oda Paylaş", + "Link to most recent message": "En son mesaja bağlantı", + "Share User": "Kullanıcı Paylaş", + "Share Community": "Topluluk Paylaş", + "Share Room Message": "Oda Mesajı Paylaş", + "Link to selected message": "Seçili mesaja bağlantı", + "COPY": "KOPYA", + "Command Help": "Komut Yardımı", + "Missing session data": "Kayıp oturum verisi", + "Integration Manager": "Bütünleştirme Yöneticisi", + "Find others by phone or email": "Kişileri telefon yada e-posta ile bul", + "Be found by phone or email": "Telefon veya e-posta ile bulunun", + "Terms of Service": "Hizmet Şartları", + "Service": "Hizmet", + "Summary": "Özet", + "Document": "Belge", + "Next": "İleri", + "Upload files": "Dosyaları yükle", + "Upload all": "Hepsini yükle", + "Cancel All": "Hepsi İptal", + "Upload Error": "Yükleme Hatası", + "Allow": "İzin ver", + "Enter secret storage recovery key": "Depolama kurtarma anahtarı için şifre gir", + "This looks like a valid recovery key!": "Bu geçerli bir kurtarma anahtarına benziyor!", + "Not a valid recovery key": "Geçersiz bir kurtarma anahtarı", + "Unable to load backup status": "Yedek durumu yüklenemiyor", + "Recovery Key Mismatch": "Kurtarma Anahtarı Kurtarma", + "Unable to restore backup": "Yedek geri dönüşü yapılamıyor", + "No backup found!": "Yedek bulunamadı!", + "Backup Restored": "Yedek Geri Dönüldü", + "Enter Recovery Key": "Kurtarma Anahtarını Gir", + "Warning: You should only set up key backup from a trusted computer.": "Uyarı: Yedek anahtarı kurulumunu sadece güvenli bir bilgisayardan yapmalısınız.", + "Unable to reject invite": "Davet reddedilemedi", + "Pin Message": "Pin Mesajı", + "Share Message": "Mesajı Paylaş", + "Report Content": "İçeriği Raporla", + "Notification settings": "Bildirim ayarları", + "Clear status": "Durumu temizle", + "Update status": "Durumu güncelle", + "Set status": "Durumu ayarla", + "Set a new status...": "Yeni bir durum ayarla...", + "View Community": "Topluluğu Gör", + "Hide": "Gizle", + "Reload": "Yeniden Yükle", + "Remove for everyone": "Herkes için sil", + "Remove for me": "Benim için sil", + "User Status": "Kullanıcı Durumu", + "This homeserver would like to make sure you are not a robot.": "Bu ana sunucu sizin bir robot olup olmadığınızdan emin olmak istiyor.", + "Country Dropdown": "Ülke Listesi", + "Code": "Kod", + "Unable to validate homeserver/identity server": "Ana/kimlik sunucu doğrulanamıyor", + "Your Modular server": "Sizin Modüler sunucunuz", + "Server Name": "Sunucu Adı", + "The email field must not be blank.": "E-posta alanı boş bırakılamaz.", + "The username field must not be blank.": "Kullanıcı adı alanı boş bırakılamaz.", + "The phone number field must not be blank.": "Telefon numarası alanı boş bırakılamaz.", + "The password field must not be blank.": "Şifre alanı boş bırakılamaz.", + "Username": "Kullanıcı Adı", + "Use an email address to recover your account": "Hesabınızı kurtarmak için bir e-posta adresi kullanın", + "Enter email address (required on this homeserver)": "E-posta adresi gir ( bu ana sunucuda gerekli)", + "Doesn't look like a valid email address": "Geçerli bir e-posta adresine benzemiyor", + "Enter password": "Şifre gir", + "Password is allowed, but unsafe": "Şifreye izin var, fakat kullanmak güvenli değil", + "Nice, strong password!": "Güzel, güçlü şifre!", + "Keep going...": "Devam et...", + "Passwords don't match": "Şifreler uyuşmuyor", + "Enter phone number (required on this homeserver)": "Telefon numarası gir ( bu ana sunucuda gerekli)", + "Doesn't look like a valid phone number": "Geçerli bir telefon numarasına benzemiyor", + "Enter username": "Kullanıcı adı gir", + "Email (optional)": "E-posta (opsiyonel)", + "Confirm": "Doğrula", + "Phone (optional)": "Telefon (opsiyonel)", + "Create your Matrix account on %(serverName)s": "%(serverName)s üzerinde Matrix hesabınızı oluşturun", + "Create your Matrix account on ": " üzerinde Matrix hesabınızı oluşturun", + "Homeserver URL": "Ana sunucu URL", + "Identity Server URL": "Kimlik Sunucu URL", + "Other servers": "Diğer sunucular", + "Couldn't load page": "Sayfa yüklenemiyor", + "Add a Room": "Bir Oda Ekle", + "Add a User": "Bir Kullanıcı Ekle", + "Failed to upload image": "Resim yükleme başarısız", + "Unable to accept invite": "Davet kabul edilemiyor", + "Unable to join community": "Topluluğa katılınamıyor", + "Leave Community": "Topluluktan Ayrıl", + "Leave %(groupName)s?": "%(groupName)s den ayrıl?", + "Unable to leave community": "Topluluktan ayrılınamıyor", + "Community Settings": "Topluluk Ayarları", + "Join this community": "Bu topluluğa katıl", + "Leave this community": "Bu topluluktan ayrıl", + "You are an administrator of this community": "Bu topluluğun yöneticisi sizsiniz", + "You are a member of this community": "Bu topluluğun bir üyesisiniz", + "Who can join this community?": "Bu topluluğa kimler katılabilir?", + "Everyone": "Herkes", + "Long Description (HTML)": "Uzun Tanım (HTML)", + "Description": "Tanım", + "Community %(groupId)s not found": "%(groupId)s topluluğu bulunamıyor", + "This homeserver does not support communities": "Bu ana sunucu toplulukları desteklemiyor", + "Failed to load %(groupId)s": "%(groupId)s yükleme başarısız", + "Filter": "Filtre", + "Filter rooms…": "Odaları filtrele…", + "Old cryptography data detected": "Eski kriptolama verisi tespit edildi", + "Verification Request": "Doğrulama Talebi", + "Your Communities": "Topluluklarınız", + "Error whilst fetching joined communities": "Katılım sağlanmış topluluklar getirilirken hata oluştu", + "Create a new community": "Yeni bir topluluk oluştur", + "The homeserver may be unavailable or overloaded.": "Ana sunucunu mevcut değil yada fazla yüklü.", + "Preview": "Önizleme", + "View": "Görüntüle", + "Find a room…": "Bir oda bul…", + "Find a room… (e.g. %(exampleRoom)s)": "Bir oda bul... (örn. %(exampleRoom)s)", + "%(count)s of your messages have not been sent.|one": "Mesajınız gönderilmedi.", + "Jump to first unread room.": "Okunmamış ilk odaya zıpla.", + "Jump to first invite.": "İlk davete zıpla.", + "Add room": "Oda ekle", + "Clear filter": "Filtre temizle", + "Guest": "Misafir", + "Your profile": "Profiliniz", + "Could not load user profile": "Kullanıcı profili yüklenemedi", + "Your Matrix account on %(serverName)s": "%(serverName)s sunucusundaki Matrix hesabınız", + "Your password has been reset.": "Parolanız sıfırlandı.", + "Set a new password": "Yeni bir şifre belirle", + "General failure": "Genel başarısızlık", + "This homeserver does not support login using email address.": "Bu ana sunucu e-posta adresiyle oturum açmayı desteklemiyor.", + "This account has been deactivated.": "Bu hesap pasifleştirilmiş.", + "Create account": "Yeni hesap", + "Unable to query for supported registration methods.": "Desteklenen kayıt yöntemleri için sorgulama yapılamıyor.", + "Continue with previous account": "Önceki hesapla devam et", + "Log in to your new account.": "Yeni hesabınızla Oturum açın.", + "Registration Successful": "Kayıt Başarılı", + "Create your account": "Hesabınızı oluşturun", + "Forgotten your password?": "Parolanızı mı unuttunuz?", + "Sign in and regain access to your account.": "Oturum açın ve yeniden hesabınıza ulaşın." } From c1c0c36008fa21f0729feead80d9f0bc948ccd8a Mon Sep 17 00:00:00 2001 From: "ferhad.necef" Date: Wed, 25 Dec 2019 22:18:37 +0000 Subject: [PATCH 035/101] Translated using Weblate (Azerbaijani) Currently translated at 22.4% (448 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/az/ --- src/i18n/strings/az.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/az.json b/src/i18n/strings/az.json index e4316496ec..538403f164 100644 --- a/src/i18n/strings/az.json +++ b/src/i18n/strings/az.json @@ -350,7 +350,7 @@ "Failed to invite users to community": "İstifadəçiləri cəmiyyətə dəvət etmək alınmadı", "Unnamed Room": "Adı açıqlanmayan otaq", "Unable to load! Check your network connectivity and try again.": "Yükləmək olmur! Şəbəkə bağlantınızı yoxlayın və yenidən cəhd edin.", - "Dismiss": "Rədd et", + "Dismiss": "Nəzərə almayın", "Riot does not have permission to send you notifications - please check your browser settings": "Riot-un sizə bildiriş göndərmək icazəsi yoxdur - brauzerinizin parametrlərini yoxlayın", "Riot was not given permission to send notifications - please try again": "Riot bildiriş göndərmək üçün icazə verilmədi - lütfən yenidən cəhd edin", "This email address was not found": "Bu e-poçt ünvanı tapılmadı", @@ -447,5 +447,6 @@ "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "Bu otaqda %(qruplar)s üçün %(senderDisplayName)s aktiv oldu.", "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "Bu otaqda %(groups)s üçün %(senderDisplayName)s aktiv oldu.", "powered by Matrix": "Matrix tərəfindən təchiz edilmişdir", - "Custom Server Options": "Xüsusi Server Seçimləri" + "Custom Server Options": "Fərdi Server Seçimləri", + "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "Bu otaqda %(newGroups)s üçün aktiv və %(oldGroups)s üçün %(senderDisplayName)s deaktiv oldu." } From 6f7ddc37139db7ebbd82823c9d1b771623981ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Luke=C5=A1?= Date: Fri, 27 Dec 2019 13:17:10 +0000 Subject: [PATCH 036/101] Translated using Weblate (Czech) Currently translated at 98.8% (1976 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/cs/ --- src/i18n/strings/cs.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 4b69e2da82..5aacb27c80 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -1976,5 +1976,23 @@ "Set up secret storage": "Nastavit bezpečné úložiště", "Secure your encrypted messages with a passphrase": "Zabezpečte vaše šifrované zprávy heslem", "Storing secrets...": "Ukládám tajná data...", - "Unable to set up secret storage": "Nepovedlo se nastavit bezpečné úložiště" + "Unable to set up secret storage": "Nepovedlo se nastavit bezpečné úložiště", + "The message you are trying to send is too large.": "Zpráva kterou se snažíte odeslat je příliš velká.", + "not found": "nenalezeno", + "Backup has a valid signature from this user": "Záloha má platný podpis od tohoto uživatele", + "Backup has a invalid signature from this user": "Záloha má neplatný podpis od tohoto uživatele", + "Backup has a signature from unknown user with ID %(deviceId)s": "Záloha je podepsaná neznámým uživatelem %(deviceId)s", + "Backup has a signature from unknown device with ID %(deviceId)s": "Záloha je podepsaná neznámým zařízením %(deviceId)s", + "This user has not verified all of their devices.": "Uživatel neověřil všechna svá zařízení.", + "You have not verified this user. This user has verified all of their devices.": "Tohoto uživatele jste neověřili. Uživatel má ověřená všechna svá zařízení.", + "You have verified this user. This user has verified all of their devices.": "Tohoto uživatele jste ověřili. Uživatel má ověřená všechna svá zařízení.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Někteřé uživatele v této šifrované místnosti jste neověřili nebo nemají ověřená některá svá zařízení.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Všichni uživatelé v této šifrované místnosti jsou ověření a mají ověřená všechna svá zařízení.", + "Close preview": "Zavřít náhled", + "Hide verified sessions": "Schovat ověřené relace", + "%(count)s verified sessions|other": "%(count)s ověřených relací", + "%(count)s verified sessions|one": "1 ověřená relace", + "Language Dropdown": "Menu jazyků", + "Help": "Pomoc", + "Country Dropdown": "Menu států" } From 145741d16846b5ddca99a0800970209b04dc8139 Mon Sep 17 00:00:00 2001 From: strix aluco Date: Fri, 27 Dec 2019 08:28:07 +0000 Subject: [PATCH 037/101] Translated using Weblate (Ukrainian) Currently translated at 27.1% (542 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index e2df3fc774..9a522e6ee2 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -405,7 +405,7 @@ "%(widgetName)s widget removed by %(senderName)s": "%(senderName)s вилучив/ла %(widgetName)s", "Failure to create room": "Не вдалося створити кімнату", "Server may be unavailable, overloaded, or you hit a bug.": "Сервер може бути недоступний, перевантажений, або ж ви натрапили на ваду.", - "Send anyway": "Все-таки надіслати", + "Send anyway": "Надіслати хоч би там що", "Unnamed Room": "Кімната без назви", "This homeserver has hit its Monthly Active User limit.": "Цей домашній сервер досягнув свого ліміту щомісячних активних користувачів.", "This homeserver has exceeded one of its resource limits.": "Цей домашній сервер досягнув одного зі своїх лімітів ресурсів.", From 957b1efc00885b3f6fd55160e35de16a01b8cae0 Mon Sep 17 00:00:00 2001 From: random Date: Fri, 27 Dec 2019 17:13:48 +0000 Subject: [PATCH 038/101] Translated using Weblate (Italian) Currently translated at 99.9% (2000 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 5d0c769e6e..390729950b 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2016,5 +2016,6 @@ "Country Dropdown": "Nazione a tendina", "The message you are trying to send is too large.": "Il messaggio che stai tentando di inviare è troppo grande.", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "L'archivio segreto verrà impostato usando i dettagli del tuo backup chiavi. La password dell'archivio segreto e la chiave di ripristino saranno le stesse del tuo backup chiavi", - "Migrate from Key Backup": "Migra dal backup chiavi" + "Migrate from Key Backup": "Migra dal backup chiavi", + "Help": "Aiuto" } From a0cb6ce30287633192a7be5a13b02036a74395f6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 28 Dec 2019 17:51:38 +0000 Subject: [PATCH 039/101] User Info fetch latest RoomMember instead of showing historical data --- src/components/views/right_panel/UserInfo.js | 43 +++++++++++--------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 208c5e8906..69555bf79f 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1020,6 +1020,8 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { // Load room if we are given a room id and memoize it const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]); + // fetch latest room member if we have a room, so we don't show historical information + const member = useMemo(() => room ? room.getMember(user.userId) : user, [room, user]); // only display the devices list if our client supports E2E const _enableDevices = cli.isCryptoEnabled(); @@ -1051,7 +1053,7 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { setPendingUpdateCount(pendingUpdateCount - 1); }, [pendingUpdateCount]); - const roomPermissions = useRoomPermissions(cli, room, user); + const roomPermissions = useRoomPermissions(cli, room, member); const onSynapseDeactivate = useCallback(async () => { const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog'); @@ -1084,7 +1086,6 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { }, [cli, user.userId]); const onMemberAvatarClick = useCallback(() => { - const member = user; const avatarUrl = member.getMxcAvatarUrl ? member.getMxcAvatarUrl() : member.avatarUrl; if (!avatarUrl) return; @@ -1096,7 +1097,7 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { }; Modal.createDialog(ImageView, params, "mx_Dialog_lightbox"); - }, [cli, user]); + }, [cli, member]); let synapseDeactivateButton; let spinner; @@ -1113,11 +1114,11 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { } let adminToolsContainer; - if (room && user.roomId) { + if (room && member.roomId) { adminToolsContainer = ( @@ -1147,20 +1148,20 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { spinner = ; } - const displayName = user.name || user.displayname; + const displayName = member.name || member.displayname; let presenceState; let presenceLastActiveAgo; let presenceCurrentlyActive; let statusMessage; - if (user instanceof RoomMember && user.user) { - presenceState = user.user.presence; - presenceLastActiveAgo = user.user.lastActiveAgo; - presenceCurrentlyActive = user.user.currentlyActive; + if (member instanceof RoomMember && member.user) { + presenceState = member.user.presence; + presenceLastActiveAgo = member.user.lastActiveAgo; + presenceCurrentlyActive = member.user.currentlyActive; if (SettingsStore.isFeatureEnabled("feature_custom_status")) { - statusMessage = user.user._unstable_statusMessage; + statusMessage = member.user._unstable_statusMessage; } } @@ -1190,13 +1191,13 @@ const UserInfo = ({user, groupId, roomId, onClose}) => {
    + urls={member.avatarUrl ? [member.avatarUrl] : undefined} />
    @@ -1210,10 +1211,14 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { title={_t('Close')} />; } - const memberDetails = ; + const memberDetails = ( + + ); const isRoomEncrypted = useIsEncrypted(cli, room); // undefined means yet to be loaded, null means failed to load, otherwise list of devices @@ -1349,7 +1354,7 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { devices={devices} canInvite={roomPermissions.canInvite} isIgnored={isIgnored} - member={user} /> + member={member} /> { adminToolsContainer } From 705bfcd20dca6f68ece3d12254818bbaec124871 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 28 Dec 2019 17:54:46 +0000 Subject: [PATCH 040/101] fix RoomViewStore import in UserInfo --- src/components/views/right_panel/UserInfo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 69555bf79f..9fd7a1ddff 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -32,7 +32,7 @@ import SdkConfig from '../../../SdkConfig'; import SettingsStore from "../../../settings/SettingsStore"; import {EventTimeline} from "matrix-js-sdk"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; -import * as RoomViewStore from "../../../stores/RoomViewStore"; +import RoomViewStore from "../../../stores/RoomViewStore"; import MultiInviter from "../../../utils/MultiInviter"; import GroupStore from "../../../stores/GroupStore"; import MatrixClientPeg from "../../../MatrixClientPeg"; From 4489b5a21aa7550d1bfeee1b4f3960ebc4698cb7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 28 Dec 2019 20:05:55 +0000 Subject: [PATCH 041/101] Escape HTML in og:description and render any html &-encoded entities --- src/components/views/rooms/LinkPreviewWidget.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js index ee63cd1bb7..06c0201af8 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -128,15 +128,15 @@ module.exports = createReactClass({ } const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + // Escape to prevent any HTML injections, we can't replace & as the description may contain & encoded html entities + const safeDescription = (p["og:description"] || "").replace("<", "<").replace(">", ">"); return (
    { img }
    { p["og:site_name"] ? (" - " + p["og:site_name"]) : null }
    -
    - { p["og:description"] } -
    +
    Date: Sat, 28 Dec 2019 23:52:57 -0600 Subject: [PATCH 042/101] Fix inverted diff line highlighting in dark theme Signed-off-by: Justin Sleep --- res/themes/dark/css/_dark.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index eadde4c672..d1d0e333a0 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -243,3 +243,12 @@ $breadcrumb-placeholder-bg-color: #272c35; } } } + +// Fixes diff color inversion by swapping add / del colors +.hljs-addition { + background: #fdd; +} + +.hljs-deletion { + background: #dfd; +} From b1c28870878ee0e5fb6f634bf8a4b5ef030b3c12 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 30 Dec 2019 16:08:24 +0000 Subject: [PATCH 043/101] line length --- src/components/views/dialogs/RoomSettingsDialog.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index 9ac2b17f23..e01319e3bd 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -55,7 +55,8 @@ export default class RoomSettingsDialog extends React.Component { _getTabs() { const tabs = []; const featureFlag = SettingsStore.isFeatureEnabled("feature_bridge_state"); - const shouldShowBridgeIcon = featureFlag && BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; + const shouldShowBridgeIcon = featureFlag && + BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; tabs.push(new Tab( _td("General"), From fb94be4abdb713bb23b8b22461500b90f682ec92 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 30 Dec 2019 16:11:59 +0000 Subject: [PATCH 044/101] No trailing space --- src/components/views/dialogs/RoomSettingsDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index e01319e3bd..c31fe1992d 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -55,7 +55,7 @@ export default class RoomSettingsDialog extends React.Component { _getTabs() { const tabs = []; const featureFlag = SettingsStore.isFeatureEnabled("feature_bridge_state"); - const shouldShowBridgeIcon = featureFlag && + const shouldShowBridgeIcon = featureFlag && BridgeSettingsTab.getBridgeStateEvents(this.props.roomId).length > 0; tabs.push(new Tab( From d7a680db71b94c46ebeeba9f93b9acae6c472379 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Sun, 29 Dec 2019 08:54:04 +0000 Subject: [PATCH 045/101] Translated using Weblate (Basque) Currently translated at 100.0% (2001 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eu/ --- src/i18n/strings/eu.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index b3e9a731ee..7214c231e0 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -2007,5 +2007,16 @@ "Set up secret storage": "Ezarri biltegi sekretua", "Secure your encrypted messages with a passphrase": "Babestu zure zifratutako mezuak pasa-esaldi batekin", "Storing secrets...": "Sekretuak gordetzen...", - "Unable to set up secret storage": "Ezin izan da biltegi sekretua ezarri" + "Unable to set up secret storage": "Ezin izan da biltegi sekretua ezarri", + "The message you are trying to send is too large.": "Bidali nahi duzun mezua handiegia da.", + "This user has not verified all of their devices.": "Erabiltzaileak ez ditu bere gailu guztiak egiaztatu.", + "You have not verified this user. This user has verified all of their devices.": "Ez duzu erabiltzaile hau egiaztatu. Erabiltzaile honek bere gailu guztiak egiaztatu ditu.", + "You have verified this user. This user has verified all of their devices.": "Erabiltzaile hau egiaztatu duzu. Erabiltzaile honek bere gailu guztiak egiaztatu ditu.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Ez dituzu zifratutako gela honetako erabiltzaile batzuk egiaztatu, edo hauek ez dituzte bere gailu guztiak egiaztatu.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Zifratutako gela honetako erabiltzaile guztiak egiaztatu dituzu, eta hauek bere gailu guztiak egiaztatu dituzte.", + "Language Dropdown": "Hizkuntza menua", + "Help": "Laguntza", + "Country Dropdown": "Herrialde menua", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Biltegi sekretua zure oraingo gakoen babes-kopiaren xehetasunak erabiliz ezarriko da. Zure biltegi sekretuaren pasa-esaldia eta berreskuratze gakoa lehen gakoen babes-kopiarako ziren berdinak izango dira", + "Migrate from Key Backup": "Migratu gakoen babes-kopiatik" } From 44c181576e8aa7c0bc6a17d987ab4dd2dc09efd2 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 27 Dec 2019 19:29:34 +0000 Subject: [PATCH 046/101] Translated using Weblate (Turkish) Currently translated at 47.9% (959 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 85715846ac..85cfeedd06 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -589,7 +589,7 @@ "The platform you're on": "Bulunduğun platform", "The version of Riot.im": "Riot.im'in sürümü", "Your language of choice": "Seçtiginiz diliniz", - "Which officially provided instance you are using, if any": "", + "Which officially provided instance you are using, if any": "Hangi resmi destekli örneği(eğer varsa) kullanmaktasınız", "Add Email Address": "E-posta Adresi Ekle", "Add Phone Number": "Telefon Numarası Ekle", "Your identity server's URL": "Kimlik sunucunuzun linki", @@ -899,5 +899,69 @@ "Registration Successful": "Kayıt Başarılı", "Create your account": "Hesabınızı oluşturun", "Forgotten your password?": "Parolanızı mı unuttunuz?", - "Sign in and regain access to your account.": "Oturum açın ve yeniden hesabınıza ulaşın." + "Sign in and regain access to your account.": "Oturum açın ve yeniden hesabınıza ulaşın.", + "Whether or not you're logged in (we don't record your username)": "İster oturum açın yasa açmayın (biz kullanıcı adınızı kaydetmiyoruz)", + "Whether or not you're using the Richtext mode of the Rich Text Editor": "Zengin Metin Düzenleyicinin Zengin metin modunu kullanıyor ya da kullanmıyorsunuz", + "Your homeserver's URL": "Ana sunucunuzun URL’i", + "The information being sent to us to help make Riot.im better includes:": "Riot.im i daha iyi yapmamıza yardımcı olacak bize gönderdiğiniz bilgilerin içeriği:", + "Try using turn.matrix.org": "turn.matrix.org i kullanarak dene", + "You do not have permission to start a conference call in this room": "Bu odada bir konferans başlatmak için izniniz yok", + "The file '%(fileName)s' failed to upload.": "%(fileName)s dosyası için yükleme başarısız.", + "The server does not support the room version specified.": "Belirtilen oda sürümünü sunucu desteklemiyor.", + "Who would you like to add to this community?": "Bu topluluğa kimi eklemek isterdiniz?", + "Which rooms would you like to add to this community?": "Hangi odaları bu topluluğa eklemek isterdiniz?", + "Unable to create widget.": "Görsel bileşen oluşturulamıyor.", + "Changes your display nickname in the current room only": "sadece mevcut odada görüntülenen lakabınızı değiştirir", + "Changes the avatar of the current room": "Mevcut odadaki avatarınızı değiştirir", + "Use an identity server to invite by email. Manage in Settings.": "E-posta ile davet etmek için bir kimlik sunucusu kullan. Ayarlardan Yönet.", + "You cannot modify widgets in this room.": "Bu odadaki görsel bileşenleri değiştiremezsiniz.", + "Sends the given message coloured as a rainbow": "Verilen mesajı gökkuşağı renklerinde gönderir", + "Displays list of commands with usages and descriptions": "Komutların listesini kullanımı ve tanımlarıyla gösterir", + "%(senderName)s made no change.": "%(senderName)s değişiklik yapmadı.", + "%(senderName)s changed the pinned messages for the room.": "Oda için sabitlenmiş mesajları %(senderName)s değiştirdi.", + "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s görsel bileşeni %(senderName)s tarafından düzenlendi", + "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s görsel bileşeni %(senderName)s tarafından eklendi", + "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s görsel bileşeni %(senderName)s tarafından silindi", + "%(names)s and %(count)s others are typing …|other": "%(names)s ve diğer %(count)s kişi yazıyor…", + "This homeserver has exceeded one of its resource limits.": "Bu anasunucu kaynak limitlerinden birini aştı.", + "Unable to connect to Homeserver. Retrying...": "Anasunucuya bağlanılamıyor. Yeniden deneniyor...", + "%(items)s and %(count)s others|other": "%(items)s ve diğer %(count)s", + "%(items)s and %(count)s others|one": "%(items)s ve bir diğeri", + "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", + "Unrecognised address": "Tanınmayan adres", + "You do not have permission to invite people to this room.": "Bu odaya kişi davet etme izniniz yok.", + "User %(userId)s is already in the room": "Kullanıcı %(userId)s zaten odada", + "User %(user_id)s does not exist": "Kullanıcı %(user_id)s mevcut değil", + "User %(user_id)s may or may not exist": "Kullanıcı %(user_id)s mevcut olup olmadığı belli değil", + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Hesabınıza giriş yapamazsınız. Lütfen daha fazla bilgi için ana sunucu yöneticiniz ile bağlantıya geçiniz.", + "You're signed out": "Çıkış yaptınız", + "Clear personal data": "Kişisel veri temizle", + "Command Autocomplete": "Oto tamamlama komutu", + "Community Autocomplete": "Oto tamlama topluluğu", + "DuckDuckGo Results": "DuckDuckGo Sonuçları", + "Emoji Autocomplete": "Emoji Oto Tamamlama", + "Notify the whole room": "Tüm odayı bilgilendir", + "Room Notification": "Oda Bildirimi", + "Set up with a recovery key": "Kurtarma anahtarı ile kur", + "That matches!": "Eşleşti!", + "That doesn't match.": "Eşleşmiyor.", + "Your Recovery Key": "Kurtarma Anahtarınız", + "Download": "İndir", + "Print it and store it somewhere safe": "Yazdır ve güvenli bir yerde sakla", + "Save it on a USB key or backup drive": "Bir USB anahtara kaydet veya sürücüye yedekle", + "Copy it to your personal cloud storage": "Kişisel bulut depolamaya kopyala", + "Your access to encrypted messages is now protected.": "Şifrelenmiş mesajlara erişiminiz şimdi korunuyor.", + "Migrate from Key Backup": "Anahtar Yedeğinden Göç Et", + "Recovery key": "Kurtarma anahtarı", + "Success!": "Başarılı!", + "Retry": "Yeniden Dene", + "Set up with a Recovery Key": "Bir Kurtarma Anahtarı ile Kur", + "Set up Secure Message Recovery": "Güvenli Mesaj Kurtarma Kurulumu", + "Starting backup...": "Yedeklemeye başlanıyor...", + "Create Key Backup": "Anahtar Yedeği Oluştur", + "Unable to create key backup": "Anahtar yedeği oluşturulamıyor", + "Don't ask again": "Yeniden sorma", + "New Recovery Method": "Yeni Kurtarma Yöntemi", + "Go to Settings": "Ayarlara Git", + "Recovery Method Removed": "Kurtarma Yöntemi Silindi" } From 827ddaf7df34a20a0ddf02bb584ae92889e3fb49 Mon Sep 17 00:00:00 2001 From: strix aluco Date: Mon, 30 Dec 2019 08:15:28 +0000 Subject: [PATCH 047/101] Translated using Weblate (Ukrainian) Currently translated at 28.6% (572 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 42 +++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index 9a522e6ee2..c6ae464a2b 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -438,8 +438,8 @@ "Incorrect verification code": "Неправильний код перевірки", "Submit": "Надіслати", "Phone": "Телефон", - "Failed to upload profile picture!": "Не вдалося завантажити світлину профілю!", - "Upload new:": "Завантажити нову:", + "Failed to upload profile picture!": "Не вдалося відвантажити світлину профілю!", + "Upload new:": "Відвантажити нову:", "No display name": "Немає імені для показу", "New passwords don't match": "Нові паролі не збігаються", "Passwords can't be empty": "Пароль не може бути пустим", @@ -460,7 +460,7 @@ "Enable Notifications": "Увімкнути сповіщення", "The maximum permitted number of widgets have already been added to this room.": "Максимально дозволену кількість віджетів уже додано до цієї кімнати.", "Drop File Here": "Киньте файл сюди", - "Drop file here to upload": "Киньте файл сюди, щоб вивантажити", + "Drop file here to upload": "Киньте файл сюди, щоб відвантажити", " (unsupported)": " (не підтримується)", "Join as voice or video.": "Приєднатися голосом або відео.", "Ongoing conference call%(supportedText)s.": "Триває дзвінок-конференція%(supportedText)s.", @@ -508,7 +508,7 @@ "Alternatively, you can try to use the public server at turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Також ви можете спробувати використати публічний сервер turn.matrix.org, але це буде не настільки надійно, а також цей сервер матиме змогу бачити вашу IP-адресу. Ви можете керувати цим у налаштуваннях.", "Try using turn.matrix.org": "Спробуйте використати turn.matrix.org", "Replying With Files": "Відповісти файлами", - "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Зараз неможливо відповісти файлом. Хочете завантажити цей файл без відповіді?", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Зараз неможливо відповісти файлом. Хочете відвантажити цей файл без відповіді?", "Name or Matrix ID": "Імʼя або Matrix ID", "Identity server has no terms of service": "Сервер ідентифікації не має умов надання послуг", "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Щоб підтвердити адресу е-пошту або телефон ця дія потребує доступу до типового серверу ідентифікації , але сервер не має жодних умов надання послуг.", @@ -547,5 +547,37 @@ "Forces the current outbound group session in an encrypted room to be discarded": "Примусово відкидає поточний вихідний груповий сеанс у шифрованій кімнаті", "Sends the given message coloured as a rainbow": "Надсилає вказане повідомлення розфарбоване веселкою", "Your Riot is misconfigured": "Ваш Riot налаштовано неправильно", - "Join the discussion": "Приєднатися до обговорення" + "Join the discussion": "Приєднатися до обговорення", + "Upload": "Відвантажити", + "Upload file": "Відвантажити файл", + "Send an encrypted message…": "Надіслати зашифроване повідомлення…", + "Send a message (unencrypted)…": "Надіслати повідомлення (незашифроване)…", + "The conversation continues here.": "Розмова триває тут.", + "This room has been replaced and is no longer active.": "Ця кімната була замінена і не є активною.", + "You do not have permission to post to this room": "У вас нема дозволу дописувати у цю кімнату", + "Sign out": "Вийти", + "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of Riot to do this": "Щоб уникнути втрати історії ваших листувань, ви маєте експортувати ключі кімнати перед виходом. Вам треба буде повернутися до новішої версії Riot аби зробити це", + "You've previously used a newer version of Riot on %(host)s. To use this version again with end to end encryption, you will need to sign out and back in again. ": "Раніше ви використовували новішу версію Riot на %(host)s. Для повторного користування цією версією з наскрізним шифруванням вам треба буде вийти та зайти знову. ", + "Incompatible Database": "Несумісна база даних", + "Continue With Encryption Disabled": "Продовжити із вимкненим шифруванням", + "Unknown error": "Невідома помилка", + "Incorrect password": "Неправильний пароль", + "Are you sure you want to sign out?": "Ви впевнені, що хочете вийти?", + "Your homeserver doesn't seem to support this feature.": "Схоже, що ваш домашній сервер не підтримує цю властивість.", + "Sign out and remove encryption keys?": "Вийти та видалити ключі шифрування?", + "Clear Storage and Sign Out": "Очистити сховище та вийти", + "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.": "Очищення сховища вашого оглядача може усунути проблему, але воно виведе вас з системи і зробить непрочитною історію ваших зашифрованих листувань.", + "Verification Pending": "Очікується перевірка", + "Upload files (%(current)s of %(total)s)": "Відвантажити файли (%(current)s з %(total)s)", + "Upload files": "Відвантажити файли", + "Upload all": "Відвантажити всі", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Файл є надто великим для відвантаження. Допустимий розмір файлів — %(limit)s, але цей файл займає %(sizeOfThisFile)s.", + "These files are too large to upload. The file size limit is %(limit)s.": "Ці файли є надто великими для відвантаження. Допустимий розмір файлів — %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.": "Деякі файли є надто великими для відвантаження. Допустимий розмір файлів — %(limit)s.", + "Upload %(count)s other files|other": "Відвантажити %(count)s інших файли(ів)", + "Upload Error": "Помилка відвантаження", + "Failed to upload image": "Не вдалось відвантажити зображення", + "Upload avatar": "Відвантажити аватар", + "For security, this session has been signed out. Please sign in again.": "З метою безпеки вашу сесію було завершено. Зайдіть, будь ласка, знову.", + "Upload an avatar:": "Відвантажити аватар:" } From 2bfe1e6be3f8db2caad9d26770de5168021d75c7 Mon Sep 17 00:00:00 2001 From: Volodymyr Kostyrko Date: Tue, 31 Dec 2019 11:19:17 +0000 Subject: [PATCH 048/101] Translated using Weblate (Ukrainian) Currently translated at 28.9% (578 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/uk/ --- src/i18n/strings/uk.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index c6ae464a2b..7795060406 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -160,7 +160,7 @@ "The server may be unavailable or overloaded": "Сервер може бути недосяжним або перевантаженим", "Room not found": "Кімнату не знайдено", "Reject": "Відмовитись", - "Failed to set Direct Message status of room": "Не вдалось встановити статус прямого спілкування в кімнаті", + "Failed to set Direct Message status of room": "Не вдалося встановити статус прямого спілкування в кімнаті", "Monday": "Понеділок", "Remove from Directory": "Прибрати з каталогу", "Enable them now": "Увімкнути їх зараз", @@ -577,7 +577,11 @@ "Upload %(count)s other files|other": "Відвантажити %(count)s інших файли(ів)", "Upload Error": "Помилка відвантаження", "Failed to upload image": "Не вдалось відвантажити зображення", - "Upload avatar": "Відвантажити аватар", + "Upload avatar": "Завантажити аватар", "For security, this session has been signed out. Please sign in again.": "З метою безпеки вашу сесію було завершено. Зайдіть, будь ласка, знову.", - "Upload an avatar:": "Відвантажити аватар:" + "Upload an avatar:": "Завантажити аватар:", + "Send cross-signing keys to homeserver": "Надсилання ключей підпису на домашній сервер", + "Custom (%(level)s)": "Власний (%(level)s)", + "Error upgrading room": "Помилка оновлення кімнати", + "Double check that your server supports the room version chosen and try again.": "Перевірте, чи підримує ваш сервер вказану версію кімнати та спробуйте ще." } From fa9fd97a4e5b2a7e94d0185650402e214a649909 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Thu, 2 Jan 2020 09:41:50 +0000 Subject: [PATCH 049/101] missing } --- res/css/views/dialogs/_RoomSettingsDialog.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index b7641a4ad0..4b13684d9f 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -54,6 +54,7 @@ limitations under the License. mask-repeat: no-repeat; mask-size: 36px; mask-position: center; +} .mx_RoomSettingsDialog_BridgeList { padding: 0; From 31367bac57b3ef6e7f6983cce6386d691b2775b0 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 2 Jan 2020 08:49:30 +0000 Subject: [PATCH 050/101] Translated using Weblate (Bulgarian) Currently translated at 93.5% (1870 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/bg/ --- src/i18n/strings/bg.json | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 3115ca140a..d1aa0dc9a8 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1851,5 +1851,38 @@ "Connecting to integration manager...": "Свързане с мениджъра на интеграции...", "Cannot connect to integration manager": "Неуспешна връзка с мениджъра на интеграции", "The integration manager is offline or it cannot reach your homeserver.": "Мениджъра на интеграции е офлайн или не може да се свърже със сървъра ви.", - "Clear notifications": "Изчисти уведомленията" + "Clear notifications": "Изчисти уведомленията", + "Send cross-signing keys to homeserver": "Изпрати ключове за кръстосано-подписване към сървъра", + "Error upgrading room": "Грешка при обновяване на стаята", + "Double check that your server supports the room version chosen and try again.": "Проверете дали сървъра поддържа тази версия на стаята и опитайте пак.", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа на потребители отговарящи на %(glob)s", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа до стаи отговарящи на %(glob)s", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа до сървъри отговарящи на %(glob)s", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s премахна правилото блокиращо достъпа неща отговарящи на %(glob)s", + "%(senderName)s updated an invalid ban rule": "%(senderName)s обнови невалидно правило за блокиране", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа на потребители отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа до стаи отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа до сървъри отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s премахна правилото блокиращо достъпа неща отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа на потребители отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа до стаи отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа до сървъри отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s създаде правило блокиращо достъпа до неща отговарящи на %(glob)s поради %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа на потребители отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа до стаи отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа до сървъри отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s промени правило блокиращо достъпа до неща отговарящи на %(oldGlob)s към отговарящи на %(newGlob)s поради %(reason)s", + "The message you are trying to send is too large.": "Съобщението, което се опитвате да изпратите е прекалено голямо.", + "Cross-signing and secret storage are enabled.": "Кръстосано-подписване и секретно складиране са включени.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this device.": "За вашия профил има самоличност за кръстосано-подписване в секретно складиране, но все още не е доверена от това устройство.", + "Cross-signing and secret storage are not yet set up.": "Кръстосаното-подписване и секретно складиране все още не са настроени.", + "Bootstrap cross-signing and secret storage": "Инициализирай кръстосано-подписване и секретно складиране", + "Cross-signing public keys:": "Публични ключове за кръстосано-подписване:", + "on device": "на устройството", + "not found": "не са намерени", + "Cross-signing private keys:": "Private ключове за кръстосано подписване:", + "in secret storage": "в секретно складиране", + "Secret storage public key:": "Публичен ключ за секретно складиране:", + "in account data": "в данни за акаунта", + "not stored": "не е складиран" } From b6572f7042c04c0e8afad885d22346fe408716fd Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 3 Jan 2020 08:50:36 +0000 Subject: [PATCH 051/101] Translated using Weblate (Bulgarian) Currently translated at 97.4% (1948 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/bg/ --- src/i18n/strings/bg.json | 80 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index d1aa0dc9a8..7911eb4607 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1884,5 +1884,83 @@ "in secret storage": "в секретно складиране", "Secret storage public key:": "Публичен ключ за секретно складиране:", "in account data": "в данни за акаунта", - "not stored": "не е складиран" + "not stored": "не е складиран", + "Backup has a valid signature from this user": "Резервното копие има валиден подпис за този потребител", + "Backup has a invalid signature from this user": "Резервното копие има невалиден подпис за този потребител", + "Backup has a signature from unknown user with ID %(deviceId)s": "Резервното копие има подпис от непознат потребител с идентификатор %(deviceId)s", + "Backup has a signature from unknown device with ID %(deviceId)s": "Резервното копие има подпис от непознато устройство с идентификатор %(deviceId)s", + "Backup key stored in secret storage, but this feature is not enabled on this device. Please enable cross-signing in Labs to modify key backup state.": "Резервния ключ е съхранен в секретно складиране, но тази функция не е включена на това устройство. Включете кръстосано-подписване от Labs за да промените състоянието на резервното копие на ключа.", + "Backup key stored: ": "Резервният ключ е съхранен: ", + "Start using Key Backup with Secure Secret Storage": "Започни използване на резервно копие на ключ в защитено секретно складиране", + "Use an Integration Manager (%(serverName)s) to manage bots, widgets, and sticker packs.": "Използвай мениджър на интеграции %(serverName)s за управление на ботове, приспособления и стикери.", + "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Използвай мениджър на интеграции за управление на ботове, приспособления и стикери.", + "Manage integrations": "Управление на интеграциите", + "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Мениджърът на интеграции получава конфигурационни данни, може да модифицира приспособления, да изпраща покани за стаи и да настройва нива на достъп от ваше име.", + "Customise your experience with experimental labs features. Learn more.": "Настройте изживяването си с експериментални функции. Научи повече.", + "Ignored/Blocked": "Игнорирани/блокирани", + "Error adding ignored user/server": "Грешка при добавяне на игнориран потребител/сървър", + "Something went wrong. Please try again or view your console for hints.": "Нещо се обърка. Опитайте пак или вижте конзолата за информация какво не е наред.", + "Error subscribing to list": "Грешка при абониране за списък", + "Please verify the room ID or alias and try again.": "Потвърдете идентификатора или адреса на стаята и опитайте пак.", + "Error removing ignored user/server": "Грешка при премахване на игнориран потребител/сървър", + "Error unsubscribing from list": "Грешка при отписването от списък", + "Please try again or view your console for hints.": "Опитайте пак или вижте конзолата за информация какво не е наред.", + "None": "Няма нищо", + "Ban list rules - %(roomName)s": "Списък с правила за блокиране - %(roomName)s", + "Server rules": "Сървърни правила", + "User rules": "Потребителски правила", + "You have not ignored anyone.": "Не сте игнорирали никой.", + "You are currently ignoring:": "В момента игнорирате:", + "You are not subscribed to any lists": "Не сте абонирани към списъци", + "Unsubscribe": "Отпиши", + "View rules": "Виж правилата", + "You are currently subscribed to:": "В момента сте абонирани към:", + "⚠ These settings are meant for advanced users.": "⚠ Тези настройки са за напреднали потребители.", + "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Добавете тук потребители или сървъри, които искате да игнорирате. Използвайте звездички за да кажете на Riot да търси съвпадения с всеки символ. Например: @bot:* ще игнорира всички потребители с име 'bot' на кой да е сървър.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.": "Игнорирането на хора става чрез списъци за блокиране, които съдържат правила кой да бъде блокиран. Абонирането към списък за блокиране означава, че сървърите/потребителите блокирани от този списък ще бъдат скрити от вас.", + "Personal ban list": "Персонален списък за блокиране", + "Your personal ban list holds all the users/servers you personally don't want to see messages from. After ignoring your first user/server, a new room will show up in your room list named 'My Ban List' - stay in this room to keep the ban list in effect.": "Персоналния ви списък за блокиране съдържа потребители/сървъри, от които не искате да виждате съобщения. След игнориране на първия потребител/сървър, ще се появи нова стая в списъка със стаи, наречена 'My Ban List' - останете в тази стая за да работи списъкът с блокиране.", + "Server or user ID to ignore": "Сървър или потребителски идентификатор за игнориране", + "eg: @bot:* or example.org": "напр.: @bot:* или example.org", + "Subscribed lists": "Абонирани списъци", + "Subscribing to a ban list will cause you to join it!": "Абонирането към списък ще направи така, че да се присъедините към него!", + "If this isn't what you want, please use a different tool to ignore users.": "Ако това не е каквото искате, използвайте друг инструмент за игнориране на потребители.", + "Room ID or alias of ban list": "Идентификатор или име на стая списък за блокиране", + "Subscribe": "Абонирай ме", + "Cross-signing": "Кръстосано-подписване", + "This user has not verified all of their devices.": "Този потребител не е потвърдил всичките си устройства.", + "You have not verified this user. This user has verified all of their devices.": "Не сте потвърдили този потребител. Потребителят е потвърдил всичките си устройства.", + "You have verified this user. This user has verified all of their devices.": "Потвърдили сте този потребител. Потребителят е потвърдил всичките си устройства.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Някои потребители в тази стая не са потвърдени от вас или не са потвърдили собствените си устройства.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Всички потребители в тази стая са потвърдени от вас и са потвърдили всичките си устройства.", + "This message cannot be decrypted": "Съобщението не може да бъде дешифровано", + "Unencrypted": "Нешифровано", + "Close preview": "Затвори прегледа", + " wants to chat": " иска да чати", + "Start chatting": "Започни чат", + "Failed to connect to integration manager": "Неуспешна връзка с мениджъра на интеграции", + "Trusted": "Доверени", + "Not trusted": "Недоверени", + "Hide verified sessions": "Скрий потвърдените сесии", + "%(count)s verified sessions|other": "%(count)s потвърдени сесии", + "%(count)s verified sessions|one": "1 потвърдена сесия", + "Direct message": "Директно съобщение", + "Unverify user": "Отпотвърди потребители", + "%(role)s in %(roomName)s": "%(role)s в%(roomName)s", + "Messages in this room are end-to-end encrypted.": "Съобщенията в тази стая са шифровани от край-до-край.", + "Verify": "Потвърди", + "Security": "Сигурност", + "You have ignored this user, so their message is hidden. Show anyways.": "Игнорирали сте този потребител, така че съобщението им е скрито. Покажи така или иначе.", + "Reactions": "Реакции", + " reacted with %(content)s": " реагира с %(content)s", + "Any of the following data may be shared:": "Следните данни може да бъдат споделени:", + "Your display name": "Вашето име", + "Your avatar URL": "Адреса на профилната ви снимка", + "Your user ID": "Потребителския ви идентификатор", + "Your theme": "Вашата тема", + "Riot URL": "Riot URL адрес", + "Room ID": "Идентификатор на стаята", + "Widget ID": "Идентификатор на приспособлението", + "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s и с мениджъра на интеграции.", + "Using this widget may share data with %(widgetDomain)s.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s." } From c8f9df691b6250d3480b0cacc0bf2736e37f122d Mon Sep 17 00:00:00 2001 From: catborise Date: Thu, 2 Jan 2020 18:15:14 +0000 Subject: [PATCH 052/101] Translated using Weblate (Turkish) Currently translated at 50.8% (1017 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 85cfeedd06..a3a178c358 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -963,5 +963,63 @@ "Don't ask again": "Yeniden sorma", "New Recovery Method": "Yeni Kurtarma Yöntemi", "Go to Settings": "Ayarlara Git", - "Recovery Method Removed": "Kurtarma Yöntemi Silindi" + "Recovery Method Removed": "Kurtarma Yöntemi Silindi", + "Robot": "Robot", + "Hat": "Şapka", + "Glasses": "Gözlük", + "Umbrella": "Şemsiye", + "Hourglass": "Kum saati", + "Clock": "Saat", + "Gift": "Hediye", + "Light bulb": "Ampül", + "Book": "Kitap", + "Pencil": "Kalem", + "Paperclip": "Ataç", + "Scissors": "Makas", + "Key": "Anahtar", + "Hammer": "Çekiç", + "Telephone": "Telefon", + "Flag": "Bayrak", + "Train": "Tren", + "Bicycle": "Bisiklet", + "Aeroplane": "Uçak", + "Rocket": "Füze", + "Ball": "Top", + "Guitar": "Gitar", + "Trumpet": "Trampet", + "Bell": "Zil", + "Anchor": "Çıpa", + "Headphones": "Kulaklık", + "Folder": "Klasör", + "Accept to continue:": "Devam etmek için i kabul ediniz:", + "Upload": "Yükle", + "on device": "cihaz üstünde", + "not found": "bulunamadı", + "in account data": "hesap verisinde", + "Your homeserver does not support device management.": "Aan sunucunuz aygıt yönetimini desteklemiyor.", + "Delete %(count)s devices|other": "%(count)s cihazı silin", + "Delete %(count)s devices|one": "Cihaz sil", + "ID": "ID", + "Connecting to integration manager...": "Entegrasyon yöneticisine bağlanın...", + "Cannot connect to integration manager": "Entegrasyon yöneticisine bağlanılamadı", + "Delete Backup": "Yedek Sil", + "Unable to load key backup status": "Anahtar yedek durumu yüklenemiyor", + "Restore from Backup": "Yedekten Geri Dön", + "This device is backing up your keys. ": "Bu cihaz anahtarlarınızı yedekliyor. ", + "Connect this device to Key Backup": "Anahtar Yedeği için bu cihazı bağlayın", + "not stored": "depolanmadı", + "Backing up %(sessionsRemaining)s keys...": "%(sessionsRemaining)s anahtar yedekleniyor...", + "All keys backed up": "Bütün yedekler yedeklendi", + "Backup is not signed by any of your devices": "Yedek hiç bir cihazınız tarafından imzalanmadı", + "Backup version: ": "Yedek sürümü: ", + "Algorithm: ": "Algoritma: ", + "Start using Key Backup": "Anahtar Yedekleme kullanmaya başla", + "Clear notifications": "Bildirimleri temizle", + "Enable desktop notifications for this device": "Bu cihaz için masaüstü bildirimlerini aç", + "Show message in desktop notification": "Masaüstü bildiriminde mesaj göster", + "Display Name": "Ekran Adı", + "Profile picture": "Profil resmi", + "Could not connect to Identity Server": "Kimlik Sunucusuna bağlanılamadı", + "Checking server": "Sunucu kontrol ediliyor", + "Change identity server": "Kimlik sunucu değiştir" } From 668479d94a477976b4113906b21cce56f01458ac Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 3 Jan 2020 13:57:59 +0000 Subject: [PATCH 053/101] Tweak as per git review --- .../settings/tabs/room/BridgeSettingsTab.js | 69 +++++++++---------- src/i18n/strings/en_EN.json | 8 +-- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 82382e7828..9e6bdf8958 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 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. @@ -33,17 +33,6 @@ export default class BridgeSettingsTab extends React.Component { roomId: PropTypes.string.isRequired, }; - constructor() { - super(); - - this.state = { - }; - } - - componentWillMount() { - - } - _renderBridgeCard(event, room) { const content = event.getContent(); if (!content || !content.channel || !content.protocol) { @@ -56,21 +45,18 @@ export default class BridgeSettingsTab extends React.Component { let creator = null; if (content.creator) { - const pill = ; - creator = (

    { - _t("This bridge was provisioned by %(pill)s", { - pill, - }) - }

    ); + creator =

    { _t("This bridge was provisioned by ", {}, { + user: , + })}

    ; } - const bot = (

    {_t("This bridge is managed by the %(pill)s bot user.", { - pill: {_t("This bridge is managed by the bot user.", {}, { + user: ); let channelLink = channelName; if (channel.external_url) { - channelLink = {channelName}; + channelLink = {channelName}; } let networkLink = networkName; if (network && network.external_url) { - networkLink = {networkName}; + networkLink = {networkName}; } const chanAndNetworkInfo = ( - (_t("Bridged into %(channelLink)s %(networkLink)s, on %(protocolName)s", { + _t("Bridged into , on ", {}, { channelLink, networkLink, protocolName, - })) + }) ); let networkIcon = null; @@ -101,9 +87,13 @@ export default class BridgeSettingsTab extends React.Component { MatrixClientPeg.get().getHomeserverUrl(), network.avatar, 32, 32, "crop", ); - networkIcon = ; + networkIcon = ; } let channelIcon = null; @@ -112,13 +102,16 @@ export default class BridgeSettingsTab extends React.Component { MatrixClientPeg.get().getHomeserverUrl(), channel.avatar, 32, 32, "crop", ); - console.log(channel.avatar); - channelIcon = ; + channelIcon = ; } - const heading = _t("Connected to %(channelIcon)s %(channelName)s on %(networkIcon)s %(networkName)s", { + const heading = _t("Connected to on ", { }, { channelIcon, channelName, networkName, @@ -127,7 +120,7 @@ export default class BridgeSettingsTab extends React.Component { return (

  • -

    {heading}

    +

    {heading}

    {_t("Connected via %(protocolName)s", { protocolName })}

    {creator} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6fcfd2eb95..1f66891857 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -763,10 +763,10 @@ "Room version:": "Room version:", "Developer options": "Developer options", "Open Devtools": "Open Devtools", - "This bridge was provisioned by %(pill)s": "This bridge was provisioned by %(pill)s", - "This bridge is managed by the %(pill)s bot user.": "This bridge is managed by the %(pill)s bot user.", - "Bridged into %(channelLink)s %(networkLink)s, on %(protocolName)s": "Bridged into %(channelLink)s %(networkLink)s, on %(protocolName)s", - "Connected to %(channelIcon)s %(channelName)s on %(networkIcon)s %(networkName)s": "Connected to %(channelIcon)s %(channelName)s on %(networkIcon)s %(networkName)s", + "This bridge was provisioned by ": "This bridge was provisioned by ", + "This bridge is managed by the bot user.": "This bridge is managed by the bot user.", + "Bridged into , on ": "Bridged into , on ", + "Connected to on ": "Connected to on ", "Connected via %(protocolName)s": "Connected via %(protocolName)s", "Bridge Info": "Bridge Info", "Below is a list of bridges connected to this room.": "Below is a list of bridges connected to this room.", From 831053de112921eaffafac5d94933e0e6dcc6a88 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 3 Jan 2020 13:58:07 +0000 Subject: [PATCH 054/101] Make bridge info cards more obvious --- res/css/views/dialogs/_RoomSettingsDialog.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 4b13684d9f..aa66e97f9e 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -62,8 +62,10 @@ limitations under the License. .mx_RoomSettingsDialog_BridgeList li { list-style-type: none; - padding: 0; - margin: 0; - border-bottom: 1px solid $panel-divider-color; + padding: 5px; + margin-bottom: 5px; + border-width: 1px 0px; + border-color: #dee1f3; + border-style: solid; } From 50e19ba43d64e168e670e4657153f9359477bcb6 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 3 Jan 2020 14:04:59 +0000 Subject: [PATCH 055/101] User may not be a bot, therefore do not imply it. --- src/components/views/settings/tabs/room/BridgeSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 9e6bdf8958..c3c85ec31c 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -55,7 +55,7 @@ export default class BridgeSettingsTab extends React.Component { })}

    ; } - const bot = (

    {_t("This bridge is managed by the bot user.", {}, { + const bot = (

    {_t("This bridge is managed by .", {}, { user: ": "This bridge was provisioned by ", - "This bridge is managed by the bot user.": "This bridge is managed by the bot user.", + "This bridge is managed by .": "This bridge is managed by .", "Bridged into , on ": "Bridged into , on ", "Connected to on ": "Connected to on ", "Connected via %(protocolName)s": "Connected via %(protocolName)s", From 0b16f862e97b79a72acf0f4c7ce19e6a9f55ef4a Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 3 Jan 2020 14:51:20 +0000 Subject: [PATCH 056/101] Translated using Weblate (Bulgarian) Currently translated at 97.8% (1956 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/bg/ --- src/i18n/strings/bg.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 7911eb4607..2c53a45fc2 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1962,5 +1962,13 @@ "Room ID": "Идентификатор на стаята", "Widget ID": "Идентификатор на приспособлението", "Using this widget may share data with %(widgetDomain)s & your Integration Manager.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s и с мениджъра на интеграции.", - "Using this widget may share data with %(widgetDomain)s.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s." + "Using this widget may share data with %(widgetDomain)s.": "Използването на това приспособление може да сподели данни с %(widgetDomain)s.", + "Widgets do not use message encryption.": "Приспособленията не използваш шифроване на съобщенията.", + "Widget added by": "Приспособлението е добавено от", + "This widget may use cookies.": "Това приспособление може да използва бисквитки.", + "More options": "Още опции", + "Language Dropdown": "Избор на език", + "Integrations are disabled": "Интеграциите са изключени", + "Enable 'Manage Integrations' in Settings to do this.": "Включете 'Управление на интеграции' от настройките за направите това.", + "Integrations not allowed": "Интеграциите не са разрешени" } From 731279ab2d0de023743b16fdcefde385f722232d Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 3 Jan 2020 14:53:05 +0000 Subject: [PATCH 057/101] Translated using Weblate (Bulgarian) Currently translated at 99.2% (1985 of 2001 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/bg/ --- src/i18n/strings/bg.json | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 2c53a45fc2..5744640ec8 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1967,8 +1967,37 @@ "Widget added by": "Приспособлението е добавено от", "This widget may use cookies.": "Това приспособление може да използва бисквитки.", "More options": "Още опции", - "Language Dropdown": "Избор на език", + "Language Dropdown": "Падащо меню за избор на език", "Integrations are disabled": "Интеграциите са изключени", "Enable 'Manage Integrations' in Settings to do this.": "Включете 'Управление на интеграции' от настройките за направите това.", - "Integrations not allowed": "Интеграциите не са разрешени" + "Integrations not allowed": "Интеграциите не са разрешени", + "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Вашият Riot не позволява да използвате мениджъра на интеграции за да направите това. Свържете се с администратор.", + "Automatically invite users": "Автоматично кани потребители", + "Upgrade private room": "Обнови лична стая", + "Upgrade public room": "Обнови публична стая", + "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Обновяването на стая е действие за напреднали и обикновено се препоръчва когато стаята е нестабилна поради бъгове, липсващи функции или проблеми със сигурността.", + "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please report a bug.": "Това обикновено влия само на това как стаята се обработва на сървъра. Ако имате проблеми с Riot, съобщете за проблем.", + "You'll upgrade this room from to .": "Ще обновите стаята от до .", + "Upgrade": "Обнови", + "Enter secret storage passphrase": "Въведете парола за секретно складиране", + "Unable to access secret storage. Please verify that you entered the correct passphrase.": "Неуспешен достъп до секретно складиране. Уверете се, че сте въвели правилната парола.", + "Warning: You should only access secret storage from a trusted computer.": "Внимание: Трябва да достъпвате секретно складиране само от доверен компютър.", + "Access your secure message history and your cross-signing identity for verifying other devices by entering your passphrase.": "Въведете парола за да достъпите защитената история на съобщенията и самоличността за кръстосано-подписване и потвърждение на други устройства.", + "If you've forgotten your passphrase you can use your recovery key or set up new recovery options.": "Ако сте забравили паролата си, може да използвате ключ за възстановяване или да настройте опции за възстановяване.", + "Enter secret storage recovery key": "Въведете ключ за възстановяване на секретно складиране", + "Unable to access secret storage. Please verify that you entered the correct recovery key.": "Неуспешен достъп до секретно складиране. Подсигурете се, че сте въвели правилния ключ за възстановяване.", + "Access your secure message history and your cross-signing identity for verifying other devices by entering your recovery key.": "Въведете ключа за възстановяване за да достъпите защитената история на съобщенията и самоличността за кръстосано-подписване и потвърждение на други устройства.", + "If you've forgotten your recovery key you can .": "Ако сте забравили ключа си за възстановяване, може да .", + "Warning: You should only set up key backup from a trusted computer.": "Внимание: Трябва да настройвате резервно копие на ключове само от доверен компютър.", + "If you've forgotten your recovery key you can ": "Ако сте забравили ключа за възстановяване, може да ", + "Notification settings": "Настройки на уведомленията", + "Help": "Помощ", + "Reload": "Презареди", + "Take picture": "Направи снимка", + "Remove for everyone": "Премахни за всички", + "Remove for me": "Премахни за мен", + "User Status": "Потребителски статус", + "Country Dropdown": "Падащо меню за избор на държава", + "Verification Request": "Заявка за потвърждение", + " (1/%(totalCount)s)": " (1/%(totalCount)s)" } From 33d35c83ba2cd472f0ade060a5162c1c6f2d6306 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 3 Jan 2020 15:42:11 +0000 Subject: [PATCH 058/101] Translated using Weblate (Bulgarian) Currently translated at 100.0% (2000 of 2000 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/bg/ --- src/i18n/strings/bg.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 5744640ec8..e179c9d61b 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1999,5 +1999,21 @@ "User Status": "Потребителски статус", "Country Dropdown": "Падащо меню за избор на държава", "Verification Request": "Заявка за потвърждение", - " (1/%(totalCount)s)": " (1/%(totalCount)s)" + " (1/%(totalCount)s)": " (1/%(totalCount)s)", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Секретно Складиране ще бъде настроено със съществуващия ви резервен ключ. Паролата и ключа за секретно складиране ще бъдат същите каквито са за резервно складиране", + "Warning: You should only set up secret storage from a trusted computer.": "Внимание: Желателно е да настройвате секретно складиране само от доверен компютър.", + "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "Ще използваме секретно складиране за да предоставим опцията да се съхрани шифровано копие на идентичността ви за кръстосано-подписване, за потвърждаване на други устройства и ключове. Защитете достъпа си до шифровани съобщения с парола за да е защитено.", + "Set up with a recovery key": "Настрой с ключ за възстановяване", + "As a safety net, you can use it to restore your access to encrypted messages if you forget your passphrase.": "Като предпазна мярка, ако забравите паролата, може да го използвате за да възстановите достъпа до шифрованите съобщения.", + "As a safety net, you can use it to restore your access to encrypted messages.": "Като предпазна мярка, може да го използвате за да възстановите достъпа до шифрованите съобщения.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe).": "Съхранявайте ключа за възстановяване на сигурно място, като в password manager (или сейф).", + "Your recovery key has been copied to your clipboard, paste it to:": "Ключа за възстановяване беше копиран в клиборд, поставете го в:", + "Your recovery key is in your Downloads folder.": "Ключа за възстановяване е във вашата папка Изтегляния.", + "Your access to encrypted messages is now protected.": "Достъпът ви до шифровани съобщения вече е защитен.", + "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Без настройка на секретно складиране, ако излезе от профила или използвате друго устройство, няма да можете да възстановите достъпа до шифровани съобщения или идентичността за кръстосано-подписване за потвърждаване на други устройства.", + "Set up secret storage": "Настрой секретно складиране", + "Migrate from Key Backup": "Мигрирай от резервно копие на ключове", + "Secure your encrypted messages with a passphrase": "Защитете шифрованите съобщения с парола", + "Storing secrets...": "Складиране на тайни...", + "Unable to set up secret storage": "Неуспешна настройка на секретно складиране" } From 31d5617c97e34deda775a05c1e0337f2c7bd23d8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 3 Jan 2020 19:41:06 -0700 Subject: [PATCH 059/101] Add suggestions for which users to invite to chat Fixes https://github.com/vector-im/riot-web/issues/11198 Note this doesn't implement the entire algorithm in 11198 because it feels too complicated at this stage. Instead, the idea is to review the suggestions closer to when the whole dialog is complete and fix them then: https://github.com/vector-im/riot-web/issues/11769 Algorithm for picking members is largely based on https://github.com/matrix-org/matrix-react-sdk/commit/db5218e19a26b650d4af37de81f516c68ab7e9b8 --- .../views/dialogs/DMInviteDialog.js | 91 ++++++++++++++++--- src/i18n/strings/en_EN.json | 3 +- 2 files changed, 81 insertions(+), 13 deletions(-) diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js index ff498e3e75..bdeae6bc3e 100644 --- a/src/components/views/dialogs/DMInviteDialog.js +++ b/src/components/views/dialogs/DMInviteDialog.js @@ -23,6 +23,7 @@ import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import DMRoomMap from "../../../utils/DMRoomMap"; import {RoomMember} from "matrix-js-sdk/lib/matrix"; import * as humanize from "humanize"; +import SdkConfig from "../../../SdkConfig"; // TODO: [TravisR] Make this generic for all kinds of invites @@ -36,10 +37,6 @@ class DMRoomTile extends React.PureComponent { onToggle: PropTypes.func.isRequired, }; - constructor() { - super(); - } - _onClick = (e) => { // Stop the browser from highlighting text e.preventDefault(); @@ -84,6 +81,8 @@ export default class DMInviteDialog extends React.PureComponent { filterText: "", recents: this._buildRecents(), numRecentsShown: INITIAL_ROOMS_SHOWN, + suggestions: this._buildSuggestions(), + numSuggestionsShown: INITIAL_ROOMS_SHOWN, }; } @@ -109,6 +108,59 @@ export default class DMInviteDialog extends React.PureComponent { return recents; } + _buildSuggestions(): {userId: string, user: RoomMember} { + const maxConsideredMembers = 200; + const client = MatrixClientPeg.get(); + const excludedUserIds = [client.getUserId(), SdkConfig.get()['welcomeUserId']]; + const joinedRooms = client.getRooms() + .filter(r => r.getMyMembership() === 'join') + .filter(r => r.getJoinedMemberCount() <= maxConsideredMembers); + const memberRooms = joinedRooms.reduce((members, room) => { + const joinedMembers = room.getJoinedMembers().filter(u => !excludedUserIds.includes(u.userId)); + for (const member of joinedMembers) { + if (!members[member.userId]) { + members[member.userId] = { + member: member, + // Track the room size of the 'picked' member so we can use the profile of + // the smallest room (likely a DM). + pickedMemberRoomSize: room.getJoinedMemberCount(), + rooms: [], + }; + } + + members[member.userId].rooms.push(room); + + if (room.getJoinedMemberCount() < members[member.userId].pickedMemberRoomSize) { + members[member.userId].member = member; + members[member.userId].pickedMemberRoomSize = room.getJoinedMemberCount(); + } + } + return members; + }, {/* userId => {member, rooms[]} */}); + const memberScores = Object.values(memberRooms).reduce((scores, entry) => { + const numMembersTotal = entry.rooms.reduce((c, r) => c + r.getJoinedMemberCount(), 0); + const maxRange = maxConsideredMembers * entry.rooms.length; + scores[entry.member.userId] = { + member: entry.member, + numRooms: entry.rooms.length, + score: Math.max(0, Math.pow(1 - (numMembersTotal / maxRange), 5)), + }; + return scores; + }, {/* userId => {member, numRooms, score} */}); + const members = Object.values(memberScores); + members.sort((a, b) => { + if (a.score === b.score) { + if (a.numRooms === b.numRooms) { + return a.member.userId.localeCompare(b.member.userId); + } + + return b.numRooms - a.numRooms; + } + return b.score - a.score; + }); + return members.map(m => ({userId: m.userId, user: m.member})); + } + _startDm = () => { this.props.onFinished(this.state.targets); }; @@ -125,6 +177,10 @@ export default class DMInviteDialog extends React.PureComponent { this.setState({numRecentsShown: this.state.numRecentsShown + INCREMENT_ROOMS_SHOWN}); }; + _showMoreSuggestions = () => { + this.setState({numSuggestionsShown: this.state.numSuggestionsShown + INCREMENT_ROOMS_SHOWN}); + }; + _toggleMember = (userId) => { const targets = this.state.targets.map(t => t); // cheap clone for mutation const idx = targets.indexOf(userId); @@ -133,29 +189,39 @@ export default class DMInviteDialog extends React.PureComponent { this.setState({targets}); }; - _renderRecents() { - if (!this.state.recents || this.state.recents.length === 0) return null; + _renderSection(kind: "recents"|"suggestions") { + const sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions; + let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown; + const showMoreFn = kind === 'recents' ? this._showMoreRecents.bind(this) : this._showMoreSuggestions.bind(this); + const lastActive = (m) => kind === 'recents' ? m.lastActive : null; + const sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions"); + + if (!sourceMembers || sourceMembers.length === 0) return null; + + // If we're going to hide one member behind 'show more', just use up the space of the button + // with the member's tile instead. + if (showNum === sourceMembers.length - 1) showNum++; // .slice() will return an incomplete array but won't error on us if we go too far - const toRender = this.state.recents.slice(0, this.state.numRecentsShown); - const hasMore = toRender.length < this.state.recents.length; + const toRender = sourceMembers.slice(0, showNum); + const hasMore = toRender.length < sourceMembers.length; const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); let showMore = null; if (hasMore) { showMore = ( - + {_t("Show more")} ); } const tiles = toRender.map(r => ( - + )); return (

    -

    {_t("Recent Conversations")}

    +

    {sectionName}

    {tiles} {showMore}
    @@ -209,7 +275,8 @@ export default class DMInviteDialog extends React.PureComponent { {_t("Go")}
    - {this._renderRecents()} + {this._renderSection('recents')} + {this._renderSection('suggestions')}
  • ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cac3f2f619..4666a1fe9d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1431,8 +1431,9 @@ "View Servers in Room": "View Servers in Room", "Toolbox": "Toolbox", "Developer Tools": "Developer Tools", - "Show more": "Show more", "Recent Conversations": "Recent Conversations", + "Suggestions": "Suggestions", + "Show more": "Show more", "Direct Messages": "Direct Messages", "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.", "Go": "Go", From df25a988104a978b23cc3aa45ae739c97cfe31ef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 3 Jan 2020 20:35:30 -0700 Subject: [PATCH 060/101] Implement basic filtering for invite targets Part of https://github.com/vector-im/riot-web/issues/11200 --- res/css/views/dialogs/_DMInviteDialog.scss | 4 + src/HtmlUtils.js | 5 ++ .../views/dialogs/DMInviteDialog.js | 75 +++++++++++++++++-- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/res/css/views/dialogs/_DMInviteDialog.scss b/res/css/views/dialogs/_DMInviteDialog.scss index 1153ecb0d4..364c796f16 100644 --- a/res/css/views/dialogs/_DMInviteDialog.scss +++ b/res/css/views/dialogs/_DMInviteDialog.scss @@ -77,5 +77,9 @@ limitations under the License. float: right; line-height: 36px; // Height of the avatar to keep the time vertically aligned } + + .mx_DMInviteDialog_roomTile_highlight { + font-weight: 900; + } } diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 7cdff26a21..ce677e6c68 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -528,3 +528,8 @@ export function checkBlockNode(node) { return false; } } + +export function htmlEntitiesEncode(str: string) { + // Source: https://stackoverflow.com/a/18750001/7037379 + return str.replace(/[\u00A0-\u9999<>&]/gim, i => `&#${i.charCodeAt(0)};`); +} diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js index bdeae6bc3e..ba3221d632 100644 --- a/src/components/views/dialogs/DMInviteDialog.js +++ b/src/components/views/dialogs/DMInviteDialog.js @@ -24,6 +24,7 @@ import DMRoomMap from "../../../utils/DMRoomMap"; import {RoomMember} from "matrix-js-sdk/lib/matrix"; import * as humanize from "humanize"; import SdkConfig from "../../../SdkConfig"; +import {htmlEntitiesEncode} from "../../../HtmlUtils"; // TODO: [TravisR] Make this generic for all kinds of invites @@ -35,6 +36,7 @@ class DMRoomTile extends React.PureComponent { member: PropTypes.object.isRequired, lastActiveTs: PropTypes.number, onToggle: PropTypes.func.isRequired, + highlightWord: PropTypes.string, }; _onClick = (e) => { @@ -45,6 +47,44 @@ class DMRoomTile extends React.PureComponent { this.props.onToggle(this.props.member.userId); }; + _highlightName(str: string) { + if (!this.props.highlightWord) return str; + + // First encode the thing to avoid injection + str = htmlEntitiesEncode(str); + + // We convert things to lowercase for index searching, but pull substrings from + // the submitted text to preserve case. + const lowerStr = str.toLowerCase(); + const filterStr = this.props.highlightWord.toLowerCase(); + + const result = []; + + let i = 0; + let ii; + while ((ii = lowerStr.indexOf(filterStr, i)) >= 0) { + // Push any text we missed (first bit/middle of text) + if (ii > i) { + // Push any text we aren't highlighting (middle of text match) + result.push({str.substring(i, ii)}); + } + + i = ii; // copy over ii only if we have a match (to preserve i for end-of-text matching) + + // Highlight the word the user entered + const substr = str.substring(i, filterStr.length + i); + result.push({substr}); + i += substr.length; + } + + // Push any text we missed (end of text) + if (i < (str.length - 1)) { + result.push({str.substring(i)}); + } + + return result; + } + render() { const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); @@ -59,8 +99,8 @@ class DMRoomTile extends React.PureComponent { return (
    - {this.props.member.name} - {this.props.member.userId} + {this._highlightName(this.props.member.name)} + {this._highlightName(this.props.member.userId)} {timestamp}
    ); @@ -158,7 +198,7 @@ export default class DMInviteDialog extends React.PureComponent { } return b.score - a.score; }); - return members.map(m => ({userId: m.userId, user: m.member})); + return members.map(m => ({userId: m.member.userId, user: m.member})); } _startDm = () => { @@ -190,14 +230,32 @@ export default class DMInviteDialog extends React.PureComponent { }; _renderSection(kind: "recents"|"suggestions") { - const sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions; + let sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions; let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown; const showMoreFn = kind === 'recents' ? this._showMoreRecents.bind(this) : this._showMoreSuggestions.bind(this); const lastActive = (m) => kind === 'recents' ? m.lastActive : null; const sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions"); + // Hide the section if there's nothing to filter by if (!sourceMembers || sourceMembers.length === 0) return null; + // Do some simple filtering on the input before going much further. If we get no results, say so. + if (this.state.filterText) { + const filterBy = this.state.filterText.toLowerCase(); + sourceMembers = sourceMembers + .filter(m => m.user.name.toLowerCase().includes(filterBy) || m.userId.toLowerCase().includes(filterBy)); + + if (sourceMembers.length === 0) { + return ( +
    +

    {sectionName}

    +

    {_t("No results")}

    +
    + ); + } + } + + // If we're going to hide one member behind 'show more', just use up the space of the button // with the member's tile instead. if (showNum === sourceMembers.length - 1) showNum++; @@ -217,7 +275,13 @@ export default class DMInviteDialog extends React.PureComponent { } const tiles = toRender.map(r => ( - + )); return (
    @@ -241,7 +305,6 @@ export default class DMInviteDialog extends React.PureComponent { id="inviteTargets" value={this.state.filterText} onChange={this._updateFilter} - placeholder="TODO: Implement filtering/searching (vector-im/riot-web#11199)" />
    ); From 8b4c1e3dec79f699951d87fb11eb29541637c943 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 3 Jan 2020 21:17:48 -0700 Subject: [PATCH 061/101] Support searching in the user directory for invite targets Part of https://github.com/vector-im/riot-web/issues/11200 --- .../views/dialogs/DMInviteDialog.js | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js index ba3221d632..aec64919a0 100644 --- a/src/components/views/dialogs/DMInviteDialog.js +++ b/src/components/views/dialogs/DMInviteDialog.js @@ -25,14 +25,41 @@ import {RoomMember} from "matrix-js-sdk/lib/matrix"; import * as humanize from "humanize"; import SdkConfig from "../../../SdkConfig"; import {htmlEntitiesEncode} from "../../../HtmlUtils"; +import {getHttpUriForMxc} from "matrix-js-sdk/lib/content-repo"; // TODO: [TravisR] Make this generic for all kinds of invites const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked +class DirectoryMember { + _userId: string; + _displayName: string; + _avatarUrl: string; + + constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) { + this._userId = userDirResult.user_id; + this._displayName = userDirResult.display_name; + this._avatarUrl = userDirResult.avatar_url; + } + + // These next members are to implement the contract expected by DMRoomTile + get name(): string { + return this._displayName || this._userId; + } + + get userId(): string { + return this._userId; + } + + getMxcAvatarUrl(): string { + return this._avatarUrl; + } +} + class DMRoomTile extends React.PureComponent { static propTypes = { + // Has properties to match RoomMember: userId (str), name (str), getMxcAvatarUrl(): string member: PropTypes.object.isRequired, lastActiveTs: PropTypes.number, onToggle: PropTypes.func.isRequired, @@ -86,7 +113,7 @@ class DMRoomTile extends React.PureComponent { } render() { - const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); + const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar"); let timestamp = null; if (this.props.lastActiveTs) { @@ -96,9 +123,20 @@ class DMRoomTile extends React.PureComponent { timestamp = {humanTs}; } + const avatarSize = 36; + const avatarUrl = getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(), + avatarSize, avatarSize, "crop"); + return (
    - + {this._highlightName(this.props.member.name)} {this._highlightName(this.props.member.userId)} {timestamp} @@ -113,6 +151,8 @@ export default class DMInviteDialog extends React.PureComponent { onFinished: PropTypes.func.isRequired, }; + _debounceTimer: number = null; + constructor() { super(); @@ -123,6 +163,7 @@ export default class DMInviteDialog extends React.PureComponent { numRecentsShown: INITIAL_ROOMS_SHOWN, suggestions: this._buildSuggestions(), numSuggestionsShown: INITIAL_ROOMS_SHOWN, + serverResultsMixin: [], // { user: DirectoryMember, userId: string }[], like recents and suggestions }; } @@ -210,7 +251,35 @@ export default class DMInviteDialog extends React.PureComponent { }; _updateFilter = (e) => { - this.setState({filterText: e.target.value}); + const term = e.target.value; + this.setState({filterText: term}); + + // Debounce server lookups to reduce spam. We don't clear the existing server + // results because they might still be vaguely accurate, likewise for races which + // could happen here. + if (this._debounceTimer) { + clearTimeout(this._debounceTimer); + } + this._debounceTimer = setTimeout(() => { + MatrixClientPeg.get().searchUserDirectory({term}).then(r => { + if (term !== this.state.filterText) { + // Discard the results - we were probably too slow on the server-side to make + // these results useful. This is a race we want to avoid because we could overwrite + // more accurate results. + return; + } + this.setState({ + serverResultsMixin: r.results.map(u => ({ + userId: u.user_id, + user: new DirectoryMember(u), + })), + }); + }).catch(e => { + console.error("Error searching user directory:"); + console.error(e); + this.setState({serverResultsMixin: []}); // clear results because it's moderately fatal + }); + }, 150); // 150ms debounce (human reaction time + some) }; _showMoreRecents = () => { @@ -236,6 +305,15 @@ export default class DMInviteDialog extends React.PureComponent { const lastActive = (m) => kind === 'recents' ? m.lastActive : null; const sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions"); + // Mix in the server results if we have any, but only if we're searching + if (this.state.filterText && this.state.serverResultsMixin && kind === 'suggestions') { + // only pick out the server results that aren't already covered though + const uniqueServerResults = this.state.serverResultsMixin + .filter(u => !sourceMembers.some(m => m.userId === u.userId)); + + sourceMembers = sourceMembers.concat(uniqueServerResults); + } + // Hide the section if there's nothing to filter by if (!sourceMembers || sourceMembers.length === 0) return null; From 295dcbfe487ebb9bb4928d029a052f28cb6a079c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 4 Jan 2020 13:34:05 +0000 Subject: [PATCH 062/101] Fix ability to remove avatars Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/room_settings/RoomProfileSettings.js | 2 ++ src/components/views/settings/ProfileSettings.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/components/views/room_settings/RoomProfileSettings.js b/src/components/views/room_settings/RoomProfileSettings.js index 2093a31a28..abcdce23f8 100644 --- a/src/components/views/room_settings/RoomProfileSettings.js +++ b/src/components/views/room_settings/RoomProfileSettings.js @@ -98,6 +98,8 @@ export default class RoomProfileSettings extends React.Component { newState.avatarUrl = client.mxcUrlToHttp(uri, 96, 96, 'crop', false); newState.originalAvatarUrl = newState.avatarUrl; newState.avatarFile = null; + } else if (this.state.originalAvatarUrl !== this.state.avatarUrl) { + await client.sendStateEvent(this.props.roomId, 'm.room.avatar', {url: undefined}, ''); } if (this.state.originalTopic !== this.state.topic) { diff --git a/src/components/views/settings/ProfileSettings.js b/src/components/views/settings/ProfileSettings.js index 480b414911..72ff7e3d15 100644 --- a/src/components/views/settings/ProfileSettings.js +++ b/src/components/views/settings/ProfileSettings.js @@ -88,6 +88,8 @@ export default class ProfileSettings extends React.Component { newState.avatarUrl = client.mxcUrlToHttp(uri, 96, 96, 'crop', false); newState.originalAvatarUrl = newState.avatarUrl; newState.avatarFile = null; + } else if (this.state.originalAvatarUrl !== this.state.avatarUrl) { + await client.setAvatarUrl(""); // use empty string as Synapse 500's on undefined } this.setState(newState); From 2ccc8caa6974ed3787436815aea95e5fc38a5205 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 5 Jan 2020 15:50:06 +0000 Subject: [PATCH 063/101] Fix indent --- .../settings/tabs/room/BridgeSettingsTab.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index c3c85ec31c..5536a2eb06 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -88,12 +88,12 @@ export default class BridgeSettingsTab extends React.Component { network.avatar, 32, 32, "crop", ); networkIcon = ; + width={32} + height={32} + resizeMethod='crop' + name={ networkName } + idName={ networkName } + url={ avatarUrl } />; } let channelIcon = null; @@ -103,12 +103,12 @@ export default class BridgeSettingsTab extends React.Component { channel.avatar, 32, 32, "crop", ); channelIcon = ; + width={32} + height={32} + resizeMethod='crop' + name={ networkName } + idName={ networkName } + url={ avatarUrl } />; } const heading = _t("Connected to on ", { }, { From 814c0aa4c2469fa71c2372609d85c4185a4320ca Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Jan 2020 20:52:54 +0000 Subject: [PATCH 064/101] Send enabled_labs over rageshake as comma delimited list Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/rageshake/submit-rageshake.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rageshake/submit-rageshake.js b/src/rageshake/submit-rageshake.js index 457958eb82..44f1039016 100644 --- a/src/rageshake/submit-rageshake.js +++ b/src/rageshake/submit-rageshake.js @@ -27,6 +27,7 @@ import rageshake from './rageshake'; // polyfill textencoder if necessary import * as TextEncodingUtf8 from 'text-encoding-utf-8'; +import SettingsStore from "../settings/SettingsStore"; let TextEncoder = window.TextEncoder; if (!TextEncoder) { TextEncoder = TextEncodingUtf8.TextEncoder; @@ -85,6 +86,12 @@ export default async function sendBugReport(bugReportEndpoint, opts) { body.append('label', opts.label); } + // add labs options + const enabledLabs = SettingsStore.getLabsFeatures().filter(SettingsStore.isFeatureEnabled); + if (enabledLabs.length) { + body.append('enabled_labs', enabledLabs.join(', ')); + } + if (opts.sendLogs) { progressCallback(_t("Collecting logs")); const logs = await rageshake.getLogsForReport(); From cf071c5ac659898f19865411bfb3e9c874bc2da8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Jan 2020 21:23:34 +0000 Subject: [PATCH 065/101] Deduplicate recent emoji Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/emojipicker/EmojiPicker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/emojipicker/EmojiPicker.js b/src/components/views/emojipicker/EmojiPicker.js index 4d49b25100..0325dfd807 100644 --- a/src/components/views/emojipicker/EmojiPicker.js +++ b/src/components/views/emojipicker/EmojiPicker.js @@ -47,10 +47,10 @@ class EmojiPicker extends React.Component { viewportHeight: 280, }; - // Convert recent emoji characters to emoji data, removing unknowns. - this.recentlyUsed = recent.get() + // Convert recent emoji characters to emoji data, removing unknowns and duplicates + this.recentlyUsed = Array.from(new Set(recent.get() .map(unicode => getEmojiFromUnicode(unicode)) - .filter(data => !!data); + .filter(data => !!data))); this.memoizedDataByCategory = { recent: this.recentlyUsed, ...DATA_BY_CATEGORY, From ba2f4aa973db060afd62807b837e9ee266b245ab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Jan 2020 21:26:32 +0000 Subject: [PATCH 066/101] tidy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/emojipicker/EmojiPicker.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/views/emojipicker/EmojiPicker.js b/src/components/views/emojipicker/EmojiPicker.js index 0325dfd807..a9cf2a4732 100644 --- a/src/components/views/emojipicker/EmojiPicker.js +++ b/src/components/views/emojipicker/EmojiPicker.js @@ -48,9 +48,7 @@ class EmojiPicker extends React.Component { }; // Convert recent emoji characters to emoji data, removing unknowns and duplicates - this.recentlyUsed = Array.from(new Set(recent.get() - .map(unicode => getEmojiFromUnicode(unicode)) - .filter(data => !!data))); + this.recentlyUsed = Array.from(new Set(recent.get().map(getEmojiFromUnicode).filter(Boolean))); this.memoizedDataByCategory = { recent: this.recentlyUsed, ...DATA_BY_CATEGORY, From c2c4426396312670617632a21f62eb8620c0ebd6 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sat, 4 Jan 2020 10:32:00 +0000 Subject: [PATCH 067/101] Translated using Weblate (Albanian) Currently translated at 99.1% (1988 of 2006 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 4d67df4fa4..15576d8d9a 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -1964,5 +1964,50 @@ "Set up secret storage": "Ujdisni depozitë të fshehtë", "Secure your encrypted messages with a passphrase": "Sigurojini mesazhet tuaj të fshehtëzuar me një frazëkalim", "Storing secrets...": "Po depozitohen të fshehta…", - "Unable to set up secret storage": "S’u arrit të ujdiset depozitë e fshehtë" + "Unable to set up secret storage": "S’u arrit të ujdiset depozitë e fshehtë", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s hoqi rregullin për dëbim përdoruesish që kanë përputhje me %(glob)s", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s hoqi rregullin që dëbon dhoma që kanë përputhje me %(glob)s", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s hoqi rregullin që dëbon shërbyes që kanë përputhje me %(glob)s", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s hoqi një rregull dëbimi mbi përputhje me %(glob)s", + "%(senderName)s updated an invalid ban rule": "%(senderName)s përditësoi një rregull të pavlefshëm dëbimesh", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s përditësoi rregullin mbi dëbim përdoruesish që kanë përputhje me %(glob)s për %(reason)s", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s përditësoi rregullin për dëbim dhomash që kanë përputhje me %(glob)s për %(reason)s", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s përditësoi rregullin për dëbim shërbyesish që kanë përputhje me %(glob)s për %(reason)s", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s përditësoi një rregull dëbimi rreth përputhjesh me %(glob)s për %(reason)s", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull mbi dëbim përdoruesish që kanë përputhje me %(glob)s për %(reason)s", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull mbi dëbim dhomash që kanë përputhje me %(glob)s për %(reason)s", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull mbi dëbim shërbyesish që kanë përputhje me %(glob)s për %(reason)s", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s krijoi një rregull dëbimi rreth përputhjesh me %(glob)s për %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ndryshoi një rregull që dëbonte përdorues me përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ndryshoi një rregull që dëbonte dhoma me përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ndryshoi një rregull që dëbonte shërbyes me përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s përditësoi një rregull dëbimesh mbi përputhje me %(oldGlob)s për përputhje me %(newGlob)s për %(reason)s", + "The message you are trying to send is too large.": "Mesazhi që po rrekeni të dërgoni është shumë i madh.", + "New DM invite dialog (under development)": "Dialog ftese të re për MD (në zhvillim)", + "not stored": "e padepozituar", + "Backup has a valid signature from this user": "Kopjeruajtja ka një nënshkrim të vlefshëm prej këtij përdoruesi", + "Backup has a invalid signature from this user": "Kopjeruajtja ka një nënshkrim të pavlefshëm prej këtij përdoruesi", + "Backup has a signature from unknown user with ID %(deviceId)s": "Kopjeruajtja ka një nënshkrim nga një përdorues i panjohur me ID %(deviceId)s", + "Backup has a signature from unknown device with ID %(deviceId)s": "Kopjeruajtja ka një nënshkrim nga një pajisje e panjohur me ID %(deviceId)s", + "Backup key stored: ": "Kyç kopjeruajtjeje i depozituar: ", + "Start using Key Backup with Secure Secret Storage": "Fillo të përdorësh Kopejruajtje Kyçesh me Depozitim Kyçi të Fshehtë", + "This user has not verified all of their devices.": "Ky përdorues s’ka verifikuar krejt pajisjet e tij.", + "You have not verified this user. This user has verified all of their devices.": "S’e keni verifikuar këtë përdorues. Ky përdorues ka verifikuar krejt pajisjet e veta.", + "You have verified this user. This user has verified all of their devices.": "E keni verifikuar këtë përdorues. Ky përdorues ka verifikuar krejt pajisjet e veta.", + "Some users in this encrypted room are not verified by you or they have not verified their own devices.": "Disa përdorues në këtë dhomë të fshehtëzuar nuk janë verifikuar nga ju ose nuk kanë verifikuar pajisjet e tyre.", + "All users in this encrypted room are verified by you and they have verified their own devices.": "Krejt përdorues në këtë dhomë të fshehtëzuar janë verifikuar nga ju dhe kanë verifikuar pajisjet e tyre.", + "Close preview": "Mbylle paraparjen", + "Hide verified sessions": "Fshih sesione të verifikuar", + "%(count)s verified sessions|other": "%(count)s sesione të verifikuar", + "%(count)s verified sessions|one": "1 sesion i verifikuar", + "Language Dropdown": "Menu Hapmbyll Gjuhësh", + "Show more": "Shfaq më tepër", + "Recent Conversations": "Biseda Së Fundi", + "Direct Messages": "Mesazhe të Drejtpërdrejtë", + "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Nëse s’gjeni dot dikë, kërkojuni emrin e përdoruesit, ose jepuni emrin tuaj të përdoruesit (%(userId)s) ose profile link.", + "Go": "Shko", + "Help": "Ndihmë", + "Country Dropdown": "Menu Hapmbyll Vendesh", + "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Depozita e Fshehtë do të ujdiset duke përdorur hollësitë tuaja ekzistuese për kopjeruajtje kyçesh.Frazëkalimi juaj për në depozitën e fshehtë dhe kyçi i rimarrjes do të jenë të njëjtë me ata për kopjeruajtjen tuaj të kyçeve", + "Migrate from Key Backup": "Migroji prej Kopjeruajtje Kyçesh" } From b8f1ca679b744d6be4373e0b02e04020d6697858 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Sat, 4 Jan 2020 13:20:53 +0000 Subject: [PATCH 068/101] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2006 of 2006 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index e6ce4077e5..b755e16735 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2019,5 +2019,11 @@ "The message you are trying to send is too large.": "您正試圖傳送的訊息太大了。", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "秘密儲存空間將使用你既有的金鑰備份資訊來設定。您的秘密儲存空間密碼與復原金鑰會與您的金鑰備份相同", "Migrate from Key Backup": "從金鑰備份導入", - "Help": "說明" + "Help": "說明", + "New DM invite dialog (under development)": "新的直接訊息邀請對話框(開發中)", + "Show more": "顯示更多", + "Recent Conversations": "最近的對話", + "Direct Messages": "直接訊息", + "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "如果您找不到某人,請向他們詢問他們的使用者名稱,或是分享您的使用者名稱 (%(userId)s) 或簡介連結。", + "Go": "到" } From e6c80160861b9a33d0f217be048c37cb8e50be57 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Sat, 4 Jan 2020 16:49:31 +0000 Subject: [PATCH 069/101] Translated using Weblate (Finnish) Currently translated at 100.0% (2006 of 2006 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 8881456d8c..e53afb2598 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -2019,5 +2019,11 @@ "Migrate from Key Backup": "Siirrä tiedot vanhasta avainten varmuuskopiointijärjestelmästä", "Secure your encrypted messages with a passphrase": "Turvaa salatut viestisi salalauseella", "Storing secrets...": "Tallennetaan salaisuuksia...", - "Unable to set up secret storage": "Salavaraston käyttöönotto epäonnistui" + "Unable to set up secret storage": "Salavaraston käyttöönotto epäonnistui", + "New DM invite dialog (under development)": "Uusi dialogi kutsuille yksityiskeskusteluun (kehitysversio)", + "Show more": "Näytä lisää", + "Recent Conversations": "Viimeaikaiset keskustelut", + "Direct Messages": "Yksityisviestit", + "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Jos et löydä jotakuta, kysy hänen käyttäjätunnusta, tai anna oma käyttäjätunnuksesi (%(userId)s) tai linkin profiiliisi hänelle.", + "Go": "Mene" } From d1405233bd82a67beaae0f04a6c0669cce4fcbe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Sat, 4 Jan 2020 08:16:36 +0000 Subject: [PATCH 070/101] Translated using Weblate (French) Currently translated at 100.0% (2006 of 2006 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 727d171ca4..a44df701f9 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2019,5 +2019,11 @@ "The message you are trying to send is too large.": "Le message que vous essayez d’envoyer est trop gros.", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Le coffre secret sera configuré en utilisant vos informations de sauvegarde de clés existantes. Votre phrase de passe et votre clé de récupération seront les mêmes que pour la sauvegarde de clés", "Migrate from Key Backup": "Migrer depuis la sauvegarde de clés", - "Help": "Aide" + "Help": "Aide", + "New DM invite dialog (under development)": "Nouveau dialogue d’invitation aux MD (en développement)", + "Show more": "En savoir plus", + "Recent Conversations": "Conversations récentes", + "Direct Messages": "Messages directs", + "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Si vous n’arrivez pas à trouver quelqu’un, demandez-lui son nom d’utilisateur ou partagez votre nom d’utilisateur (%(userId)s) ou votre lien de profil.", + "Go": "C’est parti" } From 8128b97f3571ce2a19fca74e1293cbbf82956db4 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sun, 5 Jan 2020 21:12:03 +0000 Subject: [PATCH 071/101] Translated using Weblate (Hungarian) Currently translated at 100.0% (2006 of 2006 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 2f31f75407..df482cd072 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2020,5 +2020,11 @@ "The message you are trying to send is too large.": "Túl nagy képet próbálsz elküldeni.", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "A Biztonsági Tároló a már létező kulcs mentés adatai felhasználásával lesz létrehozva. A biztonsági tároló jelmondata és a visszaállítási kulcs meg fog egyezni a kulcs mentésénél használttal", "Migrate from Key Backup": "Mozgatás a Kulcs Mentésből", - "Help": "Segítség" + "Help": "Segítség", + "New DM invite dialog (under development)": "Új KB (DM) meghívó párbeszédablak (fejlesztés alatt)", + "Show more": "Mutass többet", + "Recent Conversations": "Legújabb Beszélgetések", + "Direct Messages": "Közvetlen Beszélgetések", + "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Ha nem találsz meg valakit, kérdezd meg a felhasználói nevét vagy oszd meg a te felhasználói nevedet (%(userId)s) vagy a profil hivatkozást.", + "Go": "Menj" } From a231419a775350df8628f4b1885e01b61417dfc2 Mon Sep 17 00:00:00 2001 From: catborise Date: Sat, 4 Jan 2020 09:27:22 +0000 Subject: [PATCH 072/101] Translated using Weblate (Turkish) Currently translated at 53.2% (1067 of 2006 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 52 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index a3a178c358..159cb61965 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -1021,5 +1021,55 @@ "Profile picture": "Profil resmi", "Could not connect to Identity Server": "Kimlik Sunucusuna bağlanılamadı", "Checking server": "Sunucu kontrol ediliyor", - "Change identity server": "Kimlik sunucu değiştir" + "Change identity server": "Kimlik sunucu değiştir", + "Sorry, your homeserver is too old to participate in this room.": "Üzgünüm, ana sunucunuz bu odaya katılabilmek için oldukça eski.", + "Please contact your homeserver administrator.": "Lütfen anasunucu yöneticiniz ile bağlantıya geçin.", + "Message Pinning": "Mesaj Sabitleme", + "Multiple integration managers": "Çoklu entegrasyon yöneticileri", + "New DM invite dialog (under development)": "Yeni DM davet diyalog kutusu (geliştirilmekte)", + "Enable Emoji suggestions while typing": "Yazarken Emoji önerilerini aç", + "Show join/leave messages (invites/kicks/bans unaffected)": "Katılma/ayrılma mesajları göster (davetler/atılmalar/yasaklamalar etkilenmeyecek)", + "Show avatar changes": "Avatar değişikliklerini göster", + "Always show encryption icons": "Şifreleme ikonlarını her zaman göster", + "Enable big emoji in chat": "Sohbette büyük emojileri aç", + "Send typing notifications": "Yazma bildirimlerini gönder", + "Send analytics data": "Analiz verilerini gönder", + "Show developer tools": "Geliştirici araçlarını göster", + "Low bandwidth mode": "Düşük bant genişliği modu", + "Messages containing my username": "Kullanıcı adımı içeren mesajlar", + "When rooms are upgraded": "Odaların güncellenme zamanları", + "My Ban List": "Yasaklı Listem", + "Verified!": "Doğrulandı!", + "You've successfully verified this user.": "Bu kullanıcıyı başarılı şekilde doğruladınız.", + "Unable to find a supported verification method.": "Desteklenen doğrulama yöntemi bulunamadı.", + "Dog": "Köpek", + "Cat": "Kedi", + "Lion": "Aslan", + "Horse": "At", + "Unicorn": "Tek boynuzlu at", + "Pig": "Domuz", + "Elephant": "Fil", + "Rabbit": "Tavşan", + "Panda": "Panda", + "Penguin": "Penguen", + "Turtle": "Kaplumbağa", + "Fish": "Balık", + "Octopus": "Ahtapot", + "Butterfly": "Kelebek", + "Flower": "Çiçek", + "Tree": "Ağaç", + "Cactus": "Kaktüs", + "Mushroom": "Mantar", + "Globe": "Dünya", + "Moon": "Ay", + "Cloud": "Bulut", + "Fire": "Ateş", + "Banana": "Muz", + "Apple": "Elma", + "Strawberry": "Çilek", + "Corn": "Mısır", + "Pizza": "Pizza", + "Cake": "Kek", + "Heart": "Kalp", + "Trophy": "Ödül" } From b6098ddb47e138a3aca2a37d444788db08afa0dc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Jan 2020 21:58:36 +0000 Subject: [PATCH 073/101] Delegate all room alias validation to the RoomAliasField validator Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/room_settings/AliasSettings.js | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index aa9d46c9d5..8e75680a89 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -17,7 +17,7 @@ limitations under the License. import EditableItemList from "../elements/EditableItemList"; -const React = require('react'); +import React, {createRef} from 'react'; import PropTypes from 'prop-types'; const MatrixClientPeg = require('../../../MatrixClientPeg'); const sdk = require("../../../index"); @@ -28,22 +28,41 @@ import AccessibleButton from "../elements/AccessibleButton"; const Modal = require("../../../Modal"); class EditableAliasesList extends EditableItemList { + constructor(props) { + super(props); + + this._aliasField = createRef(); + } + + _onAliasAdded = async () => { + await this._aliasField.current.validate({ allowEmpty: false }); + + if (this._aliasField.current.isValid) { + if (this.props.onItemAdded) this.props.onItemAdded(this.props.newItem); + return; + } + + this._aliasField.current.focus(); + this._aliasField.current.validate({ allowEmpty: false, focused: true }); + }; + _renderNewItemField() { const RoomAliasField = sdk.getComponent('views.elements.RoomAliasField'); const onChange = (alias) => this._onNewItemChanged({target: {value: alias}}); return (
    - + { _t("Add") } @@ -99,11 +118,6 @@ export default class AliasSettings extends React.Component { return dict; } - isAliasValid(alias) { - // XXX: FIXME https://github.com/matrix-org/matrix-doc/issues/668 - return (alias.match(/^#([^/:,]+?):(.+)$/) && encodeURI(alias) === alias); - } - changeCanonicalAlias(alias) { if (!this.props.canSetCanonicalAlias) return; @@ -139,38 +153,31 @@ export default class AliasSettings extends React.Component { const localDomain = MatrixClientPeg.get().getDomain(); if (!alias.includes(':')) alias += ':' + localDomain; - if (this.isAliasValid(alias) && alias.endsWith(localDomain)) { - MatrixClientPeg.get().createAlias(alias, this.props.roomId).then(() => { - const localAliases = this.state.domainToAliases[localDomain] || []; - const domainAliases = Object.assign({}, this.state.domainToAliases); - domainAliases[localDomain] = [...localAliases, alias]; - this.setState({ - domainToAliases: domainAliases, - // Reset the add field - newAlias: "", - }); + MatrixClientPeg.get().createAlias(alias, this.props.roomId).then(() => { + const localAliases = this.state.domainToAliases[localDomain] || []; + const domainAliases = Object.assign({}, this.state.domainToAliases); + domainAliases[localDomain] = [...localAliases, alias]; - if (!this.state.canonicalAlias) { - this.changeCanonicalAlias(alias); - } - }).catch((err) => { - console.error(err); - Modal.createTrackedDialog('Error creating alias', '', ErrorDialog, { - title: _t("Error creating alias"), - description: _t( - "There was an error creating that alias. It may not be allowed by the server " + - "or a temporary failure occurred.", - ), - }); + this.setState({ + domainToAliases: domainAliases, + // Reset the add field + newAlias: "", }); - } else { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Invalid alias format', '', ErrorDialog, { - title: _t('Invalid alias format'), - description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }), + + if (!this.state.canonicalAlias) { + this.changeCanonicalAlias(alias); + } + }).catch((err) => { + console.error(err); + Modal.createTrackedDialog('Error creating alias', '', ErrorDialog, { + title: _t("Error creating alias"), + description: _t( + "There was an error creating that alias. It may not be allowed by the server " + + "or a temporary failure occurred.", + ), }); - } + }); }; onLocalAliasDeleted = (index) => { From 4a836349ab5dbfc0c83f7e7983a64ced93604594 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Jan 2020 22:01:59 +0000 Subject: [PATCH 074/101] regenerate i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cac3f2f619..9cae17157a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1081,8 +1081,6 @@ "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.", "Error creating alias": "Error creating alias", "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.", - "Invalid alias format": "Invalid alias format", - "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", "Error removing alias": "Error removing alias", "There was an error removing that alias. It may no longer exist or a temporary error occurred.": "There was an error removing that alias. It may no longer exist or a temporary error occurred.", "Main address": "Main address", From 378a82e6fb760657b5f77c1d13cad81c298af0da Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Jan 2020 22:22:09 +0000 Subject: [PATCH 075/101] Use html-entities instead Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 3 ++- src/components/views/rooms/LinkPreviewWidget.js | 10 ++++++---- yarn.lock | 5 +++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7ef14e6635..7e2c242b3a 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "file-saver": "^1.3.3", "filesize": "3.5.6", "flux": "2.1.1", - "react-focus-lock": "^2.2.1", "focus-visible": "^5.0.2", "fuse.js": "^2.2.0", "gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566", @@ -82,6 +81,7 @@ "glob": "^5.0.14", "glob-to-regexp": "^0.4.1", "highlight.js": "^9.15.8", + "html-entities": "^1.2.1", "is-ip": "^2.0.0", "isomorphic-fetch": "^2.2.1", "linkifyjs": "^2.1.6", @@ -99,6 +99,7 @@ "react-addons-css-transition-group": "15.6.2", "react-beautiful-dnd": "^4.0.1", "react-dom": "^16.9.0", + "react-focus-lock": "^2.2.1", "react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594", "resize-observer-polyfill": "^1.5.0", "sanitize-html": "^1.18.4", diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js index 06c0201af8..4822848233 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -18,7 +18,9 @@ limitations under the License. import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import { linkifyElement } from '../../../HtmlUtils'; +import { AllHtmlEntities } from 'html-entities'; + +import {linkifyElement} from '../../../HtmlUtils'; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from "../../../languageHandler"; @@ -128,15 +130,15 @@ module.exports = createReactClass({ } const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - // Escape to prevent any HTML injections, we can't replace & as the description may contain & encoded html entities - const safeDescription = (p["og:description"] || "").replace("<", "<").replace(">", ">"); return (
    { img }
    { p["og:site_name"] ? (" - " + p["og:site_name"]) : null }
    -
    +
    + { AllHtmlEntities.decode(p["og:description"] || "") } +
    Date: Sun, 5 Jan 2020 23:32:49 +0000 Subject: [PATCH 076/101] Update BridgeSettingsTab.js --- .../settings/tabs/room/BridgeSettingsTab.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.js b/src/components/views/settings/tabs/room/BridgeSettingsTab.js index 5536a2eb06..3022885701 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.js +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.js @@ -88,12 +88,13 @@ export default class BridgeSettingsTab extends React.Component { network.avatar, 32, 32, "crop", ); networkIcon = ; + width={32} + height={32} + resizeMethod='crop' + name={ networkName } + idName={ networkName } + url={ avatarUrl } + />; } let channelIcon = null; @@ -103,12 +104,13 @@ export default class BridgeSettingsTab extends React.Component { channel.avatar, 32, 32, "crop", ); channelIcon = ; + width={32} + height={32} + resizeMethod='crop' + name={ networkName } + idName={ networkName } + url={ avatarUrl } + />; } const heading = _t("Connected to on ", { }, { From dcdf68d7e1cb9f9eb4f18c627fe0029d7c57db67 Mon Sep 17 00:00:00 2001 From: Justin Sleep Date: Sun, 5 Jan 2020 17:35:05 -0600 Subject: [PATCH 077/101] Explicitly define diff colors in light theme Signed-off-by: Justin Sleep --- res/themes/dark/css/_dark.scss | 2 +- res/themes/light/css/_light.scss | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index d1d0e333a0..212513347d 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -244,7 +244,7 @@ $breadcrumb-placeholder-bg-color: #272c35; } } -// Fixes diff color inversion by swapping add / del colors +// diff highlight colors .hljs-addition { background: #fdd; } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 0a3ef812b8..f4fc459596 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -338,3 +338,12 @@ $breadcrumb-placeholder-bg-color: #e8eef5; color: $accent-color; text-decoration: none; } + +// diff highlight colors +.hljs-addition { + background: #dfd; +} + +.hljs-deletion { + background: #fdd; +} From 8ec8a7c01534818178259b0708681e9b3f43b50a Mon Sep 17 00:00:00 2001 From: Justin Sleep Date: Sun, 5 Jan 2020 17:58:07 -0600 Subject: [PATCH 078/101] Comment rationale for swapping dark theme colors Signed-off-by: Justin Sleep --- res/themes/dark/css/_dark.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 212513347d..0c47943301 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -245,6 +245,7 @@ $breadcrumb-placeholder-bg-color: #272c35; } // diff highlight colors +// intentionally swapped to avoid inversion .hljs-addition { background: #fdd; } From d31c863563e8d33e99a8890503968a308210f96f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 00:13:16 +0000 Subject: [PATCH 079/101] Remove all usages of slate in favour of CIDER Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintignore.errorfiles | 1 - package.json | 4 - src/SlateComposerHistoryManager.js | 86 - src/autocomplete/PlainWithPillsSerializer.js | 92 - src/components/structures/LoggedInView.js | 7 - src/components/structures/RoomView.js | 43 +- .../views/rooms/MessageComposerInput.js | 1516 ----------------- .../views/rooms/SlateMessageComposer.js | 485 ------ .../tabs/user/PreferencesUserSettingsTab.js | 1 - src/i18n/strings/en_EN.json | 21 +- src/settings/Settings.js | 5 - src/stores/MessageComposerStore.js | 66 - .../views/rooms/MessageComposerInput-test.js | 2 +- yarn.lock | 151 +- 14 files changed, 15 insertions(+), 2465 deletions(-) delete mode 100644 src/SlateComposerHistoryManager.js delete mode 100644 src/autocomplete/PlainWithPillsSerializer.js delete mode 100644 src/components/views/rooms/MessageComposerInput.js delete mode 100644 src/components/views/rooms/SlateMessageComposer.js delete mode 100644 src/stores/MessageComposerStore.js diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 7d998f8c4b..c3bb34ae26 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -33,7 +33,6 @@ src/components/views/rooms/RoomList.js src/components/views/rooms/RoomPreviewBar.js src/components/views/rooms/SearchBar.js src/components/views/rooms/SearchResultTile.js -src/components/views/rooms/SlateMessageComposer.js src/components/views/settings/ChangeAvatar.js src/components/views/settings/ChangePassword.js src/components/views/settings/DevicesPanel.js diff --git a/package.json b/package.json index a1ebc6602d..ad446e26cc 100644 --- a/package.json +++ b/package.json @@ -103,10 +103,6 @@ "react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594", "resize-observer-polyfill": "^1.5.0", "sanitize-html": "^1.18.4", - "slate": "^0.41.2", - "slate-html-serializer": "^0.6.1", - "slate-md-serializer": "github:matrix-org/slate-md-serializer#f7c4ad3", - "slate-react": "^0.18.10", "text-encoding-utf-8": "^1.0.1", "url": "^0.11.0", "velocity-animate": "^1.5.2", diff --git a/src/SlateComposerHistoryManager.js b/src/SlateComposerHistoryManager.js deleted file mode 100644 index 948dcf64ff..0000000000 --- a/src/SlateComposerHistoryManager.js +++ /dev/null @@ -1,86 +0,0 @@ -//@flow -/* -Copyright 2017 Aviral Dasgupta - -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 {Value} from 'slate'; - -import _clamp from 'lodash/clamp'; - -type MessageFormat = 'rich' | 'markdown'; - -class HistoryItem { - // We store history items in their native format to ensure history is accurate - // and then convert them if our RTE has subsequently changed format. - value: Value; - format: MessageFormat = 'rich'; - - constructor(value: ?Value, format: ?MessageFormat) { - this.value = value; - this.format = format; - } - - static fromJSON(obj: Object): HistoryItem { - return new HistoryItem( - Value.fromJSON(obj.value), - obj.format, - ); - } - - toJSON(): Object { - return { - value: this.value.toJSON(), - format: this.format, - }; - } -} - -export default class SlateComposerHistoryManager { - history: Array = []; - prefix: string; - lastIndex: number = 0; // used for indexing the storage - currentIndex: number = 0; // used for indexing the loaded validated history Array - - constructor(roomId: string, prefix: string = 'mx_composer_history_') { - this.prefix = prefix + roomId; - - // TODO: Performance issues? - let item; - for (; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) { - try { - this.history.push( - HistoryItem.fromJSON(JSON.parse(item)), - ); - } catch (e) { - console.warn("Throwing away unserialisable history", e); - } - } - this.lastIndex = this.currentIndex; - // reset currentIndex to account for any unserialisable history - this.currentIndex = this.history.length; - } - - save(value: Value, format: MessageFormat) { - const item = new HistoryItem(value, format); - this.history.push(item); - this.currentIndex = this.history.length; - sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item.toJSON())); - } - - getItem(offset: number): ?HistoryItem { - this.currentIndex = _clamp(this.currentIndex + offset, 0, this.history.length - 1); - return this.history[this.currentIndex]; - } -} diff --git a/src/autocomplete/PlainWithPillsSerializer.js b/src/autocomplete/PlainWithPillsSerializer.js deleted file mode 100644 index 09bb3772ac..0000000000 --- a/src/autocomplete/PlainWithPillsSerializer.js +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Based originally on slate-plain-serializer - -import { Block } from 'slate'; - -/** - * Plain text serializer, which converts a Slate `value` to a plain text string, - * serializing pills into various different formats as required. - * - * @type {PlainWithPillsSerializer} - */ - -class PlainWithPillsSerializer { - /* - * @param {String} options.pillFormat - either 'md', 'plain', 'id' - */ - constructor(options = {}) { - const { - pillFormat = 'plain', - } = options; - this.pillFormat = pillFormat; - } - - /** - * Serialize a Slate `value` to a plain text string, - * serializing pills as either MD links, plain text representations or - * ID representations as required. - * - * @param {Value} value - * @return {String} - */ - serialize = value => { - return this._serializeNode(value.document); - } - - /** - * Serialize a `node` to plain text. - * - * @param {Node} node - * @return {String} - */ - _serializeNode = node => { - if ( - node.object == 'document' || - (node.object == 'block' && Block.isBlockList(node.nodes)) - ) { - return node.nodes.map(this._serializeNode).join('\n'); - } else if (node.type == 'emoji') { - return node.data.get('emojiUnicode'); - } else if (node.type == 'pill') { - const completion = node.data.get('completion'); - // over the wire the @room pill is just plaintext - if (completion === '@room') return completion; - - switch (this.pillFormat) { - case 'plain': - return completion; - case 'md': - return `[${ completion }](${ node.data.get('href') })`; - case 'id': - return node.data.get('completionId') || completion; - } - } else if (node.nodes) { - return node.nodes.map(this._serializeNode).join(''); - } else { - return node.text; - } - } -} - -/** - * Export. - * - * @type {PlainWithPillsSerializer} - */ - -export default PlainWithPillsSerializer; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 7261af3bf0..3420e69ce6 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -393,13 +393,6 @@ const LoggedInView = createReactClass({ return; } - // XXX: Remove after CIDER replaces Slate completely: https://github.com/vector-im/riot-web/issues/11036 - // If using Slate, consume the Backspace without first focusing as it causes an implosion - if (ev.key === Key.BACKSPACE && !SettingsStore.getValue("useCiderComposer")) { - ev.stopPropagation(); - return; - } - if (!isClickShortcut && ev.key !== Key.TAB && !canElementReceiveInput(ev.target)) { // synchronous dispatch so we focus before key generates input dis.dispatch({action: 'focus_composer'}, true); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 939f422a36..c552e2f8f5 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -157,8 +157,6 @@ module.exports = createReactClass({ canReact: false, canReply: false, - - useCider: false, }; }, @@ -180,18 +178,10 @@ module.exports = createReactClass({ WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate); - this._onCiderUpdated(); - this._ciderWatcherRef = SettingsStore.watchSetting( - "useCiderComposer", null, this._onCiderUpdated); - this._roomView = createRef(); this._searchResultsPanel = createRef(); }, - _onCiderUpdated: function() { - this.setState({useCider: SettingsStore.getValue("useCiderComposer")}); - }, - _onRoomViewStoreUpdate: function(initial) { if (this.unmounted) { return; @@ -1806,29 +1796,16 @@ module.exports = createReactClass({ myMembership === 'join' && !this.state.searchResults ); if (canSpeak) { - if (this.state.useCider) { - const MessageComposer = sdk.getComponent('rooms.MessageComposer'); - messageComposer = - ; - } else { - const SlateMessageComposer = sdk.getComponent('rooms.SlateMessageComposer'); - messageComposer = - ; - } + const MessageComposer = sdk.getComponent('rooms.MessageComposer'); + messageComposer = + ; } // TODO: Why aren't we storing the term/scope/count in this format diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js deleted file mode 100644 index 595ec0681f..0000000000 --- a/src/components/views/rooms/MessageComposerInput.js +++ /dev/null @@ -1,1516 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017, 2018 New Vector Ltd -Copyright 2019 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 { Editor } from 'slate-react'; -import { getEventTransfer } from 'slate-react'; -import { Value, Block, Inline, Range } from 'slate'; -import type { Change } from 'slate'; - -import Html from 'slate-html-serializer'; -import Md from 'slate-md-serializer'; -import Plain from 'slate-plain-serializer'; -import PlainWithPillsSerializer from "../../../autocomplete/PlainWithPillsSerializer"; - -import classNames from 'classnames'; - -import MatrixClientPeg from '../../../MatrixClientPeg'; -import type {MatrixClient} from 'matrix-js-sdk/lib/matrix'; -import {processCommandInput} from '../../../SlashCommands'; -import { isOnlyCtrlOrCmdKeyEvent, Key} from '../../../Keyboard'; -import Modal from '../../../Modal'; -import sdk from '../../../index'; -import { _t } from '../../../languageHandler'; -import Analytics from '../../../Analytics'; - -import dis from '../../../dispatcher'; - -import * as HtmlUtils from '../../../HtmlUtils'; -import Autocomplete from './Autocomplete'; -import {Completion} from "../../../autocomplete/Autocompleter"; -import Markdown from '../../../Markdown'; -import MessageComposerStore from '../../../stores/MessageComposerStore'; -import ContentMessages from '../../../ContentMessages'; - -import EMOTICON_REGEX from 'emojibase-regex/emoticon'; - -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/Permalinks"; -import ReplyPreview from "./ReplyPreview"; -import RoomViewStore from '../../../stores/RoomViewStore'; -import ReplyThread from "../elements/ReplyThread"; -import {ContentHelpers} from 'matrix-js-sdk'; -import AccessibleButton from '../elements/AccessibleButton'; -import {findEditableEvent} from '../../../utils/EventUtils'; -import SlateComposerHistoryManager from "../../../SlateComposerHistoryManager"; -import TypingStore from "../../../stores/TypingStore"; -import {EMOTICON_TO_EMOJI} from "../../../emoji"; - -const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$'); - -// the Slate node type to default to for unstyled text -const DEFAULT_NODE = 'paragraph'; - -// map HTML elements through to our Slate schema node types -// used for the HTML deserializer. -// (The names here are chosen to match the MD serializer's schema for convenience) -const BLOCK_TAGS = { - p: 'paragraph', - blockquote: 'block-quote', - ul: 'bulleted-list', - h1: 'heading1', - h2: 'heading2', - h3: 'heading3', - h4: 'heading4', - h5: 'heading5', - h6: 'heading6', - li: 'list-item', - ol: 'numbered-list', - pre: 'code', -}; - -const MARK_TAGS = { - strong: 'bold', - b: 'bold', // deprecated - em: 'italic', - i: 'italic', // deprecated - code: 'code', - u: 'underlined', - del: 'deleted', - strike: 'deleted', // deprecated - s: 'deleted', // deprecated -}; - -const SLATE_SCHEMA = { - inlines: { - pill: { - isVoid: true, - }, - emoji: { - isVoid: true, - }, - }, -}; - -function onSendMessageFailed(err, room) { - // XXX: temporary logging to try to diagnose - // https://github.com/vector-im/riot-web/issues/3148 - console.log('MessageComposer got send failure: ' + err.name + '('+err+')'); - dis.dispatch({ - action: 'message_send_failed', - }); -} - -function rangeEquals(a: Range, b: Range): boolean { - return (a.anchor.key === b.anchor.key - && a.anchor.offset === b.anchorOffset - && a.focus.key === b.focusKey - && a.focus.offset === b.focusOffset - && a.isFocused === b.isFocused - && a.isBackward === b.isBackward); -} - -/* - * The textInput part of the MessageComposer - */ -export default class MessageComposerInput extends React.Component { - static propTypes = { - // js-sdk Room object - room: PropTypes.object.isRequired, - - onInputStateChanged: PropTypes.func, - }; - - client: MatrixClient; - autocomplete: Autocomplete; - historyManager: SlateComposerHistoryManager; - - constructor(props) { - super(props); - - const isRichTextEnabled = SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'); - - Analytics.setRichtextMode(isRichTextEnabled); - - this.client = MatrixClientPeg.get(); - - // track whether we should be trying to show autocomplete suggestions on the current editor - // contents. currently it's only suppressed when navigating history to avoid ugly flashes - // of unexpected corrections as you navigate. - // XXX: should this be in state? - this.suppressAutoComplete = false; - - // track whether we've just pressed an arrowkey left or right in order to skip void nodes. - // see https://github.com/ianstormtaylor/slate/issues/762#issuecomment-304855095 - this.direction = ''; - - this.plainWithMdPills = new PlainWithPillsSerializer({ pillFormat: 'md' }); - this.plainWithIdPills = new PlainWithPillsSerializer({ pillFormat: 'id' }); - this.plainWithPlainPills = new PlainWithPillsSerializer({ pillFormat: 'plain' }); - - this.md = new Md({ - rules: [ - { - // if serialize returns undefined it falls through to the default hardcoded - // serialization rules - serialize: (obj, children) => { - if (obj.object !== 'inline') return; - switch (obj.type) { - case 'pill': - return `[${ obj.data.get('completion') }](${ obj.data.get('href') })`; - case 'emoji': - return obj.data.get('emojiUnicode'); - } - }, - }, { - serialize: (obj, children) => { - if (obj.object !== 'mark') return; - // XXX: slate-md-serializer consumes marks other than bold, italic, code, inserted, deleted - switch (obj.type) { - case 'underlined': - return `${ children }`; - case 'deleted': - return `${ children }`; - case 'code': - // XXX: we only ever get given `code` regardless of whether it was inline or block - // XXX: workaround for https://github.com/tommoor/slate-md-serializer/issues/14 - // strip single backslashes from children, as they would have been escaped here - return `\`${ children.split('\\').map((v) => v ? v : '\\').join('') }\``; - } - }, - }, - ], - }); - - this.html = new Html({ - rules: [ - { - deserialize: (el, next) => { - const tag = el.tagName.toLowerCase(); - let type = BLOCK_TAGS[tag]; - if (type) { - return { - object: 'block', - type: type, - nodes: next(el.childNodes), - }; - } - type = MARK_TAGS[tag]; - if (type) { - return { - object: 'mark', - type: type, - nodes: next(el.childNodes), - }; - } - // special case links - if (tag === 'a') { - const href = el.getAttribute('href'); - const permalinkEntity = getPrimaryPermalinkEntity(href); - if (permalinkEntity) { - return { - object: 'inline', - type: 'pill', - data: { - href, - completion: el.innerText, - completionId: permalinkEntity, - }, - }; - } else { - return { - object: 'inline', - type: 'link', - data: { href }, - nodes: next(el.childNodes), - }; - } - } - }, - serialize: (obj, children) => { - if (obj.object === 'block') { - return this.renderNode({ - node: obj, - children: children, - }); - } else if (obj.object === 'mark') { - return this.renderMark({ - mark: obj, - children: children, - }); - } else if (obj.object === 'inline') { - // special case links, pills and emoji otherwise we - // end up with React components getting rendered out(!) - switch (obj.type) { - case 'pill': - return { obj.data.get('completion') }; - case 'link': - return { children }; - case 'emoji': - // XXX: apparently you can't return plain strings from serializer rules - // until https://github.com/ianstormtaylor/slate/pull/1854 is merged. - // So instead we temporarily wrap emoji from RTE in a span. - return { obj.data.get('emojiUnicode') }; - } - return this.renderNode({ - node: obj, - children: children, - }); - } - }, - }, - ], - }); - - const savedState = MessageComposerStore.getEditorState(this.props.room.roomId); - this.state = { - // whether we're in rich text or markdown mode - isRichTextEnabled, - - // the currently displayed editor state (note: this is always what is modified on input) - editorState: this.createEditorState( - isRichTextEnabled, - savedState ? savedState.editor_state : undefined, - savedState ? savedState.rich_text : undefined, - ), - - // the original editor state, before we started tabbing through completions - originalEditorState: null, - - // the virtual state "above" the history stack, the message currently being composed that - // we want to persist whilst browsing history - currentlyComposedEditorState: null, - - // whether there were any completions - someCompletions: null, - }; - } - - /* - * "Does the right thing" to create an Editor value, based on: - * - whether we've got rich text mode enabled - * - contentState was passed in - * - whether the contentState that was passed in was rich text - */ - createEditorState(wantRichText: boolean, editorState: ?Value, wasRichText: ?boolean): Value { - if (editorState instanceof Value) { - if (wantRichText && !wasRichText) { - return this.mdToRichEditorState(editorState); - } - if (wasRichText && !wantRichText) { - return this.richToMdEditorState(editorState); - } - return editorState; - } else { - // ...or create a new one. and explicitly focus it otherwise tab in-out issues - const base = Plain.deserialize('', { defaultBlock: DEFAULT_NODE }); - return base.change().focus().value; - } - } - - componentWillMount() { - this.dispatcherRef = dis.register(this.onAction); - this.historyManager = new SlateComposerHistoryManager(this.props.room.roomId, 'mx_slate_composer_history_'); - } - - componentWillUnmount() { - dis.unregister(this.dispatcherRef); - } - - _collectEditor = (e) => { - this._editor = e; - } - - onAction = (payload) => { - const editorState = this.state.editorState; - - switch (payload.action) { - case 'reply_to_event': - case 'focus_composer': - this.focusComposer(); - break; - case 'insert_mention': - { - // Pretend that we've autocompleted this user because keeping two code - // paths for inserting a user pill is not fun - const selection = this.getSelectionRange(this.state.editorState); - const member = this.props.room.getMember(payload.user_id); - const completion = member ? - member.rawDisplayName : payload.user_id; - this.setDisplayedCompletion({ - completion, - completionId: payload.user_id, - selection, - href: makeUserPermalink(payload.user_id), - suffix: (selection.beginning && selection.start === 0) ? ': ' : ' ', - }); - } - break; - case 'quote': { - const html = HtmlUtils.bodyToHtml(payload.event.getContent(), null, { - forComposerQuote: true, - returnString: true, - }); - const fragment = this.html.deserialize(html); - // FIXME: do we want to put in a permalink to the original quote here? - // If so, what should be the format, and how do we differentiate it from replies? - - const quote = Block.create('block-quote'); - if (this.state.isRichTextEnabled) { - let change = editorState.change(); - const anchorText = editorState.anchorText; - if ((!anchorText || anchorText.text === '') && editorState.anchorBlock.nodes.size === 1) { - // replace the current block rather than split the block - // XXX: this destroys our focus by deleting the thing we are anchored/focused on - change = change.replaceNodeByKey(editorState.anchorBlock.key, quote); - } else { - // insert it into the middle of the block (splitting it) - change = change.insertBlock(quote); - } - - // XXX: heuristic to strip out wrapping

    which breaks quoting in RT mode - if (fragment.document.nodes.size && fragment.document.nodes.get(0).type === DEFAULT_NODE) { - change = change.insertFragmentByKey(quote.key, 0, fragment.document.nodes.get(0)); - } else { - change = change.insertFragmentByKey(quote.key, 0, fragment.document); - } - - // XXX: this is to bring back the focus in a sane place and add a paragraph after it - change = change.select(Range.create({ - anchor: { - key: quote.key, - }, - focus: { - key: quote.key, - }, - })).moveToEndOfBlock().insertBlock(Block.create(DEFAULT_NODE)).focus(); - - this.onChange(change); - } else { - const fragmentChange = fragment.change(); - fragmentChange.moveToRangeOfNode(fragment.document) - .wrapBlock(quote); - - // FIXME: handle pills and use commonmark rather than md-serialize - const md = this.md.serialize(fragmentChange.value); - const change = editorState.change() - .insertText(md + '\n\n') - .focus(); - this.onChange(change); - } - } - break; - } - }; - - onChange = (change: Change, originalEditorState?: Value) => { - let editorState = change.value; - - if (this.direction !== '') { - const focusedNode = editorState.focusInline || editorState.focusText; - if (editorState.schema.isVoid(focusedNode)) { - // XXX: does this work in RTL? - const edge = this.direction === 'Previous' ? 'End' : 'Start'; - if (editorState.selection.isCollapsed) { - change = change[`moveTo${ edge }Of${ this.direction }Text`](); - } else { - const block = this.direction === 'Previous' ? editorState.previousText : editorState.nextText; - if (block) { - change = change[`moveFocusTo${ edge }OfNode`](block); - } - } - editorState = change.value; - } - } - - // when in autocomplete mode and selection changes hide the autocomplete. - // Selection changes when we enter text so use a heuristic to compare documents without doing it recursively - if (this.autocomplete.state.completionList.length > 0 && !this.autocomplete.state.hide && - !rangeEquals(this.state.editorState.selection, editorState.selection) && - // XXX: the heuristic failed when inlines like pills weren't taken into account. This is inideal - this.state.editorState.document.toJSON() === editorState.document.toJSON()) { - this.autocomplete.hide(); - } - - if (Plain.serialize(editorState) !== '') { - TypingStore.sharedInstance().setSelfTyping(this.props.room.roomId, true); - } else { - TypingStore.sharedInstance().setSelfTyping(this.props.room.roomId, false); - } - - if (editorState.startText !== null) { - const text = editorState.startText.text; - const currentStartOffset = editorState.selection.start.offset; - - // Automatic replacement of plaintext emoji to Unicode emoji - if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) { - // The first matched group includes just the matched plaintext emoji - const emoticonMatch = REGEX_EMOTICON_WHITESPACE.exec(text.slice(0, currentStartOffset)); - if (emoticonMatch) { - const query = emoticonMatch[1].toLowerCase().replace("-", ""); - const data = EMOTICON_TO_EMOJI.get(query); - - // only perform replacement if we found a match, otherwise we would be not letting user type - if (data) { - const range = Range.create({ - anchor: { - key: editorState.startText.key, - offset: currentStartOffset - emoticonMatch[1].length - 1, - }, - focus: { - key: editorState.startText.key, - offset: currentStartOffset - 1, - }, - }); - change = change.insertTextAtRange(range, data.unicode); - editorState = change.value; - } - } - } - } - - if (this.props.onInputStateChanged && editorState.blocks.size > 0) { - let blockType = editorState.blocks.first().type; - // console.log("onInputStateChanged; current block type is " + blockType + " and marks are " + editorState.activeMarks); - - if (blockType === 'list-item') { - const parent = editorState.document.getParent(editorState.blocks.first().key); - if (parent.type === 'numbered-list') { - blockType = 'numbered-list'; - } else if (parent.type === 'bulleted-list') { - blockType = 'bulleted-list'; - } - } - const inputState = { - marks: editorState.activeMarks, - blockType, - }; - this.props.onInputStateChanged(inputState); - } - - // Record the editor state for this room so that it can be retrieved after switching to another room and back - MessageComposerStore.setEditorState(this.props.room.roomId, editorState, this.state.isRichTextEnabled); - - this.setState({ - editorState, - originalEditorState: originalEditorState || null, - }); - }; - - mdToRichEditorState(editorState: Value): Value { - // for consistency when roundtripping, we could use slate-md-serializer rather than - // commonmark, but then we would lose pills as the MD deserialiser doesn't know about - // them and doesn't have any extensibility hooks. - // - // The code looks like this: - // - // const markdown = this.plainWithMdPills.serialize(editorState); - // - // // weirdly, the Md serializer can't deserialize '' to a valid Value... - // if (markdown !== '') { - // editorState = this.md.deserialize(markdown); - // } - // else { - // editorState = Plain.deserialize('', { defaultBlock: DEFAULT_NODE }); - // } - - // so, instead, we use commonmark proper (which is arguably more logical to the user - // anyway, as they'll expect the RTE view to match what they'll see in the timeline, - // but the HTML->MD conversion is anyone's guess). - - const textWithMdPills = this.plainWithMdPills.serialize(editorState); - const markdown = new Markdown(textWithMdPills); - // HTML deserialize has custom rules to turn permalinks into pill objects. - return this.html.deserialize(markdown.toHTML()); - } - - richToMdEditorState(editorState: Value): Value { - // FIXME: this conversion loses pills (turning them into pure MD links). - // We need to add a pill-aware deserialize method - // to PlainWithPillsSerializer which recognises pills in raw MD and turns them into pills. - return Plain.deserialize( - // FIXME: we compile the MD out of the RTE state using slate-md-serializer - // which doesn't roundtrip symmetrically with commonmark, which we use for - // compiling MD out of the MD editor state above. - this.md.serialize(editorState), - { defaultBlock: DEFAULT_NODE }, - ); - } - - enableRichtext(enabled: boolean) { - if (enabled === this.state.isRichTextEnabled) return; - - Analytics.setRichtextMode(enabled); - - this.setState({ - editorState: this.createEditorState( - enabled, - this.state.editorState, - this.state.isRichTextEnabled, - ), - isRichTextEnabled: enabled, - }, () => { - this._editor.focus(); - if (this.props.onInputStateChanged) { - this.props.onInputStateChanged({ - isRichTextEnabled: enabled, - }); - } - }); - - SettingsStore.setValue("MessageComposerInput.isRichTextEnabled", null, SettingLevel.ACCOUNT, enabled); - } - - /** - * Check if the current selection has a mark with `type` in it. - * - * @param {String} type - * @return {Boolean} - */ - - hasMark = type => { - const { editorState } = this.state; - return editorState.activeMarks.some(mark => mark.type === type); - }; - - /** - * Check if the any of the currently selected blocks are of `type`. - * - * @param {String} type - * @return {Boolean} - */ - - hasBlock = type => { - const { editorState } = this.state; - return editorState.blocks.some(node => node.type === type); - }; - - onKeyDown = (ev: KeyboardEvent, change: Change, editor: Editor) => { - this.suppressAutoComplete = false; - this.direction = ''; - - // Navigate autocomplete list with arrow keys - if (this.autocomplete.countCompletions() > 0) { - if (!(ev.ctrlKey || ev.shiftKey || ev.altKey || ev.metaKey)) { - switch (ev.key) { - case Key.ARROW_UP: - this.autocomplete.moveSelection(-1); - ev.preventDefault(); - return true; - case Key.ARROW_DOWN: - this.autocomplete.moveSelection(+1); - ev.preventDefault(); - return true; - } - } - } - - // skip void nodes - see - // https://github.com/ianstormtaylor/slate/issues/762#issuecomment-304855095 - if (ev.key === Key.ARROW_LEFT) { - this.direction = 'Previous'; - } else if (ev.key === Key.ARROW_RIGHT) { - this.direction = 'Next'; - } - - switch (ev.key) { - case Key.ENTER: - return this.handleReturn(ev, change); - case Key.BACKSPACE: - return this.onBackspace(ev, change); - case Key.ARROW_UP: - return this.onVerticalArrow(ev, true); - case Key.ARROW_DOWN: - return this.onVerticalArrow(ev, false); - case Key.TAB: - return this.onTab(ev); - case Key.ESCAPE: - return this.onEscape(ev); - case Key.SPACE: - return this.onSpace(ev, change); - } - - if (isOnlyCtrlOrCmdKeyEvent(ev)) { - const ctrlCmdCommand = { - // C-m => Toggles between rich text and markdown modes - [Key.M]: 'toggle-mode', - [Key.B]: 'bold', - [Key.I]: 'italic', - [Key.U]: 'underlined', - [Key.J]: 'inline-code', - }[ev.key]; - - if (ctrlCmdCommand) { - ev.preventDefault(); // to prevent clashing with Mac's minimize window - return this.handleKeyCommand(ctrlCmdCommand); - } - } - }; - - onSpace = (ev: KeyboardEvent, change: Change): Change => { - if (ev.metaKey || ev.altKey || ev.shiftKey || ev.ctrlKey) { - return; - } - - // drop a point in history so the user can undo a word - // XXX: this seems nasty but adding to history manually seems a no-go - ev.preventDefault(); - return change.withoutMerging(() => { - change.insertText(ev.key); - }); - }; - - onBackspace = (ev: KeyboardEvent, change: Change): Change => { - if (ev.metaKey || ev.altKey || ev.shiftKey) { - return; - } - - const { editorState } = this.state; - - // Allow Ctrl/Cmd-Backspace when focus starts at the start of the composer (e.g select-all) - // for some reason if slate sees you Ctrl-backspace and your anchor.offset=0 it just resets your focus - // XXX: Doing this now seems to put slate into a broken state, and it didn't appear to be doing - // what it claims to do on the old version of slate anyway... - /*if (!editorState.isCollapsed && editorState.selection.anchor.offset === 0) { - return change.delete(); - }*/ - - if (this.state.isRichTextEnabled) { - // let backspace exit lists - const isList = this.hasBlock('list-item'); - - if (isList && editorState.selection.anchor.offset == 0) { - change - .setBlocks(DEFAULT_NODE) - .unwrapBlock('bulleted-list') - .unwrapBlock('numbered-list'); - return change; - } else if (editorState.selection.anchor.offset == 0 && editorState.isCollapsed) { - // turn blocks back into paragraphs - if ((this.hasBlock('block-quote') || - this.hasBlock('heading1') || - this.hasBlock('heading2') || - this.hasBlock('heading3') || - this.hasBlock('heading4') || - this.hasBlock('heading5') || - this.hasBlock('heading6') || - this.hasBlock('code'))) { - return change.setBlocks(DEFAULT_NODE); - } - - // remove paragraphs entirely if they're nested - const parent = editorState.document.getParent(editorState.anchorBlock.key); - if (editorState.selection.anchor.offset == 0 && - this.hasBlock('paragraph') && - parent.nodes.size == 1 && - parent.object !== 'document') { - return change.replaceNodeByKey(editorState.anchorBlock.key, editorState.anchorText) - .moveToEndOfNode(parent) - .focus(); - } - } - } - return; - }; - - handleKeyCommand = (command: string): boolean => { - if (command === 'toggle-mode') { - this.enableRichtext(!this.state.isRichTextEnabled); - return true; - } - - //const newState: ?Value = null; - - // Draft handles rich text mode commands by default but we need to do it ourselves for Markdown. - if (this.state.isRichTextEnabled) { - const type = command; - const { editorState } = this.state; - const change = editorState.change(); - const { document } = editorState; - switch (type) { - // list-blocks: - case 'bulleted-list': - case 'numbered-list': { - // Handle the extra wrapping required for list buttons. - const isList = this.hasBlock('list-item'); - const isType = editorState.blocks.some(block => { - return !!document.getClosest(block.key, parent => parent.type === type); - }); - - if (isList && isType) { - change - .setBlocks(DEFAULT_NODE) - .unwrapBlock('bulleted-list') - .unwrapBlock('numbered-list'); - } else if (isList) { - change - .unwrapBlock( - type === 'bulleted-list' ? 'numbered-list' : 'bulleted-list', - ) - .wrapBlock(type); - } else { - change.setBlocks('list-item').wrapBlock(type); - } - } - break; - - // simple blocks - case 'paragraph': - case 'block-quote': - case 'heading1': - case 'heading2': - case 'heading3': - case 'heading4': - case 'heading5': - case 'heading6': - case 'list-item': - case 'code': { - const isActive = this.hasBlock(type); - const isList = this.hasBlock('list-item'); - - if (isList) { - change - .setBlocks(isActive ? DEFAULT_NODE : type) - .unwrapBlock('bulleted-list') - .unwrapBlock('numbered-list'); - } else { - change.setBlocks(isActive ? DEFAULT_NODE : type); - } - } - break; - - // marks: - case 'bold': - case 'italic': - case 'inline-code': - case 'underlined': - case 'deleted': { - change.toggleMark(type === 'inline-code' ? 'code' : type); - } - break; - - default: - console.warn(`ignoring unrecognised RTE command ${type}`); - return false; - } - - this.onChange(change); - - return true; - } else { -/* - const contentState = this.state.editorState.getCurrentContent(); - const multipleLinesSelected = RichText.hasMultiLineSelection(this.state.editorState); - - const selectionState = this.state.editorState.getSelection(); - const start = selectionState.getStartOffset(); - const end = selectionState.getEndOffset(); - - // If multiple lines are selected or nothing is selected, insert a code block - // instead of applying inline code formatting. This is an attempt to mimic what - // happens in non-MD mode. - const treatInlineCodeAsBlock = multipleLinesSelected || start === end; - const textMdCodeBlock = (text) => `\`\`\`\n${text}\n\`\`\`\n`; - const modifyFn = { - 'bold': (text) => `**${text}**`, - 'italic': (text) => `*${text}*`, - 'underline': (text) => `${text}`, - 'strike': (text) => `${text}`, - // ("code" is triggered by ctrl+j by draft-js by default) - 'code': (text) => treatInlineCodeAsBlock ? textMdCodeBlock(text) : `\`${text}\``, - 'code': textMdCodeBlock, - 'blockquote': (text) => text.split('\n').map((line) => `> ${line}\n`).join('') + '\n', - 'unordered-list-item': (text) => text.split('\n').map((line) => `\n- ${line}`).join(''), - 'ordered-list-item': (text) => text.split('\n').map((line, i) => `\n${i + 1}. ${line}`).join(''), - }[command]; - - const selectionAfterOffset = { - 'bold': -2, - 'italic': -1, - 'underline': -4, - 'strike': -6, - 'code': treatInlineCodeAsBlock ? -5 : -1, - 'code': -5, - 'blockquote': -2, - }[command]; - - // Returns a function that collapses a selection to its end and moves it by offset - const collapseAndOffsetSelection = (selection, offset) => { - const key = selection.endKey(); - return new Range({ - anchorKey: key, anchor.offset: offset, - focus.key: key, focus.offset: offset, - }); - }; - - if (modifyFn) { - - const previousSelection = this.state.editorState.getSelection(); - const newContentState = RichText.modifyText(contentState, previousSelection, modifyFn); - newState = EditorState.push( - this.state.editorState, - newContentState, - 'insert-characters', - ); - - let newSelection = newContentState.getSelectionAfter(); - // If the selection range is 0, move the cursor inside the formatted body - if (previousSelection.getStartOffset() === previousSelection.getEndOffset() && - previousSelection.getStartKey() === previousSelection.getEndKey() && - selectionAfterOffset !== undefined - ) { - const selectedBlock = newContentState.getBlockForKey(previousSelection.getAnchorKey()); - const blockLength = selectedBlock.getText().length; - const newOffset = blockLength + selectionAfterOffset; - newSelection = collapseAndOffsetSelection(newSelection, newOffset); - } - - newState = EditorState.forceSelection(newState, newSelection); - } - } - - if (newState != null) { - this.setState({editorState: newState}); - return true; - } -*/ - } - return false; - }; - - onPaste = (event: Event, change: Change, editor: Editor): Change => { - const transfer = getEventTransfer(event); - - switch (transfer.type) { - case 'files': - // This actually not so much for 'files' as such (at time of writing - // neither chrome nor firefox let you paste a plain file copied - // from Finder) but more images copied from a different website - // / word processor etc. - return ContentMessages.sharedInstance().sendContentListToRoom( - transfer.files, this.props.room.roomId, this.client, - ); - case 'html': { - if (this.state.isRichTextEnabled) { - // FIXME: https://github.com/ianstormtaylor/slate/issues/1497 means - // that we will silently discard nested blocks (e.g. nested lists) :( - const fragment = this.html.deserialize(transfer.html); - return change - // XXX: this somehow makes Slate barf on undo and get too empty and break entirely - // .setOperationFlag("skip", false) - // .setOperationFlag("merge", false) - .insertFragment(fragment.document); - } else { - // in MD mode we don't want the rich content pasted as the magic was annoying people so paste plain - return change.withoutMerging(() => { - change.insertText(transfer.text); - }); - } - } - case 'text': - // don't skip/merge so that multiple consecutive pastes can be undone individually - return change.withoutMerging(() => { - change.insertText(transfer.text); - }); - } - }; - - handleReturn = (ev, change) => { - const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; - if (ev.shiftKey || (isMac && ev.altKey)) { - return change.insertText('\n'); - } - - if (this.autocomplete.hasSelection()) { - this.autocomplete.hide(); - ev.preventDefault(); - return true; - } - - const editorState = this.state.editorState; - - const lastBlock = editorState.blocks.last(); - if (['code', 'block-quote', 'list-item'].includes(lastBlock.type)) { - const text = lastBlock.text; - if (text === '') { - // allow the user to cancel empty block by hitting return, useful in conjunction with below `inBlock` - return change - .setBlocks(DEFAULT_NODE) - .unwrapBlock('bulleted-list') - .unwrapBlock('numbered-list'); - } - - // TODO strip trailing lines from blockquotes/list entries - // the below code seemingly works but doesn't account for edge cases like return with caret not at end - /* const trailingNewlines = text.match(/\n*$/); - if (trailingNewlines && trailingNewlines[0]) { - remove trailing newlines at the end of this block before making a new one - return change.deleteBackward(trailingNewlines[0].length); - }*/ - - return; - } - - let contentText; - let contentHTML; - - // only look for commands if the first block contains simple unformatted text - // i.e. no pills or rich-text formatting and begins with a /. - let cmd; let commandText; - const firstChild = editorState.document.nodes.get(0); - const firstGrandChild = firstChild && firstChild.nodes.get(0); - if (firstChild && firstGrandChild && - firstChild.object === 'block' && firstGrandChild.object === 'text' && - firstGrandChild.text[0] === '/') { - commandText = this.plainWithIdPills.serialize(editorState); - cmd = processCommandInput(this.props.room.roomId, commandText); - } - - if (cmd) { - if (!cmd.error) { - this.historyManager.save(editorState, this.state.isRichTextEnabled ? 'rich' : 'markdown'); - this.setState({ - editorState: this.createEditorState(), - }, ()=>{ - this._editor.focus(); - }); - } - if (cmd.promise) { - cmd.promise.then(()=>{ - console.log("Command success."); - }, (err)=>{ - console.error("Command failure: %s", err); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Server error', '', ErrorDialog, { - title: _t("Server error"), - description: ((err && err.message) ? err.message : _t( - "Server unavailable, overloaded, or something else went wrong.", - )), - }); - }); - } else if (cmd.error) { - console.error(cmd.error); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - // TODO possibly track which command they ran (not its Arguments) here - Modal.createTrackedDialog('Command error', '', ErrorDialog, { - title: _t("Command error"), - description: cmd.error, - }); - } - return true; - } - - const replyingToEv = RoomViewStore.getQuotingEvent(); - const mustSendHTML = Boolean(replyingToEv); - - if (this.state.isRichTextEnabled) { - // We should only send HTML if any block is styled or contains inline style - let shouldSendHTML = false; - - if (mustSendHTML) shouldSendHTML = true; - - if (!shouldSendHTML) { - shouldSendHTML = !!editorState.document.findDescendant(node => { - // N.B. node.getMarks() might be private? - return ((node.object === 'block' && node.type !== 'paragraph') || - (node.object === 'inline') || - (node.object === 'text' && node.getMarks().size > 0)); - }); - } - - contentText = this.plainWithPlainPills.serialize(editorState); - if (contentText === '') return true; - - if (shouldSendHTML) { - contentHTML = HtmlUtils.processHtmlForSending(this.html.serialize(editorState)); - } - } else { - const sourceWithPills = this.plainWithMdPills.serialize(editorState); - if (sourceWithPills === '') return true; - - const mdWithPills = new Markdown(sourceWithPills); - - // if contains no HTML and we're not quoting (needing HTML) - if (mdWithPills.isPlainText() && !mustSendHTML) { - // N.B. toPlainText is only usable here because we know that the MD - // didn't contain any formatting in the first place... - contentText = mdWithPills.toPlaintext(); - } else { - // to avoid ugliness on clients which ignore the HTML body we don't - // send pills in the plaintext body. - contentText = this.plainWithPlainPills.serialize(editorState); - contentHTML = mdWithPills.toHTML(); - } - } - - let sendHtmlFn = ContentHelpers.makeHtmlMessage; - let sendTextFn = ContentHelpers.makeTextMessage; - - this.historyManager.save(editorState, this.state.isRichTextEnabled ? 'rich' : 'markdown'); - - if (commandText && commandText.startsWith('/me')) { - if (replyingToEv) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Emote Reply Fail', '', ErrorDialog, { - title: _t("Unable to reply"), - description: _t("At this time it is not possible to reply with an emote."), - }); - return false; - } - - contentText = contentText.substring(4); - // bit of a hack, but the alternative would be quite complicated - if (contentHTML) contentHTML = contentHTML.replace(/\/me ?/, ''); - sendHtmlFn = ContentHelpers.makeHtmlEmote; - sendTextFn = ContentHelpers.makeEmoteMessage; - } - - let content = contentHTML ? - sendHtmlFn(contentText, contentHTML) : - sendTextFn(contentText); - - if (replyingToEv) { - const replyContent = ReplyThread.makeReplyMixIn(replyingToEv); - content = Object.assign(replyContent, content); - - // Part of Replies fallback support - prepend the text we're sending - // with the text we're replying to - const nestedReply = ReplyThread.getNestedReplyText(replyingToEv, this.props.permalinkCreator); - if (nestedReply) { - if (content.formatted_body) { - content.formatted_body = nestedReply.html + content.formatted_body; - } - content.body = nestedReply.body + content.body; - } - - // Clear reply_to_event as we put the message into the queue - // if the send fails, retry will handle resending. - dis.dispatch({ - action: 'reply_to_event', - event: null, - }); - } - - this.client.sendMessage(this.props.room.roomId, content).then((res) => { - dis.dispatch({ - action: 'message_sent', - }); - }).catch((e) => { - onSendMessageFailed(e, this.props.room); - }); - - this.setState({ - editorState: this.createEditorState(), - }, ()=>{ this._editor.focus(); }); - - return true; - }; - - onVerticalArrow = (e, up) => { - if (e.ctrlKey || e.shiftKey || e.metaKey) return; - - const shouldSelectHistory = e.altKey; - const shouldEditLastMessage = !e.altKey && up && !RoomViewStore.getQuotingEvent(); - - if (shouldSelectHistory) { - // Try select composer history - const selected = this.selectHistory(up); - if (selected) { - // We're selecting history, so prevent the key event from doing anything else - e.preventDefault(); - } - } else if (shouldEditLastMessage) { - // selection must be collapsed - const selection = this.state.editorState.selection; - if (!selection.isCollapsed) return; - // and we must be at the edge of the document (up=start, down=end) - const document = this.state.editorState.document; - if (up) { - if (!selection.anchor.isAtStartOfNode(document)) return; - } else { - if (!selection.anchor.isAtEndOfNode(document)) return; - } - - const editEvent = findEditableEvent(this.props.room, false); - if (editEvent) { - // We're selecting history, so prevent the key event from doing anything else - e.preventDefault(); - dis.dispatch({ - action: 'edit_event', - event: editEvent, - }); - } - } - }; - - selectHistory = (up) => { - const delta = up ? -1 : 1; - - // True if we are not currently selecting history, but composing a message - if (this.historyManager.currentIndex === this.historyManager.history.length) { - // We can't go any further - there isn't any more history, so nop. - if (!up) { - return; - } - this.setState({ - currentlyComposedEditorState: this.state.editorState, - }); - } else if (this.historyManager.currentIndex + delta === this.historyManager.history.length) { - // True when we return to the message being composed currently - this.setState({ - editorState: this.state.currentlyComposedEditorState, - }); - this.historyManager.currentIndex = this.historyManager.history.length; - return; - } - - let editorState; - const historyItem = this.historyManager.getItem(delta); - if (!historyItem) return; - - if (historyItem.format === 'rich' && !this.state.isRichTextEnabled) { - editorState = this.richToMdEditorState(historyItem.value); - } else if (historyItem.format === 'markdown' && this.state.isRichTextEnabled) { - editorState = this.mdToRichEditorState(historyItem.value); - } else { - editorState = historyItem.value; - } - - // Move selection to the end of the selected history - const change = editorState.change().moveToEndOfNode(editorState.document); - - // We don't call this.onChange(change) now, as fixups on stuff like pills - // should already have been done and persisted in the history. - editorState = change.value; - - this.suppressAutoComplete = true; - - this.setState({ editorState }, ()=>{ - this._editor.focus(); - }); - return true; - }; - - onTab = async (e) => { - this.setState({ - someCompletions: null, - }); - e.preventDefault(); - if (this.autocomplete.countCompletions() === 0) { - // Force completions to show for the text currently entered - const completionCount = await this.autocomplete.forceComplete(); - this.setState({ - someCompletions: completionCount > 0, - }); - // Select the first item by moving "down" - await this.autocomplete.moveSelection(+1); - } else { - await this.autocomplete.moveSelection(e.shiftKey ? -1 : +1); - } - }; - - onEscape = async (e) => { - e.preventDefault(); - if (this.autocomplete) { - this.autocomplete.onEscape(e); - } - await this.setDisplayedCompletion(null); // restore originalEditorState - }; - - onAutocompleteConfirm = (displayedCompletion: ?Completion) => { - this.focusComposer(); - // XXX: this fails if the composer isn't focused so focus it and delay the completion until next tick - setImmediate(() => { - this.setDisplayedCompletion(displayedCompletion); - }); - }; - - /* If passed null, restores the original editor content from state.originalEditorState. - * If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState. - */ - setDisplayedCompletion = async (displayedCompletion: ?Completion): boolean => { - const activeEditorState = this.state.originalEditorState || this.state.editorState; - - if (displayedCompletion == null) { - if (this.state.originalEditorState) { - const editorState = this.state.originalEditorState; - this.setState({editorState}); - } - return false; - } - - const { - range = null, - completion = '', - completionId = '', - href = null, - suffix = '', - } = displayedCompletion; - - let inline; - if (href) { - inline = Inline.create({ - type: 'pill', - data: { completion, completionId, href }, - }); - } else if (completion === '@room') { - inline = Inline.create({ - type: 'pill', - data: { completion, completionId }, - }); - } - - let editorState = activeEditorState; - - if (range) { - const change = editorState.change() - .moveToAnchor() - .moveAnchorTo(range.start) - .moveFocusTo(range.end) - .focus(); - editorState = change.value; - } - - let change; - if (inline) { - change = editorState.change() - .insertInlineAtRange(editorState.selection, inline) - .insertText(suffix) - .focus(); - } else { - change = editorState.change() - .insertTextAtRange(editorState.selection, completion) - .insertText(suffix) - .focus(); - } - // for good hygiene, keep editorState updated to track the result of the change - // even though we don't do anything subsequently with it - editorState = change.value; - - this.onChange(change, activeEditorState); - - return true; - }; - - renderNode = props => { - const { attributes, children, node, isSelected } = props; - - switch (node.type) { - case 'paragraph': - return

    {children}

    ; - case 'block-quote': - return
    {children}
    ; - case 'bulleted-list': - return
      {children}
    ; - case 'heading1': - return

    {children}

    ; - case 'heading2': - return

    {children}

    ; - case 'heading3': - return

    {children}

    ; - case 'heading4': - return

    {children}

    ; - case 'heading5': - return
    {children}
    ; - case 'heading6': - return
    {children}
    ; - case 'list-item': - return
  • {children}
  • ; - case 'numbered-list': - return
      {children}
    ; - case 'code': - return
    {children}
    ; - case 'link': - return {children}; - case 'pill': { - const { data } = node; - const url = data.get('href'); - const completion = data.get('completion'); - - const shouldShowPillAvatar = SettingsStore.getValue("Pill.shouldShowPillAvatar"); - const Pill = sdk.getComponent('elements.Pill'); - - if (completion === '@room') { - return ; - } else if (Pill.isPillUrl(url)) { - return ; - } else { - const { text } = node; - return - { text } - ; - } - } - case 'emoji': { - const { data } = node; - return data.get('emojiUnicode'); - } - } - }; - - renderMark = props => { - const { children, mark, attributes } = props; - switch (mark.type) { - case 'bold': - return {children}; - case 'italic': - return {children}; - case 'code': - return {children}; - case 'underlined': - return {children}; - case 'deleted': - return {children}; - } - }; - - onFormatButtonClicked = (name, e) => { - e.preventDefault(); - - // XXX: horrible evil hack to ensure the editor is focused so the act - // of focusing it doesn't then cancel the format button being pressed - // FIXME: can we just tell handleKeyCommand's change to invoke .focus()? - if (document.activeElement && document.activeElement.className !== 'mx_MessageComposer_editor') { - this._editor.focus(); - setTimeout(()=>{ - this.handleKeyCommand(name); - }, 500); // can't find any callback to hook this to. onFocus and onChange and willComponentUpdate fire too early. - return; - } - - this.handleKeyCommand(name); - }; - - getAutocompleteQuery(editorState: Value) { - // We can just return the current block where the selection begins, which - // should be enough to capture any autocompletion input, given autocompletion - // providers only search for the first match which intersects with the current selection. - // This avoids us having to serialize the whole thing to plaintext and convert - // selection offsets in & out of the plaintext domain. - - if (editorState.selection.anchor.key) { - return editorState.document.getDescendant(editorState.selection.anchor.key).text; - } else { - return ''; - } - } - - getSelectionRange(editorState: Value) { - let beginning = false; - const firstChild = editorState.document.nodes.get(0); - const firstGrandChild = firstChild && firstChild.nodes.get(0); - beginning = (firstChild && firstGrandChild && - firstChild.object === 'block' && firstGrandChild.object === 'text' && - editorState.selection.anchor.key === firstGrandChild.key); - - // return a character range suitable for handing to an autocomplete provider. - // the range is relative to the anchor of the current editor selection. - // if the selection spans multiple blocks, then we collapse it for the calculation. - const range = { - beginning, // whether the selection is in the first block of the editor or not - start: editorState.selection.anchor.offset, - end: (editorState.selection.anchor.key == editorState.selection.focus.key) ? - editorState.selection.focus.offset : editorState.selection.anchor.offset, - }; - if (range.start > range.end) { - const tmp = range.start; - range.start = range.end; - range.end = tmp; - } - return range; - } - - onMarkdownToggleClicked = (e) => { - e.preventDefault(); // don't steal focus from the editor! - this.handleKeyCommand('toggle-mode'); - }; - - focusComposer = () => { - this._editor.focus(); - }; - - render() { - const activeEditorState = this.state.originalEditorState || this.state.editorState; - - const className = classNames('mx_MessageComposer_input', { - mx_MessageComposer_input_error: this.state.someCompletions === false, - }); - - const isEmpty = Plain.serialize(this.state.editorState) === ''; - - let {placeholder} = this.props; - // XXX: workaround for placeholder being shown when there is a formatting block e.g blockquote but no text - if (isEmpty && this.state.editorState.startBlock && this.state.editorState.startBlock.type !== DEFAULT_NODE) { - placeholder = undefined; - } - - const markdownClasses = classNames({ - mx_MessageComposer_input_markdownIndicator: true, - mx_MessageComposer_markdownDisabled: this.state.isRichTextEnabled, - }); - - return ( -
    -
    - - this.autocomplete = e} - room={this.props.room} - onConfirm={this.onAutocompleteConfirm} - onSelectionChange={this.setDisplayedCompletion} - query={ this.suppressAutoComplete ? '' : this.getAutocompleteQuery(activeEditorState) } - selection={this.getSelectionRange(activeEditorState)} - /> -
    -
    - - -
    -
    - ); - } -} diff --git a/src/components/views/rooms/SlateMessageComposer.js b/src/components/views/rooms/SlateMessageComposer.js deleted file mode 100644 index 2b68e0d338..0000000000 --- a/src/components/views/rooms/SlateMessageComposer.js +++ /dev/null @@ -1,485 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017, 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -import React, {createRef} from 'react'; -import PropTypes from 'prop-types'; -import { _t, _td } from '../../../languageHandler'; -import CallHandler from '../../../CallHandler'; -import MatrixClientPeg from '../../../MatrixClientPeg'; -import sdk from '../../../index'; -import dis from '../../../dispatcher'; -import RoomViewStore from '../../../stores/RoomViewStore'; -import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import Stickerpicker from './Stickerpicker'; -import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; -import ContentMessages from '../../../ContentMessages'; - -import E2EIcon from './E2EIcon'; - -const formatButtonList = [ - _td("bold"), - _td("italic"), - _td("deleted"), - _td("underlined"), - _td("inline-code"), - _td("block-quote"), - _td("bulleted-list"), - _td("numbered-list"), -]; - -function ComposerAvatar(props) { - const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar'); - return
    - -
    ; -} - -ComposerAvatar.propTypes = { - me: PropTypes.object.isRequired, -} - -function CallButton(props) { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const onVoiceCallClick = (ev) => { - dis.dispatch({ - action: 'place_call', - type: "voice", - room_id: props.roomId, - }); - }; - - return -} - -CallButton.propTypes = { - roomId: PropTypes.string.isRequired -} - -function VideoCallButton(props) { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const onCallClick = (ev) => { - dis.dispatch({ - action: 'place_call', - type: ev.shiftKey ? "screensharing" : "video", - room_id: props.roomId, - }); - }; - - return ; -} - -VideoCallButton.propTypes = { - roomId: PropTypes.string.isRequired, -}; - -function HangupButton(props) { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const onHangupClick = () => { - const call = CallHandler.getCallForRoom(props.roomId); - if (!call) { - return; - } - dis.dispatch({ - action: 'hangup', - // hangup the call for this room, which may not be the room in props - // (e.g. conferences which will hangup the 1:1 room instead) - room_id: call.roomId, - }); - }; - return ; -} - -HangupButton.propTypes = { - roomId: PropTypes.string.isRequired, -} - -function FormattingButton(props) { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - return ; -} - -FormattingButton.propTypes = { - showFormatting: PropTypes.bool.isRequired, - onClickHandler: PropTypes.func.isRequired, -} - -class UploadButton extends React.Component { - static propTypes = { - roomId: PropTypes.string.isRequired, - } - constructor(props) { - super(props); - this.onUploadClick = this.onUploadClick.bind(this); - this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this); - - this._uploadInput = createRef(); - } - - onUploadClick(ev) { - if (MatrixClientPeg.get().isGuest()) { - dis.dispatch({action: 'require_registration'}); - return; - } - this._uploadInput.current.click(); - } - - onUploadFileInputChange(ev) { - if (ev.target.files.length === 0) return; - - // take a copy so we can safely reset the value of the form control - // (Note it is a FileList: we can't use slice or sesnible iteration). - const tfiles = []; - for (let i = 0; i < ev.target.files.length; ++i) { - tfiles.push(ev.target.files[i]); - } - - ContentMessages.sharedInstance().sendContentListToRoom( - tfiles, this.props.roomId, MatrixClientPeg.get(), - ); - - // This is the onChange handler for a file form control, but we're - // not keeping any state, so reset the value of the form control - // to empty. - // NB. we need to set 'value': the 'files' property is immutable. - ev.target.value = ''; - } - - render() { - const uploadInputStyle = {display: 'none'}; - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - return ( - - - - ); - } -} - -export default class SlateMessageComposer extends React.Component { - constructor(props) { - super(props); - this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this); - this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this); - this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this); - this.onInputStateChanged = this.onInputStateChanged.bind(this); - this.onEvent = this.onEvent.bind(this); - this._onRoomStateEvents = this._onRoomStateEvents.bind(this); - this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this); - this._onTombstoneClick = this._onTombstoneClick.bind(this); - this.renderPlaceholderText = this.renderPlaceholderText.bind(this); - this.renderFormatBar = this.renderFormatBar.bind(this); - - this.state = { - inputState: { - marks: [], - blockType: null, - isRichTextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'), - }, - showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'), - isQuoting: Boolean(RoomViewStore.getQuotingEvent()), - tombstone: this._getRoomTombstone(), - canSendMessages: this.props.room.maySendMessage(), - }; - } - - componentDidMount() { - // N.B. using 'event' rather than 'RoomEvents' otherwise the crypto handler - // for 'event' fires *after* 'RoomEvent', and our room won't have yet been - // marked as encrypted. - // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something. - MatrixClientPeg.get().on("event", this.onEvent); - MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents); - this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); - this._waitForOwnMember(); - } - - _waitForOwnMember() { - // if we have the member already, do that - const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()); - if (me) { - this.setState({me}); - return; - } - // Otherwise, wait for member loading to finish and then update the member for the avatar. - // The members should already be loading, and loadMembersIfNeeded - // will return the promise for the existing operation - this.props.room.loadMembersIfNeeded().then(() => { - const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()); - this.setState({me}); - }); - } - - componentWillUnmount() { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("event", this.onEvent); - MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents); - } - if (this._roomStoreToken) { - this._roomStoreToken.remove(); - } - } - - onEvent(event) { - if (event.getType() !== 'm.room.encryption') return; - if (event.getRoomId() !== this.props.room.roomId) return; - this.forceUpdate(); - } - - _onRoomStateEvents(ev, state) { - if (ev.getRoomId() !== this.props.room.roomId) return; - - if (ev.getType() === 'm.room.tombstone') { - this.setState({tombstone: this._getRoomTombstone()}); - } - if (ev.getType() === 'm.room.power_levels') { - this.setState({canSendMessages: this.props.room.maySendMessage()}); - } - } - - _getRoomTombstone() { - return this.props.room.currentState.getStateEvents('m.room.tombstone', ''); - } - - _onRoomViewStoreUpdate() { - const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); - if (this.state.isQuoting === isQuoting) return; - this.setState({ isQuoting }); - } - - - onInputStateChanged(inputState) { - // Merge the new input state with old to support partial updates - inputState = Object.assign({}, this.state.inputState, inputState); - this.setState({inputState}); - } - - _onAutocompleteConfirm(range, completion) { - if (this.messageComposerInput) { - this.messageComposerInput.setDisplayedCompletion(range, completion); - } - } - - onFormatButtonClicked(name, event) { - event.preventDefault(); - this.messageComposerInput.onFormatButtonClicked(name, event); - } - - onToggleFormattingClicked() { - SettingsStore.setValue("MessageComposer.showFormatting", null, SettingLevel.DEVICE, !this.state.showFormatting); - this.setState({showFormatting: !this.state.showFormatting}); - } - - onToggleMarkdownClicked(e) { - e.preventDefault(); // don't steal focus from the editor! - this.messageComposerInput.enableRichtext(!this.state.inputState.isRichTextEnabled); - } - - _onTombstoneClick(ev) { - ev.preventDefault(); - - const replacementRoomId = this.state.tombstone.getContent()['replacement_room']; - const replacementRoom = MatrixClientPeg.get().getRoom(replacementRoomId); - let createEventId = null; - if (replacementRoom) { - const createEvent = replacementRoom.currentState.getStateEvents('m.room.create', ''); - if (createEvent && createEvent.getId()) createEventId = createEvent.getId(); - } - - const viaServers = [this.state.tombstone.getSender().split(':').splice(1).join(':')]; - dis.dispatch({ - action: 'view_room', - highlighted: true, - event_id: createEventId, - room_id: replacementRoomId, - auto_join: true, - - // Try to join via the server that sent the event. This converts @something:example.org - // into a server domain by splitting on colons and ignoring the first entry ("@something"). - via_servers: viaServers, - opts: { - // These are passed down to the js-sdk's /join call - viaServers: viaServers, - }, - }); - } - - renderPlaceholderText() { - const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); - if (this.state.isQuoting) { - if (roomIsEncrypted) { - return _t('Send an encrypted reply…'); - } else { - return _t('Send a reply (unencrypted)…'); - } - } else { - if (roomIsEncrypted) { - return _t('Send an encrypted message…'); - } else { - return _t('Send a message (unencrypted)…'); - } - } - } - - renderFormatBar() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const {marks, blockType} = this.state.inputState; - const formatButtons = formatButtonList.map((name) => { - // special-case to match the md serializer and the special-case in MessageComposerInput.js - const markName = name === 'inline-code' ? 'code' : name; - const active = marks.some(mark => mark.type === markName) || blockType === name; - const suffix = active ? '-on' : ''; - const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name); - const className = 'mx_MessageComposer_format_button mx_filterFlipColor'; - return ( - - ); - }) - - return ( -
    -
    - { formatButtons } -
    - - -
    -
    - ); - } - - render() { - const controls = [ - this.state.me ? : null, - this.props.e2eStatus ? : null, - ]; - - if (!this.state.tombstone && this.state.canSendMessages) { - // This also currently includes the call buttons. Really we should - // check separately for whether we can call, but this is slightly - // complex because of conference calls. - - const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput"); - const showFormattingButton = this.state.inputState.isRichTextEnabled; - const callInProgress = this.props.callState && this.props.callState !== 'ended'; - - controls.push( - this.messageComposerInput = c} - key="controls_input" - room={this.props.room} - placeholder={this.renderPlaceholderText()} - onInputStateChanged={this.onInputStateChanged} - permalinkCreator={this.props.permalinkCreator} />, - showFormattingButton ? : null, - , - , - callInProgress ? : null, - callInProgress ? null : , - callInProgress ? null : , - ); - } else if (this.state.tombstone) { - const replacementRoomId = this.state.tombstone.getContent()['replacement_room']; - - const continuesLink = replacementRoomId ? ( - - {_t("The conversation continues here.")} - - ) : ''; - - controls.push(
    -
    - - - {_t("This room has been replaced and is no longer active.")} -
    - { continuesLink } -
    -
    ); - } else { - controls.push( -
    - { _t('You do not have permission to post to this room') } -
    , - ); - } - - const showFormatBar = this.state.showFormatting && this.state.inputState.isRichTextEnabled; - - return ( -
    -
    -
    - { controls } -
    -
    - { showFormatBar ? this.renderFormatBar() : null } -
    - ); - } -} - -SlateMessageComposer.propTypes = { - // js-sdk Room object - room: PropTypes.object.isRequired, - - // string representing the current voip call state - callState: PropTypes.string, - - // string representing the current room app drawer state - showApps: PropTypes.bool -}; diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index 6fc854c155..a8a393887b 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -26,7 +26,6 @@ import PlatformPeg from "../../../../../PlatformPeg"; export default class PreferencesUserSettingsTab extends React.Component { static COMPOSER_SETTINGS = [ - 'useCiderComposer', 'MessageComposerInput.autoReplaceEmoji', 'MessageComposerInput.suggestEmoji', 'sendTypingNotifications', diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5d32066fd8..9c2105afab 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -362,7 +362,6 @@ "Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)", "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)", "Show info about bridges in room settings": "Show info about bridges in room settings", - "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", @@ -956,13 +955,6 @@ "Strikethrough": "Strikethrough", "Code block": "Code block", "Quote": "Quote", - "Server error": "Server error", - "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", - "Command error": "Command error", - "Unable to reply": "Unable to reply", - "At this time it is not possible to reply with an emote.": "At this time it is not possible to reply with an emote.", - "Markdown is disabled": "Markdown is disabled", - "Markdown is enabled": "Markdown is enabled", "No pinned messages.": "No pinned messages.", "Loading...": "Loading...", "Pinned Messages": "Pinned Messages", @@ -1064,16 +1056,9 @@ "This Room": "This Room", "All Rooms": "All Rooms", "Search…": "Search…", - "bold": "bold", - "italic": "italic", - "deleted": "deleted", - "underlined": "underlined", - "inline-code": "inline-code", - "block-quote": "block-quote", - "bulleted-list": "bulleted-list", - "numbered-list": "numbered-list", - "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", - "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", + "Server error": "Server error", + "Command error": "Command error", + "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", "Failed to connect to integration manager": "Failed to connect to integration manager", "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled", "Add some now": "Add some now", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 1d24e81469..14208e1f03 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -161,11 +161,6 @@ export const SETTINGS = { displayName: _td("Show info about bridges in room settings"), default: false, }, - "useCiderComposer": { - displayName: _td("Use the new, faster, composer for writing messages"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: true, - }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), diff --git a/src/stores/MessageComposerStore.js b/src/stores/MessageComposerStore.js deleted file mode 100644 index ab2dbfedec..0000000000 --- a/src/stores/MessageComposerStore.js +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2017, 2018 Vector Creations Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -import { Value } from 'slate'; - -const localStoragePrefix = 'editor_state_'; - -/** - * A class for storing application state to do with the message composer (specifically in-progress message drafts). - * It does not worry about cleaning up on log out as this is handled in Lifecycle.js by localStorage.clear() - */ -class MessageComposerStore { - constructor() { - this.prefix = localStoragePrefix; - } - - _getKey(roomId: string): string { - return this.prefix + roomId; - } - - setEditorState(roomId: string, editorState: Value, richText: boolean) { - localStorage.setItem(this._getKey(roomId), JSON.stringify({ - editor_state: editorState.toJSON({ - preserveSelection: true, - // XXX: re-hydrating history is not currently supported by fromJSON - // preserveHistory: true, - // XXX: this seems like a workaround for selection.isSet being based on anchorKey instead of anchorPath - preserveKeys: true, - }), - rich_text: richText, - })); - } - - getEditorState(roomId): {editor_state: Value, rich_text: boolean} { - const stateStr = localStorage.getItem(this._getKey(roomId)); - - let state; - if (stateStr) { - state = JSON.parse(stateStr); - - // if it does not have the fields we expect then bail - if (!state || state.rich_text === undefined || state.editor_state === undefined) return; - state.editor_state = Value.fromJSON(state.editor_state); - } - - return state; - } -} - -let singletonMessageComposerStore = null; -if (!singletonMessageComposerStore) { - singletonMessageComposerStore = new MessageComposerStore(); -} -module.exports = singletonMessageComposerStore; diff --git a/test/components/views/rooms/MessageComposerInput-test.js b/test/components/views/rooms/MessageComposerInput-test.js index 60380eecd2..518e7e06ac 100644 --- a/test/components/views/rooms/MessageComposerInput-test.js +++ b/test/components/views/rooms/MessageComposerInput-test.js @@ -19,7 +19,7 @@ function addTextToDraft(text) { } } -// FIXME: These tests need to be updated from Draft to Slate. +// FIXME: These tests need to be updated from Draft to CIDER. xdescribe('MessageComposerInput', () => { let parentDiv = null, diff --git a/yarn.lock b/yarn.lock index fc2b9e04c4..a491ba3941 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2592,11 +2592,6 @@ dir-glob@^2.2.2: dependencies: path-type "^3.0.0" -direction@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c" - integrity sha1-zl15f5fib4vnvv9T99xA4cGp7Ew= - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -3065,11 +3060,6 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" -esrever@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/esrever/-/esrever-0.2.0.tgz#96e9d28f4f1b1a76784cd5d490eaae010e7407b8" - integrity sha1-lunSj08bGnZ4TNXUkOquAQ50B7g= - estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -3653,11 +3643,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-document@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-document/-/get-document-1.0.0.tgz#4821bce66f1c24cb0331602be6cb6b12c4f01c4b" - integrity sha1-SCG85m8cJMsDMWAr5strEsTwHEs= - get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -3675,13 +3660,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -get-window@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-window/-/get-window-1.1.2.tgz#65fbaa999fb87f86ea5d30770f4097707044f47f" - integrity sha512-yjWpFcy9fjhLQHW1dPtg9ga4pmizLY8y4ZSHdGrAQ1NU277MRhnGnnLPxe19X8W5lWVsCZz++5xEuNozWMVmTw== - dependencies: - get-document "1" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -4509,16 +4487,6 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz#e8a426a69b6d31470d3a33a47bb825cda02506ee" integrity sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA== -is-hotkey@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.4.tgz#c34d2c85d6ec8d09a871dcf71931c8067a824c7d" - integrity sha512-Py+aW4r5mBBY18TGzGz286/gKS+fCQ0Hee3qkaiSmEPiD0PqFpe0wuA3l7rTOUKyeXl8Mxf3XzJxIoTlSv+kxA== - -is-in-browser@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" - integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= - is-ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab" @@ -4651,11 +4619,6 @@ is-whitespace-character@^1.0.0: resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac" integrity sha512-SNPgMLz9JzPccD3nPctcj8sZlX9DAMJSKH8bP7Z6bohCwuNgX8xbWr1eTAYXX9Vpi/aSn8Y1akL9WgM3t43YNQ== -is-window@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" - integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= - is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4715,11 +4678,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isomorphic-base64@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/isomorphic-base64/-/isomorphic-base64-1.0.2.tgz#f426aae82569ba8a4ec5ca73ad21a44ab1ee7803" - integrity sha1-9Caq6CVpuopOxcpzrSGkSrHueAM= - isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" @@ -5154,7 +5112,7 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash@^4.1.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -5379,11 +5337,6 @@ memoize-one@^3.0.1: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-3.1.1.tgz#ef609811e3bc28970eac2884eece64d167830d17" integrity sha512-YqVh744GsMlZu6xkhGslPSqSurOv6P+kLN2J3ysBZfagLcL5FdRK/0UpgLoL8hwjjEvvAVkjJZyFP+1T6p1vgA== -memoize-one@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" - integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== - memory-fs@^0.4.0, memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -6715,11 +6668,6 @@ react-focus-lock@^2.2.1: dependencies: gemini-scrollbar matrix-org/gemini-scrollbar#91e1e566 -react-immutable-proptypes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4" - integrity sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ= - react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" @@ -7271,11 +7219,6 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -selection-is-backward@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/selection-is-backward/-/selection-is-backward-1.0.0.tgz#97a54633188a511aba6419fc5c1fa91b467e6be1" - integrity sha1-l6VGMxiKURq6ZBn8XB+pG0Z+a+E= - "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -7371,93 +7314,6 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== -slate-base64-serializer@^0.2.69: - version "0.2.112" - resolved "https://registry.yarnpkg.com/slate-base64-serializer/-/slate-base64-serializer-0.2.112.tgz#791d04a0ae7b9796844f068a904e185f2afc91f9" - integrity sha512-Vo94bkCq8cbFj7Lutdh2RaM9S4WlLxnnMqZPKGUyefklUN4q2EzM/WUH7s9CIlLUH1qRfC/b0V25VJZr5XXTzA== - dependencies: - isomorphic-base64 "^1.0.2" - -slate-dev-environment@^0.2.0, slate-dev-environment@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/slate-dev-environment/-/slate-dev-environment-0.2.2.tgz#bd8946e1fe4cf5447060c84a362a1d026ed8b77f" - integrity sha512-JZ09llrRQu6JUsLJCUlGC0lB1r1qIAabAkSd454iyYBq6lDuY//Bypi3Jo8yzIfzZ4+mRLdQvl9e8MbeM9l48Q== - dependencies: - is-in-browser "^1.1.3" - -slate-dev-logger@^0.1.43: - version "0.1.43" - resolved "https://registry.yarnpkg.com/slate-dev-logger/-/slate-dev-logger-0.1.43.tgz#77f6ca7207fcbf453a5516f3aa8b19794d1d26dc" - integrity sha512-GkcPMGzmPVm85AL+jaKnzhIA0UH9ktQDEIDM+FuQtz+TAPcpPCQiRAaZ6I2p2uD0Hq9bImhKSCtHIa0qRxiVGw== - -slate-dev-warning@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/slate-dev-warning/-/slate-dev-warning-0.0.1.tgz#f6c36731babea5e301b5bd504fe64911dd24200a" - integrity sha512-QdXa+qmOG46VrTfnzn2gUVzs1WiO3Q+zCv3XomzMNGdgAJjCgHBs3jaeQD845h15loS3OJ181gCNAkB3dby6Hw== - -slate-hotkeys@^0.2.5: - version "0.2.9" - resolved "https://registry.yarnpkg.com/slate-hotkeys/-/slate-hotkeys-0.2.9.tgz#0cc9eb750a49ab9ef11601305b7c82b5402348e3" - integrity sha512-y+C/s5vJEmBxo8fIqHmUcdViGwALL/A6Qow3sNG1OHYD5SI11tC2gfYtGbPh+2q0H7O4lufffCmFsP5bMaDHqA== - dependencies: - is-hotkey "0.1.4" - slate-dev-environment "^0.2.2" - -slate-html-serializer@^0.6.1: - version "0.6.32" - resolved "https://registry.yarnpkg.com/slate-html-serializer/-/slate-html-serializer-0.6.32.tgz#69b0fcdb89a0bdcea28b60b6a90b944651ad3277" - integrity sha512-x1RP1R2HMaVFflk9UXiuepcbN4wMoJRv0VWtxFw8efGNFmJfNBWME4iXAy6GNFRV0rRPlG3xCuQv2wHZ/+JMYw== - dependencies: - slate-dev-logger "^0.1.43" - type-of "^2.0.1" - -"slate-md-serializer@github:matrix-org/slate-md-serializer#f7c4ad3": - version "3.1.0" - resolved "https://codeload.github.com/matrix-org/slate-md-serializer/tar.gz/f7c4ad394f5af34d4c623de7909ce95ab78072d3" - -slate-plain-serializer@^0.6.8: - version "0.6.39" - resolved "https://registry.yarnpkg.com/slate-plain-serializer/-/slate-plain-serializer-0.6.39.tgz#5fb8d4dc530a2e7e0689548d48964ce242c4516a" - integrity sha512-EGl+Y+9Fw9IULtPg8sttydaeiAoaibJolMXNfqI79+5GWTQwJFIbg24keKvsTw+3f2RieaPu8fcrKyujKtZ7ZQ== - -slate-prop-types@^0.4.67: - version "0.4.67" - resolved "https://registry.yarnpkg.com/slate-prop-types/-/slate-prop-types-0.4.67.tgz#c6aa74195466546a44fcb85d1c7b15fefe36ce6b" - integrity sha512-FmdwitAw1Y69JHm326dfwP6Zd6R99jz1Im8jvKcnG2hytk72I1vIv6ct2CkNGwc3sg90+OIO/Rf18frYxxoTzw== - -slate-react@^0.18.10: - version "0.18.11" - resolved "https://registry.yarnpkg.com/slate-react/-/slate-react-0.18.11.tgz#f452e7eb73f0271422d2a17e8090dcd8d889aef6" - integrity sha512-7u0+LLabGaxjWYb0oTqUDcs3iCvJdaZwcGW6hLc1hFv06KkwaIxAqYpP8dUBRVlQd+0/X0TdyagCmf0IjFSPhg== - dependencies: - debug "^3.1.0" - get-window "^1.1.1" - is-window "^1.0.2" - lodash "^4.1.1" - memoize-one "^4.0.0" - prop-types "^15.5.8" - react-immutable-proptypes "^2.1.0" - selection-is-backward "^1.0.0" - slate-base64-serializer "^0.2.69" - slate-dev-environment "^0.2.0" - slate-dev-warning "^0.0.1" - slate-hotkeys "^0.2.5" - slate-plain-serializer "^0.6.8" - slate-prop-types "^0.4.67" - -slate@^0.41.2: - version "0.41.3" - resolved "https://registry.yarnpkg.com/slate/-/slate-0.41.3.tgz#fa468de5db53afc453a0a7d7875b4de05737a900" - integrity sha512-I/ymHWRxtoSOWYKh/SFgW3Vkkojt5ywWf7Wh4oBvaKic/3mAsM1wymyZmhnvSKK59IeE0JJzD4uyyQaM1KEFHA== - dependencies: - debug "^3.1.0" - direction "^0.1.5" - esrever "^0.2.0" - is-plain-object "^2.0.4" - lodash "^4.17.4" - slate-dev-warning "^0.0.1" - type-of "^2.0.1" - slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -8264,11 +8120,6 @@ type-is@~1.6.17: media-typer "0.3.0" mime-types "~2.1.24" -type-of@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/type-of/-/type-of-2.0.1.tgz#e72a1741896568e9f628378d816d6912f7f23972" - integrity sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI= - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" From 64754a3f88c8f531100b46359da7bce45267f2c6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 00:18:24 +0000 Subject: [PATCH 080/101] Add comment and delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/LinkPreviewWidget.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js index 4822848233..3b5545e0e0 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -19,7 +19,6 @@ import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import { AllHtmlEntities } from 'html-entities'; - import {linkifyElement} from '../../../HtmlUtils'; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from "../../../languageHandler"; @@ -129,6 +128,10 @@ module.exports = createReactClass({
    ; } + // The description includes &-encoded HTML entities, we decode those as React treats the thing as an + // opaque string. This does not allow any HTML to be injected into the DOM. + const description = AllHtmlEntities.decode(p["og:description"] || ""); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); return (
    @@ -137,7 +140,7 @@ module.exports = createReactClass({
    { p["og:site_name"] ? (" - " + p["og:site_name"]) : null }
    - { AllHtmlEntities.decode(p["og:description"] || "") } + { description }
    From c802f9a302f0463d9029802df93a6d613baa658c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 00:23:19 +0000 Subject: [PATCH 081/101] remove unused stale Test Suite Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .eslintignore.errorfiles | 1 - .../views/rooms/MessageComposerInput-test.js | 303 ------------------ 2 files changed, 304 deletions(-) delete mode 100644 test/components/views/rooms/MessageComposerInput-test.js diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index c3bb34ae26..36b03b121c 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -57,7 +57,6 @@ src/utils/Receipt.js src/Velociraptor.js test/components/structures/MessagePanel-test.js test/components/views/dialogs/InteractiveAuthDialog-test.js -test/components/views/rooms/MessageComposerInput-test.js test/mock-clock.js test/notifications/ContentRules-test.js test/notifications/PushRuleVectorState-test.js diff --git a/test/components/views/rooms/MessageComposerInput-test.js b/test/components/views/rooms/MessageComposerInput-test.js deleted file mode 100644 index 518e7e06ac..0000000000 --- a/test/components/views/rooms/MessageComposerInput-test.js +++ /dev/null @@ -1,303 +0,0 @@ -import React from 'react'; -import ReactTestUtils from 'react-dom/test-utils'; -import ReactDOM from 'react-dom'; -import expect from 'expect'; -import sinon from 'sinon'; -import * as testUtils from '../../../test-utils'; -import sdk from 'matrix-react-sdk'; -const MessageComposerInput = sdk.getComponent('views.rooms.MessageComposerInput'); -import MatrixClientPeg from '../../../../src/MatrixClientPeg'; -import {sleep} from "../../../../src/utils/promise"; - -function addTextToDraft(text) { - const components = document.getElementsByClassName('public-DraftEditor-content'); - if (components && components.length) { - const textarea = components[0]; - const textEvent = document.createEvent('TextEvent'); - textEvent.initTextEvent('textInput', true, true, null, text); - textarea.dispatchEvent(textEvent); - } -} - -// FIXME: These tests need to be updated from Draft to CIDER. - -xdescribe('MessageComposerInput', () => { - let parentDiv = null, - sandbox = null, - client = null, - mci = null, - room = testUtils.mkStubRoom('!DdJkzRliezrwpNebLk:matrix.org'); - - beforeEach(function() { - testUtils.beforeEach(this); - sandbox = testUtils.stubClient(sandbox); - client = MatrixClientPeg.get(); - client.credentials = {userId: '@me:domain.com'}; - - parentDiv = document.createElement('div'); - document.body.appendChild(parentDiv); - mci = ReactDOM.render( - , - parentDiv); - }); - - afterEach((done) => { - // hack: let the component finish mounting before unmounting, to avoid - // warnings - // (please can we make the components not setState() after - // they are unmounted?) - sleep(10).done(() => { - if (parentDiv) { - ReactDOM.unmountComponentAtNode(parentDiv); - parentDiv.remove(); - parentDiv = null; - } - sandbox.restore(); - done(); - }); - }); - - // XXX this fails - xit('should change mode if indicator is clicked', (done) => { - mci.enableRichtext(true); - - setTimeout(() => { - const indicator = ReactTestUtils.findRenderedDOMComponentWithClass( - mci, - 'mx_MessageComposer_input_markdownIndicator'); - ReactTestUtils.Simulate.click(indicator); - - expect(mci.state.isRichTextEnabled).toEqual(false, 'should have changed mode'); - done(); - }); - }); - - it('should not send messages when composer is empty', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(true); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(false, 'should not send message'); - }); - - it('should not change content unnecessarily on RTE -> Markdown conversion', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(true); - addTextToDraft('a'); - mci.handleKeyCommand('toggle-mode'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('a'); - }); - - it('should not change content unnecessarily on Markdown -> RTE conversion', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('a'); - mci.handleKeyCommand('toggle-mode'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('a'); - }); - - it('should send emoji messages when rich text is enabled', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(true); - addTextToDraft('☹'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true, 'should send message'); - }); - - it('should send emoji messages when Markdown is enabled', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('☹'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true, 'should send message'); - }); - - // FIXME - // it('should convert basic Markdown to rich text correctly', () => { - // const spy = sinon.spy(client, 'sendHtmlMessage'); - // mci.enableRichtext(false); - // addTextToDraft('*abc*'); - // mci.handleKeyCommand('toggle-mode'); - // mci.handleReturn(sinon.stub()); - // console.error(spy.args[0][2]); - // expect(spy.args[0][2]).toContain('abc'); - // }); - // - // it('should convert basic rich text to Markdown correctly', () => { - // const spy = sinon.spy(client, 'sendHtmlMessage'); - // mci.enableRichtext(true); - // process.nextTick(() => { - // - // }); - // mci.handleKeyCommand('italic'); - // addTextToDraft('abc'); - // mci.handleKeyCommand('toggle-mode'); - // mci.handleReturn(sinon.stub()); - // expect(['_abc_', '*abc*']).toContain(spy.args[0][1]); - // }); - - it('should insert formatting characters in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - mci.handleKeyCommand('italic'); - mci.handleReturn(sinon.stub()); - expect(['__', '**']).toContain(spy.args[0][1].body); - }); - - it('should not entity-encode " in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('"'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('"'); - }); - - it('should escape characters without other markup in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('\\*escaped\\*'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('*escaped*'); - }); - - it('should escape characters with other markup in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('\\*escaped\\* *italic*'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('\\*escaped\\* *italic*'); - expect(spy.args[0][1].formatted_body).toEqual('*escaped* italic'); - }); - - it('should not convert -_- into a horizontal rule in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('-_-'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('-_-'); - }); - - it('should not strip tags in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('striked-out'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('striked-out'); - expect(spy.args[0][1].formatted_body).toEqual('striked-out'); - }); - - it('should not strike-through ~~~ in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('~~~striked-out~~~'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('~~~striked-out~~~'); - }); - - it('should not mark single unmarkedup paragraphs as HTML in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); - }); - - it('should not mark two unmarkedup paragraphs as HTML in Markdown mode', () => { - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - addTextToDraft('Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nFusce congue sapien sed neque molestie volutpat.'); - mci.handleReturn(sinon.stub()); - - expect(spy.calledOnce).toEqual(true); - expect(spy.args[0][1].body).toEqual('Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nFusce congue sapien sed neque molestie volutpat.'); - }); - - it('should strip tab-completed mentions so that only the display name is sent in the plain body in Markdown mode', () => { - // Sending a HTML message because we have entities in the composer (because of completions) - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(false); - mci.setDisplayedCompletion({ - completion: 'Some Member', - selection: mci.state.editorState.getSelection(), - href: `https://matrix.to/#/@some_member:domain.bla`, - }); - - mci.handleReturn(sinon.stub()); - - expect(spy.args[0][1].body).toEqual( - 'Some Member', - 'the plaintext body should only include the display name', - ); - expect(spy.args[0][1].formatted_body).toEqual( - 'Some Member', - 'the html body should contain an anchor tag with a matrix.to href and display name text', - ); - }); - - it('should strip tab-completed mentions so that only the display name is sent in the plain body in RTE mode', () => { - // Sending a HTML message because we have entities in the composer (because of completions) - const spy = sinon.spy(client, 'sendMessage'); - mci.enableRichtext(true); - mci.setDisplayedCompletion({ - completion: 'Some Member', - selection: mci.state.editorState.getSelection(), - href: `https://matrix.to/#/@some_member:domain.bla`, - }); - - mci.handleReturn(sinon.stub()); - - expect(spy.args[0][1].body).toEqual('Some Member'); - expect(spy.args[0][1].formatted_body).toEqual('Some Member'); - }); - - it('should not strip non-tab-completed mentions when manually typing MD', () => { - // Sending a HTML message because we have entities in the composer (because of completions) - const spy = sinon.spy(client, 'sendMessage'); - // Markdown mode enabled - mci.enableRichtext(false); - addTextToDraft('[My Not-Tab-Completed Mention](https://matrix.to/#/@some_member:domain.bla)'); - - mci.handleReturn(sinon.stub()); - - expect(spy.args[0][1].body).toEqual('[My Not-Tab-Completed Mention](https://matrix.to/#/@some_member:domain.bla)'); - expect(spy.args[0][1].formatted_body).toEqual('My Not-Tab-Completed Mention'); - }); - - it('should not strip arbitrary typed (i.e. not tab-completed) MD links', () => { - // Sending a HTML message because we have entities in the composer (because of completions) - const spy = sinon.spy(client, 'sendMessage'); - // Markdown mode enabled - mci.enableRichtext(false); - addTextToDraft('[Click here](https://some.lovely.url)'); - - mci.handleReturn(sinon.stub()); - - expect(spy.args[0][1].body).toEqual('[Click here](https://some.lovely.url)'); - expect(spy.args[0][1].formatted_body).toEqual('Click here'); - }); -}); From 98ede6437e8d0132bec7ebe61a0dd3cc375c2729 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Sun, 5 Jan 2020 23:04:42 -0600 Subject: [PATCH 082/101] Use display name when pinned messages are changed Signed-off-by: Aaron Raimist --- src/TextForEvent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TextForEvent.js b/src/TextForEvent.js index c3c8396e26..2e79ff2044 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -473,7 +473,7 @@ function textForPowerEvent(event) { } function textForPinnedEvent(event) { - const senderName = event.getSender(); + const senderName = event.sender ? event.sender.name : event.getSender(); return _t("%(senderName)s changed the pinned messages for the room.", {senderName}); } From ac1d9b03bfbf1358ec3e092dc5a35c2e9c4598ad Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 09:35:14 +0000 Subject: [PATCH 083/101] undo one of the "fixes" Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/SettingsFlag.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/SettingsFlag.js b/src/components/views/elements/SettingsFlag.js index d62f3182fc..a3a6d18d33 100644 --- a/src/components/views/elements/SettingsFlag.js +++ b/src/components/views/elements/SettingsFlag.js @@ -34,13 +34,12 @@ module.exports = createReactClass({ getInitialState: function() { return { - // convert to Boolean to protect against null-capable "tri-state" Settings e.g fallbackICEServerAllowed - value: Boolean(SettingsStore.getValueAt( + value: SettingsStore.getValueAt( this.props.level, this.props.name, this.props.roomId, this.props.isExplicit, - )), + ), }; }, From 2af4cdf532f7e82388840f4afb13e352cdc97090 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Mon, 6 Jan 2020 10:05:50 +0000 Subject: [PATCH 084/101] Translated using Weblate (Albanian) Currently translated at 99.1% (1995 of 2014 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 15576d8d9a..e833009d59 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2009,5 +2009,12 @@ "Help": "Ndihmë", "Country Dropdown": "Menu Hapmbyll Vendesh", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Depozita e Fshehtë do të ujdiset duke përdorur hollësitë tuaja ekzistuese për kopjeruajtje kyçesh.Frazëkalimi juaj për në depozitën e fshehtë dhe kyçi i rimarrjes do të jenë të njëjtë me ata për kopjeruajtjen tuaj të kyçeve", - "Migrate from Key Backup": "Migroji prej Kopjeruajtje Kyçesh" + "Migrate from Key Backup": "Migroji prej Kopjeruajtje Kyçesh", + "Show info about bridges in room settings": "Shfaq te rregullime dhome të dhëna rreth urash", + "This bridge was provisioned by ": "Kjo urë është dhënë nga ", + "This bridge is managed by .": "Kjo urë administrohet nga .", + "Connected to on ": "Lidhur me ", + "Connected via %(protocolName)s": "Lidhur përmes %(protocolName)s", + "Bridge Info": "Të dhëna Ure", + "Below is a list of bridges connected to this room.": "Më poshtë keni një listë urash të lidhura në këtë dhomë." } From 3b91b31534e7261d04fad63ad97791572eb2d2ef Mon Sep 17 00:00:00 2001 From: Osoitz Date: Mon, 6 Jan 2020 05:49:40 +0000 Subject: [PATCH 085/101] Translated using Weblate (Basque) Currently translated at 100.0% (2014 of 2014 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eu/ --- src/i18n/strings/eu.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 7214c231e0..e0e667ecce 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -2018,5 +2018,19 @@ "Help": "Laguntza", "Country Dropdown": "Herrialde menua", "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Biltegi sekretua zure oraingo gakoen babes-kopiaren xehetasunak erabiliz ezarriko da. Zure biltegi sekretuaren pasa-esaldia eta berreskuratze gakoa lehen gakoen babes-kopiarako ziren berdinak izango dira", - "Migrate from Key Backup": "Migratu gakoen babes-kopiatik" + "Migrate from Key Backup": "Migratu gakoen babes-kopiatik", + "New DM invite dialog (under development)": "Mezu zuzen bidezko gonbidapen elkarrizketa-koadro berria (garapenean)", + "Show info about bridges in room settings": "Erakutsi zubiei buruzko informazioa gelaren ezarpenetan", + "This bridge was provisioned by ": "Zubi hau erabiltzaileak hornitu du", + "This bridge is managed by .": "Zubi hau erabiltzaileak kudeatzen du.", + "Bridged into , on ": " kanalera zubia, protokoloan", + "Connected to on ": " kanalera konektatuta, sarean", + "Connected via %(protocolName)s": "%(protocolName)s bidez konektatuta", + "Bridge Info": "Zubiaren informazioa", + "Below is a list of bridges connected to this room.": "Behean gela honetara konektatutako zubien informazioa dago.", + "Show more": "Erakutsi gehiago", + "Recent Conversations": "Azken elkarrizketak", + "Direct Messages": "Mezu zuzenak", + "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Ez baduzu baten bat aurkitzen, eska egiezu bere erabiltzaile-izena, edo eman egiezu zurea (%(userId)s) edo profilera esteka.", + "Go": "Joan" } From 51a1507f47d06993574760ae190e7575096c2645 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Mon, 6 Jan 2020 07:27:45 +0000 Subject: [PATCH 086/101] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2014 of 2014 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index b755e16735..db75121f07 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2025,5 +2025,13 @@ "Recent Conversations": "最近的對話", "Direct Messages": "直接訊息", "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "如果您找不到某人,請向他們詢問他們的使用者名稱,或是分享您的使用者名稱 (%(userId)s) 或簡介連結。", - "Go": "到" + "Go": "到", + "Show info about bridges in room settings": "顯示關於聊天室設定中橋接相關的資訊", + "This bridge was provisioned by ": "此橋接由 配置", + "This bridge is managed by .": "此橋接由 管理。", + "Bridged into , on ": "橋接於 ,在 上", + "Connected to on ": "連線到 ", + "Connected via %(protocolName)s": "透過 %(protocolName)s 連線", + "Bridge Info": "橋接資訊", + "Below is a list of bridges connected to this room.": "以下是連線到此聊天室的橋接列表。" } From d8a88387f349e2c995fbefa8d19e976f925559d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Mon, 6 Jan 2020 07:24:10 +0000 Subject: [PATCH 087/101] Translated using Weblate (French) Currently translated at 100.0% (2014 of 2014 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index a44df701f9..c2ee7c575b 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2025,5 +2025,13 @@ "Recent Conversations": "Conversations récentes", "Direct Messages": "Messages directs", "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Si vous n’arrivez pas à trouver quelqu’un, demandez-lui son nom d’utilisateur ou partagez votre nom d’utilisateur (%(userId)s) ou votre lien de profil.", - "Go": "C’est parti" + "Go": "C’est parti", + "Show info about bridges in room settings": "Afficher des informations à propos des passerelles dans les paramètres du salon", + "This bridge was provisioned by ": "Cette passerelle est fournie par ", + "This bridge is managed by .": "Cette passerelle est gérée par .", + "Bridged into , on ": "Relié à , sur ", + "Connected to on ": "Connecté à sur ", + "Connected via %(protocolName)s": "Connecté via %(protocolName)s", + "Bridge Info": "Informations de la passerelle", + "Below is a list of bridges connected to this room.": "Vous trouverez ci-dessous la liste des passerelles connectées à ce salon." } From 393c610b5a6faea93014b1a68ffd05f03805f45b Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 6 Jan 2020 07:06:31 +0000 Subject: [PATCH 088/101] Translated using Weblate (Hungarian) Currently translated at 100.0% (2014 of 2014 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index df482cd072..92113c3238 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2026,5 +2026,13 @@ "Recent Conversations": "Legújabb Beszélgetések", "Direct Messages": "Közvetlen Beszélgetések", "If you can't find someone, ask them for their username, or share your username (%(userId)s) or profile link.": "Ha nem találsz meg valakit, kérdezd meg a felhasználói nevét vagy oszd meg a te felhasználói nevedet (%(userId)s) vagy a profil hivatkozást.", - "Go": "Menj" + "Go": "Menj", + "Show info about bridges in room settings": "Híd információk megmutatása a szoba beállításoknál", + "This bridge was provisioned by ": "Ezt a hidat ez a felhasználó hozta létre: ", + "This bridge is managed by .": "Ezt a hidat ez a felhasználó kezeli: .", + "Bridged into , on ": "Híd ide: , ezzel a protokollal: ", + "Connected to on ": "Ide csatlakozva: itt: ", + "Connected via %(protocolName)s": "Ezzel a protokollal csatlakozva: %(protocolName)s", + "Bridge Info": "Híd információ", + "Below is a list of bridges connected to this room.": "Alább látható a lista a szobához kapcsolódó hidakról." } From d5eb4ff310d2cd328f8795bd6eea37b90a4db86a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 13:02:32 +0000 Subject: [PATCH 089/101] Update src/components/views/settings/ProfileSettings.js Co-Authored-By: J. Ryan Stinnett --- src/components/views/settings/ProfileSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/ProfileSettings.js b/src/components/views/settings/ProfileSettings.js index 72ff7e3d15..b56321c1c9 100644 --- a/src/components/views/settings/ProfileSettings.js +++ b/src/components/views/settings/ProfileSettings.js @@ -89,7 +89,7 @@ export default class ProfileSettings extends React.Component { newState.originalAvatarUrl = newState.avatarUrl; newState.avatarFile = null; } else if (this.state.originalAvatarUrl !== this.state.avatarUrl) { - await client.setAvatarUrl(""); // use empty string as Synapse 500's on undefined + await client.setAvatarUrl(""); // use empty string as Synapse 500s on undefined } this.setState(newState); From c551f2983a2495805f9158e1d335e4ae195ad018 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 13:28:29 +0000 Subject: [PATCH 090/101] Add TextualBody-test.js Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 2 + src/components/views/elements/Pill.js | 2 +- src/utils/pillify.js | 1 + .../views/messages/TextualBody-test.js | 195 +++++++++ test/test-utils.js | 1 + yarn.lock | 376 +++++++++++++++++- 6 files changed, 567 insertions(+), 10 deletions(-) create mode 100644 test/components/views/messages/TextualBody-test.js diff --git a/package.json b/package.json index ad446e26cc..2f8d5f9f7a 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,8 @@ "babel-preset-react": "^6.24.1", "chokidar": "^2.1.2", "concurrently": "^4.0.1", + "enzyme": "^3.10.0", + "enzyme-adapter-react-16": "^1.15.1", "eslint": "^5.12.0", "eslint-config-google": "^0.7.1", "eslint-plugin-babel": "^5.2.1", diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 99005de03b..da40c53700 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -261,7 +261,7 @@ const Pill = createReactClass({ } const classes = classNames("mx_Pill", pillClass, { - "mx_UserPill_me": userId === MatrixClientPeg.get().credentials.userId, + "mx_UserPill_me": userId === MatrixClientPeg.get().getUserId(), "mx_UserPill_selected": this.props.isSelected, }); diff --git a/src/utils/pillify.js b/src/utils/pillify.js index e943cfe657..ed8a7e18cd 100644 --- a/src/utils/pillify.js +++ b/src/utils/pillify.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import React from "react"; import ReactDOM from 'react-dom'; import MatrixClientPeg from '../MatrixClientPeg'; import SettingsStore from "../settings/SettingsStore"; diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js new file mode 100644 index 0000000000..d93bfd307b --- /dev/null +++ b/test/components/views/messages/TextualBody-test.js @@ -0,0 +1,195 @@ +import React from "react"; +import expect from 'expect'; +import Adapter from "enzyme-adapter-react-16"; +import { configure, mount } from "enzyme"; + +import sdk from "../../../skinned-sdk"; +import {mkEvent, mkStubRoom} from "../../../test-utils"; +import MatrixClientPeg from "../../../../src/MatrixClientPeg"; + +const TextualBody = sdk.getComponent("views.messages.TextualBody"); + +configure({ adapter: new Adapter() }); + +describe("", () => { + afterEach(() => { + MatrixClientPeg.matrixClient = null; + }); + + describe("renders m.emote correctly", () => { + MatrixClientPeg.matrixClient = { + getRoom: () => mkStubRoom("room_id"), + getAccountData: () => undefined, + }; + + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "winks", + msgtype: "m.emote", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe("* sender winks"); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe('winks'); + }); + + describe("renders m.notice correctly", () => { + MatrixClientPeg.matrixClient = { + getRoom: () => mkStubRoom("room_id"), + getAccountData: () => undefined, + }; + + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "bot_sender", + content: { + body: "this is a notice, probably from a bot", + msgtype: "m.notice", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe(ev.getContent().body); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe(`${ ev.getContent().body }`); + }); + + describe("renders plain-text m.text correctly", () => { + MatrixClientPeg.matrixClient = { + getRoom: () => mkStubRoom("room_id"), + getAccountData: () => undefined, + }; + + describe("simple message renders as expected", () => { + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "this is a plaintext message", + msgtype: "m.text", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe(ev.getContent().body); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe(`${ ev.getContent().body }`); + }); + + // If pills were rendered within a Portal/same shadow DOM then it'd be easier to test + describe("linkification get applied correctly into the DOM", () => { + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "Visit https://matrix.org/", + msgtype: "m.text", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe(ev.getContent().body); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe('' + + 'Visit ' + + 'https://matrix.org/'); + }); + }); + + describe("renders formatted m.text correctly", () => { + MatrixClientPeg.matrixClient = { + getRoom: () => mkStubRoom("room_id"), + getAccountData: () => undefined, + getUserId: () => "@me:my_server", + getHomeserverUrl: () => "https://my_server/", + on: () => undefined, + removeListener: () => undefined, + }; + + describe("italics, bold, underline and strikethrough render as expected", () => { + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "foo *baz* __bar__ del u", + msgtype: "m.text", + format: "org.matrix.custom.html", + formatted_body: "foo baz bar del u", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe("foo baz bar del u"); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe('' + + ev.getContent().formatted_body + ''); + }); + + describe("spoilers get injected properly into the DOM", () => { + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "Hey [Spoiler for movie](mxc://someserver/somefile)", + msgtype: "m.text", + format: "org.matrix.custom.html", + formatted_body: "Hey the movie was awesome", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe("Hey (movie) the movie was awesome"); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe('' + + 'Hey ' + + '' + + '(movie) ' + + 'the movie was awesome' + + ''); + }); + + // If pills were rendered within a Portal/same shadow DOM then it'd be easier to test + describe("pills get injected correctly into the DOM", () => { + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "Hey User", + msgtype: "m.text", + format: "org.matrix.custom.html", + formatted_body: "Hey Member", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe("Hey Member"); + const content = wrapper.find(".mx_EventTile_body"); + expect(content.html()).toBe('' + + 'Hey ' + + '' + + 'Member' + + ''); + }); + }); +}); + + diff --git a/test/test-utils.js b/test/test-utils.js index 5c8c7f8a10..e27fff3388 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -238,6 +238,7 @@ export function mkStubRoom(roomId = null) { getMember: sinon.stub().returns({ userId: '@member:domain.bla', name: 'Member', + rawDisplayName: 'Member', roomId: roomId, getAvatarUrl: () => 'mxc://avatar.url/image.png', }), diff --git a/yarn.lock b/yarn.lock index a491ba3941..c32a108934 100644 --- a/yarn.lock +++ b/yarn.lock @@ -531,6 +531,22 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +airbnb-prop-types@^2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef" + integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== + dependencies: + array.prototype.find "^2.1.0" + function.prototype.name "^1.1.1" + has "^1.0.3" + is-regex "^1.0.4" + object-is "^1.0.1" + object.assign "^4.1.0" + object.entries "^1.1.0" + prop-types "^15.7.2" + prop-types-exact "^1.2.0" + react-is "^16.9.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -659,6 +675,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -699,6 +720,22 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.find@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7" + integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.13.0" + +array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" @@ -1621,6 +1658,11 @@ body-parser@^1.16.1: raw-body "2.4.0" type-is "~1.6.17" +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1957,6 +1999,18 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +cheerio@^1.0.0-rc.3: + version "1.0.0-rc.3" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" + integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.1" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + chokidar@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -2136,7 +2190,7 @@ commander@2.15.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== -commander@^2.11.0, commander@^2.20.0: +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2374,6 +2428,21 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -2592,6 +2661,11 @@ dir-glob@^2.2.2: dependencies: path-type "^3.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2631,12 +2705,20 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +dom-serializer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.1: +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -2653,6 +2735,14 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + domutils@^1.5.1: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -2816,7 +2906,7 @@ ent@~2.2.0: resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= -entities@^1.1.1, "entities@~ 1.1.1": +entities@^1.1.1, "entities@~ 1.1.1", entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -2826,6 +2916,69 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +enzyme-adapter-react-16@^1.15.1: + version "1.15.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz#b16db2f0ea424d58a808f9df86ab6212895a4501" + integrity sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q== + dependencies: + enzyme-adapter-utils "^1.13.0" + enzyme-shallow-equal "^1.0.1" + has "^1.0.3" + object.assign "^4.1.0" + object.values "^1.1.1" + prop-types "^15.7.2" + react-is "^16.12.0" + react-test-renderer "^16.0.0-0" + semver "^5.7.0" + +enzyme-adapter-utils@^1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz#01c885dde2114b4690bf741f8dc94cee3060eb78" + integrity sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ== + dependencies: + airbnb-prop-types "^2.15.0" + function.prototype.name "^1.1.2" + object.assign "^4.1.0" + object.fromentries "^2.0.2" + prop-types "^15.7.2" + semver "^5.7.1" + +enzyme-shallow-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz#7afe03db3801c9b76de8440694096412a8d9d49e" + integrity sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ== + dependencies: + has "^1.0.3" + object-is "^1.0.2" + +enzyme@^3.10.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28" + integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw== + dependencies: + array.prototype.flat "^1.2.3" + cheerio "^1.0.0-rc.3" + enzyme-shallow-equal "^1.0.1" + function.prototype.name "^1.1.2" + has "^1.0.3" + html-element-map "^1.2.0" + is-boolean-object "^1.0.1" + is-callable "^1.1.5" + is-number-object "^1.0.4" + is-regex "^1.0.5" + is-string "^1.0.5" + is-subset "^0.1.1" + lodash.escape "^4.0.1" + lodash.isequal "^4.5.0" + object-inspect "^1.7.0" + object-is "^1.0.2" + object.assign "^4.1.0" + object.entries "^1.1.1" + object.values "^1.1.1" + raf "^3.4.1" + rst-selector-parser "^2.2.3" + string.prototype.trim "^1.2.1" + err-code@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" @@ -2861,6 +3014,23 @@ es-abstract@^1.12.0, es-abstract@^1.15.0, es-abstract@^1.16.2, es-abstract@^1.7. string.prototype.trimleft "^2.1.0" string.prototype.trimright "^2.1.0" +es-abstract@^1.13.0, es-abstract@^1.17.0-next.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" + integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + es-get-iterator@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.0.1.tgz#ebc4d3bbc2d59dd95a0ecef441ca2dbce7d9e2cd" @@ -3600,6 +3770,15 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.1, function.prototype.name@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45" + integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + functions-have-names "^1.2.0" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -3988,12 +4167,19 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== +html-element-map@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.2.0.tgz#dfbb09efe882806af63d990cf6db37993f099f22" + integrity sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw== + dependencies: + array-filter "^1.0.0" + html-tags@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= -htmlparser2@^3.10.0: +htmlparser2@^3.10.0, htmlparser2@^3.9.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -4312,6 +4498,11 @@ is-boolean-object@^1.0.0: resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" integrity sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M= +is-boolean-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -4327,6 +4518,11 @@ is-callable@^1.0.4, is-callable@^1.1.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4504,6 +4700,11 @@ is-number-object@^1.0.3: resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" integrity sha1-8mWrian0RQNO9q/xWo8AsA9VF5k= +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -4567,6 +4768,13 @@ is-regex@^1.0.3, is-regex@^1.0.4: dependencies: has "^1.0.1" +is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" @@ -4587,6 +4795,16 @@ is-string@^1.0.4: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64" integrity sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ= +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-subset@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= + is-supported-regexp-flag@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz#21ee16518d2c1dd3edd3e9a0d57e50207ac364ca" @@ -5072,11 +5290,21 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= + lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -5087,6 +5315,11 @@ lodash.isboolean@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -5112,7 +5345,7 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: +lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -5559,6 +5792,11 @@ mocha@^5.0.5: mkdirp "0.5.1" supports-color "5.4.0" +moo@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" + integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -5613,6 +5851,17 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +nearley@^2.7.10: + version "2.19.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.0.tgz#37717781d0fd0f2bfc95e233ebd75678ca4bda46" + integrity sha512-2v52FTw7RPqieZr3Gth1luAXZR7Je6q3KaDHY5bjl/paDUdMu35fZ8ICNgiYJRr3tf3NMvIQQR1r27AvEr9CRA== + dependencies: + commander "^2.19.0" + moo "^0.4.3" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + semver "^5.4.1" + needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -5787,6 +6036,13 @@ npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + null-check@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" @@ -5831,6 +6087,11 @@ object-inspect@^1.1.0, object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== +object-is@^1.0.1, object-is@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.9, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5863,6 +6124,16 @@ object.entries@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +object.entries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" + integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + object.fromentries@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.1.tgz#050f077855c7af8ae6649f45c80b16ee2d31e704" @@ -5873,6 +6144,16 @@ object.fromentries@^2.0.1: function-bind "^1.1.1" has "^1.0.3" +object.fromentries@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" + integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -5898,6 +6179,16 @@ object.values@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -6108,6 +6399,13 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -6426,6 +6724,15 @@ promise@^7.0.3, promise@^7.1.1: dependencies: asap "~2.0.3" +prop-types-exact@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" + integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + dependencies: + has "^1.0.3" + object.assign "^4.1.0" + reflect.ownkeys "^0.2.0" + prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -6554,13 +6861,26 @@ raf-schd@^2.1.0: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-2.1.2.tgz#ec622b5167f2912089f054dc03ebd5bcf33c8f62" integrity sha512-Orl0IEvMtUCgPddgSxtxreK77UiQz4nPYJy9RggVzu4mKsZkQWiAaG1y9HlYWdvm9xtN348xRaT37qkvL/+A+g== -raf@^3.1.0: +raf@^3.1.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== dependencies: performance-now "^2.1.0" +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + randomatic@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" @@ -6668,7 +6988,7 @@ react-focus-lock@^2.2.1: dependencies: gemini-scrollbar matrix-org/gemini-scrollbar#91e1e566 -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: +react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== @@ -6700,7 +7020,7 @@ react-redux@^5.0.6: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react-test-renderer@^16.9.0: +react-test-renderer@^16.0.0-0, react-test-renderer@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.12.0.tgz#11417ffda579306d4e841a794d32140f3da1b43f" integrity sha512-Vj/teSqt2oayaWxkbhQ6gKis+t5JrknXfPVo+aIJ8QwYAqMPH77uptOdrlphyxl8eQI/rtkOYg86i/UWkpFu0w== @@ -6817,6 +7137,11 @@ redux@^3.7.2: loose-envify "^1.1.0" symbol-observable "^1.0.3" +reflect.ownkeys@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" + integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= + regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" @@ -7133,6 +7458,14 @@ rollup-pluginutils@^2.8.1: dependencies: estree-walker "^0.6.1" +rst-selector-parser@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" + integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= + dependencies: + lodash.flattendeep "^4.4.0" + nearley "^2.7.10" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -7219,7 +7552,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -7668,6 +8001,15 @@ string.prototype.repeat@^0.2.0: resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" integrity sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8= +string.prototype.trim@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782" + integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + string.prototype.trimleft@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" @@ -7676,6 +8018,14 @@ string.prototype.trimleft@^2.1.0: define-properties "^1.1.3" function-bind "^1.1.1" +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + string.prototype.trimright@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" @@ -7684,6 +8034,14 @@ string.prototype.trimright@^2.1.0: define-properties "^1.1.3" function-bind "^1.1.1" +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" From f1de6d060acd66b5ba69f7f494fd176d161b40f5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 14:38:21 +0000 Subject: [PATCH 091/101] Add a regression test for editing events and url previews Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../keybackup/CreateKeyBackupDialog.js | 2 +- .../views/settings/KeyBackupPanel.js | 2 +- .../views/messages/TextualBody-test.js | 62 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 19720e077a..1095fb4aee 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -24,7 +24,7 @@ import MatrixClientPeg from '../../../../MatrixClientPeg'; import { scorePassword } from '../../../../utils/PasswordScorer'; import { _t } from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../CrossSigningManager'; -import SettingsStore from '../../../../../lib/settings/SettingsStore'; +import SettingsStore from '../../../../settings/SettingsStore'; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; diff --git a/src/components/views/settings/KeyBackupPanel.js b/src/components/views/settings/KeyBackupPanel.js index bfa96f277f..3781f97dbe 100644 --- a/src/components/views/settings/KeyBackupPanel.js +++ b/src/components/views/settings/KeyBackupPanel.js @@ -21,7 +21,7 @@ import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; -import SettingsStore from '../../../../lib/settings/SettingsStore'; +import SettingsStore from '../../../settings/SettingsStore'; export default class KeyBackupPanel extends React.PureComponent { constructor(props) { diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index d93bfd307b..9e721d1d09 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -6,6 +6,8 @@ import { configure, mount } from "enzyme"; import sdk from "../../../skinned-sdk"; import {mkEvent, mkStubRoom} from "../../../test-utils"; import MatrixClientPeg from "../../../../src/MatrixClientPeg"; +import * as languageHandler from "../../../../src/languageHandler"; +import {sleep} from "../../../../src/utils/promise"; const TextualBody = sdk.getComponent("views.messages.TextualBody"); @@ -190,6 +192,66 @@ describe("", () => { ''); }); }); + + describe("renders url previews correctly", () => { + languageHandler.setMissingEntryGenerator(key => key.split('|', 2)[1]); + + MatrixClientPeg.matrixClient = { + getRoom: () => mkStubRoom("room_id"), + getAccountData: () => undefined, + getUrlPreview: (url) => new Promise(() => {}), + }; + + const ev = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + body: "Visit https://matrix.org/", + msgtype: "m.text", + }, + event: true, + }); + + const wrapper = mount(); + expect(wrapper.text()).toBe(ev.getContent().body); + + let widgets = wrapper.find("LinkPreviewWidget"); + // at this point we should have exactly one widget + expect(widgets.length).toBe(1); + expect(widgets.at(0).prop("link")).toBe("https://matrix.org/"); + + // simulate an event edit and check the transition from the old URL preview to the new one + const ev2 = mkEvent({ + type: "m.room.message", + room: "room_id", + user: "sender", + content: { + "m.new_content": { + body: "Visit https://vector.im/ and https://riot.im/", + msgtype: "m.text", + }, + }, + event: true, + }); + ev.makeReplaced(ev2); + + wrapper.setProps({ + mxEvent: ev, + replacingEventId: ev.getId(), + }, () => { + expect(wrapper.text()).toBe(ev2.getContent()["m.new_content"].body + "(edited)"); + + // XXX: this is to give TextualBody enough time for state to settle + wrapper.setState({}, () => { + widgets = wrapper.find("LinkPreviewWidget"); + // at this point we should have exactly two widgets (not the matrix.org one anymore) + expect(widgets.length).toBe(2); + expect(widgets.at(0).prop("link")).toBe("https://vector.im/"); + expect(widgets.at(1).prop("link")).toBe("https://riot.im/"); + }); + }); + }); }); From bdef54622b81c2855d0605f2944b88bb1519e337 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 14:52:08 +0000 Subject: [PATCH 092/101] delint and add copyright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/messages/TextualBody-test.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 9e721d1d09..7aeff94404 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -1,3 +1,19 @@ +/* +Copyright 2019 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 expect from 'expect'; import Adapter from "enzyme-adapter-react-16"; @@ -7,7 +23,6 @@ import sdk from "../../../skinned-sdk"; import {mkEvent, mkStubRoom} from "../../../test-utils"; import MatrixClientPeg from "../../../../src/MatrixClientPeg"; import * as languageHandler from "../../../../src/languageHandler"; -import {sleep} from "../../../../src/utils/promise"; const TextualBody = sdk.getComponent("views.messages.TextualBody"); From bef824e84ee36769712fc3c48171032056875df0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 6 Jan 2020 12:21:59 -0700 Subject: [PATCH 093/101] Remove harmful html entities encoding and other style nits React will take care of this for us. It's harmful because simple characters get converted to something illegible. --- src/HtmlUtils.js | 5 ----- src/components/views/dialogs/DMInviteDialog.js | 12 ++++-------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index ce677e6c68..7cdff26a21 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -528,8 +528,3 @@ export function checkBlockNode(node) { return false; } } - -export function htmlEntitiesEncode(str: string) { - // Source: https://stackoverflow.com/a/18750001/7037379 - return str.replace(/[\u00A0-\u9999<>&]/gim, i => `&#${i.charCodeAt(0)};`); -} diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js index aec64919a0..bb3e38a304 100644 --- a/src/components/views/dialogs/DMInviteDialog.js +++ b/src/components/views/dialogs/DMInviteDialog.js @@ -24,7 +24,6 @@ import DMRoomMap from "../../../utils/DMRoomMap"; import {RoomMember} from "matrix-js-sdk/lib/matrix"; import * as humanize from "humanize"; import SdkConfig from "../../../SdkConfig"; -import {htmlEntitiesEncode} from "../../../HtmlUtils"; import {getHttpUriForMxc} from "matrix-js-sdk/lib/content-repo"; // TODO: [TravisR] Make this generic for all kinds of invites @@ -77,11 +76,9 @@ class DMRoomTile extends React.PureComponent { _highlightName(str: string) { if (!this.props.highlightWord) return str; - // First encode the thing to avoid injection - str = htmlEntitiesEncode(str); - // We convert things to lowercase for index searching, but pull substrings from - // the submitted text to preserve case. + // the submitted text to preserve case. Note: we don't need to htmlEntities the + // string because React will safely encode the text for us. const lowerStr = str.toLowerCase(); const filterStr = this.props.highlightWord.toLowerCase(); @@ -92,8 +89,8 @@ class DMRoomTile extends React.PureComponent { while ((ii = lowerStr.indexOf(filterStr, i)) >= 0) { // Push any text we missed (first bit/middle of text) if (ii > i) { - // Push any text we aren't highlighting (middle of text match) - result.push({str.substring(i, ii)}); + // Push any text we aren't highlighting (middle of text match, or beginning of text) + result.push({str.substring(i, ii)}); } i = ii; // copy over ii only if we have a match (to preserve i for end-of-text matching) @@ -333,7 +330,6 @@ export default class DMInviteDialog extends React.PureComponent { } } - // If we're going to hide one member behind 'show more', just use up the space of the button // with the member's tile instead. if (showNum === sourceMembers.length - 1) showNum++; From 6c410e602787ef041f85fb6aa62ca6ae5bae3b4f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 6 Jan 2020 12:23:35 -0700 Subject: [PATCH 094/101] Fix comment style --- src/components/views/dialogs/DMInviteDialog.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js index bdeae6bc3e..2854c914f8 100644 --- a/src/components/views/dialogs/DMInviteDialog.js +++ b/src/components/views/dialogs/DMInviteDialog.js @@ -115,6 +115,8 @@ export default class DMInviteDialog extends React.PureComponent { const joinedRooms = client.getRooms() .filter(r => r.getMyMembership() === 'join') .filter(r => r.getJoinedMemberCount() <= maxConsideredMembers); + + // Generates { userId: {member, rooms[]} } const memberRooms = joinedRooms.reduce((members, room) => { const joinedMembers = room.getJoinedMembers().filter(u => !excludedUserIds.includes(u.userId)); for (const member of joinedMembers) { @@ -136,7 +138,9 @@ export default class DMInviteDialog extends React.PureComponent { } } return members; - }, {/* userId => {member, rooms[]} */}); + }, {}); + + // Generates { userId: {member, numRooms, score} } const memberScores = Object.values(memberRooms).reduce((scores, entry) => { const numMembersTotal = entry.rooms.reduce((c, r) => c + r.getJoinedMemberCount(), 0); const maxRange = maxConsideredMembers * entry.rooms.length; @@ -146,7 +150,8 @@ export default class DMInviteDialog extends React.PureComponent { score: Math.max(0, Math.pow(1 - (numMembersTotal / maxRange), 5)), }; return scores; - }, {/* userId => {member, numRooms, score} */}); + }, {}); + const members = Object.values(memberScores); members.sort((a, b) => { if (a.score === b.score) { From 7b91d2056b2098e7a12e574a8495503d23570c15 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Jan 2020 23:50:42 +0000 Subject: [PATCH 095/101] Attempt to fix e2e tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- test/end-to-end-tests/src/usecases/room-settings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/end-to-end-tests/src/usecases/room-settings.js b/test/end-to-end-tests/src/usecases/room-settings.js index 5b425f14b7..8e9e267d45 100644 --- a/test/end-to-end-tests/src/usecases/room-settings.js +++ b/test/end-to-end-tests/src/usecases/room-settings.js @@ -55,6 +55,7 @@ module.exports = async function changeRoomSettings(session, settings) { await session.replaceInputText(aliasField, settings.alias.substring(1, settings.alias.lastIndexOf(":"))); const addButton = await session.query(".mx_RoomSettingsDialog .mx_AliasSettings .mx_AccessibleButton"); await addButton.click(); + await session.delay(10); // delay to give time for the validator to run and check the alias session.log.done(); } From a62c260f794f2f89815045458b05a656bc5962dc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 7 Jan 2020 12:58:24 +0000 Subject: [PATCH 096/101] Fix userinfo for users not in the room Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/right_panel/UserInfo.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index b55991eda8..9c90953114 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1000,8 +1000,8 @@ const UserInfo = ({user, groupId, roomId, onClose}) => { // Load room if we are given a room id and memoize it const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]); - // fetch latest room member if we have a room, so we don't show historical information - const member = useMemo(() => room ? room.getMember(user.userId) : user, [room, user]); + // fetch latest room member if we have a room, so we don't show historical information, falling back to user + const member = useMemo(() => room ? (room.getMember(user.userId) || user) : user, [room, user]); // only display the devices list if our client supports E2E const _enableDevices = cli.isCryptoEnabled(); From 40b23d6aa4cb6ad465c4ec1c9c2b55bd2ebe3cc9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 7 Jan 2020 18:48:55 +0000 Subject: [PATCH 097/101] Strip all variation selectors on emoji ...when inserting into or looking up in the unicode to emoji map. This broke with emojibase 4.2.0 which changed the type of a whole load of emojis to 'text' when previously they were 'emoji'. This caused them to get the 'text' variant of the unicode string which has the text variation selector (15) appended instead of the emoji variation selector (16). We were only stripping the emoji selector, so upgrading to 4.2.0 caused riot to fail to find the heart in the unicode map, which therefore prevented the app from starting. --- src/emoji.js | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/emoji.js b/src/emoji.js index 7b7a9c1bfe..8b3c4c9fe4 100644 --- a/src/emoji.js +++ b/src/emoji.js @@ -16,14 +16,12 @@ limitations under the License. import EMOJIBASE from 'emojibase-data/en/compact.json'; -export const VARIATION_SELECTOR = String.fromCharCode(0xFE0F); - // The unicode is stored without the variant selector const UNICODE_TO_EMOJI = new Map(); // not exported as gets for it are handled by getEmojiFromUnicode export const EMOTICON_TO_EMOJI = new Map(); export const SHORTCODE_TO_EMOJI = new Map(); -export const getEmojiFromUnicode = unicode => UNICODE_TO_EMOJI.get(unicode.replace(VARIATION_SELECTOR, "")); +export const getEmojiFromUnicode = unicode => UNICODE_TO_EMOJI.get(stripVariation(unicode)); const EMOJIBASE_GROUP_ID_TO_CATEGORY = [ "people", // smileys @@ -51,13 +49,6 @@ export const DATA_BY_CATEGORY = { // Store various mappings from unicode/emoticon/shortcode to the Emoji objects EMOJIBASE.forEach(emoji => { - if (emoji.unicode.includes(VARIATION_SELECTOR)) { - // Clone data into variation-less version - emoji = Object.assign({}, emoji, { - unicode: emoji.unicode.replace(VARIATION_SELECTOR, ""), - }); - } - const categoryId = EMOJIBASE_GROUP_ID_TO_CATEGORY[emoji.group]; if (DATA_BY_CATEGORY.hasOwnProperty(categoryId)) { DATA_BY_CATEGORY[categoryId].push(emoji); @@ -66,7 +57,13 @@ EMOJIBASE.forEach(emoji => { emoji.filterString = `${emoji.annotation}\n${emoji.shortcodes.join('\n')}}\n${emoji.emoticon || ''}`.toLowerCase(); // Add mapping from unicode to Emoji object - UNICODE_TO_EMOJI.set(emoji.unicode, emoji); + // The 'unicode' field that we use in emojibase has either + // VS15 or VS16 appended to any characters that can take + // variation selectors. Which one it appends depends + // on whether emojibase considers their type to be 'text' or + // 'emoji'. We therefore strip any variation chars from strings + // both when building the map and when looking up. + UNICODE_TO_EMOJI.set(stripVariation(emoji.unicode), emoji); if (emoji.emoticon) { // Add mapping from emoticon to Emoji object @@ -80,3 +77,23 @@ EMOJIBASE.forEach(emoji => { }); } }); + +/** + * Strips variation selectors from a string + * NB. Skin tone modifers are not variation selectors: + * this function does not touch them. (Should it?) + * + * @param {string} str string to strip + * @returns {string} stripped string + */ +function stripVariation(str) { + let ret = ''; + for (let i = 0; i < str.length; ++i) { + const charCode = str.charCodeAt(i); + // append to output only if it's outside the variation selector range + if (charCode < 0xFE00 && charCode > 0xFE0F) { + ret += str.charAt(i); + } + } + return ret; +} From 2d410c91acdc41fa7bcd985787429e40f8188055 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 7 Jan 2020 19:57:17 +0000 Subject: [PATCH 098/101] Use a regex because it's simpler and works and my loop did not because I meant 'or', not 'and' --- src/emoji.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/emoji.js b/src/emoji.js index 8b3c4c9fe4..d62630ae08 100644 --- a/src/emoji.js +++ b/src/emoji.js @@ -87,13 +87,5 @@ EMOJIBASE.forEach(emoji => { * @returns {string} stripped string */ function stripVariation(str) { - let ret = ''; - for (let i = 0; i < str.length; ++i) { - const charCode = str.charCodeAt(i); - // append to output only if it's outside the variation selector range - if (charCode < 0xFE00 && charCode > 0xFE0F) { - ret += str.charAt(i); - } - } - return ret; + return str.replace("\uFE00-\uFE0F", ""); } From 45ef57d86f9d2a5523468c2a8c7ee87fd158bcaa Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 7 Jan 2020 20:41:19 +0000 Subject: [PATCH 099/101] Use a regex that actually works --- src/emoji.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emoji.js b/src/emoji.js index d62630ae08..125864e381 100644 --- a/src/emoji.js +++ b/src/emoji.js @@ -87,5 +87,5 @@ EMOJIBASE.forEach(emoji => { * @returns {string} stripped string */ function stripVariation(str) { - return str.replace("\uFE00-\uFE0F", ""); + return str.replace(/[\uFE00-\uFE0F]/, ""); } From 8116ac74cef1235ef8efd34b137955433a14c35c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Jan 2020 12:10:44 +0000 Subject: [PATCH 100/101] Update src/components/views/context_menus/MessageContextMenu.js Co-Authored-By: Travis Ralston --- src/components/views/context_menus/MessageContextMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index 2084a67cdc..ef1822b555 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -422,7 +422,7 @@ module.exports = createReactClass({ ); - if (this.props.eventTileOps) { // this event is rendered using TextuaLBody + if (this.props.eventTileOps) { // this event is rendered using TextualBody quoteButton = ( { _t('Quote') } From f3f929207c6c9774e8bb159d876b4de2acae4967 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Jan 2020 12:18:14 +0000 Subject: [PATCH 101/101] Update yarn.lock Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- yarn.lock | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 367 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 36b6acee26..34c089dc97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -531,6 +531,22 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +airbnb-prop-types@^2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef" + integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== + dependencies: + array.prototype.find "^2.1.0" + function.prototype.name "^1.1.1" + has "^1.0.3" + is-regex "^1.0.4" + object-is "^1.0.1" + object.assign "^4.1.0" + object.entries "^1.1.0" + prop-types "^15.7.2" + prop-types-exact "^1.2.0" + react-is "^16.9.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -659,6 +675,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -699,6 +720,22 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.find@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7" + integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.13.0" + +array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" @@ -1621,6 +1658,11 @@ body-parser@^1.16.1: raw-body "2.4.0" type-is "~1.6.17" +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1957,6 +1999,18 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +cheerio@^1.0.0-rc.3: + version "1.0.0-rc.3" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" + integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.1" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + chokidar@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -2136,7 +2190,7 @@ commander@2.15.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== -commander@^2.11.0, commander@^2.20.0: +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2374,6 +2428,21 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -2592,6 +2661,11 @@ dir-glob@^2.2.2: dependencies: path-type "^3.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2631,12 +2705,20 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +dom-serializer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.1: +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -2653,6 +2735,14 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + domutils@^1.5.1: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -2816,7 +2906,7 @@ ent@~2.2.0: resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= -entities@^1.1.1, "entities@~ 1.1.1": +entities@^1.1.1, "entities@~ 1.1.1", entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -2826,6 +2916,69 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +enzyme-adapter-react-16@^1.15.1: + version "1.15.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz#b16db2f0ea424d58a808f9df86ab6212895a4501" + integrity sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q== + dependencies: + enzyme-adapter-utils "^1.13.0" + enzyme-shallow-equal "^1.0.1" + has "^1.0.3" + object.assign "^4.1.0" + object.values "^1.1.1" + prop-types "^15.7.2" + react-is "^16.12.0" + react-test-renderer "^16.0.0-0" + semver "^5.7.0" + +enzyme-adapter-utils@^1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz#01c885dde2114b4690bf741f8dc94cee3060eb78" + integrity sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ== + dependencies: + airbnb-prop-types "^2.15.0" + function.prototype.name "^1.1.2" + object.assign "^4.1.0" + object.fromentries "^2.0.2" + prop-types "^15.7.2" + semver "^5.7.1" + +enzyme-shallow-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz#7afe03db3801c9b76de8440694096412a8d9d49e" + integrity sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ== + dependencies: + has "^1.0.3" + object-is "^1.0.2" + +enzyme@^3.10.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28" + integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw== + dependencies: + array.prototype.flat "^1.2.3" + cheerio "^1.0.0-rc.3" + enzyme-shallow-equal "^1.0.1" + function.prototype.name "^1.1.2" + has "^1.0.3" + html-element-map "^1.2.0" + is-boolean-object "^1.0.1" + is-callable "^1.1.5" + is-number-object "^1.0.4" + is-regex "^1.0.5" + is-string "^1.0.5" + is-subset "^0.1.1" + lodash.escape "^4.0.1" + lodash.isequal "^4.5.0" + object-inspect "^1.7.0" + object-is "^1.0.2" + object.assign "^4.1.0" + object.entries "^1.1.1" + object.values "^1.1.1" + raf "^3.4.1" + rst-selector-parser "^2.2.3" + string.prototype.trim "^1.2.1" + err-code@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" @@ -2861,6 +3014,23 @@ es-abstract@^1.12.0, es-abstract@^1.15.0, es-abstract@^1.16.2, es-abstract@^1.7. string.prototype.trimleft "^2.1.0" string.prototype.trimright "^2.1.0" +es-abstract@^1.13.0, es-abstract@^1.17.0-next.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" + integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + es-get-iterator@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.0.1.tgz#ebc4d3bbc2d59dd95a0ecef441ca2dbce7d9e2cd" @@ -3600,6 +3770,15 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.1, function.prototype.name@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45" + integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + functions-have-names "^1.2.0" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -3988,6 +4167,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== +html-element-map@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.2.0.tgz#dfbb09efe882806af63d990cf6db37993f099f22" + integrity sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw== + dependencies: + array-filter "^1.0.0" + html-entities@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" @@ -3998,7 +4184,7 @@ html-tags@^2.0.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= -htmlparser2@^3.10.0: +htmlparser2@^3.10.0, htmlparser2@^3.9.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -4317,6 +4503,11 @@ is-boolean-object@^1.0.0: resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" integrity sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M= +is-boolean-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -4332,6 +4523,11 @@ is-callable@^1.0.4, is-callable@^1.1.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4509,6 +4705,11 @@ is-number-object@^1.0.3: resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" integrity sha1-8mWrian0RQNO9q/xWo8AsA9VF5k= +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -4572,6 +4773,13 @@ is-regex@^1.0.3, is-regex@^1.0.4: dependencies: has "^1.0.1" +is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" @@ -4592,6 +4800,16 @@ is-string@^1.0.4: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64" integrity sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ= +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-subset@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= + is-supported-regexp-flag@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz#21ee16518d2c1dd3edd3e9a0d57e50207ac364ca" @@ -5077,11 +5295,21 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= + lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -5092,6 +5320,11 @@ lodash.isboolean@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -5117,7 +5350,7 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: +lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -5564,6 +5797,11 @@ mocha@^5.0.5: mkdirp "0.5.1" supports-color "5.4.0" +moo@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" + integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -5618,6 +5856,17 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +nearley@^2.7.10: + version "2.19.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.0.tgz#37717781d0fd0f2bfc95e233ebd75678ca4bda46" + integrity sha512-2v52FTw7RPqieZr3Gth1luAXZR7Je6q3KaDHY5bjl/paDUdMu35fZ8ICNgiYJRr3tf3NMvIQQR1r27AvEr9CRA== + dependencies: + commander "^2.19.0" + moo "^0.4.3" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + semver "^5.4.1" + needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -5792,6 +6041,13 @@ npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + null-check@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" @@ -5836,6 +6092,11 @@ object-inspect@^1.1.0, object-inspect@^1.7.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== +object-is@^1.0.1, object-is@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.9, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5868,6 +6129,16 @@ object.entries@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +object.entries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" + integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + object.fromentries@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.1.tgz#050f077855c7af8ae6649f45c80b16ee2d31e704" @@ -5878,6 +6149,16 @@ object.fromentries@^2.0.1: function-bind "^1.1.1" has "^1.0.3" +object.fromentries@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" + integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -5903,6 +6184,16 @@ object.values@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -6113,6 +6404,13 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -6431,6 +6729,15 @@ promise@^7.0.3, promise@^7.1.1: dependencies: asap "~2.0.3" +prop-types-exact@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" + integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + dependencies: + has "^1.0.3" + object.assign "^4.1.0" + reflect.ownkeys "^0.2.0" + prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -6559,13 +6866,26 @@ raf-schd@^2.1.0: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-2.1.2.tgz#ec622b5167f2912089f054dc03ebd5bcf33c8f62" integrity sha512-Orl0IEvMtUCgPddgSxtxreK77UiQz4nPYJy9RggVzu4mKsZkQWiAaG1y9HlYWdvm9xtN348xRaT37qkvL/+A+g== -raf@^3.1.0: +raf@^3.1.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== dependencies: performance-now "^2.1.0" +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + randomatic@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" @@ -6673,7 +6993,7 @@ react-focus-lock@^2.2.1: dependencies: gemini-scrollbar matrix-org/gemini-scrollbar#91e1e566 -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: +react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== @@ -6705,7 +7025,7 @@ react-redux@^5.0.6: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react-test-renderer@^16.9.0: +react-test-renderer@^16.0.0-0, react-test-renderer@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.12.0.tgz#11417ffda579306d4e841a794d32140f3da1b43f" integrity sha512-Vj/teSqt2oayaWxkbhQ6gKis+t5JrknXfPVo+aIJ8QwYAqMPH77uptOdrlphyxl8eQI/rtkOYg86i/UWkpFu0w== @@ -6822,6 +7142,11 @@ redux@^3.7.2: loose-envify "^1.1.0" symbol-observable "^1.0.3" +reflect.ownkeys@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" + integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= + regenerate@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" @@ -7138,6 +7463,14 @@ rollup-pluginutils@^2.8.1: dependencies: estree-walker "^0.6.1" +rst-selector-parser@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" + integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= + dependencies: + lodash.flattendeep "^4.4.0" + nearley "^2.7.10" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -7224,7 +7557,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -7673,6 +8006,15 @@ string.prototype.repeat@^0.2.0: resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" integrity sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8= +string.prototype.trim@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782" + integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + string.prototype.trimleft@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" @@ -7681,6 +8023,14 @@ string.prototype.trimleft@^2.1.0: define-properties "^1.1.3" function-bind "^1.1.1" +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + string.prototype.trimright@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" @@ -7689,6 +8039,14 @@ string.prototype.trimright@^2.1.0: define-properties "^1.1.3" function-bind "^1.1.1" +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"