From cde75dc4f7dead13eaa84d78b7ee9517489d4452 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Sep 2019 12:18:19 +0100 Subject: [PATCH 01/55] fix PropTypes.oneOfType Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/dialogs/AddressPickerDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js index 1eac07dbfd..ff52b03691 100644 --- a/src/components/views/dialogs/AddressPickerDialog.js +++ b/src/components/views/dialogs/AddressPickerDialog.js @@ -52,7 +52,7 @@ module.exports = createReactClass({ // Extra node inserted after picker input, dropdown and errors extraNode: PropTypes.node, value: PropTypes.string, - placeholder: PropTypes.oneOfType(PropTypes.string, PropTypes.func), + placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), roomId: PropTypes.string, button: PropTypes.string, focus: PropTypes.bool, From 12c46838c30af24f7d3070bf2e2030ac5746f0f8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Sep 2019 12:20:48 +0100 Subject: [PATCH 02/55] fix sdk import Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/terms/InlineTermsAgreement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/terms/InlineTermsAgreement.js b/src/components/views/terms/InlineTermsAgreement.js index a22359933f..836b34c585 100644 --- a/src/components/views/terms/InlineTermsAgreement.js +++ b/src/components/views/terms/InlineTermsAgreement.js @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; import PropTypes from "prop-types"; import {_t, pickBestLanguage} from "../../../languageHandler"; -import sdk from "../../../.."; +import sdk from "../../.."; export default class InlineTermsAgreement extends React.Component { static propTypes = { From 37978e5ef59d03e48a96f79e76d597918dfdb8d9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Sep 2019 12:24:01 +0100 Subject: [PATCH 03/55] Fix GroupMemberTile wrong default avatar letter and colour Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/groups/GroupMemberTile.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/groups/GroupMemberTile.js b/src/components/views/groups/GroupMemberTile.js index ed305382be..c4b41d23ce 100644 --- a/src/components/views/groups/GroupMemberTile.js +++ b/src/components/views/groups/GroupMemberTile.js @@ -59,7 +59,9 @@ export default createReactClass({ ); const av = ( - From 66f450b99573dda6559b11661808d56930c8796c Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 26 Sep 2019 18:03:12 +0100 Subject: [PATCH 04/55] Lowercase terms --- src/components/views/dialogs/TermsDialog.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/TermsDialog.js b/src/components/views/dialogs/TermsDialog.js index dee183bc6c..f80a0dc059 100644 --- a/src/components/views/dialogs/TermsDialog.js +++ b/src/components/views/dialogs/TermsDialog.js @@ -184,7 +184,7 @@ export default class TermsDialog extends React.PureComponent { hasCancel={false} >
-

{_t("To continue you need to accept the Terms of this service.")}

+

{_t("To continue you need to accept the terms of this service.")}

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c23cd6d324..8c17a2c976 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1383,7 +1383,7 @@ "Be found by phone or email": "Be found by phone or email", "Use bots, bridges, widgets and sticker packs": "Use bots, bridges, widgets and sticker packs", "Terms of Service": "Terms of Service", - "To continue you need to accept the Terms of this service.": "To continue you need to accept the Terms of this service.", + "To continue you need to accept the terms of this service.": "To continue you need to accept the terms of this service.", "Service": "Service", "Summary": "Summary", "Terms": "Terms", From 701ec259e66ad733f8edc757d35b2f88856dcbe4 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 27 Sep 2019 17:43:04 +0100 Subject: [PATCH 05/55] Rename Terms column in modal to Document --- src/components/views/dialogs/TermsDialog.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/TermsDialog.js b/src/components/views/dialogs/TermsDialog.js index f80a0dc059..20c9fe5367 100644 --- a/src/components/views/dialogs/TermsDialog.js +++ b/src/components/views/dialogs/TermsDialog.js @@ -190,7 +190,7 @@ export default class TermsDialog extends React.PureComponent { - + {rows} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8c17a2c976..6453298874 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1386,7 +1386,7 @@ "To continue you need to accept the terms of this service.": "To continue you need to accept the terms of this service.", "Service": "Service", "Summary": "Summary", - "Terms": "Terms", + "Document": "Document", "Next": "Next", "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.", "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.", From d80a2df979334d2f4dc8d98d8cac32e2bd0d7231 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 27 Sep 2019 17:57:10 +0100 Subject: [PATCH 06/55] Move document name to new column --- res/css/views/dialogs/_TermsDialog.scss | 1 + src/components/views/dialogs/TermsDialog.js | 11 +++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/res/css/views/dialogs/_TermsDialog.scss b/res/css/views/dialogs/_TermsDialog.scss index df2a72010f..aad679a5b3 100644 --- a/res/css/views/dialogs/_TermsDialog.scss +++ b/res/css/views/dialogs/_TermsDialog.scss @@ -40,6 +40,7 @@ limitations under the License. } .mx_TermsDialog_link { + display: inline-block; mask-image: url('$(res)/img/external-link.svg'); background-color: $accent-color; width: 10px; diff --git a/src/components/views/dialogs/TermsDialog.js b/src/components/views/dialogs/TermsDialog.js index 20c9fe5367..1e706ff1a7 100644 --- a/src/components/views/dialogs/TermsDialog.js +++ b/src/components/views/dialogs/TermsDialog.js @@ -90,21 +90,17 @@ export default class TermsDialog extends React.PureComponent { } } - _summaryForServiceType(serviceType, docName) { + _summaryForServiceType(serviceType) { switch (serviceType) { case Matrix.SERVICE_TYPES.IS: return
{_t("Find others by phone or email")}
{_t("Be found by phone or email")} - {docName !== null ?
: ''} - {docName !== null ? '('+docName+')' : ''}
; case Matrix.SERVICE_TYPES.IM: return
{_t("Use bots, bridges, widgets and sticker packs")} - {docName !== null ?
: ''} - {docName !== null ? '('+docName+')' : ''}
; } } @@ -133,14 +129,13 @@ export default class TermsDialog extends React.PureComponent { } const summary = this._summaryForServiceType( policiesAndService.service.serviceType, - policyValues.length > 1 ? termDoc[termsLang].name : null, ); rows.push(
- From 64aa6695f5cd135ca2eb21455588e3e3d39ccd08 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 12:16:46 -0600 Subject: [PATCH 30/55] Move matrix-to.js to utils/permalinks/RoomPermalinkCreator Just a little bit of refactoring to make the feature of custom prefixes a bit easier. --- src/autocomplete/CommunityProvider.js | 2 +- src/autocomplete/RoomProvider.js | 2 +- src/autocomplete/UserProvider.js | 2 +- src/components/structures/GroupView.js | 2 +- src/components/structures/RoomView.js | 2 +- src/components/views/dialogs/ShareDialog.js | 2 +- src/components/views/elements/ReplyThread.js | 2 +- src/components/views/messages/RoomCreate.js | 2 +- src/components/views/messages/TextualBody.js | 2 +- src/components/views/rooms/MessageComposer.js | 2 +- src/components/views/rooms/MessageComposerInput.js | 2 +- src/components/views/rooms/ReplyPreview.js | 2 +- src/components/views/rooms/SlateMessageComposer.js | 2 +- src/linkify-matrix.js | 2 +- .../permalinks/RoomPermalinkCreator.js} | 2 +- .../permalinks/RoomPermalinkCreator-test.js} | 8 ++++---- 16 files changed, 19 insertions(+), 19 deletions(-) rename src/{matrix-to.js => utils/permalinks/RoomPermalinkCreator.js} (99%) rename test/{matrix-to-test.js => utils/permalinks/RoomPermalinkCreator-test.js} (98%) diff --git a/src/autocomplete/CommunityProvider.js b/src/autocomplete/CommunityProvider.js index ffce1e71cf..14c0116604 100644 --- a/src/autocomplete/CommunityProvider.js +++ b/src/autocomplete/CommunityProvider.js @@ -23,7 +23,7 @@ import QueryMatcher from './QueryMatcher'; import {PillCompletion} from './Components'; import sdk from '../index'; import _sortBy from 'lodash/sortBy'; -import {makeGroupPermalink} from "../matrix-to"; +import {makeGroupPermalink} from "../utils/permalinks/RoomPermalinkCreator"; import type {Completion, SelectionRange} from "./Autocompleter"; import FlairStore from "../stores/FlairStore"; diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index b94edf590c..062fb5a5c5 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -26,7 +26,7 @@ import {PillCompletion} from './Components'; import {getDisplayAliasForRoom} from '../Rooms'; import sdk from '../index'; import _sortBy from 'lodash/sortBy'; -import {makeRoomPermalink} from "../matrix-to"; +import {makeRoomPermalink} from "../utils/permalinks/RoomPermalinkCreator"; import type {Completion, SelectionRange} from "./Autocompleter"; const ROOM_REGEX = /\B#\S*/g; diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 62ae5d4970..d154e02a9d 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -28,7 +28,7 @@ import _sortBy from 'lodash/sortBy'; import MatrixClientPeg from '../MatrixClientPeg'; import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk'; -import {makeUserPermalink} from "../matrix-to"; +import {makeUserPermalink} from "../utils/permalinks/RoomPermalinkCreator"; import type {Completion, SelectionRange} from "./Autocompleter"; const USER_REGEX = /\B@\S*/g; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 70d8b2e298..6c96dec04f 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -36,7 +36,7 @@ import classnames from 'classnames'; import GroupStore from '../../stores/GroupStore'; import FlairStore from '../../stores/FlairStore'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; -import {makeGroupPermalink, makeUserPermalink} from "../../matrix-to"; +import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/RoomPermalinkCreator"; import {Group} from "matrix-js-sdk"; const LONG_DESC_PLACEHOLDER = _td( diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 3446901331..057b1f2d80 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -31,7 +31,7 @@ import Promise from 'bluebird'; import classNames from 'classnames'; import {Room} from "matrix-js-sdk"; import { _t } from '../../languageHandler'; -import {RoomPermalinkCreator} from '../../matrix-to'; +import {RoomPermalinkCreator} from '../../utils/permalinks/RoomPermalinkCreator'; import MatrixClientPeg from '../../MatrixClientPeg'; import ContentMessages from '../../ContentMessages'; diff --git a/src/components/views/dialogs/ShareDialog.js b/src/components/views/dialogs/ShareDialog.js index bd6746a1e5..ae43ee2ae5 100644 --- a/src/components/views/dialogs/ShareDialog.js +++ b/src/components/views/dialogs/ShareDialog.js @@ -20,7 +20,7 @@ import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; import QRCode from 'qrcode-react'; -import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../matrix-to"; +import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; import * as ContextualMenu from "../../structures/ContextualMenu"; const socials = [ diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 08630a16a5..5c1d6a3ef4 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import dis from '../../../dispatcher'; import {wantsDateSeparator} from '../../../DateUtils'; import {MatrixEvent, MatrixClient} from 'matrix-js-sdk'; -import {makeUserPermalink, RoomPermalinkCreator} from "../../../matrix-to"; +import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/RoomPermalinkCreator"; import SettingsStore from "../../../settings/SettingsStore"; // This component does no cycle detection, simply because the only way to make such a cycle would be to diff --git a/src/components/views/messages/RoomCreate.js b/src/components/views/messages/RoomCreate.js index bf0ef32460..282ba28b8a 100644 --- a/src/components/views/messages/RoomCreate.js +++ b/src/components/views/messages/RoomCreate.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import dis from '../../../dispatcher'; -import { RoomPermalinkCreator } from '../../../matrix-to'; +import { RoomPermalinkCreator } from '../../../utils/permalinks/RoomPermalinkCreator'; import { _t } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 7143d02e74..ebcf4857c4 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -30,7 +30,7 @@ import { _t } from '../../../languageHandler'; import * as ContextualMenu from '../../structures/ContextualMenu'; import SettingsStore from "../../../settings/SettingsStore"; import ReplyThread from "../elements/ReplyThread"; -import {host as matrixtoHost} from '../../../matrix-to'; +import {host as matrixtoHost} from '../../../utils/permalinks/RoomPermalinkCreator'; import {pillifyLinks} from '../../../utils/pillify'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 022d45e60e..4e7f8825ad 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -23,7 +23,7 @@ import sdk from '../../../index'; import dis from '../../../dispatcher'; import RoomViewStore from '../../../stores/RoomViewStore'; import Stickerpicker from './Stickerpicker'; -import { makeRoomPermalink } from '../../../matrix-to'; +import { makeRoomPermalink } from '../../../utils/permalinks/RoomPermalinkCreator'; import ContentMessages from '../../../ContentMessages'; import classNames from 'classnames'; import E2EIcon from './E2EIcon'; diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index df7ba27493..917465aaaa 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -54,7 +54,7 @@ import EMOJIBASE from 'emojibase-data/en/compact.json'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import {makeUserPermalink} from "../../../matrix-to"; +import {makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; import ReplyPreview from "./ReplyPreview"; import RoomViewStore from '../../../stores/RoomViewStore'; import ReplyThread from "../elements/ReplyThread"; diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index 58e7237801..97b29dddee 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler'; import RoomViewStore from '../../../stores/RoomViewStore'; import SettingsStore from "../../../settings/SettingsStore"; import PropTypes from "prop-types"; -import {RoomPermalinkCreator} from "../../../matrix-to"; +import {RoomPermalinkCreator} from "../../../utils/permalinks/RoomPermalinkCreator"; function cancelQuoting() { dis.dispatch({ diff --git a/src/components/views/rooms/SlateMessageComposer.js b/src/components/views/rooms/SlateMessageComposer.js index d7aa745753..8b51f8266f 100644 --- a/src/components/views/rooms/SlateMessageComposer.js +++ b/src/components/views/rooms/SlateMessageComposer.js @@ -25,7 +25,7 @@ import dis from '../../../dispatcher'; import RoomViewStore from '../../../stores/RoomViewStore'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import Stickerpicker from './Stickerpicker'; -import { makeRoomPermalink } from '../../../matrix-to'; +import { makeRoomPermalink } from '../../../utils/permalinks/RoomPermalinkCreator'; import ContentMessages from '../../../ContentMessages'; import classNames from 'classnames'; diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index a9e894d582..1c8c27fc42 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {baseUrl} from "./matrix-to"; +import {baseUrl} from "./utils/permalinks/RoomPermalinkCreator"; function matrixLinkify(linkify) { // Text tokens diff --git a/src/matrix-to.js b/src/utils/permalinks/RoomPermalinkCreator.js similarity index 99% rename from src/matrix-to.js rename to src/utils/permalinks/RoomPermalinkCreator.js index 14467cb4c5..3cc8169434 100644 --- a/src/matrix-to.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import MatrixClientPeg from "./MatrixClientPeg"; +import MatrixClientPeg from "../../MatrixClientPeg"; import isIp from "is-ip"; import utils from 'matrix-js-sdk/lib/utils'; diff --git a/test/matrix-to-test.js b/test/utils/permalinks/RoomPermalinkCreator-test.js similarity index 98% rename from test/matrix-to-test.js rename to test/utils/permalinks/RoomPermalinkCreator-test.js index 23434a57e2..09bfaa3974 100644 --- a/test/matrix-to-test.js +++ b/test/utils/permalinks/RoomPermalinkCreator-test.js @@ -11,15 +11,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import expect from 'expect'; -import peg from '../src/MatrixClientPeg'; +import expect from 'expect/build/index'; +import peg from '../../../src/MatrixClientPeg'; import { makeGroupPermalink, makeRoomPermalink, makeUserPermalink, RoomPermalinkCreator, -} from "../src/matrix-to"; -import * as testUtils from "./test-utils"; +} from "../../../src/utils/permalinks/RoomPermalinkCreator"; +import * as testUtils from "../../test-utils"; function mockRoom(roomId, members, serverACL) { members.forEach(m => m.membership = "join"); From f9d5e89017fbc663556567d2079894daf35c69cf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 12:37:13 -0600 Subject: [PATCH 31/55] Move early parts of matrix.to bits into its own class --- src/components/views/messages/TextualBody.js | 7 +++-- src/linkify-matrix.js | 2 +- src/utils/permalinks/RoomPermalinkCreator.js | 18 +++++++------ src/utils/permalinks/SpecPermalinks.js | 28 ++++++++++++++++++++ 4 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 src/utils/permalinks/SpecPermalinks.js diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index ebcf4857c4..3277f215bf 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -30,9 +30,9 @@ import { _t } from '../../../languageHandler'; import * as ContextualMenu from '../../structures/ContextualMenu'; import SettingsStore from "../../../settings/SettingsStore"; import ReplyThread from "../elements/ReplyThread"; -import {host as matrixtoHost} from '../../../utils/permalinks/RoomPermalinkCreator'; import {pillifyLinks} from '../../../utils/pillify'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; +import {RoomPermalinkCreator} from "../../../utils/permalinks/RoomPermalinkCreator"; module.exports = createReactClass({ displayName: 'TextualBody', @@ -251,7 +251,10 @@ module.exports = createReactClass({ // never preview matrix.to links (if anything we should give a smart // preview of the room/user they point to: nobody needs to be reminded // what the matrix.to site looks like). - if (host === matrixtoHost) return false; + if (this.props.mxEvent && this.props.mxEvent.getRoom()) { + const permalinks = new RoomPermalinkCreator(this.props.mxEvent.getRoom()); + if (permalinks.isPermalinkHost(host)) return false; + } if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) { // it's a "foo.pl" style link diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index 1c8c27fc42..e823e507c4 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {baseUrl} from "./utils/permalinks/RoomPermalinkCreator"; +import {baseUrl} from "./utils/permalinks/SpecPermalinks"; function matrixLinkify(linkify) { // Text tokens diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 3cc8169434..bd2e0b72fc 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -17,9 +17,7 @@ limitations under the License. import MatrixClientPeg from "../../MatrixClientPeg"; import isIp from "is-ip"; import utils from 'matrix-js-sdk/lib/utils'; - -export const host = "matrix.to"; -export const baseUrl = `https://${host}`; +import {host as matrixtoHost, baseUrl as matrixtoBaseUrl} from "SpecPermalinks"; // The maximum number of servers to pick when working out which servers // to add to permalinks. The servers are appended as ?via=example.org @@ -123,15 +121,19 @@ export class RoomPermalinkCreator { return this._started; } + isPermalinkHost(host: string): boolean { + return host === matrixtoHost; + } + forEvent(eventId) { const roomId = this._roomId; - const permalinkBase = `${baseUrl}/#/${roomId}/${eventId}`; + const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}/${eventId}`; return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`; } forRoom() { const roomId = this._roomId; - const permalinkBase = `${baseUrl}/#/${roomId}`; + const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}`; return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`; } @@ -255,11 +257,11 @@ export class RoomPermalinkCreator { } export function makeUserPermalink(userId) { - return `${baseUrl}/#/${userId}`; + return `${matrixtoBaseUrl}/#/${userId}`; } export function makeRoomPermalink(roomId) { - const permalinkBase = `${baseUrl}/#/${roomId}`; + const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}`; if (!roomId) { throw new Error("can't permalink a falsey roomId"); @@ -280,7 +282,7 @@ export function makeRoomPermalink(roomId) { } export function makeGroupPermalink(groupId) { - return `${baseUrl}/#/${groupId}`; + return `${matrixtoBaseUrl}/#/${groupId}`; } export function encodeServerCandidates(candidates) { diff --git a/src/utils/permalinks/SpecPermalinks.js b/src/utils/permalinks/SpecPermalinks.js new file mode 100644 index 0000000000..461696bfea --- /dev/null +++ b/src/utils/permalinks/SpecPermalinks.js @@ -0,0 +1,28 @@ +/* +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. +*/ + +export const host = "matrix.to"; +export const baseUrl = `https://${host}`; + +/** + * Generates matrix.to permalinks + */ +export default class SpecPermalinks { + constructor() { + } + + // TODO: The class +} From 926e1146f94eff20605150eb28ef27729baa10cd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 12:51:17 -0600 Subject: [PATCH 32/55] Move spec permalinks into their own class This allows for Riot permalinks to be introduced without if-else ladders everywhere. --- src/linkify-matrix.js | 2 +- ...cPermalinks.js => PermalinkConstructor.js} | 23 ++++++--- src/utils/permalinks/RoomPermalinkCreator.js | 49 +++++++++--------- .../permalinks/SpecPermalinkConstructor.js | 50 +++++++++++++++++++ 4 files changed, 93 insertions(+), 31 deletions(-) rename src/utils/permalinks/{SpecPermalinks.js => PermalinkConstructor.js} (50%) create mode 100644 src/utils/permalinks/SpecPermalinkConstructor.js diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index e823e507c4..2d72b7fb41 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {baseUrl} from "./utils/permalinks/SpecPermalinks"; +import {baseUrl} from "./utils/permalinks/SpecPermalinkConstructor"; function matrixLinkify(linkify) { // Text tokens diff --git a/src/utils/permalinks/SpecPermalinks.js b/src/utils/permalinks/PermalinkConstructor.js similarity index 50% rename from src/utils/permalinks/SpecPermalinks.js rename to src/utils/permalinks/PermalinkConstructor.js index 461696bfea..014bf3e225 100644 --- a/src/utils/permalinks/SpecPermalinks.js +++ b/src/utils/permalinks/PermalinkConstructor.js @@ -14,15 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -export const host = "matrix.to"; -export const baseUrl = `https://${host}`; - /** - * Generates matrix.to permalinks + * Interface for classes that actually produce permalinks (strings). + * TODO: Convert this to a real TypeScript interface */ -export default class SpecPermalinks { - constructor() { +export default class PermalinkConstructor { + forEvent(roomId: string, eventId: string, serverCandidates: string[]): string { + throw new Error("Not implemented"); } - // TODO: The class + forRoom(roomIdOrAlias: string, serverCandidates: string[]): string { + throw new Error("Not implemented"); + } + + forGroup(groupId: string): string { + throw new Error("Not implemented"); + } + + forUser(userId: string): string { + throw new Error("Not implemented"); + } } diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index bd2e0b72fc..984b4233ba 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -17,7 +17,13 @@ limitations under the License. import MatrixClientPeg from "../../MatrixClientPeg"; import isIp from "is-ip"; import utils from 'matrix-js-sdk/lib/utils'; -import {host as matrixtoHost, baseUrl as matrixtoBaseUrl} from "SpecPermalinks"; +import SpecPermalinkConstructor, { + baseUrl as matrixtoBaseUrl, + host as matrixtoHost +} from "./SpecPermalinkConstructor"; +import PermalinkConstructor from "./PermalinkConstructor"; + +const SdkConfig = require("../../SdkConfig"); // The maximum number of servers to pick when working out which servers // to add to permalinks. The servers are appended as ?via=example.org @@ -71,7 +77,7 @@ export class RoomPermalinkCreator { // We support being given a roomId as a fallback in the event the `room` object // doesn't exist or is not healthy for us to rely on. For example, loading a // permalink to a room which the MatrixClient doesn't know about. - constructor(room, roomId=null) { + constructor(room, roomId = null) { this._room = room; this._roomId = room ? room.roomId : roomId; this._highestPlUserId = null; @@ -126,15 +132,11 @@ export class RoomPermalinkCreator { } forEvent(eventId) { - const roomId = this._roomId; - const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}/${eventId}`; - return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`; + return getPermalinkConstructor().forEvent(this._roomId, eventId, this._serverCandidates); } forRoom() { - const roomId = this._roomId; - const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}`; - return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`; + return getPermalinkConstructor().forRoom(this._roomId, this._serverCandidates); } onRoomState(event) { @@ -184,8 +186,8 @@ export class RoomPermalinkCreator { } const serverName = getServerName(userId); return !isHostnameIpAddress(serverName) && - !isHostInRegex(serverName, this._bannedHostsRegexps) && - isHostInRegex(serverName, this._allowedHostsRegexps); + !isHostInRegex(serverName, this._bannedHostsRegexps) && + isHostInRegex(serverName, this._allowedHostsRegexps); }); const maxEntry = allowedEntries.reduce((max, entry) => { return (entry[1] > max[1]) ? entry : max; @@ -223,7 +225,7 @@ export class RoomPermalinkCreator { } _updatePopulationMap() { - const populationMap: {[server:string]:number} = {}; + const populationMap: { [server: string]: number } = {}; for (const member of this._room.getJoinedMembers()) { const serverName = getServerName(member.userId); if (!populationMap[serverName]) { @@ -244,9 +246,9 @@ export class RoomPermalinkCreator { .sort((a, b) => this._populationMap[b] - this._populationMap[a]) .filter(a => { return !candidates.includes(a) && - !isHostnameIpAddress(a) && - !isHostInRegex(a, this._bannedHostsRegexps) && - isHostInRegex(a, this._allowedHostsRegexps); + !isHostnameIpAddress(a) && + !isHostInRegex(a, this._bannedHostsRegexps) && + isHostInRegex(a, this._allowedHostsRegexps); }); const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length); @@ -257,24 +259,22 @@ export class RoomPermalinkCreator { } export function makeUserPermalink(userId) { - return `${matrixtoBaseUrl}/#/${userId}`; + return getPermalinkConstructor().forUser(userId); } export function makeRoomPermalink(roomId) { - const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}`; - if (!roomId) { throw new Error("can't permalink a falsey roomId"); } // If the roomId isn't actually a room ID, don't try to list the servers. // Aliases are already routable, and don't need extra information. - if (roomId[0] !== '!') return permalinkBase; + if (roomId[0] !== '!') return getPermalinkConstructor().forRoom(roomId, []); const client = MatrixClientPeg.get(); const room = client.getRoom(roomId); if (!room) { - return permalinkBase; + return getPermalinkConstructor().forRoom(roomId, []); } const permalinkCreator = new RoomPermalinkCreator(room); permalinkCreator.load(); @@ -282,12 +282,15 @@ export function makeRoomPermalink(roomId) { } export function makeGroupPermalink(groupId) { - return `${matrixtoBaseUrl}/#/${groupId}`; + return getPermalinkConstructor().forGroup(groupId); } -export function encodeServerCandidates(candidates) { - if (!candidates || candidates.length === 0) return ''; - return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; +function getPermalinkConstructor(): PermalinkConstructor { + if (SdkConfig.get()['useRiotToCreatePermalinks']) { + // TODO: Return a RiotPermalinkConstructor + } + + return new SpecPermalinkConstructor(); } function getServerName(userId) { diff --git a/src/utils/permalinks/SpecPermalinkConstructor.js b/src/utils/permalinks/SpecPermalinkConstructor.js new file mode 100644 index 0000000000..6c53bfc5f2 --- /dev/null +++ b/src/utils/permalinks/SpecPermalinkConstructor.js @@ -0,0 +1,50 @@ +/* +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 PermalinkConstructor from "./PermalinkConstructor"; + +export const host = "matrix.to"; +export const baseUrl = `https://${host}`; + +/** + * Generates matrix.to permalinks + */ +export default class SpecPermalinkConstructor extends PermalinkConstructor { + constructor() { + super(); + } + + forEvent(roomId: string, eventId: string, serverCandidates: string[]): string { + return `${baseUrl}/#/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`; + } + + forRoom(roomIdOrAlias: string, serverCandidates: string[]): string { + return `${baseUrl}/#/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`; + } + + forUser(userId: string): string { + return `${baseUrl}/#/${userId}`; + } + + forGroup(groupId: string): string { + return `${baseUrl}/#/${groupId}`; + } + + encodeServerCandidates(candidates: string[]) { + if (!candidates || candidates.length === 0) return ''; + return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; + } +} From f879185aef3ed3f1849ea011219baf58a306d9cc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 12:53:45 -0600 Subject: [PATCH 33/55] Move permalink host check into permalink constructors Without the requirement for a room to work --- src/components/views/messages/TextualBody.js | 7 ++----- src/utils/permalinks/PermalinkConstructor.js | 4 ++++ src/utils/permalinks/RoomPermalinkCreator.js | 13 +++++-------- src/utils/permalinks/SpecPermalinkConstructor.js | 4 ++++ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 3277f215bf..d3c53c8a33 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -32,7 +32,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import ReplyThread from "../elements/ReplyThread"; import {pillifyLinks} from '../../../utils/pillify'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; -import {RoomPermalinkCreator} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {isPermalinkHost} from "../../../utils/permalinks/RoomPermalinkCreator"; module.exports = createReactClass({ displayName: 'TextualBody', @@ -251,10 +251,7 @@ module.exports = createReactClass({ // never preview matrix.to links (if anything we should give a smart // preview of the room/user they point to: nobody needs to be reminded // what the matrix.to site looks like). - if (this.props.mxEvent && this.props.mxEvent.getRoom()) { - const permalinks = new RoomPermalinkCreator(this.props.mxEvent.getRoom()); - if (permalinks.isPermalinkHost(host)) return false; - } + if (isPermalinkHost(host)) return false; if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) { // it's a "foo.pl" style link diff --git a/src/utils/permalinks/PermalinkConstructor.js b/src/utils/permalinks/PermalinkConstructor.js index 014bf3e225..47d813390f 100644 --- a/src/utils/permalinks/PermalinkConstructor.js +++ b/src/utils/permalinks/PermalinkConstructor.js @@ -34,4 +34,8 @@ export default class PermalinkConstructor { forUser(userId: string): string { throw new Error("Not implemented"); } + + isPermalinkHost(host: string): boolean { + throw new Error("Not implemented"); + } } diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 984b4233ba..7394854c18 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -17,10 +17,7 @@ limitations under the License. import MatrixClientPeg from "../../MatrixClientPeg"; import isIp from "is-ip"; import utils from 'matrix-js-sdk/lib/utils'; -import SpecPermalinkConstructor, { - baseUrl as matrixtoBaseUrl, - host as matrixtoHost -} from "./SpecPermalinkConstructor"; +import SpecPermalinkConstructor from "./SpecPermalinkConstructor"; import PermalinkConstructor from "./PermalinkConstructor"; const SdkConfig = require("../../SdkConfig"); @@ -127,10 +124,6 @@ export class RoomPermalinkCreator { return this._started; } - isPermalinkHost(host: string): boolean { - return host === matrixtoHost; - } - forEvent(eventId) { return getPermalinkConstructor().forEvent(this._roomId, eventId, this._serverCandidates); } @@ -285,6 +278,10 @@ export function makeGroupPermalink(groupId) { return getPermalinkConstructor().forGroup(groupId); } +export function isPermalinkHost(host: string): boolean { + return getPermalinkConstructor().isPermalinkHost(host); +} + function getPermalinkConstructor(): PermalinkConstructor { if (SdkConfig.get()['useRiotToCreatePermalinks']) { // TODO: Return a RiotPermalinkConstructor diff --git a/src/utils/permalinks/SpecPermalinkConstructor.js b/src/utils/permalinks/SpecPermalinkConstructor.js index 6c53bfc5f2..5dd4a5a24e 100644 --- a/src/utils/permalinks/SpecPermalinkConstructor.js +++ b/src/utils/permalinks/SpecPermalinkConstructor.js @@ -43,6 +43,10 @@ export default class SpecPermalinkConstructor extends PermalinkConstructor { return `${baseUrl}/#/${groupId}`; } + isPermalinkHost(testHost: string): boolean { + return testHost === host; + } + encodeServerCandidates(candidates: string[]) { if (!candidates || candidates.length === 0) return ''; return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; From f183e96d66bd6f48fa8b765bfc5412a11b927cc8 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 13:04:20 -0600 Subject: [PATCH 34/55] Introduce a RiotPermalinkConstructor and fix the setting name Originally we were planning on using the current location as the permalink prefix, but that doesn't work if the user is a desktop user. --- .../permalinks/RiotPermalinkConstructor.js | 60 +++++++++++++++++++ src/utils/permalinks/RoomPermalinkCreator.js | 7 ++- 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/utils/permalinks/RiotPermalinkConstructor.js diff --git a/src/utils/permalinks/RiotPermalinkConstructor.js b/src/utils/permalinks/RiotPermalinkConstructor.js new file mode 100644 index 0000000000..87d8d058f9 --- /dev/null +++ b/src/utils/permalinks/RiotPermalinkConstructor.js @@ -0,0 +1,60 @@ +/* +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 PermalinkConstructor from "./PermalinkConstructor"; + +/** + * Generates permalinks that self-reference the running webapp + */ +export default class RiotPermalinkConstructor extends PermalinkConstructor { + + _riotUrl: string; + + constructor(riotUrl: string) { + super(); + this._riotUrl = riotUrl; + } + + forEvent(roomId: string, eventId: string, serverCandidates: string[]): string { + // TODO: Fix URL + return `${this._riotUrl}/#/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`; + } + + forRoom(roomIdOrAlias: string, serverCandidates: string[]): string { + // TODO: Fix URL + return `${this._riotUrl}/#/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`; + } + + forUser(userId: string): string { + // TODO: Fix URL + return `${this._riotUrl}/#/${userId}`; + } + + forGroup(groupId: string): string { + // TODO: Fix URL + return `${this._riotUrl}/#/${groupId}`; + } + + isPermalinkHost(testHost: string): boolean { + // TODO: Actual check + return testHost === this._riotUrl; + } + + encodeServerCandidates(candidates: string[]) { + if (!candidates || candidates.length === 0) return ''; + return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; + } +} diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 7394854c18..aeb7d3b72c 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -17,8 +17,9 @@ limitations under the License. import MatrixClientPeg from "../../MatrixClientPeg"; import isIp from "is-ip"; import utils from 'matrix-js-sdk/lib/utils'; -import SpecPermalinkConstructor from "./SpecPermalinkConstructor"; +import SpecPermalinkConstructor, {baseUrl as matrixtoBaseUrl} from "./SpecPermalinkConstructor"; import PermalinkConstructor from "./PermalinkConstructor"; +import RiotPermalinkConstructor from "./RiotPermalinkConstructor"; const SdkConfig = require("../../SdkConfig"); @@ -283,8 +284,8 @@ export function isPermalinkHost(host: string): boolean { } function getPermalinkConstructor(): PermalinkConstructor { - if (SdkConfig.get()['useRiotToCreatePermalinks']) { - // TODO: Return a RiotPermalinkConstructor + if (SdkConfig.get()['permalinkPrefix'] && SdkConfig.get()['permalinkPrefix'] !== matrixtoBaseUrl) { + return new RiotPermalinkConstructor(SdkConfig.get()['permalinkPrefix']); } return new SpecPermalinkConstructor(); From baf78da79179812155355e3c053b06f7263879cc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:03:24 -0600 Subject: [PATCH 35/55] Support spec-level permalink parsing --- src/utils/permalinks/PermalinkConstructor.js | 38 +++++++++++++++++++ .../permalinks/SpecPermalinkConstructor.js | 38 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/utils/permalinks/PermalinkConstructor.js b/src/utils/permalinks/PermalinkConstructor.js index 47d813390f..507f16f795 100644 --- a/src/utils/permalinks/PermalinkConstructor.js +++ b/src/utils/permalinks/PermalinkConstructor.js @@ -38,4 +38,42 @@ export default class PermalinkConstructor { isPermalinkHost(host: string): boolean { throw new Error("Not implemented"); } + + parsePermalink(fullUrl: string): PermalinkParts { + throw new Error("Not implemented"); + } +} + +// Inspired by/Borrowed with permission from the matrix-bot-sdk: +// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L1-L6 +export class PermalinkParts { + roomIdOrAlias: string; + eventId: string; + userId: string; + groupId: string; + viaServers: string[]; + + constructor(roomIdOrAlias: string, eventId: string, userId: string, groupId: string, viaServers: string[]) { + this.roomIdOrAlias = roomIdOrAlias; + this.eventId = eventId; + this.groupId = groupId; + this.userId = userId; + this.viaServers = viaServers; + } + + static forUser(userId: string): PermalinkParts { + return new PermalinkParts(null, null, userId, null, null); + } + + static forGroup(groupId: string): PermalinkParts { + return new PermalinkParts(null, null, null, groupId, null); + } + + static forRoom(roomIdOrAlias: string, viaServers: string[]): PermalinkParts { + return new PermalinkParts(roomIdOrAlias, null, null, null, viaServers || []); + } + + static forEvent(roomId: string, eventId: string, viaServers: string[]): PermalinkParts { + return new PermalinkParts(roomId, eventId, null, null, viaServers || []); + } } diff --git a/src/utils/permalinks/SpecPermalinkConstructor.js b/src/utils/permalinks/SpecPermalinkConstructor.js index 5dd4a5a24e..e4cff798a7 100644 --- a/src/utils/permalinks/SpecPermalinkConstructor.js +++ b/src/utils/permalinks/SpecPermalinkConstructor.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import PermalinkConstructor from "./PermalinkConstructor"; +import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor"; export const host = "matrix.to"; export const baseUrl = `https://${host}`; @@ -51,4 +51,40 @@ export default class SpecPermalinkConstructor extends PermalinkConstructor { if (!candidates || candidates.length === 0) return ''; return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; } + + // Heavily inspired by/borrowed from the matrix-bot-sdk (with permission): + // https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L33-L61 + parsePermalink(fullUrl: string): PermalinkParts { + if (!fullUrl || !fullUrl.startsWith(baseUrl)) { + throw new Error("Does not appear to be a permalink"); + } + + const parts = fullUrl.substring(`${baseUrl}/#/`.length).split("/"); + + const entity = parts[0]; + if (entity[0] === '@') { + // Probably a user, no further parsing needed. + return PermalinkParts.forUser(entity); + } else if (entity[0] === '+') { + // Probably a group, no further parsing needed. + return PermalinkParts.forGroup(entity); + } else if (entity[0] === '#' || entity[0] === '!') { + if (parts.length === 1) { + return PermalinkParts.forRoom(entity, []); + } + + // rejoin the rest because v3 events can have slashes (annoyingly) + const eventIdAndQuery = parts.length > 1 ? parts.slice(1).join('/') : ""; + const secondaryParts = eventIdAndQuery.split("?"); + + const eventId = secondaryParts[0]; + const query = secondaryParts.length > 1 ? secondaryParts[1] : ""; + + const via = query.split("via=").filter(p => !!p); + + return PermalinkParts.forEvent(entity, eventId, via); + } else { + throw new Error("Unknown entity type in permalink"); + } + } } From 9bb1ebb89db0f94c5056f2bd2fec3f07be1fd3a4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:03:52 -0600 Subject: [PATCH 36/55] Support riot-level permalink parsing --- .../permalinks/RiotPermalinkConstructor.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/utils/permalinks/RiotPermalinkConstructor.js b/src/utils/permalinks/RiotPermalinkConstructor.js index 87d8d058f9..207e181a15 100644 --- a/src/utils/permalinks/RiotPermalinkConstructor.js +++ b/src/utils/permalinks/RiotPermalinkConstructor.js @@ -57,4 +57,46 @@ export default class RiotPermalinkConstructor extends PermalinkConstructor { if (!candidates || candidates.length === 0) return ''; return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; } + + // Heavily inspired by/borrowed from the matrix-bot-sdk (with permission): + // https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L33-L61 + // Adapted for Riot's URL format + parsePermalink(fullUrl: string): PermalinkParts { + if (!fullUrl || !fullUrl.startsWith(this._riotUrl)) { + throw new Error("Does not appear to be a permalink"); + } + + const parts = fullUrl.substring(`${this._riotUrl}/#/`.length).split("/"); + if (parts.length < 2) { // we're expecting an entity and an ID of some kind at least + throw new Error("URL is missing parts"); + } + + const entityType = parts[0]; + const entity = parts[1]; + if (entityType === 'user') { + // Probably a user, no further parsing needed. + return PermalinkParts.forUser(entity); + } else if (entityType === 'group') { + // Probably a group, no further parsing needed. + return PermalinkParts.forGroup(entity); + } else if (entityType === 'room') { + if (parts.length === 2) { + return PermalinkParts.forRoom(entity, []); + } + + // rejoin the rest because v3 events can have slashes (annoyingly) + const eventIdAndQuery = parts.length > 2 ? parts.slice(2).join('/') : ""; + const secondaryParts = eventIdAndQuery.split("?"); + + const eventId = secondaryParts[0]; + const query = secondaryParts.length > 1 ? secondaryParts[1] : ""; + + // TODO: Verify Riot works with via args + const via = query.split("via=").filter(p => !!p); + + return PermalinkParts.forEvent(entity, eventId, via); + } else { + throw new Error("Unknown entity type in permalink"); + } + } } From 3e5a39d646f521f0be37c91aaebcdbb38ed34d51 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:04:10 -0600 Subject: [PATCH 37/55] Add utility function for permalink parsing --- src/utils/permalinks/RoomPermalinkCreator.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index aeb7d3b72c..88bcd5768a 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -18,7 +18,7 @@ import MatrixClientPeg from "../../MatrixClientPeg"; import isIp from "is-ip"; import utils from 'matrix-js-sdk/lib/utils'; import SpecPermalinkConstructor, {baseUrl as matrixtoBaseUrl} from "./SpecPermalinkConstructor"; -import PermalinkConstructor from "./PermalinkConstructor"; +import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor"; import RiotPermalinkConstructor from "./RiotPermalinkConstructor"; const SdkConfig = require("../../SdkConfig"); @@ -291,6 +291,17 @@ function getPermalinkConstructor(): PermalinkConstructor { return new SpecPermalinkConstructor(); } +export function parsePermalink(fullUrl: string): PermalinkParts { + const riotPrefix = SdkConfig.get()['permalinkPrefix']; + if (fullUrl.startsWith(matrixtoBaseUrl)) { + return new SpecPermalinkConstructor().parsePermalink(fullUrl); + } else if (riotPrefix && fullUrl.startsWith(riotPrefix)) { + return new RiotPermalinkConstructor(riotPrefix).parsePermalink(fullUrl); + } + + return null; // not a permalink we can handle +} + function getServerName(userId) { return userId.split(":").splice(1).join(":"); } From 199dfa7bf91a00c2668e1209a84bd6d4b9f1300e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:04:49 -0600 Subject: [PATCH 38/55] Always check if the permalink is a spec permalink See code for rationale --- src/utils/permalinks/RoomPermalinkCreator.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 88bcd5768a..7df204a17b 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -280,6 +280,9 @@ export function makeGroupPermalink(groupId) { } export function isPermalinkHost(host: string): boolean { + // Always check if the permalink is a spec permalink (callers are likely to call + // parsePermalink after this function). + if (new SpecPermalinkConstructor().isPermalinkHost(host)) return true; return getPermalinkConstructor().isPermalinkHost(host); } From 8acaa3ce95e03df9a8996112df81c9e67ad36033 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:05:28 -0600 Subject: [PATCH 39/55] Update generated Riot permalinks Also adds some safety around the Riot URL to ensure it mostly looks like a URL --- .../permalinks/RiotPermalinkConstructor.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/utils/permalinks/RiotPermalinkConstructor.js b/src/utils/permalinks/RiotPermalinkConstructor.js index 207e181a15..1b985e8190 100644 --- a/src/utils/permalinks/RiotPermalinkConstructor.js +++ b/src/utils/permalinks/RiotPermalinkConstructor.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import PermalinkConstructor from "./PermalinkConstructor"; +import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor"; /** * Generates permalinks that self-reference the running webapp @@ -26,31 +26,31 @@ export default class RiotPermalinkConstructor extends PermalinkConstructor { constructor(riotUrl: string) { super(); this._riotUrl = riotUrl; + + if (!this._riotUrl.startsWith("http:") && !this._riotUrl.startsWith("https:")) { + throw new Error("Riot prefix URL does not appear to be an HTTP(S) URL"); + } } forEvent(roomId: string, eventId: string, serverCandidates: string[]): string { - // TODO: Fix URL - return `${this._riotUrl}/#/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`; + return `${this._riotUrl}/#/room/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`; } forRoom(roomIdOrAlias: string, serverCandidates: string[]): string { - // TODO: Fix URL - return `${this._riotUrl}/#/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`; + return `${this._riotUrl}/#/room/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`; } forUser(userId: string): string { - // TODO: Fix URL - return `${this._riotUrl}/#/${userId}`; + return `${this._riotUrl}/#/user/${userId}`; } forGroup(groupId: string): string { - // TODO: Fix URL - return `${this._riotUrl}/#/${groupId}`; + return `${this._riotUrl}/#/group/${groupId}`; } isPermalinkHost(testHost: string): boolean { - // TODO: Actual check - return testHost === this._riotUrl; + const parsedUrl = new URL(this._riotUrl); + return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match } encodeServerCandidates(candidates: string[]) { From 6656ef112f250df0c5c9ff9ab6d2fa73509baa31 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:06:00 -0600 Subject: [PATCH 40/55] Rework /join to parse permalinks more safely They might not be matrix.to anymore, so parse them more correctly. --- src/SlashCommands.js | 49 +++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 21c837030b..6a329761c8 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -23,8 +23,6 @@ import dis from './dispatcher'; import sdk from './index'; import {_t, _td} from './languageHandler'; import Modal from './Modal'; -import {MATRIXTO_URL_PATTERN} from "./linkify-matrix"; -import * as querystring from "querystring"; import MultiInviter from './utils/MultiInviter'; import { linkifyAndSanitizeHtml } from './HtmlUtils'; import QuestionDialog from "./components/views/dialogs/QuestionDialog"; @@ -34,6 +32,7 @@ import Promise from "bluebird"; import { getAddressType } from './UserAddress'; import { abbreviateUrl } from './utils/UrlUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; +import {isPermalinkHost, parsePermalink} from "./utils/permalinks/RoomPermalinkCreator"; const singleMxcUpload = async () => { return new Promise((resolve) => { @@ -441,7 +440,19 @@ export const CommandMap = { const params = args.split(' '); if (params.length < 1) return reject(this.getUsage()); - const matrixToMatches = params[0].match(MATRIXTO_URL_PATTERN); + let isPermalink = false; + if (params[0].startsWith("http:") || params[0].startsWith("https:")) { + // It's at least a URL - try and pull out a hostname to check against the + // permalink handler + const parsedUrl = new URL(params[0]); + const hostname = parsedUrl.host || parsedUrl.hostname; // takes first non-falsey value + + // if we're using a Riot permalink handler, this will catch it before we get much further. + // see below where we make assumptions about parsing the URL. + if (isPermalinkHost(hostname)) { + isPermalink = true; + } + } if (params[0][0] === '#') { let roomAlias = params[0]; if (!roomAlias.includes(':')) { @@ -469,29 +480,25 @@ export const CommandMap = { auto_join: true, }); return success(); - } else if (matrixToMatches) { - let entity = matrixToMatches[1]; - let eventId = null; - let viaServers = []; + } else if (isPermalink) { + const permalinkParts = parsePermalink(params[0]); - if (entity[0] !== '!' && entity[0] !== '#') return reject(this.getUsage()); - - if (entity.indexOf('?') !== -1) { - const parts = entity.split('?'); - entity = parts[0]; - - const parsed = querystring.parse(parts[1]); - viaServers = parsed["via"]; - if (typeof viaServers === 'string') viaServers = [viaServers]; + // This check technically isn't needed because we already did our + // safety checks up above. However, for good measure, let's be sure. + if (!permalinkParts) { + return reject(this.getUsage()); } - // We quietly support event ID permalinks too - if (entity.indexOf('/$') !== -1) { - const parts = entity.split("/$"); - entity = parts[0]; - eventId = `$${parts[1]}`; + // If for some reason someone wanted to join a group or user, we should + // stop them now. + if (!permalinkParts.roomIdOrAlias) { + return reject(this.getUsage()); } + const entity = permalinkParts.roomIdOrAlias; + const viaServers = permalinkParts.viaServers; + const eventId = permalinkParts.eventId; + const dispatch = { action: 'view_room', auto_join: true, From ff4eee5239a31908e78b33e88810a659aa924c6c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 16:09:19 -0600 Subject: [PATCH 41/55] Minor cleanup of getPermalinkConstructor --- src/utils/permalinks/RoomPermalinkCreator.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 7df204a17b..82f92c80c9 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -287,8 +287,9 @@ export function isPermalinkHost(host: string): boolean { } function getPermalinkConstructor(): PermalinkConstructor { - if (SdkConfig.get()['permalinkPrefix'] && SdkConfig.get()['permalinkPrefix'] !== matrixtoBaseUrl) { - return new RiotPermalinkConstructor(SdkConfig.get()['permalinkPrefix']); + const riotPrefix = SdkConfig.get()['permalinkPrefix']; + if (riotPrefix && riotPrefix !== matrixtoBaseUrl) { + return new RiotPermalinkConstructor(riotPrefix); } return new SpecPermalinkConstructor(); From 2cb0b4903a1001dde8c85db908efec2440ab4870 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 20:17:54 -0600 Subject: [PATCH 42/55] Converge on permalink processing for HtmlUtils and linkify-matrix --- src/HtmlUtils.js | 28 +++------------- src/linkify-matrix.js | 20 +++-------- src/utils/permalinks/RoomPermalinkCreator.js | 35 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 6ede36ee81..7fc6908caf 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -2,6 +2,7 @@ Copyright 2015, 2016 OpenMarket Ltd Copyright 2017, 2018 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +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,6 +34,7 @@ import url from 'url'; import EMOJIBASE from 'emojibase-data/en/compact.json'; import EMOJIBASE_REGEX from 'emojibase-regex'; +import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/RoomPermalinkCreator"; linkifyMatrix(linkify); @@ -158,30 +160,10 @@ const transformTags = { // custom to matrix if (attribs.href) { attribs.target = '_blank'; // by default - let m; - // FIXME: horrible duplication with linkify-matrix - m = attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN); - if (m) { - attribs.href = m[1]; + const transformed = tryTransformPermalinkToLocalHref(attribs.href); + if (transformed !== attribs.href || attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN)) { + attribs.href = transformed; delete attribs.target; - } else { - m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN); - if (m) { - const entity = m[1]; - switch (entity[0]) { - case '@': - attribs.href = '#/user/' + entity; - break; - case '+': - attribs.href = '#/group/' + entity; - break; - case '#': - case '!': - attribs.href = '#/room/' + entity; - break; - } - delete attribs.target; - } } } attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/ diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index 2d72b7fb41..0bed83ce84 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket 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. @@ -15,6 +16,7 @@ limitations under the License. */ import {baseUrl} from "./utils/permalinks/SpecPermalinkConstructor"; +import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/RoomPermalinkCreator"; function matrixLinkify(linkify) { // Text tokens @@ -225,20 +227,8 @@ matrixLinkify.options = { case 'roomalias': case 'userid': case 'groupid': - return matrixLinkify.MATRIXTO_BASE_URL + '/#/' + href; default: { - // FIXME: horrible duplication with HtmlUtils' transform tags - let m = href.match(matrixLinkify.VECTOR_URL_PATTERN); - if (m) { - return m[1]; - } - m = href.match(matrixLinkify.MATRIXTO_URL_PATTERN); - if (m) { - const entity = m[1]; - if (matrixToEntityMap[entity[0]]) return matrixToEntityMap[entity[0]] + entity; - } - - return href; + return tryTransformPermalinkToLocalHref(href); } } }, @@ -249,8 +239,8 @@ matrixLinkify.options = { target: function(href, type) { if (type === 'url') { - if (href.match(matrixLinkify.VECTOR_URL_PATTERN) || - href.match(matrixLinkify.MATRIXTO_URL_PATTERN)) { + const transformed = tryTransformPermalinkToLocalHref(href); + if (transformed !== href || href.match(matrixLinkify.VECTOR_URL_PATTERN)) { return null; } else { return '_blank'; diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 82f92c80c9..489a6c8b82 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -20,6 +20,7 @@ import utils from 'matrix-js-sdk/lib/utils'; import SpecPermalinkConstructor, {baseUrl as matrixtoBaseUrl} from "./SpecPermalinkConstructor"; import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor"; import RiotPermalinkConstructor from "./RiotPermalinkConstructor"; +import * as matrixLinkify from "../../linkify-matrix"; const SdkConfig = require("../../SdkConfig"); @@ -286,6 +287,40 @@ export function isPermalinkHost(host: string): boolean { return getPermalinkConstructor().isPermalinkHost(host); } +/** + * Transforms a permalink (or possible permalink) into a local URL if possible. If + * the given permalink is found to not be a permalink, it'll be returned unaltered. + */ +export function tryTransformPermalinkToLocalHref(permalink: string): string { + if (!permalink.startsWith("http:") && !permalink.startsWith("https:")) { + return permalink; + } + + let m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); + if (m) { + return m[1]; + } + + // A bit of a hack to convert permalinks of unknown origin to Riot links + try { + const permalinkParts = parsePermalink(permalink); + if (permalinkParts) { + if (permalinkParts.roomIdOrAlias) { + const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : ''; + permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`; + } else if (permalinkParts.groupId) { + permalink = `#/group/${permalinkParts.groupId}`; + } else if (permalinkParts.userId) { + permalink = `#/user/${permalinkParts.userId}`; + } // else not a valid permalink for our purposes - do not handle + } + } catch (e) { + // Not an href we need to care about + } + + return permalink; +} + function getPermalinkConstructor(): PermalinkConstructor { const riotPrefix = SdkConfig.get()['permalinkPrefix']; if (riotPrefix && riotPrefix !== matrixtoBaseUrl) { From 2824f468d98c34b1304e5e294ea546253bff13c4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 20:27:51 -0600 Subject: [PATCH 43/55] Update pill processing to handle better permalinks --- src/components/views/elements/Pill.js | 32 +++++++++---------- .../views/rooms/MessageComposerInput.js | 13 +++----- src/utils/permalinks/RoomPermalinkCreator.js | 26 +++++++++++++++ 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 4c987a0095..abe570cdcd 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -1,6 +1,7 @@ /* Copyright 2017 Vector Creations Ltd Copyright 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. @@ -22,23 +23,21 @@ import classNames from 'classnames'; import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk'; import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix'; import { getDisplayAliasForRoom } from '../../../Rooms'; import FlairStore from "../../../stores/FlairStore"; - -const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); +import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/RoomPermalinkCreator"; // For URLs of matrix.to links in the timeline which have been reformatted by // HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`) -const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/; +const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/; const Pill = createReactClass({ statics: { isPillUrl: (url) => { - return !!REGEX_MATRIXTO.exec(url); + return !!getPrimaryPermalinkEntity(url); }, isMessagePillUrl: (url) => { - return !!REGEX_LOCAL_MATRIXTO.exec(url); + return !!REGEX_LOCAL_PERMALINK.exec(url); }, roomNotifPos: (text) => { return text.indexOf("@room"); @@ -95,22 +94,21 @@ const Pill = createReactClass({ }, async componentWillReceiveProps(nextProps) { - let regex = REGEX_MATRIXTO; - if (nextProps.inMessage) { - regex = REGEX_LOCAL_MATRIXTO; - } - - let matrixToMatch; let resourceId; let prefix; if (nextProps.url) { - // Default to the empty array if no match for simplicity - // resource and prefix will be undefined instead of throwing - matrixToMatch = regex.exec(nextProps.url) || []; + if (nextProps.inMessage) { + // Default to the empty array if no match for simplicity + // resource and prefix will be undefined instead of throwing + const matrixToMatch = REGEX_LOCAL_PERMALINK.exec(nextProps.url) || []; - resourceId = matrixToMatch[1]; // The room/user ID - prefix = matrixToMatch[2]; // The first character of prefix + resourceId = matrixToMatch[1]; // The room/user ID + prefix = matrixToMatch[2]; // The first character of prefix + } else { + resourceId = getPrimaryPermalinkEntity(nextProps.url); + prefix = resourceId ? resourceId[0] : undefined; + } } const pillType = this.props.type || { diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 917465aaaa..6696a91483 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -48,13 +48,11 @@ import Markdown from '../../../Markdown'; import MessageComposerStore from '../../../stores/MessageComposerStore'; import ContentMessages from '../../../ContentMessages'; -import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix'; - import EMOJIBASE from 'emojibase-data/en/compact.json'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import {makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; import ReplyPreview from "./ReplyPreview"; import RoomViewStore from '../../../stores/RoomViewStore'; import ReplyThread from "../elements/ReplyThread"; @@ -224,18 +222,15 @@ export default class MessageComposerInput extends React.Component { // special case links if (tag === 'a') { const href = el.getAttribute('href'); - let m; - if (href) { - m = href.match(MATRIXTO_URL_PATTERN); - } - if (m) { + const permalinkEntity = getPrimaryPermalinkEntity(href); + if (permalinkEntity) { return { object: 'inline', type: 'pill', data: { href, completion: el.innerText, - completionId: m[1], + completionId: permalinkEntity, }, }; } else { diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 489a6c8b82..666729bacd 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -321,6 +321,32 @@ export function tryTransformPermalinkToLocalHref(permalink: string): string { return permalink; } +export function getPrimaryPermalinkEntity(permalink: string): string { + try { + let permalinkParts = parsePermalink(permalink); + + // If not a permalink, try the vector patterns. + if (!permalinkParts) { + let m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); + if (m) { + // A bit of a hack, but it gets the job done + const handler = new RiotPermalinkConstructor("http://localhost"); + const entityInfo = m[1].split('#').slice(1).join('#'); + permalinkParts = handler.parsePermalink(`http://localhost/#${entityInfo}`); + } + } + + if (!permalinkParts) return null; // not processable + if (permalinkParts.userId) return permalinkParts.userId; + if (permalinkParts.groupId) return permalinkParts.groupId; + if (permalinkParts.roomIdOrAlias) return permalinkParts.roomIdOrAlias; + } catch (e) { + // no entity - not a permalink + } + + return null; +} + function getPermalinkConstructor(): PermalinkConstructor { const riotPrefix = SdkConfig.get()['permalinkPrefix']; if (riotPrefix && riotPrefix !== matrixtoBaseUrl) { From ce0a534db1ed14565eee9d690c52098433671dc4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 20:37:24 -0600 Subject: [PATCH 44/55] Fix pills for CIDER too --- src/editor/deserialize.js | 9 +++------ src/editor/serialize.js | 3 ++- src/utils/permalinks/PermalinkConstructor.js | 4 ++++ src/utils/permalinks/RiotPermalinkConstructor.js | 10 ++++++++++ src/utils/permalinks/RoomPermalinkCreator.js | 4 ++++ src/utils/permalinks/SpecPermalinkConstructor.js | 4 ++++ 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/editor/deserialize.js b/src/editor/deserialize.js index 04edd4541c..2d725af4ac 100644 --- a/src/editor/deserialize.js +++ b/src/editor/deserialize.js @@ -15,11 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MATRIXTO_URL_PATTERN } from '../linkify-matrix'; import { walkDOMDepthFirst } from "./dom"; import { checkBlockNode } from "../HtmlUtils"; - -const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN); +import {getPrimaryPermalinkEntity} from "../utils/permalinks/RoomPermalinkCreator"; function parseAtRoomMentions(text, partCreator) { const ATROOM = "@room"; @@ -41,9 +39,8 @@ function parseAtRoomMentions(text, partCreator) { function parseLink(a, partCreator) { const {href} = a; - const pillMatch = REGEX_MATRIXTO.exec(href) || []; - const resourceId = pillMatch[1]; // The room/user ID - const prefix = pillMatch[2]; // The first character of prefix + const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID + const prefix = resourceId ? resourceId[0] : undefined; // First character of ID switch (prefix) { case "@": return partCreator.userPill(a.textContent, resourceId); diff --git a/src/editor/serialize.js b/src/editor/serialize.js index 07a1ad908e..734e2ba030 100644 --- a/src/editor/serialize.js +++ b/src/editor/serialize.js @@ -16,6 +16,7 @@ limitations under the License. */ import Markdown from '../Markdown'; +import {makeGenericPermalink} from "../utils/permalinks/RoomPermalinkCreator"; export function mdSerialize(model) { return model.parts.reduce((html, part) => { @@ -29,7 +30,7 @@ export function mdSerialize(model) { return html + part.text; case "room-pill": case "user-pill": - return html + `[${part.text}](https://matrix.to/#/${part.resourceId})`; + return html + `[${part.text}](${makeGenericPermalink(part.resourceId)})`; } }, ""); } diff --git a/src/utils/permalinks/PermalinkConstructor.js b/src/utils/permalinks/PermalinkConstructor.js index 507f16f795..f74c432bf0 100644 --- a/src/utils/permalinks/PermalinkConstructor.js +++ b/src/utils/permalinks/PermalinkConstructor.js @@ -35,6 +35,10 @@ export default class PermalinkConstructor { throw new Error("Not implemented"); } + forEntity(entityId: string): string { + throw new Error("Not implemented"); + } + isPermalinkHost(host: string): boolean { throw new Error("Not implemented"); } diff --git a/src/utils/permalinks/RiotPermalinkConstructor.js b/src/utils/permalinks/RiotPermalinkConstructor.js index 1b985e8190..f8c4cb361e 100644 --- a/src/utils/permalinks/RiotPermalinkConstructor.js +++ b/src/utils/permalinks/RiotPermalinkConstructor.js @@ -48,6 +48,16 @@ export default class RiotPermalinkConstructor extends PermalinkConstructor { return `${this._riotUrl}/#/group/${groupId}`; } + forEntity(entityId: string): string { + if (entityId[0] === '!' || entityId[0] === '#') { + return this.forRoom(entityId); + } else if (entityId[0] === '@') { + return this.forUser(entityId); + } else if (entityId[0] === '+') { + return this.forGroup(entityId); + } else throw new Error("Unrecognized entity"); + } + isPermalinkHost(testHost: string): boolean { const parsedUrl = new URL(this._riotUrl); return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/RoomPermalinkCreator.js index 666729bacd..4cac095ca0 100644 --- a/src/utils/permalinks/RoomPermalinkCreator.js +++ b/src/utils/permalinks/RoomPermalinkCreator.js @@ -253,6 +253,10 @@ export class RoomPermalinkCreator { } } +export function makeGenericPermalink(entityId: string): string { + return getPermalinkConstructor().forEntity(entityId); +} + export function makeUserPermalink(userId) { return getPermalinkConstructor().forUser(userId); } diff --git a/src/utils/permalinks/SpecPermalinkConstructor.js b/src/utils/permalinks/SpecPermalinkConstructor.js index e4cff798a7..1c80ff8975 100644 --- a/src/utils/permalinks/SpecPermalinkConstructor.js +++ b/src/utils/permalinks/SpecPermalinkConstructor.js @@ -43,6 +43,10 @@ export default class SpecPermalinkConstructor extends PermalinkConstructor { return `${baseUrl}/#/${groupId}`; } + forEntity(entityId: string): string { + return `${baseUrl}/#/${entityId}`; + } + isPermalinkHost(testHost: string): boolean { return testHost === host; } From 6e6f8a13e10aa8b8eaa18f382e99f157fd36908d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 20:37:50 -0600 Subject: [PATCH 45/55] Handle BigEmoji permalinks better Now that permalinks could be not-matrix.to we should be safer in what we'll allow. --- src/HtmlUtils.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 7fc6908caf..3620eb2973 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -447,10 +447,12 @@ export function bodyToHtml(content, highlights, opts={}) { const match = BIGEMOJI_REGEX.exec(contentBodyTrimmed); emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length && // Prevent user pills expanding for users with only emoji in - // their username + // their username. Permalinks (links in pills) can be any URL + // now, so we just check for an HTTP-looking thing. ( content.formatted_body == undefined || - !content.formatted_body.includes("https://matrix.to/") + !content.formatted_body.includes("http:") || + !content.formatted_body.includes("https:") ); } From 6f5ccd4c12c366d73c43193e27cef916e7dea3e0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 20:38:03 -0600 Subject: [PATCH 46/55] Minor comment updates --- src/components/views/messages/TextualBody.js | 2 +- src/components/views/rooms/MessageComposerInput.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index d3c53c8a33..5b5de531de 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -248,7 +248,7 @@ module.exports = createReactClass({ const url = node.getAttribute("href"); const host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1]; - // never preview matrix.to links (if anything we should give a smart + // never preview permalinks (if anything we should give a smart // preview of the room/user they point to: nobody needs to be reminded // what the matrix.to site looks like). if (isPermalinkHost(host)) return false; diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 6696a91483..3a51ac19e8 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -536,7 +536,7 @@ export default class MessageComposerInput extends React.Component { const textWithMdPills = this.plainWithMdPills.serialize(editorState); const markdown = new Markdown(textWithMdPills); - // HTML deserialize has custom rules to turn matrix.to links into pill objects. + // HTML deserialize has custom rules to turn permalinks into pill objects. return this.html.deserialize(markdown.toHTML()); } From fc66e69c02f6eccb97fe27dbcdc19170e2be447a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 20:39:58 -0600 Subject: [PATCH 47/55] Rename RoomPermalinkCreator -> Permalinks due to scope The file handles more than just a RoomPermalinkCreator, so we should name it accordingly. --- src/HtmlUtils.js | 2 +- src/SlashCommands.js | 2 +- src/autocomplete/CommunityProvider.js | 2 +- src/autocomplete/RoomProvider.js | 2 +- src/autocomplete/UserProvider.js | 2 +- src/components/structures/GroupView.js | 2 +- src/components/structures/RoomView.js | 2 +- src/components/views/dialogs/ShareDialog.js | 2 +- src/components/views/elements/Pill.js | 2 +- src/components/views/elements/ReplyThread.js | 2 +- src/components/views/messages/RoomCreate.js | 2 +- src/components/views/messages/TextualBody.js | 2 +- src/components/views/rooms/MessageComposer.js | 2 +- src/components/views/rooms/MessageComposerInput.js | 2 +- src/components/views/rooms/ReplyPreview.js | 2 +- src/components/views/rooms/SlateMessageComposer.js | 2 +- src/editor/deserialize.js | 2 +- src/editor/serialize.js | 2 +- src/linkify-matrix.js | 2 +- src/utils/permalinks/{RoomPermalinkCreator.js => Permalinks.js} | 0 test/utils/permalinks/RoomPermalinkCreator-test.js | 2 +- 21 files changed, 20 insertions(+), 20 deletions(-) rename src/utils/permalinks/{RoomPermalinkCreator.js => Permalinks.js} (100%) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 3620eb2973..e1bd85a7bb 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -34,7 +34,7 @@ import url from 'url'; import EMOJIBASE from 'emojibase-data/en/compact.json'; import EMOJIBASE_REGEX from 'emojibase-regex'; -import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/RoomPermalinkCreator"; +import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks"; linkifyMatrix(linkify); diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 6a329761c8..d9fe28cc6d 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -32,7 +32,7 @@ import Promise from "bluebird"; import { getAddressType } from './UserAddress'; import { abbreviateUrl } from './utils/UrlUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; -import {isPermalinkHost, parsePermalink} from "./utils/permalinks/RoomPermalinkCreator"; +import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks"; const singleMxcUpload = async () => { return new Promise((resolve) => { diff --git a/src/autocomplete/CommunityProvider.js b/src/autocomplete/CommunityProvider.js index 14c0116604..7358fdb8a9 100644 --- a/src/autocomplete/CommunityProvider.js +++ b/src/autocomplete/CommunityProvider.js @@ -23,7 +23,7 @@ import QueryMatcher from './QueryMatcher'; import {PillCompletion} from './Components'; import sdk from '../index'; import _sortBy from 'lodash/sortBy'; -import {makeGroupPermalink} from "../utils/permalinks/RoomPermalinkCreator"; +import {makeGroupPermalink} from "../utils/permalinks/Permalinks"; import type {Completion, SelectionRange} from "./Autocompleter"; import FlairStore from "../stores/FlairStore"; diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index 062fb5a5c5..3e87545b70 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -26,7 +26,7 @@ import {PillCompletion} from './Components'; import {getDisplayAliasForRoom} from '../Rooms'; import sdk from '../index'; import _sortBy from 'lodash/sortBy'; -import {makeRoomPermalink} from "../utils/permalinks/RoomPermalinkCreator"; +import {makeRoomPermalink} from "../utils/permalinks/Permalinks"; import type {Completion, SelectionRange} from "./Autocompleter"; const ROOM_REGEX = /\B#\S*/g; diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index d154e02a9d..6f3eb3e6b8 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -28,7 +28,7 @@ import _sortBy from 'lodash/sortBy'; import MatrixClientPeg from '../MatrixClientPeg'; import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk'; -import {makeUserPermalink} from "../utils/permalinks/RoomPermalinkCreator"; +import {makeUserPermalink} from "../utils/permalinks/Permalinks"; import type {Completion, SelectionRange} from "./Autocompleter"; const USER_REGEX = /\B@\S*/g; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 6c96dec04f..dd520bb7d4 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -36,7 +36,7 @@ import classnames from 'classnames'; import GroupStore from '../../stores/GroupStore'; import FlairStore from '../../stores/FlairStore'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; -import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/RoomPermalinkCreator"; +import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Permalinks"; import {Group} from "matrix-js-sdk"; const LONG_DESC_PLACEHOLDER = _td( diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 057b1f2d80..4d52158dae 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -31,7 +31,7 @@ import Promise from 'bluebird'; import classNames from 'classnames'; import {Room} from "matrix-js-sdk"; import { _t } from '../../languageHandler'; -import {RoomPermalinkCreator} from '../../utils/permalinks/RoomPermalinkCreator'; +import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks'; import MatrixClientPeg from '../../MatrixClientPeg'; import ContentMessages from '../../ContentMessages'; diff --git a/src/components/views/dialogs/ShareDialog.js b/src/components/views/dialogs/ShareDialog.js index ae43ee2ae5..f6d5b65fd6 100644 --- a/src/components/views/dialogs/ShareDialog.js +++ b/src/components/views/dialogs/ShareDialog.js @@ -20,7 +20,7 @@ import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk'; import sdk from '../../../index'; import { _t } from '../../../languageHandler'; import QRCode from 'qrcode-react'; -import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import * as ContextualMenu from "../../structures/ContextualMenu"; const socials = [ diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index abe570cdcd..12830488b1 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -25,7 +25,7 @@ import PropTypes from 'prop-types'; import MatrixClientPeg from '../../../MatrixClientPeg'; import { getDisplayAliasForRoom } from '../../../Rooms'; import FlairStore from "../../../stores/FlairStore"; -import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks"; // For URLs of matrix.to links in the timeline which have been reformatted by // HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`) diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 5c1d6a3ef4..fac0a71617 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import dis from '../../../dispatcher'; import {wantsDateSeparator} from '../../../DateUtils'; import {MatrixEvent, MatrixClient} from 'matrix-js-sdk'; -import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; import SettingsStore from "../../../settings/SettingsStore"; // This component does no cycle detection, simply because the only way to make such a cycle would be to diff --git a/src/components/views/messages/RoomCreate.js b/src/components/views/messages/RoomCreate.js index 282ba28b8a..9bb6fcc0d8 100644 --- a/src/components/views/messages/RoomCreate.js +++ b/src/components/views/messages/RoomCreate.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import dis from '../../../dispatcher'; -import { RoomPermalinkCreator } from '../../../utils/permalinks/RoomPermalinkCreator'; +import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import { _t } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 5b5de531de..a9e0da143e 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -32,7 +32,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import ReplyThread from "../elements/ReplyThread"; import {pillifyLinks} from '../../../utils/pillify'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; -import {isPermalinkHost} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {isPermalinkHost} from "../../../utils/permalinks/Permalinks"; module.exports = createReactClass({ displayName: 'TextualBody', diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 4e7f8825ad..632ca53f82 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -23,7 +23,7 @@ import sdk from '../../../index'; import dis from '../../../dispatcher'; import RoomViewStore from '../../../stores/RoomViewStore'; import Stickerpicker from './Stickerpicker'; -import { makeRoomPermalink } from '../../../utils/permalinks/RoomPermalinkCreator'; +import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; import ContentMessages from '../../../ContentMessages'; import classNames from 'classnames'; import E2EIcon from './E2EIcon'; diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 3a51ac19e8..cc92f7c750 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -52,7 +52,7 @@ import EMOJIBASE from 'emojibase-data/en/compact.json'; import EMOTICON_REGEX from 'emojibase-regex/emoticon'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import ReplyPreview from "./ReplyPreview"; import RoomViewStore from '../../../stores/RoomViewStore'; import ReplyThread from "../elements/ReplyThread"; diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index 97b29dddee..caf8feeea2 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler'; import RoomViewStore from '../../../stores/RoomViewStore'; import SettingsStore from "../../../settings/SettingsStore"; import PropTypes from "prop-types"; -import {RoomPermalinkCreator} from "../../../utils/permalinks/RoomPermalinkCreator"; +import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; function cancelQuoting() { dis.dispatch({ diff --git a/src/components/views/rooms/SlateMessageComposer.js b/src/components/views/rooms/SlateMessageComposer.js index 8b51f8266f..4bb2f29e61 100644 --- a/src/components/views/rooms/SlateMessageComposer.js +++ b/src/components/views/rooms/SlateMessageComposer.js @@ -25,7 +25,7 @@ import dis from '../../../dispatcher'; import RoomViewStore from '../../../stores/RoomViewStore'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import Stickerpicker from './Stickerpicker'; -import { makeRoomPermalink } from '../../../utils/permalinks/RoomPermalinkCreator'; +import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; import ContentMessages from '../../../ContentMessages'; import classNames from 'classnames'; diff --git a/src/editor/deserialize.js b/src/editor/deserialize.js index 2d725af4ac..d41e413dbc 100644 --- a/src/editor/deserialize.js +++ b/src/editor/deserialize.js @@ -17,7 +17,7 @@ limitations under the License. import { walkDOMDepthFirst } from "./dom"; import { checkBlockNode } from "../HtmlUtils"; -import {getPrimaryPermalinkEntity} from "../utils/permalinks/RoomPermalinkCreator"; +import {getPrimaryPermalinkEntity} from "../utils/permalinks/Permalinks"; function parseAtRoomMentions(text, partCreator) { const ATROOM = "@room"; diff --git a/src/editor/serialize.js b/src/editor/serialize.js index 734e2ba030..a55eed97da 100644 --- a/src/editor/serialize.js +++ b/src/editor/serialize.js @@ -16,7 +16,7 @@ limitations under the License. */ import Markdown from '../Markdown'; -import {makeGenericPermalink} from "../utils/permalinks/RoomPermalinkCreator"; +import {makeGenericPermalink} from "../utils/permalinks/Permalinks"; export function mdSerialize(model) { return model.parts.reduce((html, part) => { diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index 0bed83ce84..1ec2434bf0 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -16,7 +16,7 @@ limitations under the License. */ import {baseUrl} from "./utils/permalinks/SpecPermalinkConstructor"; -import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/RoomPermalinkCreator"; +import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks"; function matrixLinkify(linkify) { // Text tokens diff --git a/src/utils/permalinks/RoomPermalinkCreator.js b/src/utils/permalinks/Permalinks.js similarity index 100% rename from src/utils/permalinks/RoomPermalinkCreator.js rename to src/utils/permalinks/Permalinks.js diff --git a/test/utils/permalinks/RoomPermalinkCreator-test.js b/test/utils/permalinks/RoomPermalinkCreator-test.js index 09bfaa3974..9b4afcfc05 100644 --- a/test/utils/permalinks/RoomPermalinkCreator-test.js +++ b/test/utils/permalinks/RoomPermalinkCreator-test.js @@ -18,7 +18,7 @@ import { makeRoomPermalink, makeUserPermalink, RoomPermalinkCreator, -} from "../../../src/utils/permalinks/RoomPermalinkCreator"; +} from "../../../src/utils/permalinks/Permalinks"; import * as testUtils from "../../test-utils"; function mockRoom(roomId, members, serverACL) { From 6b09c3e9e6db7ceaf1c5c2ebf3ba8d5c63dbfe87 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 21:03:16 -0600 Subject: [PATCH 48/55] Appease the linter --- src/linkify-matrix.js | 7 ------- src/utils/permalinks/Permalinks.js | 6 ++++-- src/utils/permalinks/RiotPermalinkConstructor.js | 1 - 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index 1ec2434bf0..fabd9d15ad 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -191,13 +191,6 @@ matrixLinkify.MATRIXTO_MD_LINK_PATTERN = '\\[([^\\]]*)\\]\\((?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!+][^\\)]*)\\)'; matrixLinkify.MATRIXTO_BASE_URL= baseUrl; -const matrixToEntityMap = { - '@': '#/user/', - '#': '#/room/', - '!': '#/room/', - '+': '#/group/', -}; - matrixLinkify.options = { events: function(href, type) { switch (type) { diff --git a/src/utils/permalinks/Permalinks.js b/src/utils/permalinks/Permalinks.js index 4cac095ca0..19dcbd062a 100644 --- a/src/utils/permalinks/Permalinks.js +++ b/src/utils/permalinks/Permalinks.js @@ -294,13 +294,15 @@ export function isPermalinkHost(host: string): boolean { /** * Transforms a permalink (or possible permalink) into a local URL if possible. If * the given permalink is found to not be a permalink, it'll be returned unaltered. + * @param {string} permalink The permalink to try and transform. + * @returns {string} The transformed permalink or original URL if unable. */ export function tryTransformPermalinkToLocalHref(permalink: string): string { if (!permalink.startsWith("http:") && !permalink.startsWith("https:")) { return permalink; } - let m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); + const m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); if (m) { return m[1]; } @@ -331,7 +333,7 @@ export function getPrimaryPermalinkEntity(permalink: string): string { // If not a permalink, try the vector patterns. if (!permalinkParts) { - let m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); + const m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN); if (m) { // A bit of a hack, but it gets the job done const handler = new RiotPermalinkConstructor("http://localhost"); diff --git a/src/utils/permalinks/RiotPermalinkConstructor.js b/src/utils/permalinks/RiotPermalinkConstructor.js index f8c4cb361e..176100aa8b 100644 --- a/src/utils/permalinks/RiotPermalinkConstructor.js +++ b/src/utils/permalinks/RiotPermalinkConstructor.js @@ -20,7 +20,6 @@ import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor"; * Generates permalinks that self-reference the running webapp */ export default class RiotPermalinkConstructor extends PermalinkConstructor { - _riotUrl: string; constructor(riotUrl: string) { From f903f431ed63d8af01114f12b4ad93d65045ea7d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 21:08:34 -0600 Subject: [PATCH 49/55] Fix BigEmoji handling for permalinks OR doesn't give us the right thing, but AND does. --- src/HtmlUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index e1bd85a7bb..7a212b2497 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -451,8 +451,8 @@ export function bodyToHtml(content, highlights, opts={}) { // now, so we just check for an HTTP-looking thing. ( content.formatted_body == undefined || - !content.formatted_body.includes("http:") || - !content.formatted_body.includes("https:") + (!content.formatted_body.includes("http:") && + !content.formatted_body.includes("https:")) ); } From 0862ad029da494554caba4ab6f1a5f870199340a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Sep 2019 21:15:31 -0600 Subject: [PATCH 50/55] Fix permalinks test --- .../{RoomPermalinkCreator-test.js => Permalinks-test.js} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename test/utils/permalinks/{RoomPermalinkCreator-test.js => Permalinks-test.js} (99%) diff --git a/test/utils/permalinks/RoomPermalinkCreator-test.js b/test/utils/permalinks/Permalinks-test.js similarity index 99% rename from test/utils/permalinks/RoomPermalinkCreator-test.js rename to test/utils/permalinks/Permalinks-test.js index 9b4afcfc05..27f06b44cb 100644 --- a/test/utils/permalinks/RoomPermalinkCreator-test.js +++ b/test/utils/permalinks/Permalinks-test.js @@ -1,5 +1,7 @@ /* Copyright 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 @@ -11,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import expect from 'expect/build/index'; +import expect from 'expect'; import peg from '../../../src/MatrixClientPeg'; import { makeGroupPermalink, @@ -62,7 +64,7 @@ function mockRoom(roomId, members, serverACL) { }; } -describe('matrix-to', function() { +describe('Permalinks', function() { let sandbox; beforeEach(function() { From d3264b03e0860d9cb5aad2f37fd33bee0146270d Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 1 Oct 2019 09:15:52 +0000 Subject: [PATCH 51/55] Translated using Weblate (Albanian) Currently translated at 99.7% (1807 of 1812 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 | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 8fc616d303..dcdb252d98 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2147,5 +2147,32 @@ "Find a room…": "Gjeni një dhomë…", "Find a room… (e.g. %(exampleRoom)s)": "Gjeni një dhomë… (p.sh. %(exampleRoom)s)", "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Nëse s’gjeni dot dhomën që po kërkoni, kërkoni një ftesë ose Krijoni një dhomë të re.", - "Explore rooms": "Eksploroni dhoma" + "Explore rooms": "Eksploroni dhoma", + "Changes the avatar of the current room": "Ndryshon avatarin e dhomës së atëçastshme", + "Read Marker lifetime (ms)": "Kohëzgjatje e Shenjës së Leximit (ms)", + "Read Marker off-screen lifetime (ms)": "Kohëzgjatje Shenje Leximi jashtë ekrani (ms)", + "Verify the link in your inbox": "Verifikoni lidhjen te mesazhet tuaj", + "Room alias": "Alias dhome", + "e.g. my-room": "p.sh., dhoma-ime", + "Please provide a room alias": "Ju lutemi, jepni një alias dhome", + "This alias is available to use": "Ky alias është i lirë të përdoret", + "This alias is already in use": "Ky alias është i përdorur tashmë", + "Close dialog": "Mbylle dialogun", + "Please enter a name for the room": "Ju lutemi, jepni një emër për dhomën", + "Set a room alias to easily share your room with other people.": "Caktoni një alias dhome për t’ua dhënë lehtësisht dhomën tuaj personave të tjerë.", + "This room is private, and can only be joined by invitation.": "Kjo dhomë është private dhe në të mund të hyhet vetëm me ftesë.", + "Create a public room": "Krijoni një dhomë publike", + "Create a private room": "Krijoni një dhomë private", + "Topic (optional)": "Temë (në daçi)", + "Make this room public": "Bëje publike këtë dhomë", + "Hide advanced": "Fshihi të mëtejshmet", + "Show advanced": "Shfaqi të mëtejshmet", + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Bllokojua hyrjen në këtë dhomë përdoruesve prej shërbyesish Matrix të tjerë Home (Ky rregullim s’mund të ndryshohet më vonë!)", + "Please fill why you're reporting.": "Ju lutemi, plotësoni arsyen pse po raportoni.", + "Report Content to Your Homeserver Administrator": "Raportoni Lëndë te Përgjegjësi i Shërbyesit Tuaj Home", + "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Raportimi i këtij mesazhi do të shkaktojë dërgimin e 'ID-së së aktit' unike te përgjegjësi i shërbyesit tuaj Home. Nëse mesazhet në këtë dhomë fshehtëzohen, përgjegjësi i shërbyesit tuaj Home s’do të jetë në gjendje të lexojë tekstin e mesazhit apo të shohë çfarëdo kartelë apo figurë.", + "Send report": "Dërgoje raportin", + "To continue you need to accept the terms of this service.": "Që të vazhdohet, lypset të pranoni kushtet e këtij shërbimi.", + "Document": "Dokument", + "Report Content": "Raportoni Lëndë" } From fa12b60c4a2cd5aad946fe07a613d79540532595 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 1 Oct 2019 11:35:33 +0100 Subject: [PATCH 52/55] Upgrade to JS SDK 2.4.1 --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a1a4f51afb..af97d446c9 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "linkifyjs": "^2.1.6", "lodash": "^4.17.14", "lolex": "4.2", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "2.4.1", "optimist": "^0.6.1", "pako": "^1.0.5", "png-chunks-extract": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 88bf4660f3..0c06663f4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5187,9 +5187,10 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.1.tgz#6dff66c99d55ecf739ca53c492e626f1d12a33cc" integrity sha512-pWB896KPGSGkp1XtyzRBftpTzwSOL0Gfk0wLvxt4f2mgzjY19o0LxJ3U25vNWTzsh7da+KTbuXQoQ3lOJZ8WHw== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "2.4.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/562bf9331bd5ee8701625cd9632c8e73374b18f4" +matrix-js-sdk@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-2.4.1.tgz#f32f8ebb1f01fa26711a4cf6b78eae125156f455" + integrity sha512-5mOp396eOtvaMiuUD85TWvuxSP532PuvtH/QLugBGenI15FGwtnC40cTnVYviYWGBi340FPrOKWulc5ILRX6qQ== dependencies: another-json "^0.2.0" babel-runtime "^6.26.0" From ca18e167db05bd21a0669a0db8978cd0d4368784 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 1 Oct 2019 11:40:46 +0100 Subject: [PATCH 53/55] Prepare changelog for v1.6.1 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c80e71115..59a4981090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +Changes in [1.6.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.1) (2019-10-01) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0...v1.6.1) + + * Upgrade to JS SDK 2.4.1 to ignore crypto events with empty content + * Update from Weblate + [\#3502](https://github.com/matrix-org/matrix-react-sdk/pull/3502) + * Adjust details of terms dialog + [\#3489](https://github.com/matrix-org/matrix-react-sdk/pull/3489) + * Okay -> OK + [\#3491](https://github.com/matrix-org/matrix-react-sdk/pull/3491) + * Guard against falsy names in getInitialLetter + [\#3498](https://github.com/matrix-org/matrix-react-sdk/pull/3498) + * Update from Weblate + [\#3495](https://github.com/matrix-org/matrix-react-sdk/pull/3495) + * Upgrade deps + [\#3488](https://github.com/matrix-org/matrix-react-sdk/pull/3488) + * Fix: allow mass redaction for members with same or larger power level + [\#3487](https://github.com/matrix-org/matrix-react-sdk/pull/3487) + * Truncate debug logs at the start, not the end + [\#3484](https://github.com/matrix-org/matrix-react-sdk/pull/3484) + * Fix: don't block Shift+Tab in CIDER autocomplete + [\#3481](https://github.com/matrix-org/matrix-react-sdk/pull/3481) + * Fix: make command detection more resilient + [\#3479](https://github.com/matrix-org/matrix-react-sdk/pull/3479) + Changes in [1.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.0) (2019-09-27) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0-rc.2...v1.6.0) From e457ef3679966f13035f7e6e0cc40b88d2b300cc Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 1 Oct 2019 11:40:47 +0100 Subject: [PATCH 54/55] v1.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af97d446c9..1f351335d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "1.6.0", + "version": "1.6.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 617a5290f9fbf222f756d79a8b6bcd3d80bfec67 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 1 Oct 2019 13:30:13 +0100 Subject: [PATCH 55/55] Change back to develop branch for deps --- package.json | 2 +- yarn.lock | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1f351335d9..8f2a206000 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "linkifyjs": "^2.1.6", "lodash": "^4.17.14", "lolex": "4.2", - "matrix-js-sdk": "2.4.1", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "optimist": "^0.6.1", "pako": "^1.0.5", "png-chunks-extract": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 0c06663f4f..542be601e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5187,10 +5187,9 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.1.tgz#6dff66c99d55ecf739ca53c492e626f1d12a33cc" integrity sha512-pWB896KPGSGkp1XtyzRBftpTzwSOL0Gfk0wLvxt4f2mgzjY19o0LxJ3U25vNWTzsh7da+KTbuXQoQ3lOJZ8WHw== -matrix-js-sdk@2.4.1: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "2.4.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-2.4.1.tgz#f32f8ebb1f01fa26711a4cf6b78eae125156f455" - integrity sha512-5mOp396eOtvaMiuUD85TWvuxSP532PuvtH/QLugBGenI15FGwtnC40cTnVYviYWGBi340FPrOKWulc5ILRX6qQ== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e024d047e358cf26caa47542c8f6d9a469a11cb2" dependencies: another-json "^0.2.0" babel-runtime "^6.26.0"
{_t("Service")} {_t("Summary")}{_t("Terms")}{_t("Document")} {_t("Accept")}
{serviceName} {summary} -
+
{termDoc[termsLang].name} + Date: Fri, 27 Sep 2019 21:32:27 -0500 Subject: [PATCH 07/55] Okay -> OK Signed-off-by: Aaron Raimist --- .../views/dialogs/keybackup/CreateKeyBackupDialog.js | 2 +- src/i18n/strings/bg.json | 1 - src/i18n/strings/cs.json | 1 - src/i18n/strings/de_DE.json | 1 - src/i18n/strings/en_EN.json | 1 - src/i18n/strings/eo.json | 1 - src/i18n/strings/eu.json | 1 - src/i18n/strings/fi.json | 1 - src/i18n/strings/fr.json | 1 - src/i18n/strings/hu.json | 1 - src/i18n/strings/it.json | 1 - src/i18n/strings/ko.json | 1 - src/i18n/strings/nl.json | 1 - src/i18n/strings/ru.json | 1 - src/i18n/strings/sk.json | 1 - src/i18n/strings/sq.json | 1 - src/i18n/strings/vls.json | 1 - src/i18n/strings/zh_Hans.json | 1 - src/i18n/strings/zh_Hant.json | 1 - 19 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index e36763591e..4953cdff68 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -446,7 +446,7 @@ export default createReactClass({

{_t( "Your keys are being backed up (the first backup could take a few minutes).", )}

- diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 7d8905d001..145c5b158d 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -1757,7 +1757,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Ключът за възстановяване дава допълнителна сигурност - може да го използвате за да възстановите достъпа до шифрованите съобщения, в случай че забравите паролата.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Пазете ключът за възстановяване на много сигурно място, например в password manager програма или сейф", "Your keys are being backed up (the first backup could take a few minutes).": "Прави се резервно копие на ключовете Ви (първото копие може да отнеме няколко минути).", - "Okay": "Добре", "Secure your backup with a passphrase": "Защитете резервното копие с парола", "Confirm your passphrase": "Потвърдете паролата", "Recovery key": "Ключ за възстановяване", diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 44b991c0c9..02ca324dc2 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -1588,7 +1588,6 @@ "Save it on a USB key or backup drive": "Uložte ho na bezpečnou USB flash disk nebo zálohovací disk", "Copy it to your personal cloud storage": "Zkopírujte si ho na osobní cloudové úložiště", "Your keys are being backed up (the first backup could take a few minutes).": "Klíče se zálohují (první záloha může trvat pár minut).", - "Okay": "Ok", "Confirm your passphrase": "Potvrďte heslo", "Recovery key": "Obnovovací klíč", "Secure your backup with a passphrase": "Zabezpečte si zálohu silným heslem", diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index bf5d21a948..9dcfcebfb0 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1662,7 +1662,6 @@ "Recovery key": "Wiederherstellungsschlüssel", "Confirm your passphrase": "Bestätige deine Passphrase", "Secure your backup with a passphrase": "Sichere dein Backup mit einer Passphrase", - "Okay": "Okay", "Your keys are being backed up (the first backup could take a few minutes).": "Deine Schlüssel werden gesichert (Das erste Backup könnte ein paar Minuten in Anspruch nehmen).", "Voice & Video": "Sprache & Video", "Use Key Backup": "Benutze Schlüssel Backup", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c23cd6d324..d6d2f2186a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1783,7 +1783,6 @@ "Save it on a USB key or backup drive": "Save it on a USB key or backup drive", "Copy it to your personal cloud storage": "Copy it to your personal cloud storage", "Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).", - "Okay": "Okay", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.", "Set up Secure Message Recovery": "Set up Secure Message Recovery", "Secure your backup with a passphrase": "Secure your backup with a passphrase", diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index cc33d39a93..34088e16da 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1389,7 +1389,6 @@ "That doesn't match.": "Tio ne kongruas.", "Repeat your passphrase...": "Ripetu vian pasfrazon...", "Download": "Elŝuti", - "Okay": "Bone", "Success!": "Sukceso!", "Retry": "Reprovi", "Set up": "Agordi", diff --git a/src/i18n/strings/eu.json b/src/i18n/strings/eu.json index 26a6acfe8d..341dc52484 100644 --- a/src/i18n/strings/eu.json +++ b/src/i18n/strings/eu.json @@ -1739,7 +1739,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Berreskuratze gakoa badaezpadako bat da, zure zifratutako mezuetara sarbidea berreskuratzeko erabili dezakezu pasaesaldia ahaztuz gero.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Gorde berreskuratze gakoa toki oso seguru batean, pasaesaldi kudeatzaile batean esaterako (edo gordailu kutxa batean)", "Your keys are being backed up (the first backup could take a few minutes).": "Zure gakoen babes-kopia egiten ari da (lehen babes-kopiak minutu batzuk behar ditzake).", - "Okay": "Ados", "Success!": "Ongi!", "A new recovery passphrase and key for Secure Messages have been detected.": "Berreskuratze pasaesaldi eta mezu seguruen gako berriak antzeman dira.", "This device is encrypting history using the new recovery method.": "Gailu honek historiala berreskuratze metodo berriarekin zifratzen du.", diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index 3bda6ba276..14dd3267ef 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1684,7 +1684,6 @@ "Save it on a USB key or backup drive": "Tallenna se muistitikulle tai varmuuskopiolevylle", "Copy it to your personal cloud storage": "Kopioi se henkilökohtaiseen pilvitallennustilaasi", "Your keys are being backed up (the first backup could take a few minutes).": "Avaimiasi varmuuskopioidaan (ensimmäinen varmuuskopio voi viedä muutaman minuutin).", - "Okay": "OK", "Unable to create key backup": "Avaimen varmuuskopiota ei voi luoda", "A verification email will be sent to your inbox to confirm setting your new password.": "Ottaaksesi käyttöön uuden salasanasi, seuraa ohjeita sinulle lähetettävässä vahvistussähköpostissa.", "Room upgrade confirmation": "Huoneen päivitysvarmistus", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 6ebf70e3a0..7c71e71d59 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1768,7 +1768,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Votre clé de récupération est une mesure de précaution. Vous pouvez l’utiliser pour restaurer l’accès à vos messages chiffrés si vous oubliez votre phrase de passe.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Conservez votre clé de récupération dans un endroit très sécurisé, comme un gestionnaire de mots de passe (ou un coffre-fort)", "Your keys are being backed up (the first backup could take a few minutes).": "Vous clés sont en cours de sauvegarde (la première sauvegarde peut prendre quelques minutes).", - "Okay": "OK", "Secure your backup with a passphrase": "Protégez votre sauvegarde avec une phrase de passe", "Confirm your passphrase": "Confirmez votre phrase de passe", "Recovery key": "Clé de récupération", diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 834bfb75a0..3c75532873 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1768,7 +1768,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "A Visszaállítási Kulcs egy olyan biztonsági elem amivel visszaállíthatod a hozzáférésed a titkosított üzenetekhez még akkor is, ha a jelmondatot elfelejtetted.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "A Visszaállítási Kulcsot nagyon biztonságos helyen tárold, mint pl. egy jelszókezelőben (vagy széfben)", "Your keys are being backed up (the first backup could take a few minutes).": "A kulcsaid mentése folyamatban van (az első mentés több percig is eltarthat).", - "Okay": "Rendben", "Secure your backup with a passphrase": "Védd a mentést egy jelmondattal", "Confirm your passphrase": "Erősítsd meg a jelmondatot", "Recovery key": "Visszaállítási Kulcs", diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 74267a577d..608609fbef 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -1777,7 +1777,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "La tua chiave di ripristino è una rete di sicurezza - puoi usarla per recuperare l'accesso ai tuoi messaggi cifrati se dimentichi la tua password.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Conserva la chiave di ripristino in un luogo molto sicuro, come un password manager (o una cassaforte)", "Your keys are being backed up (the first backup could take a few minutes).": "Il backup delle chiavi è in corso (il primo backup potrebbe richiedere qualche minuto).", - "Okay": "Okay", "Secure your backup with a passphrase": "Proteggi il tuo backup con una password", "Confirm your passphrase": "Conferma la tua password", "Recovery key": "Chiave di ripristino", diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 68333b0700..69f01385be 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -1967,7 +1967,6 @@ "Save it on a USB key or backup drive": "USB 키나 백업 드라이브에 저장", "Copy it to your personal cloud storage": "개인 클라우드 저장소에 복사", "Your keys are being backed up (the first backup could take a few minutes).": "키를 백업했습니다 (처음 백업에는 시간이 걸릴 수 있습니다).", - "Okay": "네", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "보안 메시지 복구를 설정하지 않으면, 로그아웃하거나 다른 기기를 사용하는 경우 암호화된 메시지 기록을 복구할 수 없게 됩니다.", "Set up Secure Message Recovery": "보안 메시지 복구 설정", "Secure your backup with a passphrase": "암호로 백업 보호", diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index 1f10f75ba9..da69b7c1dc 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -1712,7 +1712,6 @@ "Save it on a USB key or backup drive": "Sla hem op op een USB-stick of een back-upschijf", "Copy it to your personal cloud storage": "Kopieer hem naar uw persoonlijke cloudopslag", "Your keys are being backed up (the first backup could take a few minutes).": "Er wordt een back-up van uw sleutels gemaakt (de eerste back-up kan enkele minuten duren).", - "Okay": "Oké", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Zonder veilig berichtherstel in te stellen, zult u uw versleutelde berichtgeschiedenis niet kunnen herstellen indien u zich afmeldt of een ander apparaat gebruikt.", "Set up Secure Message Recovery": "Veilig berichtherstel instellen", "Secure your backup with a passphrase": "Beveilig uw back-up met een wachtwoord", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 1708ac274d..4a83fdc605 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -1882,7 +1882,6 @@ "Your Recovery Key has been copied to your clipboard, paste it to:": "Ваш ключ восстановления скопирован в буфер обмена, вставьте его в:", "Your Recovery Key is in your Downloads folder.": "Ваш ключ восстановления находится в папке Загрузки.", "Print it and store it somewhere safe": "Распечатайте его и храните в безопасном месте", - "Okay": "ОК", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Без настройки безопасного восстановления сообщений вы не сможете восстановить историю зашифрованных сообщений, если выйдете из системы или используете другое устройство.", "Secure your backup with a passphrase": "Защитите вашу резервную копию паролем", "Confirm your passphrase": "Подтвердите свой пароль", diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index d5408e4c3e..b8438f9b49 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -1771,7 +1771,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kľúč obnovenia je bezpečnostný mechanizmus - môžete ho použiť na prístup k šifrovacím kľúčom v prípade, ak zabudnete vaše heslo obnovenia.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Kľúč obnovenia si bezpečne uchovajte, použite napr. správcu hesiel (alebo ho schovajte v trezore)", "Your keys are being backed up (the first backup could take a few minutes).": "Zálohovanie kľúčov máte aktívne (prvé zálohovanie môže trvať niekoľko minút).", - "Okay": "OK", "Secure your backup with a passphrase": "Zabezpečte si zálohu zadaním hesla obnovenia", "Confirm your passphrase": "Potvrdiť heslo obnovenia", "Recovery key": "Kľúč obnovenia", diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 8521044b6f..8fc616d303 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -1734,7 +1734,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Kyçi juaj i rikthimeve është një rrjet sigurie - mund ta përdorni për rikthim hyrjeje te mesazhet tuaj të fshehtëzuar, nëse harroni frazëkalimin tuaj.", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Mbajeni kyçin tuaj të rikthimeve diku në një vend shumë të sigurt, bie fjala, nën një përgjegjës fjalëkalimesh (ose në një kasafortë)", "Your keys are being backed up (the first backup could take a few minutes).": "Kyçet tuaj po kopjeruhen (kopjeruajtja e parë mund të hajë disa minuta).", - "Okay": "Në rregull", "Secure your backup with a passphrase": "Sigurojeni kopjeruajtjen tuaj me një frazëkalim", "Confirm your passphrase": "Ripohoni frazëkalimin tuaj", "Recovery key": "Kyç rikthimesh", diff --git a/src/i18n/strings/vls.json b/src/i18n/strings/vls.json index a429442f8a..7f34a50c82 100644 --- a/src/i18n/strings/vls.json +++ b/src/i18n/strings/vls.json @@ -1604,7 +1604,6 @@ "Save it on a USB key or backup drive": "Sloat hem ip ip een USB-stick of e back-upschyf", "Copy it to your personal cloud storage": "Kopieert hem noa je persoonlike cloudipslag", "Your keys are being backed up (the first backup could take a few minutes).": "’t Wordt e back-up van je sleuters gemakt (den eesten back-up kut e poar minuutn deurn).", - "Okay": "Oké", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Zounder veilig berichtherstel in te stelln, goa je je versleuterde berichtgeschiedenisse nie kunn herstelln indien da je jen afmeldt of een ander toestel gebruukt.", "Set up Secure Message Recovery": "Veilig berichtherstel instelln", "Secure your backup with a passphrase": "Beveilig je back-up met e paswoord", diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 1536a4d2ab..694306bf90 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -1701,7 +1701,6 @@ "Save it on a USB key or backup drive": "保存 在 U 盘或备份磁盘中", "Copy it to your personal cloud storage": "复制 到您的个人云端存储", "Your keys are being backed up (the first backup could take a few minutes).": "正在备份您的密钥(第一次备份可能会花费几分钟时间)。", - "Okay": "好", "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "如果您登出账号或使用其他设备而没有设置安全消息恢复,您将不能还原您的加密消息历史记录。", "Set up Secure Message Recovery": "设置安全消息恢复", "Secure your backup with a passphrase": "使用密码保护您的备份", diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index fd7c68226d..2c48e663c2 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -1768,7 +1768,6 @@ "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "您的復原金鑰是安全網,如果您忘記您的通關密語的話,您還可以使用它來復原您對加密訊息的存取權。", "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "把您的復原金鑰放在安全的地方,像是密碼管理員(或保險箱)", "Your keys are being backed up (the first backup could take a few minutes).": "您的金鑰正在備份(第一次備份會花費數分鐘)。", - "Okay": "好", "Secure your backup with a passphrase": "使用通關密語保障您備份的安全", "Confirm your passphrase": "確認您的通關密語", "Recovery key": "復原金鑰", From e5803c847c8d1358b2eaf0042e463c962d1f22ff Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 27 Sep 2019 11:46:20 +0000 Subject: [PATCH 08/55] Translated using Weblate (Bulgarian) Currently translated at 100.0% (1813 of 1813 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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/bg.json b/src/i18n/strings/bg.json index 7d8905d001..9ad9c06451 100644 --- a/src/i18n/strings/bg.json +++ b/src/i18n/strings/bg.json @@ -2189,5 +2189,6 @@ "Make this room public": "Направи стаята публична", "Hide advanced": "Скрий разширени настройки", "Show advanced": "Покажи разширени настройки", - "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Блокирай присъединяването на потребители от други Matrix сървъри в тази стая (Тази настройка не може да се промени по-късно!)" + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Блокирай присъединяването на потребители от други Matrix сървъри в тази стая (Тази настройка не може да се промени по-късно!)", + "Close dialog": "Затвори прозореца" } From 7ff6bc55d9a80dea361e0661af51c8ec1eca2423 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Fri, 27 Sep 2019 02:58:52 +0000 Subject: [PATCH 09/55] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (1813 of 1813 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 fd7c68226d..4521e7a2c1 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2210,5 +2210,6 @@ "Make this room public": "讓聊天室公開", "Hide advanced": "隱藏進階的", "Show advanced": "顯示進階的", - "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "阻擋其他 matrix 伺服器上的使用加入此聊天室(此設定無法在之後變更!)" + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "阻擋其他 matrix 伺服器上的使用加入此聊天室(此設定無法在之後變更!)", + "Close dialog": "關閉對話框" } From 33ebc1808c4f365d5dae5adb34aeb5ac3f45075e Mon Sep 17 00:00:00 2001 From: Tirifto Date: Thu, 26 Sep 2019 09:36:04 +0000 Subject: [PATCH 10/55] Translated using Weblate (Esperanto) Currently translated at 96.6% (1751 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 70 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index cc33d39a93..d222f5762c 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -1929,5 +1929,73 @@ "ID": "Identigilo", "Public Name": "Publika nomo", "Do not use an identity server": "Ne uzi identigan servilon", - "Enter a new identity server": "Enigi novan identigan servilon" + "Enter a new identity server": "Enigi novan identigan servilon", + "Failed to start chat": "Malsukcesis komenci babilon", + "Messages": "Mesaĝoj", + "Actions": "Agoj", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Uzu identigan servilon por inviti retpoŝte. Klaku al »[…]« por uzi la implicitan identigan servilon (%(defaultIdentityServerName)s) aŭ administru tion en Agordoj.", + "Displays list of commands with usages and descriptions": "Montras liston de komandoj kun priskribo de uzo.", + "Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Uzi la novan, pli rapidan, sed ankoraŭ eksperimentan komponilon de mesaĝoj (bezonas aktualigon)", + "Send read receipts for messages (requires compatible homeserver to disable)": "Sendi legokonfirmojn de mesaĝoj (bezonas akordan hejmservilon por malŝalto)", + "Accept to continue:": "Akceptu por daŭrigi:", + "Identity Server URL must be HTTPS": "URL de identiga servilo devas esti HTTPS-a", + "Not a valid Identity Server (status code %(code)s)": "Nevalida identiga servilo (statkodo %(code)s)", + "Could not connect to Identity Server": "Ne povis konektiĝi al identiga servilo", + "Checking server": "Kontrolante servilon", + "Change identity server": "Ŝanĝi identigan servilon", + "Disconnect from the identity server and connect to instead?": "Ĉu malkonekti de la nuna identiga servilo kaj konekti anstataŭe al ?", + "Terms of service not accepted or the identity server is invalid.": "Aŭ uzkondiĉoj ne akceptiĝis, aŭ la identiga servilo estas nevalida.", + "Identity server has no terms of service": "Identiga servilo havas neniujn uzkondiĉojn", + "The identity server you have chosen does not have any terms of service.": "La identiga servilo, kiun vi elektis, havas neniujn uzkondiĉojn.", + "Only continue if you trust the owner of the server.": "Nur daŭrigu se vi fidas al la posedanto de la servilo.", + "Disconnect identity server": "Malkonekti la identigan servilon", + "Disconnect from the identity server ?": "Ĉu malkonektiĝi de la identiga servilo ?", + "Disconnect": "Malkonekti", + "You are still sharing your personal data on the identity server .": "Vi ankoraŭ havigas siajn personajn datumojn je la identiga servilo .", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Ni rekomendas, ke vi forigu viajn retpoŝtadresojn kaj telefonnumerojn de la identiga servilo, antaŭ ol vi malkonektiĝos.", + "Disconnect anyway": "Tamen malkonekti", + "Identity Server (%(server)s)": "Identiga servilo (%(server)s)", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Vi nun uzas servilon por trovi kontaktojn, kaj troviĝi de ili. Vi povas ŝanĝi vian identigan servilon sube.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Se vi ne volas uzi servilon por trovi kontaktojn kaj troviĝi mem, enigu alian identigan servilon sube.", + "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Vi nun ne uzas identigan servilon. Por trovi kontaktojn kaj troviĝi de ili mem, aldonu iun sube.", + "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.": "Malkonektiĝo de via identiga servilo signifas, ke vi ne povos troviĝi de aliaj uzantoj, kaj vi ne povos memfare inviti aliajn per retpoŝto aŭ telefono.", + "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.": "Vi ne devas uzi identigan servilon. Se vi tion elektos, vi ne povos troviĝi de aliaj uzantoj, kaj vi ne povos memfare inviti ilin per retpoŝto aŭ telefono.", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Konsentu al uzkondiĉoj de la identiga servilo (%(serverName)s) por esti trovita per retpoŝtadreso aŭ telefonnumero.", + "Discovery": "Trovado", + "Deactivate account": "Malaktivigi konton", + "Always show the window menu bar": "Ĉiam montri la fenestran menubreton", + "A device's public name is visible to people you communicate with": "Publika nomo de aparato estas videbla de homoj, kun kiuj vi komunikas", + "Upgrade the room": "Gradaltigi la ĉambron", + "Enable room encryption": "Ŝalti ĉifradon de la ĉambro", + "Error changing power level requirement": "Eraris ŝanĝo de postulo de potenconivelo", + "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de la postuloj de la ĉambro pri potenconivelo. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.", + "Error changing power level": "Eraris ŝanĝo de potenconivelo", + "An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.": "Eraris ŝanĝo de potenconivelo de la uzanto. Certigu, ke vi havas sufiĉajn permesojn, kaj reprovu.", + "Remove %(email)s?": "Ĉu forigi %(email)s?", + "Remove %(phone)s?": "Ĉu forigi %(phone)s?", + "No recent messages by %(user)s found": "Neniuj freŝaj mesaĝoj de %(user)s troviĝis", + "Remove recent messages by %(user)s": "Forigi freŝajn mesaĝojn de %(user)s", + "You are about to remove %(count)s messages by %(user)s. This cannot be undone. Do you wish to continue?|other": "Vi estas forigonta %(count)s mesaĝojn de %(user)s. Ne eblas tion malfari. Ĉu vi volas pluigi?", + "For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.": "Je granda nombro da mesaĝoj, tio povas daŭri iomon da tempo. Bonvolu ne aktualigi vian klienton dume.", + "Remove %(count)s messages|other": "Forigi %(count)s mesaĝojn", + "Deactivate user?": "Ĉu malaktivigi uzanton?", + "Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?": "Malaktivigo de ĉi tiu uzanto adiaŭigos ĝin, kaj malebligos, ke ĝi resalutu. Plie, ĝi foriros de ĉiuj enataj ĉambroj. Tiu ago ne povas malfariĝi. Ĉu vi certe volas malaktivigi ĉi tiun uzanton?", + "Deactivate user": "Malaktivigi uzanton", + "Remove recent messages": "Forigi freŝajn mesaĝojn", + "Bold": "Grase", + "Italics": "Kursive", + "Strikethrough": "Trastrekite", + "Code block": "Kodujo", + "No Identity Server is configured so you cannot add add an email address in order to reset your password in the future.": "Neniu identiga servilo estas agordita, do vi ne povas aldoni retpoŝtadreson por restarigi ose vian pasvorton.", + "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Agordi retpoŝtadreson por rehavo de konto. Uzu retpoŝton aŭ telefonon por laŭelekte troviĝi de jamaj kontaktoj.", + "Set an email for account recovery. Use email to optionally be discoverable by existing contacts.": "Agordi retpoŝtadreson por rehavo de konto. Uzu retpoŝton por laŭelekte troviĝi de jamaj kontaktoj.", + "No Identity Server is configured: no email addreses can be added. You will be unable to reset your password.": "Neniu identiga servilo estas agordita: ne eblas aldoni retpoŝtadresojn. Vi ne povos restarigi vian pasvorton.", + "Explore": "Esplori", + "Filter": "Filtri", + "Filter rooms…": "Filtri ĉambrojn…", + "Preview": "Antaŭrigardo", + "View": "Rigardo", + "Find a room…": "Trovi ĉambron…", + "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se vi ne povas travi la serĉatan ĉambron, petu inviton aŭ kreu novan ĉambron.", + "Explore rooms": "Esplori ĉambrojn" } From cf730fed8bfa7f8bc4e2a95520acb4bd1b4096a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 25 Sep 2019 19:06:54 +0000 Subject: [PATCH 11/55] Translated using Weblate (French) Currently translated at 100.0% (1813 of 1813 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 | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 6ebf70e3a0..bcf9f88a76 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2202,5 +2202,21 @@ "Report Content": "Signaler le contenu", "Read Marker lifetime (ms)": "Durée de vie du repère de lecture (ms)", "Read Marker off-screen lifetime (ms)": "Durée de vie du repère de lecture en dehors de l’écran (ms)", - "Changes the avatar of the current room": "Change l’avatar du salon actuel" + "Changes the avatar of the current room": "Change l’avatar du salon actuel", + "Room alias": "Alias du salon", + "e.g. my-room": "par ex. mon-salon", + "Please provide a room alias": "Veuillez renseigner un alias de salon", + "This alias is available to use": "Cet alias est disponible", + "This alias is already in use": "Cet alias est déjà utilisé", + "Close dialog": "Fermer la boîte de dialogue", + "Please enter a name for the room": "Veuillez renseigner un nom pour le salon", + "Set a room alias to easily share your room with other people.": "Définissez un alias de salon pour partager facilement votre salon avec d’autres personnes.", + "This room is private, and can only be joined by invitation.": "Ce salon est privé et ne peut être rejoint que sur invitation.", + "Create a public room": "Créer un salon public", + "Create a private room": "Créer un salon privé", + "Topic (optional)": "Sujet (facultatif)", + "Make this room public": "Rendre ce salon public", + "Hide advanced": "Masquer les informations avancées", + "Show advanced": "Afficher les informations avancées", + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Empêcher les utilisateurs d’autres serveurs d’accueil matrix de rejoindre ce salon (Ce paramètre ne peut pas être modifié plus tard !)" } From ee5fed337f894d697ab2acb88431e81130843060 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 26 Sep 2019 11:32:04 +0000 Subject: [PATCH 12/55] Translated using Weblate (Hungarian) Currently translated at 100.0% (1813 of 1813 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 834bfb75a0..8f0cfc3c5d 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2207,5 +2207,6 @@ "Make this room public": "A szoba legyen nyilvános", "Hide advanced": "Haladó elrejtése", "Show advanced": "Haladó megmutatása", - "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Felhasználók más matrix szerverekről a szobába való belépésének megakadályozása (Ezt a beállítást később nem lehet megváltoztatni!)" + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Felhasználók más matrix szerverekről a szobába való belépésének megakadályozása (Ezt a beállítást később nem lehet megváltoztatni!)", + "Close dialog": "Ablak bezárása" } From 66f0b2ffc4d15044cc58c85b7fe339f3037994b6 Mon Sep 17 00:00:00 2001 From: random Date: Fri, 27 Sep 2019 14:26:41 +0000 Subject: [PATCH 13/55] Translated using Weblate (Italian) Currently translated at 99.9% (1811 of 1813 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 | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 74267a577d..4dd43bda91 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2132,5 +2132,39 @@ "Bold": "Grassetto", "Italics": "Corsivo", "Strikethrough": "Barrato", - "Code block": "Code block" + "Code block": "Code block", + "Changes the avatar of the current room": "Cambia l'avatar della stanza attuale", + "Send read receipts for messages (requires compatible homeserver to disable)": "Invia notifiche di lettura per i messaggi (richiede homeserver compatibile per disattivare)", + "Verify the link in your inbox": "Verifica il link nella tua posta in arrivo", + "Complete": "Completa", + "Room alias": "Alias stanza", + "e.g. my-room": "es. mia-stanza", + "Please provide a room alias": "Inserisci un alias per la stanza", + "This alias is available to use": "Questo alias è disponibile", + "This alias is already in use": "Questo alias è già in uso", + "Close dialog": "Chiudi finestra", + "Please enter a name for the room": "Inserisci un nome per la stanza", + "Set a room alias to easily share your room with other people.": "Imposta un alias stanza per condividerla facilmente con gli altri.", + "This room is private, and can only be joined by invitation.": "Questa stanza è privata ed è accessibile solo tramite invito.", + "Create a public room": "Crea una stanza pubblica", + "Create a private room": "Crea una stanza privata", + "Topic (optional)": "Argomento (facoltativo)", + "Make this room public": "Rendi questa stanza pubblica", + "Hide advanced": "Nascondi avanzate", + "Show advanced": "Mostra avanzate", + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Impedisci agli utenti di altri homeserver Matrix di unirsi alla stanza (non può essere cambiato successivamente!)", + "Please fill why you're reporting.": "Inserisci il motivo della segnalazione.", + "Report Content to Your Homeserver Administrator": "Segnala il contenuto all'amministratore dell'homeserver", + "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "La segnalazione di questo messaggio invierà il suo 'ID evento' univoco all'amministratore del tuo homeserver. Se i messaggi della stanza sono cifrati, l'amministratore non potrà leggere il messaggio o vedere file e immagini.", + "Send report": "Invia segnalazione", + "Report Content": "Segnala contenuto", + "Explore": "Esplora", + "Filter": "Filtra", + "Filter rooms…": "Filtra stanze…", + "Preview": "Anteprima", + "View": "Vedi", + "Find a room…": "Trova una stanza…", + "Find a room… (e.g. %(exampleRoom)s)": "Trova una stanza… (es. %(exampleRoom)s)", + "If you can't find the room you're looking for, ask for an invite or Create a new room.": "Se non trovi la stanza che stai cercando, chiedi un invito o Crea una stanza nuova.", + "Explore rooms": "Esplora stanze" } From 63f924820e399a756f8b8f8c2a83c7fc6f62983b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=ED=83=9C=EC=84=AD?= Date: Wed, 25 Sep 2019 23:58:10 +0000 Subject: [PATCH 14/55] Translated using Weblate (Korean) Currently translated at 100.0% (1813 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/ko/ --- src/i18n/strings/ko.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 68333b0700..9e1d2a210b 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -2061,5 +2061,6 @@ "Make this room public": "이 방 주제를 정하기", "Hide advanced": "고급 숨기기", "Show advanced": "고급 보이기", - "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "다른 매트릭스 홈서버에서 이 방에 참가하려는 사용자를 막기 (이 설정은 이후 변경할 수 없습니다!)" + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "다른 매트릭스 홈서버에서 이 방에 참가하려는 사용자를 막기 (이 설정은 이후 변경할 수 없습니다!)", + "Close dialog": "대화 상자 닫기" } From 730539712b7d8a4dec9c37381a75417801b82521 Mon Sep 17 00:00:00 2001 From: Kenneth Larsson Date: Thu, 26 Sep 2019 07:32:21 +0000 Subject: [PATCH 15/55] Translated using Weblate (Swedish) Currently translated at 79.5% (1441 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sv/ --- src/i18n/strings/sv.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sv.json b/src/i18n/strings/sv.json index 8ceed2e37d..f322affd1b 100644 --- a/src/i18n/strings/sv.json +++ b/src/i18n/strings/sv.json @@ -1793,5 +1793,24 @@ "This homeserver does not support communities": "Denna hemserver stöder inte communityn", "Explore": "Utforska", "Filter": "Filtrera", - "Filter rooms…": "Filtrera rum…" + "Filter rooms…": "Filtrera rum…", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Be administratören för din hemserver (%(homeserverDomain)s) att konfigurera en TURN-server för att samtal ska fungera pålitligt.", + "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.": "Alternativt kan du testa att använda den offentliga servern turn.matrix.org, men det är inte lika pålitligt och det kommer att dela din IP-adress med den servern. Du kan också hantera detta under Inställningar.", + "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Varning: Uppgradering av ett rum flyttar inte automatiskt rumsmedlemmar till den nya versionen av rummet. Vi lägger ut en länk till det nya rummet i den gamla versionen av rummet - rumsmedlemmar måste klicka på den här länken för att gå med i det nya rummet.", + "Please confirm that you'd like to go forward with upgrading this room from to .": "Bekräfta att du vill gå vidare med att uppgradera detta rum från till .", + "Changes the avatar of the current room": "Ändrar avataren i det aktuella rummet", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Använd en identitetsserver för att bjuda in via epost. Klicka på Fortsätt för att använda standardidentitetsservern (%(defaultIdentityServerName)s) eller hantera det i Inställningar.", + "Use an identity server to invite by email. Manage in Settings.": "Använd en identitetsserver för att bjuda in via epost. Hantera det i inställningar.", + "Unexpected error resolving homeserver configuration": "Oväntat fel vid inläsning av hemserverkonfiguration", + "Unexpected error resolving identity server configuration": "Oväntat fel vid inläsning av identitetsserverkonfiguration", + "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Tillåt samtalsserver turn.matrix.org som reserv när din hemserver inte erbjuder en (din IP-adress delades under ett samtal)", + "Unable to load key backup status": "Det går inte att ladda status för nyckelsäkerhetskopiering", + "Restore from Backup": "Återställ från säkerhetskopiering", + "This device is backing up your keys. ": "Den här enheten säkerhetskopierar dina nycklar. ", + "This device is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.": "Den här enheten säkerhetskopierar inte dina nycklar, men du har en befintlig säkerhetskopia som du kan återställa från och lägga till i framöver.", + "Connect this device to key backup before signing out to avoid losing any keys that may only be on this device.": "Anslut denna enhet till säkerhetskopiering innan du loggar ut för att undvika att förlora några nycklar som kanske bara finns på den här enheten.", + "Connect this device to Key Backup": "Anslut den här enheten till nyckelsäkerhetskopiering", + "Backing up %(sessionsRemaining)s keys...": "Säkerhetskopierar %(sessionsRemaining)s nycklar...", + "All keys backed up": "Alla nycklar säkerhetskopierade", + "Backup has a signature from unknown device with ID %(deviceId)s.": "Säkerhetskopian har en signatur från okänd enhet med ID %(deviceId)s." } From 610188c9ef8bc51cf4a28194081bddd7ebb07852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Luke=C5=A1?= Date: Sun, 29 Sep 2019 07:47:05 +0000 Subject: [PATCH 16/55] Translated using Weblate (Czech) Currently translated at 94.0% (1704 of 1813 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 | 106 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index 44b991c0c9..124808ca97 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -1227,7 +1227,7 @@ "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Chcete-li nadále používat domovský server %(homeserverDomain)s, měli byste si přečíst a odsouhlasit naše smluvní podmínky.", "Review terms and conditions": "Přečíst smluvní podmínky", "Did you know: you can use communities to filter your Riot.im experience!": "Věděli jste, že: práci s Riot.im si můžete zpříjemnit s použitím komunit!", - "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Pro nastavení filtru, přetáhněte obrázek komunity na pantel foltrování na leve straně obrazovky. Potom můžete kdykoliv kliknout na obrazek komunity na tomto panelu a Riot.im Vám bude zobrazovat jen místnosti a lidi z dané komunity.", + "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Pro nastavení filtru přetáhněte avatar komunity na panel filtrování na levé straně obrazovky. Potom můžete kdykoliv kliknout na avatar komunity na tomto panelu a Riot Vám bude zobrazovat jen místnosti a lidi z dané komunity.", "Show devices, send anyway or cancel.": "Zobrazit zařízení, i tak odeslat a nebo zrušit.", "You can't send any messages until you review and agree to our terms and conditions.": "Dokud si nepřečtete a neodsouhlasíte naše smluvní podmínky, nebudete moci posílat žádné zprávy.", "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Vaše zpráva nebyla odeslána, protože tento domácí server dosáhl svého měsíčního limitu pro aktivní uživatele. Prosím kontaktujte Vašeho administratora pro další využívání služby.", @@ -1276,9 +1276,9 @@ "Set a new password": "Nastavit nové heslo", "Room Name": "Jméno místnosti", "Room Topic": "Téma místnosti", - "No room avatar": "Žádná ikona místnosti", - "Room avatar": "Ikona místnosti", - "Upload room avatar": "Nahrát ikonu místnosti", + "No room avatar": "Žádný avatar místnosti", + "Room avatar": "Avatar místnosti", + "Upload room avatar": "Nahrát avatara místnosti", "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Změny toho kdo smí číst historické zprávy se aplikují jenom na další zprávy v této místosti. Viditelnost už poslaných zpráv zůstane jaká byla.", "To link to this room, please add an alias.": "K vytvoření odkazu je potřeba vyrobit místnosti alias.", "Roles & Permissions": "Funkce & Práva", @@ -1702,7 +1702,7 @@ "Scissors": "Nůžky", "Close button should minimize window to tray": "Zavírací tlačítko má jen minimalizovat okno", "Accept all %(invitedRooms)s invites": "Přijmout všechny pozvánky: %(invitedRooms)s", - "Change room avatar": "Změnit ikonu místnosti", + "Change room avatar": "Změnit avatara místnosti", "Change room name": "Změnit jméno místnosti", "Change main address for the room": "Změnit hlavní adresu místnosti", "Change history visibility": "Změnit viditelnost historie", @@ -1909,5 +1909,99 @@ "Cannot reach identity server": "Nelze se připojit k serveru identity", "You can register, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete se zaregistrovat, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru.", "You can reset your password, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete si změnit heslo, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru.", - "You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete se přihlásit, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru." + "You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.": "Můžete se přihlásit, ale některé funkce nebudou dostupné dokud nezačne server identity fungovat. Pokud se vám toto varování zobrazuje pořád, tak zkontrolujte svojí konfiguraci a nebo kontaktujte administrátora serveru.", + "Call failed due to misconfigured server": "Volání selhalo, protože je rozbitá konfigurace serveru", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Zeptejte se administrátora (%(homeserverDomain)s) jestli by nemohl nakonfigurovat server TURN, aby začalo fungoval volání.", + "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.": "Případně můžete zkusit použít veřejný server turn.matrix.org, což nemusí fungovat tak spolehlivě a řekne to tomu cizímu serveru vaší IP adresu. Můžete to udělat v Nastavení.", + "Try using turn.matrix.org": "Zkuste použít turn.matrix.org", + "Failed to start chat": "Nepovedlo se začít chat", + "Messages": "Zprávy", + "Actions": "Akce", + "Sends a message as plain text, without interpreting it as markdown": "Pošle zprávu jako prostý text, neinterpretuje jí jako Markdown", + "You do not have the required permissions to use this command.": "Na provedení tohoto příkazu nemáte dostatečná oprávnění.", + "Changes the avatar of the current room": "Změní vašeho avatara pro tuto místnost", + "Changes your avatar in all rooms": "Změní vašeho avatara pro všechny místnosti", + "Use an identity server": "Používat server identit", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Použít server identit na odeslání emailové pozvánky. Pokračováním použijete výchozí server identit (%(defaultIdentityServerName)s) nebo ho můžete změnit v Nastavení.", + "Use an identity server to invite by email. Manage in Settings.": "Použít server identit na odeslání emailové pozvánky. Můžete spravovat v Nastavení.", + "Displays list of commands with usages and descriptions": "Zobrazuje seznam příkazu s popiskem", + "%(senderName)s made no change.": "%(senderName)s neudělal žádnou změnu.", + "Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Používat nový, rychlejší ale experimentální editor na zprávy (vyžaduje znovunačtení stránky)", + "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Povolit použití serveru turn.matrix.org na spojení hlasového hovoru pokud váš domovský server tuto služby neposkytuje (sdělí to serveru vaší IP adresu)", + "Send read receipts for messages (requires compatible homeserver to disable)": "Odesílat potvrzení o přijetí (vypnutá volba vyžaduje kompatibilní domovský server)", + "Accept to continue:": "Pro pokračování odsouhlaste :", + "ID": "ID", + "Public Name": "Veřejné jméno", + "No integrations server configured": "Žádná integrace není nakonfigurovaná", + "This Riot instance does not have an integrations server configured.": "Tento Riot nemá nakonfigurovaný žádný integrační server.", + "Connecting to integrations server...": "Připojování k integračnímu serveru...", + "Cannot connect to integrations server": "Nelze se připojit k integračními serveru", + "The integrations server is offline or it cannot reach your homeserver.": "Nelze se připojit k integračnímu nebo domovskému serveru.", + "Identity Server URL must be HTTPS": "Adresa serveru identit musí být na HTTPS", + "Not a valid Identity Server (status code %(code)s)": "Toto není validní server identit (stavový kód %(code)s)", + "Could not connect to Identity Server": "Nepovedlo se připojení k serveru identit", + "Checking server": "Kontrolování serveru", + "Change identity server": "Změnit server identit", + "Disconnect from the identity server and connect to instead?": "Odpojit se ze serveru a připojit na ?", + "Terms of service not accepted or the identity server is invalid.": "Neodsouhlasené podmínky použití a nebo neplatný server identit.", + "Identity server has no terms of service": "Server identit nemá žádné podmínky použití", + "The identity server you have chosen does not have any terms of service.": "Vybraný server identit nemá žádné podmínky použití.", + "Only continue if you trust the owner of the server.": "Pokračujte pouze pokud věříte provozovateli serveru.", + "Disconnect identity server": "Odpojit se ze serveru identit", + "Disconnect from the identity server ?": "Odpojit se ze serveru identit ?", + "Disconnect": "Odpojit", + "You are still sharing your personal data on the identity server .": "Pořád sdílíte osobní údaje se serverem identit .", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Předtím než se odpojíte doporučujeme odstranit emailovou adresu a telefonní číslo ze serveru identit.", + "Disconnect anyway": "Stejně se odpojit", + "Identity Server (%(server)s)": "Server identit (%(server)s)", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.": "Aktuálně používáte server na hledání existujících kontaktů. Níže můžete server identit změnit.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.": "Pokud nechcete na hledání existujících kontaktů používat server , zvolte si jiný server.", + "Identity Server": "Server identit", + "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "Aktuálně nepoužíváte žádný server identit. Na hledání existujících kontaktů a přidání se do registru kontatů přidejte server identit níže.", + "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.": "Odpojení ze serveru identit znamená, že vás nepůjde najít podle emailové adresy a telefonního čísla and vy taky nebudete moct hledat ostatní.", + "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žití serveru identit je volitelné. Nemusíte server identit používat, ale nepůjde vás pak najít podle emailové adresy a telefonního čísla a vy také nebudete moct hledat ostatní.", + "Do not use an identity server": "Nepoužívat server identit", + "Enter a new identity server": "Zadejte nový server identit", + "Failed to update integration manager": "Nepovedlo se aktualizovat správce integrací", + "Integration manager offline or not accessible.": "Správce integrací není dostupný.", + "Terms of service not accepted or the integration manager is invalid.": "Neodsouhlasené podmínky použití nebo nefunkční správce integrací.", + "Integration manager has no terms of service": "Správce integrací nemá podmínky použití", + "The integration manager you have chosen does not have any terms of service.": "Správce integrací který jste si vybrali nemá žádné podmínky použití.", + "You are currently using %(serverName)s to manage your bots, widgets, and sticker packs.": "Aktálně používáte %(serverName)s na správu robotů, widgetů, and balíků samolepek.", + "Add which integration manager you want to manage your bots, widgets, and sticker packs.": "Zadejte kterého správce integrací chcete používat na správu robotů, widgetů a balíků samolepek.", + "Integration Manager": "Správce integrací", + "Enter a new integration manager": "Přidat nový správce integrací", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Musíte odsouhlasit podmínky použití serveru (%(serverName)s) abyste se mohli zapsat do registru emailových adres a telefonních čísel.", + "Deactivate account": "Deaktivovat účet", + "Always show the window menu bar": "Vždy zobrazovat horní lištu okna", + "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.": "Odeslat pozvánku pomocí serveru identit. Použít výchozí (%(defaultIdentityServerName)s) nebo přenastavit Nastavení.", + "Use an identity server to invite by email. Manage in Settings.": "Odeslat pozvánku pomocí serveru identit. Přenastavit v Nastavení.", + "Close dialog": "Zavřít dialog", + "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Napište nám prosím co se pokazilo a nebo nám napište issue na GitHub, kde popíšete problém.", + "Removing…": "Odstaňování…", + "Clear all data on this device?": "Smazat všechna data na tomto zařízení?", + "Clearing all data from this device is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Smazání všech dat na tomto zařízení je nevratné. Zašifrované zprávy nepůjde obnovit, pokud nejsou klíče zazálohované.", + "Clear all data": "Smazat všechna data", + "Please enter a name for the room": "Zadejte prosím jméno místnosti", + "Set a room alias to easily share your room with other people.": "Nastavte alias místnosti, abyste mohli místnost snadno sdílet s ostatními.", + "This room is private, and can only be joined by invitation.": "Tato místnost je neveřejná a lze se připojit pouze s pozvánkou.", + "Create a public room": "Vytvořit veřejnou místnost", + "Create a private room": "Vytvořit neveřejnou místnost", + "Topic (optional)": "Téma (volitelné)", + "Make this room public": "Zveřejnit místnost", + "Hide advanced": "Skrýt pokročilé", + "Show advanced": "Zobrazit pokročilé", + "Block users on other matrix homeservers from joining this room (This setting cannot be changed later!)": "Zamezit uživatelům jiných domovských serverů, aby se připojili do místnosti (Toto nelze později změnit!)", + "To verify that this device can be trusted, please check that the key you see in User Settings on that device matches the key below:": "Abyste ověřili, že je toto zařízení důvěryhodné, zkontrolujte, že klíč v Nastavení na tom zařízení se shoduje s tímto klíčem:", + "Your homeserver doesn't seem to support this feature.": "Váš domovský server asi tuto funkci nepodporuje.", + "Message edits": "Editování zpráv", + "Please fill why you're reporting.": "Vyplňte prosím co chcete nahlásit.", + "Report Content to Your Homeserver Administrator": "Nahlásit obsah administrátorovi vašeho domovského serveru", + "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Nahlášení této zprávy pošle její jedinečné 'event ID' administrátorovi vašeho domovského serveru. Pokud jsou zprávy šifrované, administrátor nebude mít možnost přečíst text zprávy ani se podívat na soubory nebo obrázky.", + "Send report": "Nahlásit", + "Upgrading this room requires closing down the current instance of the room and creating a new room in its place. To give room members the best possible experience, we will:": "Upgrade vyžaduje zrušení této místnosti a vyrobení nové, která jí nahradí. Pro usnadnění procesu pro členy místnosti, provedeme:", + "Command Help": "Nápověda příkazu", + "Integrations Manager": "Správce integrací", + "Find others by phone or email": "Hledat ostatní pomocí emailu nebo telefonu", + "Be found by phone or email": "Umožnit ostatním mě nalézt pomocí emailu nebo telefonu" } From 4357110b5949136e01be3e460b722eddd4371580 Mon Sep 17 00:00:00 2001 From: Claus Conrad Date: Sun, 29 Sep 2019 14:41:09 +0000 Subject: [PATCH 17/55] Translated using Weblate (Danish) Currently translated at 19.1% (347 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/da/ --- src/i18n/strings/da.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/da.json b/src/i18n/strings/da.json index e90de5edfc..2e6112a671 100644 --- a/src/i18n/strings/da.json +++ b/src/i18n/strings/da.json @@ -112,7 +112,7 @@ "No rooms to show": "Ingen rum at vise", "This email address is already in use": "Denne emailadresse er allerede i brug", "This phone number is already in use": "Dette telefonnummer er allerede i brug", - "Failed to verify email address: make sure you clicked the link in the email": "Kunne ikke bekræfte emailaddressen: vær sikker på at du klikke på linket i emailen", + "Failed to verify email address: make sure you clicked the link in the email": "Kunne ikke bekræfte emailaddressen: vær sikker på at klikke på linket i emailen", "Call Timeout": "Opkalds Timeout", "The remote side failed to pick up": "Den anden side tog den ikke", "Unable to capture screen": "Kunne ikke optage skærm", @@ -397,5 +397,9 @@ "View Community": "Vis community", "%(count)s Members|one": "%(count)s medlem", "Notes:": "Noter:", - "Preparing to send logs": "Forbereder afsendelse af logfiler" + "Preparing to send logs": "Forbereder afsendelse af logfiler", + "The platform you're on": "Den platform du bruger", + "The version of Riot.im": "Versionen af Riot.im", + "Whether or not you're logged in (we don't record your username)": "Om du er logget på eller ej (vi logger ikke dit brugernavn)", + "Your language of choice": "Dit foretrukne sprog" } From c8b0c92745fb8329ec28e214d2d797ea8fdde354 Mon Sep 17 00:00:00 2001 From: "Nils J. Haugen" Date: Sat, 28 Sep 2019 19:33:45 +0000 Subject: [PATCH 18/55] Translated using Weblate (Norwegian Nynorsk) Currently translated at 60.3% (1093 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/nn/ --- src/i18n/strings/nn.json | 181 ++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 40 deletions(-) diff --git a/src/i18n/strings/nn.json b/src/i18n/strings/nn.json index d52f1ea0ee..2f0b415d2c 100644 --- a/src/i18n/strings/nn.json +++ b/src/i18n/strings/nn.json @@ -986,7 +986,7 @@ "%(count)s of your messages have not been sent.|other": "Nokre av meldingane dine vart ikkje sende.", "%(count)s of your messages have not been sent.|one": "Meldinga di vart ikkje send.", "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|other": "Send alle på nytt eller avbryt alle. Du kan ogso velja enkelte meldingar til sending på nytt eller avbryting.", - "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|one": "Send melding på nytt eller bryt av.", + "%(count)s Resend all or cancel all now. You can also select individual messages to resend or cancel.|one": "Send melding på nytt eller avbryt.", "Connectivity to the server has been lost.": "Tilkoplinga til tenaren vart tapt.", "Sent messages will be stored until your connection has returned.": "Sende meldingar lagrast ikkje før tilkoplinga di er attende.", "%(count)s new messages|other": "%(count)s nye meldingar", @@ -994,27 +994,27 @@ "Active call": "Pågåande samtale", "There's no one else here! Would you like to invite others or stop warning about the empty room?": "Det er ingen andre her! Vil du byda andre inn eller enda åtvaringa om det tomme rommet??", "more": "meir", - "You seem to be uploading files, are you sure you want to quit?": "Det ser ut til at du lastar filer opp, er du sikker på at du vil slutta?", - "You seem to be in a call, are you sure you want to quit?": "Det ser ut til at du er i ei samtale, er du sikker på at du vil slutta?", + "You seem to be uploading files, are you sure you want to quit?": "Det ser ut til at du lastar opp filer, er du sikker på at du vil avslutte?", + "You seem to be in a call, are you sure you want to quit?": "Det ser ut til at du er i ein samtale, er du sikker på at du vil avslutte?", "Failed to upload file": "Fekk ikkje til å lasta fila opp", "Server may be unavailable, overloaded, or the file too big": "Tenaren er kanskje utilgjengeleg, overlasta, elles so er fila for stor", - "Search failed": "Søket gjekk gale", + "Search failed": "Søket feila", "No more results": "Ingen fleire resultat", "Unknown room %(roomId)s": "Ukjend rom %(roomId)s", "Room": "Rom", "Failed to save settings": "Fekk ikkje til å lagra innstillingar", - "Failed to reject invite": "Fekk ikkje til å seia nei til innbydinga", + "Failed to reject invite": "Fekk ikkje til å avstå invitasjonen", "Fill screen": "Fyll skjermen", "Click to unmute video": "Klikk for å avstilna videoen", "Click to mute video": "Klikk for å stilna videoen", "Click to unmute audio": "Klikk for å avstilna ljoden", - "Click to mute audio": "Klikk for å stilna ljoden", - "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Freista å lasta eit gjeve punkt i rommet si tidslinje, men du har ikkje lov til å sjå den sistnemnde meldinga.", - "Tried to load a specific point in this room's timeline, but was unable to find it.": "Freista å lasta eit gjeve punkt i rommet si tidslinje, men klarte ikkje å finna det.", + "Click to mute audio": "Klikk for å dempe lyden", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Prøvde å laste eit bestemt punkt i rommet si tidslinje, men du har ikkje lov til å sjå den spesifike meldingen.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Prøvde å lasta eit bestemt punkt i rommet si tidslinje, men klarde ikkje å finna det.", "Failed to load timeline position": "Fekk ikkje til å lasta tidslinjestillinga", - "Uploading %(filename)s and %(count)s others|other": "Lastar %(filename)s og %(count)s til opp", - "Uploading %(filename)s and %(count)s others|zero": "Lastar %(filename)s opp", - "Uploading %(filename)s and %(count)s others|one": "Lastar %(filename)s og %(count)s til opp", + "Uploading %(filename)s and %(count)s others|other": "Lastar opp %(filename)s og %(count)s andre", + "Uploading %(filename)s and %(count)s others|zero": "Lastar opp %(filename)s", + "Uploading %(filename)s and %(count)s others|one": "Lastar opp %(filename)s og %(count)s andre", "Light theme": "Ljost preg", "Dark theme": "Dimt preg", "Status.im theme": "Status.im-preg", @@ -1075,30 +1075,30 @@ "The email address linked to your account must be entered.": "Du må skriva epostadressa som er tilknytta brukaren din inn.", "A new password must be entered.": "Du må skriva eit nytt passord inn.", "New passwords must match each other.": "Dei nye passorda må vera like.", - "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Ein epost vart send til %(emailAddress)s. Når du har far fylgd lenkja i den, klikk under.", - "I have verified my email address": "Eg har godkjend epostadressa mi", + "An email has been sent to %(emailAddress)s. Once you've followed the link it contains, click below.": "Ein e-post vart send til %(emailAddress)s. Når du har har følgd linken i den, klikk under.", + "I have verified my email address": "Eg har godkjend e-postadressa mi", "Your password has been reset": "Passordet ditt vart attendesett", "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Du vart logga av alle einingar og får ikkje lenger pushvarsel. For å skru varsel på att, logg inn igjen på kvar eining", "Return to login screen": "Gå attende til innlogging", "To reset your password, enter the email address linked to your account": "For å attendestilla passordet ditt, skriv epostadressa som er lenkja til brukaren din inn", "New password": "Nytt passord", "Confirm your new password": "Stadfest det nye passordet ditt", - "Send Reset Email": "Send attendestillingsepost", + "Send Reset Email": "Send e-post for nullstilling", "Create an account": "Lag ein brukar", "This Home Server does not support login using email address.": "Denne Heimtenaren støttar ikkje innlogging med epost.", "Please contact your service administrator to continue using this service.": "Ver venleg og kontakt din tenesteadministrator for å halda fram med å bruka tenesten.", - "Incorrect username and/or password.": "Urett brukarnamn og/eller passord.", + "Incorrect username and/or password.": "Feil brukarnamn og/eller passord.", "Please note you are logging into the %(hs)s server, not matrix.org.": "Merk deg at du loggar inn på %(hs)s-tenaren, ikkje matrix.org.", "Guest access is disabled on this Home Server.": "Gjestetilgang er skrudd av på denne Heimtenaren.", - "The phone number entered looks invalid": "Det innskrivne telefonnummeret ser ugangbart ut", - "Error: Problem communicating with the given homeserver.": "Noko gjekk gale: fekk ikkje samband med den gjevne heimtenaren.", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kan ikkje kopla til heimtenaren gjennom HTTP når ein HTTPS-URL er i nettlesarsøkjafeltet ditt. Bruk anten HTTPS eller skru utrygge skript på.", - "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Kan ikkje kopla til heimtenaren - ver venleg og sjekk tilkoplinga di, og sjå til at heimtenaren din sitt CCL-sertifikat er stolt på og at ein nettlesarutviding ikkje hindrar førespurnader.", + "The phone number entered looks invalid": "Det innskrivne telefonnummeret virkar å vere ugyldig", + "Error: Problem communicating with the given homeserver.": "Feil: Det gjekk ikkje an å kommunisere med den spesifiserte heimeserveren.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kan ikkje koble til heimeserveren via HTTP fordi URL-adressa i nettlesaren er HTTPS. Bruk HTTPS, eller aktiver usikre skript.", + "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Kan ikkje kopla til heimtenaren - ver venleg og sjekk tilkoplinga di, og sjå til at heimtenaren din sitt CCL-sertifikat er stolt på og at ein nettlesartillegg ikkje hindrar førespurnader.", "Try the app first": "Prøv æppen fyrst", "Sign in to get started": "Logg inn for å koma i gang", "Failed to fetch avatar URL": "Klarte ikkje å henta avatar-URLen", "Set a display name:": "Set eit visingsnamn:", - "Upload an avatar:": "Last ein avatar opp:", + "Upload an avatar:": "Last opp ein avatar:", "This server does not support authentication with a phone number.": "Denne tenaren støttar ikkje stadfesting gjennom telefonnummer.", "Missing password.": "Vantande passord.", "Passwords don't match.": "Passorda er ikkje like.", @@ -1108,10 +1108,10 @@ "You need to enter a user name.": "Du må skriva eit brukarnamn inn.", "An unknown error occurred.": "Noko ukjend gjekk gale.", "I already have an account": "Eg har ein brukar allereie", - "Commands": "Påbod", + "Commands": "Kommandoar", "Results from DuckDuckGo": "Resultat frå DuckDuckGo", "Emoji": "Emoji", - "Notify the whole room": "Varsl heile rommet", + "Notify the whole room": "Varsle heile rommet", "Room Notification": "Romvarsel", "Users": "Brukarar", "unknown device": "ukjend eining", @@ -1120,7 +1120,7 @@ "Verification": "Godkjenning", "Ed25519 fingerprint": "Ed25519-fingeravtrykk", "User ID": "Brukar-ID", - "Curve25519 identity key": "Curve25519-identitetsnykel", + "Curve25519 identity key": "Curve25519-identitetsnøkkel", "none": "ingen", "Algorithm": "Algoritme", "unencrypted": "ikkje-kryptert", @@ -1129,10 +1129,10 @@ "End-to-end encryption information": "Ende-til-ende-krypteringsinfo", "Event information": "Hendingsinfo", "Sender device information": "Info om avsendareininga", - "Passphrases must match": "Passetningane må vera like", - "Passphrase must not be empty": "Passetningsfeltet kan ikkje vera tomt", - "Enter passphrase": "Skriv passetning inn", - "Confirm passphrase": "Stadfest passetning", + "Passphrases must match": "Passfrasane må vere identiske", + "Passphrase must not be empty": "Passefrasefeltet kan ikkje vera tomt", + "Enter passphrase": "Skriv inn passfrase", + "Confirm passphrase": "Stadfest passfrase", "You must specify an event type!": "Du må oppgje ein handlingssort!", "Call Timeout": "Tidsavbrot i Samtala", "Enable automatic language detection for syntax highlighting": "Skru automatisk måloppdaging på for syntax-understreking", @@ -1152,7 +1152,7 @@ "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Du held på å verta teken til ei tredje-partisside so du kan godkjenna brukaren din til bruk med %(integrationsUrl)s. Vil du gå fram?", "Token incorrect": "Teiknet er gale", "Filter community members": "Filtrer samfunnsmedlemer", - "Custom Server Options": "Eigentenar-innstillingar", + "Custom Server Options": "Tilpassa tenar-innstillingar", "Filter community rooms": "Filtrer samfunnsrom", "Please help improve Riot.im by sending anonymous usage data. This will use a cookie (please see our Cookie Policy).": "Ver venleg og hjelp oss å forbetra Riot.im ved å senda anonym brukardata. Dette brukar ei datakake (ver venleg og sjå på Datakakeretningslinene våre).", "Please help improve Riot.im by sending anonymous usage data. This will use a cookie.": "Ver venleg og hjelp oss å forbetra Riot.im ved å senda anonym brukardata. Dette brukar ei datakake.", @@ -1187,7 +1187,7 @@ "Hide panel": "Gøym panel", "Unable to look up room ID from server": "Klarte ikkje å henta rom-ID frå tenaren", "Your message wasn’t sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Meldinga di vart ikkje send fordi heimtenaren har truffe si Månadlege Grense for Aktive Brukarar. Ver venleg og tak kontakt med tenesteadministratoren din for å halda frama med å bruka tenesten.", - "Server may be unavailable, overloaded, or search timed out :(": "Tenaren er kanskje utilgjengeleg, overlasta, elles so vart søket tidsavbroten :(", + "Server may be unavailable, overloaded, or search timed out :(": "Tenaren er kanskje utilgjengeleg, overlasta, elles så vart søket tidsavbrote :(", "Expand panel": "Utvid panel", "Collapse panel": "Slå panel saman", "Filter room names": "Filtrer romnamn", @@ -1199,23 +1199,23 @@ "Access Token:": "Tilgangs-Teikn:", "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Å attendestilla passordet vil førebels attendestilla alle ende-til-ende-krypteringsnyklar på alle einingar, slik at krypterte samtaler vert uleselege, med mindre du fyrst hentar romnyklane ut og hentar dei inn att etterpå. Dette vil forbetrast i framtida.", "This homeserver has hit its Monthly Active User limit": "Heimtenaren har truffe den Månadlege Grensa si for Aktive Brukarar", - "This homeserver doesn't offer any login flows which are supported by this client.": "Heimtenaren tilbyd ingen nye innloggingsstraumar som støttast av denne klienten.", - "Claimed Ed25519 fingerprint key": "Gjorde krav på Ed25519-fingeravtrykksnykel", - "Export room keys": "Hent romnyklar ut", + "This homeserver doesn't offer any login flows which are supported by this client.": "Heimeserveren tilbyr ingen påloggingsmetodar som er støtta av denne klienten.", + "Claimed Ed25519 fingerprint key": "Gjorde krav på Ed25519-fingeravtrykksnøkkel", + "Export room keys": "Eksporter romnøklar", "Bulk Options": "Innverknadsrike Innstillingar", - "Export": "Hent ut", - "Import room keys": "Hent romnyklar inn", - "File to import": "Fil til innhenting", - "Import": "Hent inn", + "Export": "Eksporter", + "Import room keys": "Importer romnøklar", + "File to import": "Fil til import", + "Import": "Importer", "Failed to set direct chat tag": "Fekk ikkje til å setja direktesamtale-merke", "Failed to remove tag %(tagName)s from room": "Fekk ikkje til å fjerna merket %(tagName)s frå rommet", "Failed to add tag %(tagName)s to room": "Fekk ikkje til å leggja merket %(tagName)s til i rommet", "Hide read receipts": "Gøym lesen-lappar", "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Av sikkerheitsmessige grunnar vil det å logga ut sletta alle ende-til-ende-krypteringsnyklar frå nettlesaren. Viss du vil kunna dekryptera samtalehistoria di på framtidige Riot-øykter, ver venleg og hent ut romnyklande dine og tak vare på dei.", - "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Dette tillèt deg å henta nyklane for meldingar du har sendt i krypterte rom ut til ei lokal fil. Då kan du henta fila inn til ein annan Matrix-klient i framtida, slik at den klienten òg kan dekryptera meldingane.", + "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Dette tillèt deg å henta nøklane for meldingar du har sendt i krypterte rom ut til ei lokal fil. Då kan du importera fila i ein annan Matrix-klient i framtida, slik at den klienten òg kan dekryptera meldingane.", "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Å henta filen ut tillèt kven som helst som kan lesa ho å dekryptera alle krypterte meldingar du kan sjå, so du bør passa på å halda ho trygg. For å hjelpa til med dette bør du skriva ei passetning inn i feltet under, som vil brukast til å kryptere den uthenta dataen. Det vil berre vera mogeleg å henta dataen inn med den same passetninga.", - "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Dette tillèt deg å henta krypteringsnyklar som du tidlegare henta ut frå ein annan Matrix-klient inn. Du vil so kunna dekryptera alle meldingane som den andre klienten kunne dekryptera.", - "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Uthentingsfila vil verta verna med ei passetning. Du bør skriva passetninga inn her for å dekryptera fila.", + "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Dette tillèt deg å importere krypteringsnøklar som du tidlegare har eksportert frå ein annan Matrix-klient. Du har deretter moglegheit for å dekryptere alle meldingane som den andre klienten kunne dekryptere.", + "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Den eksporterte fila vil bli verna med ein passfrase. Du bør skriva passfrasen her, for å dekryptere fila.", "Internal room ID: ": "Indre rom-ID: ", "Room version number: ": "Romutgåvenummer: ", "This homeserver has hit its Monthly Active User limit. Please contact your service administrator to continue using the service.": "Heimtenaren har truffe si Månadlege Grense for Aktive Brukarar. Ver venleg og tak kontakt med tenesteadministratoren din for å halda fram med å bruka tenesten.", @@ -1229,5 +1229,106 @@ "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "Du kan òg velja ein eigendefinert identitetstenar, men då kjem du ikkje til å innvitere brukarar gjennom e-post, eller verta invitert med e-post sjølv.", "Whether or not you're logged in (we don't record your username)": "Om du er innlogga eller ikkje (vi lagrar ikkje brukarnamnet ditt)", "The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Fila %(fileName)s er større enn heimetenaren si grense for opplastningar", - "Unable to load! Check your network connectivity and try again.": "Klarte ikkje lasta! Sjå på nettilkoplinga di og prøv igjen." + "Unable to load! Check your network connectivity and try again.": "Klarte ikkje lasta! Sjå på nettilkoplinga di og prøv igjen.", + "Your Riot is misconfigured": "Riot-klienten din er feilkonfiguert", + "Sign In": "Logg inn", + "Explore rooms": "Utforsk romma", + "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.": "Meldingen din vart ikkje sent for denne heimeserveren har nådd grensa for maksimalt aktive brukarar pr. månad. Kontakt systemadministratoren for å kunne vidare nytte denne tenesten.", + "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "Denne meldingen vart ikkje sendt fordi heimeserveren har nådd grensa for tilgjengelege systemressursar. Kontakt systemadministratoren for å vidare nytte denne tenesten.", + "Add room": "Legg til rom", + "You have %(count)s unread notifications in a prior version of this room.|other": "Du har %(count)s uleste varslingar i ein tidligare versjon av dette rommet.", + "You have %(count)s unread notifications in a prior version of this room.|one": "Du har %(count)s ulest varsel i ein tidligare versjon av dette rommet.", + "Guest": "Gjest", + "Your profile": "Din profil", + "Could not load user profile": "Klarde ikkje å laste brukarprofilen", + "Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.": "Endring av passordet vil nullstille nøklar for ende-til-ende kryptering på alle eningane dine. Alle tidligare meldingar og historikk vil bli ulesbar. Aktiver sikkerheitskopiering for nøklar (Key Backup) eller eksporter romnøklane på ein annan eining før du nullstiller passordet.", + "Your Matrix account on %(serverName)s": "Din Matrix-konto på %(serverName)s", + "Your Matrix account on ": "Din Matrix-konto på ", + "No identity server is configured: add one in server settings to reset your password.": "Ingen identitetsserver er konfigurert: legg til ein i innstillingane for å nullstille passordet ditt.", + "Sign in instead": "Logg inn istaden", + "A verification email will be sent to your inbox to confirm setting your new password.": "For å stadfeste tilbakestilling av passordet, vil ein e-post vil bli sendt til din innboks.", + "Your password has been reset.": "Passodet ditt vart nullstilt.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.": "Du har blitt logga av frå alle einingane og vil ikkje lengre motta push-varslingar. For a reaktivere slike varsel, logg inn på nytt på kvar separate eining.", + "Set a new password": "Sett nytt passord", + "Invalid homeserver discovery response": "Ugyldig svar frå heimeserveren (discovery response)", + "Failed to get autodiscovery configuration from server": "Kladte ikkje å hente automatisk oppsett frå server", + "Invalid base_url for m.homeserver": "Ugyldig base_url for m.homeserver", + "Homeserver URL does not appear to be a valid Matrix homeserver": "URL-adressa virkar ikkje til å vere ein gyldig Matrix-heimeserver", + "Invalid identity server discovery response": "Ugyldig svar frå identitetsserveren (discovery response)", + "Invalid base_url for m.identity_server": "Ugyldig base_url for m.identity_server", + "Identity server URL does not appear to be a valid identity server": "URL-adressa virkar ikkje til å vere ein gyldig identitetsserver", + "General failure": "Generell feil", + "This homeserver does not support login using email address.": "Denne heimeserveren støttar ikkje innloggingar med e-postadresser.", + "Please contact your service administrator to continue using this service.": "Kontakt din systemadministrator for å vidare å bruke tenesta.", + "This account has been deactivated.": "Denne kontoen har blitt deaktivert.", + "Failed to perform homeserver discovery": "Fekk ikkje til å utforske heimeserveren", + "Sign in with single sign-on": "Logg på med Single-Sign-On", + "Create account": "Lag konto", + "Registration has been disabled on this homeserver.": "Registrering er deaktivert på denne heimeserveren.", + "Unable to query for supported registration methods.": "Klarte ikkje å spørre etter støtta registreringsmetodar.", + "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).": "Kontoen din %(newAccountId)s er no registrert, men du er frå tidligare logga på med ein annan konto (%(loggedInUserId)s).", + "Continue with previous account": "Fortsett med tidligare konto", + "Log in to your new account.": "Logg på den nye kontoen din.", + "You can now close this window or log in to your new account.": "Du kan lukke dette vindauget ellerlogge inn med din nye konto.", + "Registration Successful": "Registrering fullført", + "Create your account": "Lag din konto", + "Failed to re-authenticate due to a homeserver problem": "Fekk ikkje til å re-authentisere grunna ein feil på heimeserveren", + "Failed to re-authenticate": "Fekk ikkje til å re-autentisere", + "Regain access to your account and recover encryption keys stored on this device. Without them, you won’t be able to read all of your secure messages on any device.": "Ta tilbake tilgang til din konto og gjenopprett krypteringsnøklar lagra på denne eininga. Du kan ikkje lese krypterte meldingar viss du ikkje har desse.", + "Enter your password to sign in and regain access to your account.": "Skriv inn ditt passord for å logge på og ta tilbake tilgang til kontoen din.", + "Forgotten your password?": "Gløymt passord ?", + "Sign in and regain access to your account.": "Logg på og ta tilbake tilgang til din konto.", + "You cannot sign in to your account. Please contact your homeserver admin for more information.": "Du har ikkje muligheit til å logge på kontoen din. Kontakt systemadministratoren for meir informasjon.", + "You're signed out": "Du er no avlogga", + "Clear personal data": "Fjern personlege data", + "Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.": "Åtvaring: Dine personelge data (inklusive krypteringsnøklar) er fortsatt lagra på denne eininga. Fjern dette viss du ikkje skal bruke kontoen på denne eininga, eller logge på med ein annan konto.", + "Great! This passphrase looks strong enough.": "Flott! Denne passfrasen virkar til å vere sterk nok.", + "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Vi vil lagre ein kryptert kopi av nøklane dine på vår server. Vern sikkerheitskopien din med ein passfrase.", + "For maximum security, this should be different from your account password.": "For maksimal sikkerheit, bør dette vere noko anna enn kontopassordet ditt.", + "Enter a passphrase...": "Skriv inn ein passfrase...", + "Set up with a Recovery Key": "Sett opp med ein gjenopprettingsnøkkel", + "That matches!": "Dette stemmer!", + "That doesn't match.": "Dette stemmer ikkje.", + "Go back to set it again.": "Gå tilbake for å sette den på nytt.", + "Please enter your passphrase a second time to confirm.": "Skriv inn passfrasen din ein gong til for å stadfeste.", + "Repeat your passphrase...": "Gjenta din passfrase...", + "As a safety net, you can use it to restore your encrypted message history if you forget your Recovery Passphrase.": "Som eit sikkerheitsnett, kan du bruke den til å gjenopprette den krypterte meldingshistorikken i tilfelle du gløymer gjennopprettingsnøkkelen.", + "As a safety net, you can use it to restore your encrypted message history.": "Som eit sikkerheitsnett, kan du bruke den til å gjenopprette den krypterte meldingshistorikken.", + "Your recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.": "Gjennopprettingsnøkkelen eit sikkerheitsnett, kan du bruke den til å gjenopprette den krypterte meldingshistorikken i tilfelle du gløymer passfrasen.", + "Keep your recovery key somewhere very secure, like a password manager (or a safe)": "Ta vare på gjenopprettingsnøkkelen din på ein trygg plass, som for eksempel i eit passordhandteringsprogram (eller ein safe)", + "Your Recovery Key": "Gjenopprettingsnøkkelen din", + "Copy to clipboard": "Kopier til utklippstavle", + "Download": "Last ned", + "Your Recovery Key has been copied to your clipboard, paste it to:": "Gjenopprettingsnøkkelen din har blitt kopiert til utklippstavla, lim den til:", + "Your Recovery Key is in your Downloads folder.": "Gjenopprettingsnøkkelen ligg i Nedlastnings-mappa.", + "Print it and store it somewhere safe": "Skriv ut og ta vare på den på ein trygg stad", + "Save it on a USB key or backup drive": "Lagre til ein USB-pinne eller sikkerheitskopidisk", + "Copy it to your personal cloud storage": "Kopier den til personleg skylagring", + "Your keys are being backed up (the first backup could take a few minutes).": "Nøklane dine blir sikkerheitskopiert (den første kopieringa kan ta nokre minutt).", + "Okay": "Ok", + "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another device.": "Loggar du av eller brukar ein annan einheit, vil du ikkje ha mulegheit til å hente fram historikk for krypterte meldingar med mindre sikker gjenoppretting for meldingar (Secure Message Recovery) er satt opp.", + "Set up Secure Message Recovery": "Sett opp sikker gjenoppretting for meldingar", + "Secure your backup with a passphrase": "Gjer sikkerheitskopien trygg med ein passfrase", + "Confirm your passphrase": "Stadfest passfrasen din", + "Recovery key": "Gjenopprettingsnøkkel", + "Keep it safe": "Lagre den trygt", + "Starting backup...": "Startar sikkerheitskopiering...", + "Success!": "Suksess!", + "Create Key Backup": "Lag sikkerheitskopi av nøkkel", + "Unable to create key backup": "Klarte ikkje å lage sikkerheitskopi av nøkkelen", + "Retry": "Prøv om att", + "Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Utan å sette opp sikker gjenoppretting for meldingar (Secure Message Recovery) vil meldingshistorikken gå tapt når du loggar av.", + "If you don't want to set this up now, you can later in Settings.": "Ønskjer du ikkje å sette opp dette no, kan du gjere det seinare i innstillingane.", + "Set up": "Sett opp", + "Don't ask again": "Ikkje spør igjen", + "New Recovery Method": "Ny gjenopprettingsmetode", + "A new recovery passphrase and key for Secure Messages have been detected.": "Ein ny gjenopprettingspassfrase og nøkkel for sikre meldingar vart funne.", + "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.": "Har du ikkje satt opp den nye gjenopprettingsmetoden, kan ein angripar prøve å bryte seg inn på kontoen din. Endre ditt kontopassord og sett opp gjenoppretting umiddelbart under instillingane.", + "This device is encrypting history using the new recovery method.": "Denne eininga krypterer meldingshistorikken med den nye gjenopprettingsmetoden.", + "Go to Settings": "Gå til innstillingar", + "Set up Secure Messages": "Sett opp sikre meldingar (Secure Messages)", + "Recovery Method Removed": "Gjenopprettingsmetode fjerna", + "This device has detected that your recovery passphrase and key for Secure Messages have been removed.": "Denne eininga har oppdaga at gjenopprettingspassfrasen og nøkkelen for sikre meldingar er fjerna.", + "If you did this accidentally, you can setup Secure Messages on this device which will re-encrypt this device's message history with a new recovery method.": "Gjorde du dette ved eit uhell, kan du sette opp sikre meldingar på denne eininga. Dette vil kryptere all meldingshistorikk med ein ny gjenopprettingsmetode.", + "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.": "Viss du ikkje fjerna gjenopprettingsmetoden, kan ein angripar prøve å bryte seg inn på kontoen din. Endre kontopassordet ditt og sett ein opp ein ny gjenopprettingsmetode umidellbart under Innstillingar." } From 77a61519efe80b85e27480fd4d8ea9942f17dbf7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 30 Sep 2019 11:15:46 +0200 Subject: [PATCH 19/55] also allow commands to be pill-candidates --- src/components/views/rooms/SendMessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 7987949524..3d81631309 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -182,7 +182,7 @@ export default class SendMessageComposer extends React.Component { // be extra resilient when somehow the AutocompleteWrapperModel or // CommandPartCreator fails to insert a command part, so we don't send // a command as a message - if (firstPart.type === "plain" && firstPart.text.startsWith("/")) { + if (firstPart.text.startsWith("/") && (firstPart.type === "plain" || firstPart.type === "pill-candidate")) { return true; } } From f160a308b4b408862c6b919f3b6500b6c5d114ef Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2019 14:04:39 +0100 Subject: [PATCH 20/55] Make Autocomplete more accessible to screen reader users Use ARIA to: + notate that the composer has an autocomplete + notate the open/closed state of the autocomplete + notate the name of the open autocomplete options + notate the ID of the highlighted autocomplete option + improve naming of emoji autocomplete options for screen readers Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/autocomplete/CommunityProvider.js | 12 +++++++++--- src/autocomplete/Components.js | 2 +- src/autocomplete/EmojiProvider.js | 12 ++++++++---- src/autocomplete/NotifProvider.js | 12 +++++++++--- src/autocomplete/RoomProvider.js | 12 +++++++++--- src/autocomplete/UserProvider.js | 2 +- src/components/views/rooms/Autocomplete.js | 18 +++++++++--------- .../views/rooms/BasicMessageComposer.js | 15 ++++++++++++--- src/i18n/strings/en_EN.json | 5 +++++ 9 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/autocomplete/CommunityProvider.js b/src/autocomplete/CommunityProvider.js index ffce1e71cf..992df0f773 100644 --- a/src/autocomplete/CommunityProvider.js +++ b/src/autocomplete/CommunityProvider.js @@ -105,8 +105,14 @@ export default class CommunityProvider extends AutocompleteProvider { } renderCompletions(completions: [React.Component]): ?React.Component { - return
- { completions } -
; + return ( +
+ { completions } +
+ ); } } diff --git a/src/autocomplete/Components.js b/src/autocomplete/Components.js index b09f4e963e..ca105bb211 100644 --- a/src/autocomplete/Components.js +++ b/src/autocomplete/Components.js @@ -60,7 +60,7 @@ export class PillCompletion extends React.Component { ...restProps } = this.props; return ( -
+
{ initialComponent } { title } { subtitle } diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 8afcba6ab0..1e39593022 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -116,7 +116,9 @@ export default class EmojiProvider extends AutocompleteProvider { return { completion: unicode, component: ( - { unicode }} /> + { unicode } + } /> ), range, }; @@ -130,8 +132,10 @@ export default class EmojiProvider extends AutocompleteProvider { } renderCompletions(completions: [React.Component]): ?React.Component { - return
- { completions } -
; + return ( +
+ { completions } +
+ ); } } diff --git a/src/autocomplete/NotifProvider.js b/src/autocomplete/NotifProvider.js index 60a3352f9b..95cfb34616 100644 --- a/src/autocomplete/NotifProvider.js +++ b/src/autocomplete/NotifProvider.js @@ -58,8 +58,14 @@ export default class NotifProvider extends AutocompleteProvider { } renderCompletions(completions: [React.Component]): ?React.Component { - return
- { completions } -
; + return ( +
+ { completions } +
+ ); } } diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index b94edf590c..79986657b8 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -109,8 +109,14 @@ export default class RoomProvider extends AutocompleteProvider { } renderCompletions(completions: [React.Component]): ?React.Component { - return
- { completions } -
; + return ( +
+ { completions } +
+ ); } } diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 62ae5d4970..edba6d4b03 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -164,7 +164,7 @@ export default class UserProvider extends AutocompleteProvider { } renderCompletions(completions: [React.Component]): ?React.Component { - return
+ return
{ completions }
; } diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index 243cfe2f75..ad5fa198a3 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -20,18 +20,17 @@ import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import flatMap from 'lodash/flatMap'; -import isEqual from 'lodash/isEqual'; -import sdk from '../../../index'; import type {Completion} from '../../../autocomplete/Autocompleter'; import Promise from 'bluebird'; import { Room } from 'matrix-js-sdk'; -import {getCompletions} from '../../../autocomplete/Autocompleter'; import SettingsStore from "../../../settings/SettingsStore"; import Autocompleter from '../../../autocomplete/Autocompleter'; const COMPOSER_SELECTED = 0; +export const generateCompletionDomId = (number) => `mx_Autocomplete_Completion_${number}`; + export default class Autocomplete extends React.Component { constructor(props) { super(props); @@ -224,7 +223,7 @@ export default class Autocomplete extends React.Component { setSelection(selectionOffset: number) { this.setState({selectionOffset, hide: false}); if (this.props.onSelectionChange) { - this.props.onSelectionChange(this.state.completionList[selectionOffset - 1]); + this.props.onSelectionChange(this.state.completionList[selectionOffset - 1], selectionOffset - 1); } } @@ -250,9 +249,8 @@ export default class Autocomplete extends React.Component { let position = 1; const renderedCompletions = this.state.completions.map((completionResult, i) => { const completions = completionResult.completions.map((completion, i) => { - const className = classNames('mx_Autocomplete_Completion', { - 'selected': position === this.state.selectionOffset, - }); + const selected = position === this.state.selectionOffset; + const className = classNames('mx_Autocomplete_Completion', {selected}); const componentPosition = position; position++; @@ -261,10 +259,12 @@ export default class Autocomplete extends React.Component { }; return React.cloneElement(completion.component, { - key: i, - ref: `completion${position - 1}`, + "key": i, + "ref": `completion${componentPosition}`, + "id": generateCompletionDomId(componentPosition - 1), // 0 index the completion IDs className, onClick, + "aria-selected": selected, }); }); diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 895696e118..4ec43d8af2 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -28,7 +28,7 @@ import { replaceRangeAndMoveCaret, } from '../../../editor/operations'; import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom'; -import Autocomplete from '../rooms/Autocomplete'; +import Autocomplete, {generateCompletionDomId} from '../rooms/Autocomplete'; import {autoCompleteCreator} from '../../../editor/parts'; import {parsePlainTextMessage} from '../../../editor/deserialize'; import {renderModel} from '../../../editor/render'; @@ -432,8 +432,9 @@ export default class BasicMessageEditor extends React.Component { this.props.model.autoComplete.onComponentConfirm(completion); } - _onAutoCompleteSelectionChange = (completion) => { + _onAutoCompleteSelectionChange = (completion, completionIndex) => { this.props.model.autoComplete.onComponentSelectionChange(completion); + this.setState({completionIndex}); } componentWillUnmount() { @@ -535,6 +536,8 @@ export default class BasicMessageEditor extends React.Component { quote: ctrlShortcutLabel(">"), }; + const {completionIndex} = this.state; + return (
{ autoComplete } this._formatBarRef = ref} onAction={this._onFormatAction} shortcuts={shortcuts} /> @@ -548,7 +551,13 @@ export default class BasicMessageEditor extends React.Component { onKeyDown={this._onKeyDown} ref={ref => this._editorRef = ref} aria-label={this.props.label} - >
+ role="textbox" + aria-multiline="true" + aria-autocomplete="both" + aria-haspopup="listbox" + aria-expanded={Boolean(this.state.autoComplete)} + aria-activedescendant={completionIndex >= 0 ? generateCompletionDomId(completionIndex) : undefined} + />
); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c23cd6d324..9e8a0fec5c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1726,11 +1726,16 @@ "Clear personal data": "Clear personal data", "Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.": "Warning: Your personal data (including encryption keys) is still stored on this device. Clear it if you're finished using this device, or want to sign in to another account.", "Commands": "Commands", + "Community Autocomplete": "Community Autocomplete", "Results from DuckDuckGo": "Results from DuckDuckGo", "Emoji": "Emoji", + "Emoji Autocomplete": "Emoji Autocomplete", "Notify the whole room": "Notify the whole room", "Room Notification": "Room Notification", + "Notification Autocomplete": "Notification Autocomplete", + "Room Autocomplete": "Room Autocomplete", "Users": "Users", + "User Autocomplete": "User Autocomplete", "unknown device": "unknown device", "NOT verified": "NOT verified", "Blacklisted": "Blacklisted", From f299f7e092170dc054cb94cc6f0124ca29d928ec Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2019 14:32:42 +0100 Subject: [PATCH 21/55] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/autocomplete/UserProvider.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index edba6d4b03..451ae0bb83 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -164,9 +164,11 @@ export default class UserProvider extends AutocompleteProvider { } renderCompletions(completions: [React.Component]): ?React.Component { - return
- { completions } -
; + return ( +
+ { completions } +
+ ); } shouldForceComplete(): boolean { From e7fdc5002ec428e82bacfda0a8f9dd1702805f05 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 30 Sep 2019 15:40:37 +0100 Subject: [PATCH 22/55] Guard against falsy names in getInitialLetter This ensures we check for a falsy name in `getInitialLetter` instead of throwing errors. We should perhaps also fix whatever other issues have led to the input being undefined in the first place, but for now we leave this for another day. Hopefully helps with https://github.com/vector-im/riot-web/issues/10983 --- src/Avatar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avatar.js b/src/Avatar.js index 2e15874b4e..23e5fab9fb 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -67,7 +67,7 @@ module.exports = { * @return {string} the first letter */ getInitialLetter(name) { - if (name.length < 1) { + if (!name || name.length < 1) { return undefined; } From 762e0111fb188dbe39988943a0cdc231c6677560 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 30 Sep 2019 16:00:58 +0100 Subject: [PATCH 23/55] Add trace as well --- src/Avatar.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avatar.js b/src/Avatar.js index 23e5fab9fb..17860698cb 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -67,7 +67,12 @@ module.exports = { * @return {string} the first letter */ getInitialLetter(name) { - if (!name || name.length < 1) { + if (!name) { + // XXX: We should find out what causes the name to sometimes be falsy. + console.trace("`name` argument to `getInitialLetter` not supplied"); + return undefined; + } + if (name.length < 1) { return undefined; } From 22fb9257430e5a8bee44c21af1c35a217efd620b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2019 16:04:06 +0100 Subject: [PATCH 24/55] Stop using deprecated KeyboardEvent properties Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/Keyboard.js | 12 +++++++++++- src/components/structures/LoggedInView.js | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Keyboard.js b/src/Keyboard.js index fb7d692ce3..c7de04f454 100644 --- a/src/Keyboard.js +++ b/src/Keyboard.js @@ -58,7 +58,17 @@ export const KeyCode = { KEY_X: 88, KEY_Y: 89, KEY_Z: 90, - KEY_BACKTICK: 223, + KEY_BACKTICK: 223, // DO NOT USE THIS: browsers disagree on backtick 192 vs 223 +}; + +export const Key = { + HOME: "Home", + End: "End", + PAGE_UP: "PageUp", + PAGE_DOWN: "PageDown", + BACKTICK: "`", + + K: "k", }; export function isOnlyCtrlOrCmdKeyEvent(ev) { diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index bcbf9f8155..5529fb8f32 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -22,7 +22,7 @@ import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import { DragDropContext } from 'react-beautiful-dnd'; -import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; +import { Key, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; import PageTypes from '../../PageTypes'; import CallMediaHandler from '../../CallMediaHandler'; import { fixupColorFonts } from '../../utils/FontManager'; @@ -353,23 +353,23 @@ const LoggedInView = createReactClass({ const hasModifier = ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.key === "Alt" || ev.key === "Control" || ev.key === "Meta" || ev.key === "Shift"; - switch (ev.keyCode) { - case KeyCode.PAGE_UP: - case KeyCode.PAGE_DOWN: + switch (ev.key) { + case Key.PAGE_UP: + case Key.PAGE_DOWN: if (!hasModifier) { this._onScrollKeyPressed(ev); handled = true; } break; - case KeyCode.HOME: - case KeyCode.END: + case Key.HOME: + case Key.END: if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { this._onScrollKeyPressed(ev); handled = true; } break; - case KeyCode.KEY_K: + case Key.K: if (ctrlCmdOnly) { dis.dispatch({ action: 'focus_room_filter', @@ -377,7 +377,9 @@ const LoggedInView = createReactClass({ handled = true; } break; - case KeyCode.KEY_BACKTICK: + case Key.BACKTICK: + if (ev.key !== "`") break; + // Ideally this would be CTRL+P for "Profile", but that's // taken by the print dialog. CTRL+I for "Information" // was previously chosen but conflicted with italics in From 2621ad1b43cf4b6129d3611eaadc063518d8a225 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Sep 2019 16:04:43 +0100 Subject: [PATCH 25/55] Group room tiles in room sub list in the room list for ARIA Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomSubList.js | 8 ++++---- src/components/views/elements/AccessibleButton.js | 2 +- src/components/views/rooms/RoomList.js | 2 +- src/components/views/rooms/RoomTile.js | 15 +++++++++++++++ src/i18n/strings/en_EN.json | 3 +++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 5a90beb06f..f1057819ff 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -307,11 +307,11 @@ const RoomSubList = createReactClass({ }); if (isCollapsed) { - return
+ return
{this._getHeaderJsx(isCollapsed)}
; } else if (this._canUseLazyListRendering()) { - return
+ return
{this._getHeaderJsx(isCollapsed)} this.makeRoomTile(r)); const tiles = roomTiles.concat(this.props.extraTiles); - return
+ return
{this._getHeaderJsx(isCollapsed)} { tiles } @@ -340,7 +340,7 @@ const RoomSubList = createReactClass({ } return ( -
+
{ this._getHeaderJsx(isCollapsed) } { content }
diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js index a43e2ff26a..bfc3e45246 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.js @@ -68,7 +68,7 @@ export default function AccessibleButton(props) { delete restProps.inputRef; restProps.tabIndex = restProps.tabIndex || "0"; - restProps.role = "button"; + restProps.role = restProps.role || "button"; restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton"; if (kind) { diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index da2d11f34b..6c031563cd 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -805,7 +805,7 @@ module.exports = createReactClass({ const subListComponents = this._mapSubListProps(subLists); return ( -
{ subListComponents }
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 2ec5384b93..a7ba744e47 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -33,6 +33,7 @@ import AccessibleButton from '../elements/AccessibleButton'; import ActiveRoomObserver from '../../../ActiveRoomObserver'; import RoomViewStore from '../../../stores/RoomViewStore'; import SettingsStore from "../../../settings/SettingsStore"; +import {_t} from "../../../languageHandler"; module.exports = createReactClass({ displayName: 'RoomTile', @@ -368,6 +369,8 @@ module.exports = createReactClass({ const RoomAvatar = sdk.getComponent('avatars.RoomAvatar'); + let ariaLabel = name; + let dmIndicator; if (this._isDirectMessageRoom(this.props.room.roomId)) { dmIndicator = ; } + if (notifBadges && mentionBadges && !isInvite) { + ariaLabel += " " + _t("It has %(count)s unread messages including mentions.", { + count: notificationCount, + }); + } else if (notifBadges) { + ariaLabel += " " + _t("It has %(count)s unread messages.", { count: notificationCount }); + } else if (mentionBadges && !isInvite) { + ariaLabel += " " + _t("It has unread mentions."); + } + return
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c23cd6d324..a03b654a3e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -949,6 +949,9 @@ "Securely back up your keys to avoid losing them. Learn more.": "Securely back up your keys to avoid losing them. Learn more.", "Not now": "Not now", "Don't ask me again": "Don't ask me again", + "It has %(count)s unread messages including mentions.|other": "It has %(count)s unread messages including mentions.", + "It has %(count)s unread messages.|other": "It has %(count)s unread messages.", + "It has unread mentions.": "It has unread mentions.", "Add a topic": "Add a topic", "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.", "This room has already been upgraded.": "This room has already been upgraded.", From 5545012b28b9aa44f175c127ee499a1ccd4f3cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sava=20Rado=C5=A1?= Date: Mon, 30 Sep 2019 14:42:49 +0000 Subject: [PATCH 26/55] Translated using Weblate (Serbian) Currently translated at 56.3% (1021 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sr/ --- src/i18n/strings/sr.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sr.json b/src/i18n/strings/sr.json index ed14ab7d51..6ad74f823d 100644 --- a/src/i18n/strings/sr.json +++ b/src/i18n/strings/sr.json @@ -1256,5 +1256,10 @@ "Username": "Корисничко име", "You need to enter a username.": "Морате унети корисничко име.", "Not sure of your password? Set a new one": "Не сећате се лозинке? Поставите нову", - "Are you sure you want to sign out?": "Да ли сте сигурни да се желите одјавити?" + "Are you sure you want to sign out?": "Да ли сте сигурни да се желите одјавити?", + "Call failed due to misconfigured server": "Позив није успео због погрешно подешеног сервера", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Замолите администратора вашег сервера (%(homeserverDomain)s) да подеси TURN сервер како би позиви радили поуздано.", + "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", + "A conference call could not be started because the integrations server is not available": "Конференцијски позив није могао бити започет јер интеграциони сервер није расположив" } From 53d0c89e9c82a31dac2d48a5eb0d19e6c277d27c Mon Sep 17 00:00:00 2001 From: Diego Esaa Date: Mon, 30 Sep 2019 14:49:15 +0000 Subject: [PATCH 27/55] Translated using Weblate (Spanish) Currently translated at 74.5% (1350 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/es/ --- src/i18n/strings/es.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index be742727d8..9e189355d8 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -1588,5 +1588,25 @@ "Scissors": "Tijeras", "No integrations server configured": "No se ha configurado servidor de integraciones", "This Riot instance does not have an integrations server configured.": "Esta instancia de Riot no tiene servidor de integraciones configurado.", - "Connecting to integrations server...": "Conectando al servidor de integraciones..." + "Connecting to integrations server...": "Conectando al servidor de integraciones...", + "Call failed due to misconfigured server": "Llamada fallida debido a la mala configuración del servidor", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Por favor pídele al administrador de tu servidor doméstico (%(homeserverDomain)s) que configure un servidor TURN para que las llamadas funcionen correctamente.", + "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.": "Alternativamente, puedes tratar de usar el servidor público en turn.matrix.org, pero éste no será igual de confiable, y compartirá tu dirección IP con ese servidor. También puedes administrar esto en Ajustes.", + "Try using turn.matrix.org": "Trata de usar turn.matrix.org", + "Failed to start chat": "Error al iniciar el chat", + "Messages": "Mensajes", + "Actions": "Acciones", + "Other": "Otros", + "Sends a message as plain text, without interpreting it as markdown": "Envía un mensaje como texto estándar, sin interpretarlo como Markdown", + "You do not have the required permissions to use this command.": "No tienes los permisos requeridos para usar este comando.", + "Changes the avatar of the current room": "Cambia el ávatar de la sala actual", + "Use an identity server": "Usar un servidor de identidad", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "Usar un servidor de identidad para invitar por correo. Presiona continuar par usar el servidor de identidad por defecto (%(defaultIdentityServerName)s) o adminístralo en Ajustes.", + "Use an identity server to invite by email. Manage in Settings.": "Usar un servidor de identidad para invitar por correo. Administrar en Ajustes.", + "Adds a custom widget by URL to the room": "Añade un widget personalizado por URL a la sala", + "Please supply a https:// or http:// widget URL": "Por favor provisiona un URL de widget de http:// o https://", + "You cannot modify widgets in this room.": "No puedes modificar widgets en esta sala.", + "Displays list of commands with usages and descriptions": "Muestra lista de comandos con usos y descripciones", + "Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Usar el compositor nuevo y más rapido para escribir mensajes, pero todavía experimental (requiere que refresques la página)", + "Multiple integration managers": "Administradores de integración múltiples" } From c8a5f9459c35dcf5d6531d601a4b5ca34b8803bb Mon Sep 17 00:00:00 2001 From: pebles Date: Mon, 30 Sep 2019 14:54:02 +0000 Subject: [PATCH 28/55] Translated using Weblate (Spanish) Currently translated at 74.5% (1350 of 1813 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/es/ --- src/i18n/strings/es.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 9e189355d8..042915cf88 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -1608,5 +1608,6 @@ "You cannot modify widgets in this room.": "No puedes modificar widgets en esta sala.", "Displays list of commands with usages and descriptions": "Muestra lista de comandos con usos y descripciones", "Use the new, faster, but still experimental composer for writing messages (requires refresh)": "Usar el compositor nuevo y más rapido para escribir mensajes, pero todavía experimental (requiere que refresques la página)", - "Multiple integration managers": "Administradores de integración múltiples" + "Multiple integration managers": "Administradores de integración múltiples", + "Room upgrade confirmation": "Confirmación de actualización de sala" } From 8b622dbd53c31cd7d9750d85a245baf4dbc68b05 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 30 Sep 2019 17:37:30 +0100 Subject: [PATCH 29/55] Only show service summary once --- src/components/views/dialogs/TermsDialog.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/TermsDialog.js b/src/components/views/dialogs/TermsDialog.js index 1e706ff1a7..6fd818c97c 100644 --- a/src/components/views/dialogs/TermsDialog.js +++ b/src/components/views/dialogs/TermsDialog.js @@ -124,12 +124,13 @@ export default class TermsDialog extends React.PureComponent { const termDoc = policyValues[i]; const termsLang = pickBestLanguage(Object.keys(termDoc).filter((k) => k !== 'version')); let serviceName; + let summary; if (i === 0) { serviceName = this._nameForServiceType(policiesAndService.service.serviceType, parsedBaseUrl.host); + summary = this._summaryForServiceType( + policiesAndService.service.serviceType, + ); } - const summary = this._summaryForServiceType( - policiesAndService.service.serviceType, - ); rows.push(
{serviceName}